Compare commits

...
43 Commits
Author SHA1 Message Date
Peter Dillinger a66daec541 Update HISTORY.md and version for 8.10.2 2024-02-16 12:09:07 -08:00
Adam Retter 537ea661cb Update ZLib to 1.3.1 (#12358)
Summary:
pdillinger This fixes the RocksJava build, is also needed in the 8.10.fb and 8.11.fb branches please?

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12358

Reviewed By: jaykorean

Differential Revision: D53859743

Pulled By: pdillinger

fbshipit-source-id: b8417fccfee931591805f9aecdfae7c086fee708
2024-02-16 12:08:11 -08:00
akankshamahajan 29219c208c Update HISTORY.md and version for 8.10.1
Summary:

Test Plan:

Reviewers:

Subscribers:

Tasks:

Tags:
2024-01-16 15:54:18 -08:00
akankshamahajan 15a6520154 Fix bug in auto_readahead_size that returned wrong key (#12229)
Summary:
IndexType::kBinarySearchWithFirstKey +
BlockCacheLookupForReadAheadSize enabled => FindNextUserEntryInternal assertion fails or iterator lands at a wrong key because BlockCacheLookupForReadAheadSize moves the index_iter_ and in internal_wrapper.h, result_.key didn't update and pointed to wrong key. Also ikey_ was also pointing to iter_.key() instead of copying the key.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12229

Test Plan:
```
 rm -rf /dev/shm/rocksdb_test/rocksdb_crashtest_blackbox_alt3 /dev/shm/rocksdb_test/rocksdb_crashtest_expected_alt3
mkdir /dev/shm/rocksdb_test/rocksdb_crashtest_blackbox_alt3 /dev/shm/rocksdb_test/rocksdb_crashtest_expected_alt3
./db_stress -threads=1 --acquire_snapshot_one_in=0 --adaptive_readahead=0 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_setting_blob_options_dynamically=0 --async_io=0 --auto_readahead_size=1 --avoid_flush_during_recovery=0 --avoid_unnecessary_blocking_io=1 --backup_max_size=0 --backup_one_in=0 --batch_protection_bytes_per_key=0 --blob_cache_size=0 --blob_compaction_readahead_size=0 --blob_compression_type=lz4 --blob_file_size=0 --blob_file_starting_level=0 --blob_garbage_collection_age_cutoff=0 --blob_garbage_collection_force_threshold=0 --block_protection_bytes_per_key=0 --block_size=2048 --bloom_before_level=2147483646 --bloom_bits=15 --bottommost_compression_type=snappy --bottommost_file_compaction_delay=0 --bytes_per_sync=0 --cache_index_and_filter_blocks=0 --cache_size=8388608 --cache_type=lru_cache --charge_compression_dictionary_building_buffer=0 --charge_file_metadata=0 --charge_filter_construction=0 --charge_table_reader=0 --checkpoint_one_in=0 --checksum_type=kCRC32c --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=0 --compact_range_one_in=0 --compaction_pri=1 --compaction_readahead_size=0 --compaction_ttl=0 --compressed_secondary_cache_size=0 --compression_checksum=0 --compression_max_dict_buffer_bytes=511 --compression_max_dict_bytes=16384 --compression_parallel_threads=1 --compression_type=none --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --data_block_index_type=1 --db=/dev/shm/rocksdb_test/rocksdb_crashtest_blackbox_alt3 --db_write_buffer_size=0 --delpercent=0 --delrangepercent=0 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_wal=0 --enable_blob_files=0 --enable_blob_garbage_collection=0 --enable_compaction_filter=0 --enable_pipelined_write=0 --enable_thread_tracking=1 --expected_values_dir=/dev/shm/rocksdb_test/rocksdb_crashtest_expected_alt3 --fail_if_options_file_error=1 --fifo_allow_compaction=0 --file_checksum_impl=crc32c --flush_one_in=1000000 --format_version=3 --get_current_wal_file_one_in=0 --get_live_files_one_in=0 --get_property_one_in=0 --get_sorted_wal_files_one_in=0 --index_block_restart_interval=13 --index_type=3 --ingest_external_file_one_in=10 --initial_auto_readahead_size=0 --iterpercent=55 --key_len_percent_dist=1,30,69 --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=0 --long_running_snapshots=0 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=0 --max_auto_readahead_size=0 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=100000 --max_key_len=3 --max_manifest_file_size=1073741824 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=4194304 --memtable_max_range_deletions=1000 --memtable_prefix_bloom_size_ratio=0.5 --memtable_protection_bytes_per_key=0 --memtable_whole_key_filtering=0 --memtablerep=skip_list --min_blob_size=8 --min_write_buffer_number_to_merge=2 --mmap_read=0 --mock_direct_io=False --nooverwritepercent=1 --num_file_reads_for_auto_readahead=2 --open_files=-1 --open_metadata_write_fault_one_in=0 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --ops_per_thread=10000000 --optimize_filters_for_memory=0 --paranoid_file_checks=0 --partition_filters=0 --partition_pinning=0 --pause_background_one_in=0 --periodic_compaction_seconds=0 --prefix_size=1 --prefixpercent=0 --prepopulate_block_cache=0 --preserve_internal_time_seconds=0 --progress_reports=0 --read_fault_one_in=0 --readahead_size=1 --readpercent=45 --recycle_log_file_num=1 --reopen=0 --secondary_cache_fault_one_in=0 --secondary_cache_uri= --set_options_one_in=0 --snapshot_hold_ops=0 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=600 --subcompactions=1 --sync=0 --sync_fault_injection=0 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=0 --unpartitioned_pinning=0 --use_blob_cache=0 --use_direct_io_for_flush_and_compaction=0 --use_direct_reads=0 --use_full_merge_v1=0 --use_get_entity=0 --use_merge=0 --use_multi_get_entity=0 --use_multiget=0 --use_put_entity_one_in=0 --use_shared_block_and_blob_cache=0 --use_write_buffer_manager=0 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=0 --verify_checksum_one_in=0 --verify_db_one_in=0 --verify_file_checksums_one_in=0 --verify_iterator_with_expected_state_one_in=1 --verify_sst_unique_id_in_manifest=0 --wal_bytes_per_sync=0 --wal_compression=none --write_buffer_size=33554432 --write_dbid_to_manifest=0 --write_fault_one_in=0 --writepercent=0 > repro.out
Verification failed. Expected state has key 0000000000000077000000000000004178, iterator is at key 0000000000000077000000000000008A78
Column family: default, op_logs: S 0000000000000077000000000000003D7878787878 NNNN
No writes or ops?
Verification failed :(
```

Reviewed By: ajkr

Differential Revision: D52710655

Pulled By: akankshamahajan15

fbshipit-source-id: 9d2e684e190fb0832bdce3337bce1c6548cd054d
2024-01-16 15:47:09 -08:00
Levi Tamasi 54d6286027 Update HISTORY.md for 8.10 2023-12-15 13:01:14 -08:00
anand76 cc069f25b3 Add some compressed and tiered secondary cache stats (#12150)
Summary:
Add statistics for more visibility.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12150

Reviewed By: akankshamahajan15

Differential Revision: D52184633

Pulled By: anand1976

fbshipit-source-id: 9969e05d65223811cd12627102b020bb6d229352
2023-12-15 11:34:08 -08:00
Peter Dillinger 88bc91f3cc Cap eviction effort (CPU under stress) in HyperClockCache (#12141)
Summary:
HyperClockCache is intended to mitigate performance problems under stress conditions (as well as optimizing average-case parallel performance). In LRUCache, the biggest such problem is lock contention when one or a small number of cache entries becomes particularly hot. Regardless of cache sharding, accesses to any particular cache entry are linearized against a single mutex, which is held while each access updates the LRU list.  All HCC variants are fully lock/wait-free for accessing blocks already in the cache, which fully mitigates this contention problem.

However, HCC (and CLOCK in general) can exhibit extremely degraded performance under a different stress condition: when no (or almost no) entries in a cache shard are evictable (they are pinned). Unlike LRU which can find any evictable entries immediately (at the cost of more coordination / synchronization on each access), CLOCK has to search for evictable entries. Under the right conditions (almost exclusively MB-scale caches not GB-scale), the CPU cost of each cache miss could fall off a cliff and bog down the whole system.

To effectively mitigate this problem (IMHO), I'm introducing a new default behavior and tuning parameter for HCC, `eviction_effort_cap`. See the comments on the new config parameter in the public API.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12141

Test Plan:
unit test included

 ## Performance test

We can use cache_bench to validate no regression (CPU and memory) in normal operation, and to measure change in behavior when cache is almost entirely pinned. (TODO: I'm not sure why I had to get the pinned ratio parameter well over 1.0 to see truly bad performance, but the behavior is there.) Build with `make DEBUG_LEVEL=0 USE_CLANG=1 PORTABLE=0 cache_bench`. We also set MALLOC_CONF="narenas:1" for all these runs to essentially remove jemalloc variances from the results, so that the max RSS given by /usr/bin/time is essentially ideal (assuming the allocator minimizes fragmentation and other memory overheads well). Base command reproducing bad behavior:

```
./cache_bench -cache_type=auto_hyper_clock_cache -threads=12 -histograms=0 -pinned_ratio=1.7
```

```
Before, LRU (alternate baseline not exhibiting bad behavior):
Rough parallel ops/sec = 2290997
1088060 maxresident

Before, AutoHCC (bad behavior):
Rough parallel ops/sec = 141011 <- Yes, more than 10x slower
1083932 maxresident
```

Now let us sample a range of values in the solution space:

```
After, AutoHCC, eviction_effort_cap = 1:
Rough parallel ops/sec = 3212586
2402216 maxresident

After, AutoHCC, eviction_effort_cap = 10:
Rough parallel ops/sec = 2371639
1248884 maxresident

After, AutoHCC, eviction_effort_cap = 30:
Rough parallel ops/sec = 1981092
1131596 maxresident

After, AutoHCC, eviction_effort_cap = 100:
Rough parallel ops/sec = 1446188
1090976 maxresident

After, AutoHCC, eviction_effort_cap = 1000:
Rough parallel ops/sec = 549568
1084064 maxresident
```

I looks like `cap=30` is a sweet spot balancing acceptable CPU and memory overheads, so is chosen as the default.

```
Change to -pinned_ratio=0.85
Before, LRU:
Rough parallel ops/sec = 2108373
1078232 maxresident

Before, AutoHCC, averaged over ~20 runs:
Rough parallel ops/sec = 2164910
1077312 maxresident

After, AutoHCC, eviction_effort_cap = 30, averaged over ~20 runs:
Rough parallel ops/sec = 2145542
1077216 maxresident
```

The slight CPU improvement above is consistent with the cap, with no measurable memory overhead under moderate stress.

```
Change to -pinned_ratio=0.25 (low stress)
Before, AutoHCC, averaged over ~20 runs:
Rough parallel ops/sec = 2221149
1076540 maxresident

After, AutoHCC, eviction_effort_cap = 30, averaged over ~20 runs:
Rough parallel ops/sec = 2224521
1076664 maxresident
```

No measurable difference under normal circumstances.

Some tests repeated with FixedHCC, with similar results.

Reviewed By: anand1976

Differential Revision: D52174755

Pulled By: pdillinger

fbshipit-source-id: d278108031b1220c1fa4c89c5a9d34b7cf4ef1b8
2023-12-14 22:13:32 -08:00
Akanksha Mahajan cd577f6059 Fix WRITE_STALL start_time (#12147)
Summary:
`Delayed` is set true in two cases. One is when `delay` is specified. Other one is in the `while` loop - https://github.com/facebook/rocksdb/blob/cd21e4e69d76ec4ec3b080c8cdae016ac2309cc5/db/db_impl/db_impl_write.cc#L1876
However start_time is not initialized in second case, resulting in time_delayed = immutable_db_options_.clock->NowMicros() - 0(start_time);

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12147

Test Plan: Existing CircleCI

Reviewed By: cbi42

Differential Revision: D52173481

Pulled By: akankshamahajan15

fbshipit-source-id: fb9183b24c191d287a1d715346467bee66190f98
2023-12-14 13:45:06 -08:00
Ludovic Henry 5502f06729 Add support for linux-riscv64 (#12139)
Summary:
Following https://github.com/evolvedbinary/docker-rocksjava/pull/2, we can now build rocksdb on riscv64.

I've verified this works as expected with `make rocksdbjavastaticdockerriscv64`.

Also fixes https://github.com/facebook/rocksdb/issues/10500 https://github.com/facebook/rocksdb/issues/11994

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12139

Reviewed By: jaykorean

Differential Revision: D52128098

Pulled By: akankshamahajan15

fbshipit-source-id: 706d36a3f8a9e990b76f426bc450937a0cd1a537
2023-12-14 11:27:17 -08:00
akankshamahajan e7c6259447 Make auto_readahead_size default true (#12080)
Summary:
Make auto_readahead_size option default true

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12080

Test Plan: benchmarks and exisiting tests

Reviewed By: anand1976

Differential Revision: D52152132

Pulled By: akankshamahajan15

fbshipit-source-id: f1515563564e77df457dff2e865e4ede8c3ddf44
2023-12-14 11:25:51 -08:00
Levi Tamasi cd21e4e69d Some further cleanup in WriteBatchWithIndex::MultiGetFromBatchAndDB (#12143)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12143

https://github.com/facebook/rocksdb/pull/11982 changed `WriteBatchWithIndex::MultiGetFromBatchDB` to preallocate space in the `autovector`s `key_contexts` and `merges` in order to prevent any reallocations, both as an optimization and in order to prevent pointers into the container from being invalidated during subsequent insertions. On second thought, this preallocation can actually be a pessimization in cases when only a small subset of keys require querying the underlying database. To prevent any memory regressions, the PR reverts this preallocation. In addition, it makes some small code hygiene improvements like incorporating the `PinnableWideColumns` object into `MergeTuple`.

Reviewed By: jaykorean

Differential Revision: D52136513

fbshipit-source-id: 21aa835084433feab27b501d9d1fc5434acea609
2023-12-13 17:34:18 -08:00
Peter Dillinger c74531b1d2 Fix a nuisance compiler warning from clang (#12144)
Summary:
Example:

```
cache/clock_cache.cc:56:7: error: fallthrough annotation in unreachable code [-Werror,-Wimplicit-fallthrough]
      FALLTHROUGH_INTENDED;
      ^
./port/lang.h:10:30: note: expanded from macro 'FALLTHROUGH_INTENDED'
                             ^
```

In clang < 14, this is annoyingly generated from -Wimplicit-fallthrough, but was changed to -Wunreachable-code-fallthrough (implied by -Wunreachable-code) in clang 14. See https://reviews.llvm.org/D107933 for how this nuisance pattern generated false positives similar to ours in the Linux kernel.

Just to underscore the ridiculousness of this warning, here an error is reported on the annotation, not the call to do_something(), depending on the constexpr value (https://godbolt.org/z/EvxqdPTdr):

```
#include <atomic>
void do_something();
void test(int v) {
    switch (v) {
        case 1:
            if constexpr (std::atomic<long>::is_always_lock_free) {
                return;
            } else {
                do_something();
                [[fallthrough]];
            }
        case 2:
            return;
    }
}
```

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12144

Test Plan: Added the warning to our Makefile for USE_CLANG, which reproduced the warning-as-error as shown above, but is now fixed.

Reviewed By: jaykorean

Differential Revision: D52139615

Pulled By: pdillinger

fbshipit-source-id: ba967ae700c0916d1a478bc465cf917633e337d9
2023-12-13 15:58:46 -08:00
akankshamahajan d926593df5 Fix stress tests failure for auto_readahead_size (#12131)
Summary:
When auto_readahead_size is enabled, Prev operation calls SeekForPrev in db_iter  so that
- BlockBasedTableIterator can point index_iter_ to the right block.
- disable readahead_cache_lookup.
However, there can be cases where SeekForPrev might not go through Version_set and call BlockBasedTableIterator SeekForPrev. In that case, when BlockBasedTableIterator::Prev is called, it returns NotSupported error. This more like a corner case.

So to handle that case, removed SeekForPrev calling from db_iter and reseeking index_iter_ in Prev operation. block_iter_'s key already point to right block. So reseeking to index_iter_ solves the issue.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12131

Test Plan:
- Tested on db_stress command that was failing -
`./db_stress --acquire_snapshot_one_in=10000 --adaptive_readahead=1 --allow_data_in_errors=True --async_io=0 --atomic_flush=0 --auto_readahead_size=1 --avoid_flush_during_recovery=0 --avoid_unnecessary_blocking_io=1 --backup_max_size=104857600 --backup_one_in=100000 --batch_protection_bytes_per_key=0 --best_efforts_recovery=1 --block_protection_bytes_per_key=1 --block_size=16384 --bloom_before_level=2147483646 --bloom_bits=12 --bottommost_compression_type=none --bottommost_file_compaction_delay=0 --bytes_per_sync=262144 --cache_index_and_filter_blocks=0 --cache_size=33554432 --cache_type=lru_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=0 --charge_filter_construction=1 --charge_table_reader=1 --checkpoint_one_in=1000000 --checksum_type=kxxHash64 --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=1000000 --compact_range_one_in=1000000 --compaction_pri=4 --compaction_readahead_size=1048576 --compaction_ttl=10 --compressed_secondary_cache_size=16777216 --compression_checksum=0 --compression_max_dict_buffer_bytes=0 --compression_max_dict_bytes=0 --compression_parallel_threads=1 --compression_type=zlib --compression_use_zstd_dict_trainer=0 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --data_block_index_type=1 --db=/home/akankshamahajan/rocksdb_auto_tune/dev/shm/rocksdb_test/rocksdb_crashtest_blackbox --db_write_buffer_size=134217728 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=1 --disable_wal=1 --enable_compaction_filter=0 --enable_pipelined_write=0 --enable_thread_tracking=1 --expected_values_dir=/home/akankshamahajan/rocksdb_auto_tune/dev/shm/rocksdb_test/rocksdb_crashtest_expected --fail_if_options_file_error=1 --fifo_allow_compaction=1 --file_checksum_impl=big --flush_one_in=1000000 --format_version=6 --get_current_wal_file_one_in=0 --get_live_files_one_in=1000000 --get_property_one_in=1000000 --get_sorted_wal_files_one_in=0 --index_block_restart_interval=10 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --level_compaction_dynamic_level_bytes=1 --lock_wal_one_in=1000000 --long_running_snapshots=1 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=0 --max_auto_readahead_size=524288 --max_background_compactions=20 --max_bytes_for_level_base=10485760 --max_key=25000000 --max_key_len=3 --max_manifest_file_size=1073741824 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=3 --max_write_buffer_size_to_maintain=4194304 --memtable_max_range_deletions=1000 --memtable_prefix_bloom_size_ratio=0 --memtable_protection_bytes_per_key=2 --memtable_whole_key_filtering=0 --memtablerep=skip_list --min_write_buffer_number_to_merge=1 --mmap_read=1 --mock_direct_io=False --nooverwritepercent=1 --num_file_reads_for_auto_readahead=1 --open_files=-1 --open_metadata_write_fault_one_in=0 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --ops_per_thread=100000000 --optimize_filters_for_memory=1 --paranoid_file_checks=1 --partition_filters=0 --partition_pinning=1 --pause_background_one_in=1000000 --periodic_compaction_seconds=10 --prefix_size=-1 --prefixpercent=0 --prepopulate_block_cache=0 --preserve_internal_time_seconds=0 --progress_reports=0 --read_fault_one_in=1000 --readahead_size=524288 --readpercent=50 --recycle_log_file_num=0 --reopen=0 --secondary_cache_fault_one_in=0 --secondary_cache_uri= --set_options_one_in=10000 --skip_verifydb=1 --snapshot_hold_ops=100000 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=0 --subcompactions=2 --sync=0 --sync_fault_injection=0 --target_file_size_base=2097152 --target_file_size_multiplier=2 --test_batches_snapshots=0 --top_level_index_pinning=3 --unpartitioned_pinning=3 --use_direct_io_for_flush_and_compaction=0 --use_direct_reads=0 --use_full_merge_v1=0 --use_get_entity=1 --use_merge=1 --use_multi_get_entity=0 --use_multiget=1 --use_put_entity_one_in=10 --use_write_buffer_manager=0 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000000 --verify_db_one_in=0 --verify_file_checksums_one_in=1000000 --verify_iterator_with_expected_state_one_in=5 --verify_sst_unique_id_in_manifest=1 --wal_bytes_per_sync=0 --wal_compression=zstd --write_buffer_size=4194304 --write_dbid_to_manifest=0 --write_fault_one_in=0 --writepercent=35`

 - make crash_test -j32

Reviewed By: anand1976

Differential Revision: D51986326

Pulled By: akankshamahajan15

fbshipit-source-id: 90e11e63d1f1894770b457a44d8b213ae5512df9
2023-12-13 12:15:04 -08:00
Andrew Kryczka d8e47620d7 Speedup based on pending compaction bytes relative to data size (#12130)
Summary:
RocksDB self throttles per-DB compaction parallelism until it detects compaction pressure. The pressure detection based on pending compaction bytes was only comparing against the slowdown trigger (`soft_pending_compaction_bytes_limit`). Online services tend to set that extremely high to avoid stalling at all costs. Perhaps they should have set it to zero, but we never documented that zero disables stalling so I have been telling everyone to increase it for years.

This PR adds pressure detection based on pending compaction bytes relative to the size of bottommost data. The size of bottommost data should be fairly stable and proportional to the logical data size

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12130

Reviewed By: hx235

Differential Revision: D52000746

Pulled By: ajkr

fbshipit-source-id: 7e1fd170901a74c2d4a69266285e3edf6e7631c7
2023-12-13 10:37:27 -08:00
anand76 ebb5242d55 Sanitize the secondary_cache option in TieredCacheOptions (#12137)
Summary:
Sanitize the `secondary_cache` field in the `cache_opts` option of `TieredCacheOptions` to `nullptr` if set by the user. The nvm secondary cache should be directly set in `TieredCacheOptions`.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12137

Reviewed By: akankshamahajan15

Differential Revision: D52063817

Pulled By: anand1976

fbshipit-source-id: 255116c665a9b908c8f44109a2d331d4b73e7591
2023-12-12 10:58:00 -08:00
Yu Zhang c2ab4e754b Add initial support to stress test persist_user_defined_timestamps (#12124)
Summary:
This PR adds initial stress testing for the user-defined timestamps in memtable only feature. Each flavor of the `*_ts` crash test get a 1 in 3 chance to run with timestamps not persisted, this setting is initialized once and kept consistent across the following re-runs.

This initial stress test included these things besides disabling incompatible feature combinations to make the test run more stably:
1) It currently only run test methods that validates db state with expected state. Not the ones that validate db state by comparing result from one API to another API. Such as `TestMultiGet` (compared with `Get`), similarly `TestMultiGetEntity`, `TestIterate` (compare src iterator to a control iterator). Due to timestamps being removed, results from one API to another API is not directly comparable as it is now. More test logic to handle that need to be added, will do that in a follow up.

2) Even when comparing db state to expected state, sometimes the db can receive `InvalidArgument` too due to timestamps getting flushed and removed. Added some logic to handle that.

3) When timestamps are not persisted, we don't try to read with older timestamp. Since that's making it easier to get `InvalidArgument`. And this capability is not yet needed by our customer so it's disabled for now.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12124

Test Plan: running multiple flavor of this test on continuous run for sometime before checkin

Reviewed By: ltamasi

Differential Revision: D51916267

Pulled By: jowlyzhang

fbshipit-source-id: 3f3eb5f9618d05d296062820e0ef5cb8edc7c2b2
2023-12-12 09:35:29 -08:00
anand76 c1b84d0437 Fix false negative in TieredSecondaryCache nvm cache lookup (#12134)
Summary:
There is a bug in the `TieredSecondaryCache` that can result in a false negative. This can happen when a MultiGet does a cache lookup that gets a hit in the `TieredSecondaryCache` local nvm cache tier, and the result is available before MultiGet calls `WaitAll` (i.e the nvm cache `SecondaryCacheResultHandle` `IsReady` returns true).

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12134

Test Plan: Add a new unit test in tiered_secondary_cache_test

Reviewed By: akankshamahajan15

Differential Revision: D52023309

Pulled By: anand1976

fbshipit-source-id: e5ae681226a0f12753fecb2f6acc7e5f254ae72b
2023-12-11 16:59:59 -08:00
Peter Dillinger c96d9a0fbb Allow TablePropertiesCollectorFactory to return null collector (#12129)
Summary:
As part of building another feature, I wanted this:
* Custom implementations of `TablePropertiesCollectorFactory` may now return a `nullptr` collector to decline processing a file, reducing callback overheads in such cases.
* Polished, clarified some related API comments.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12129

Test Plan: unit test added

Reviewed By: ltamasi

Differential Revision: D51966667

Pulled By: pdillinger

fbshipit-source-id: 2991c08fe6ce3a8c9f14c68f1495f5a17bca2770
2023-12-11 12:02:56 -08:00
Radek Hubner 5c5e018943 Fix JNI lazy load regression. (#12133)
Summary:
A small regression that conflicted with PR https://github.com/facebook/rocksdb/pull/12133 was later merged in commit https://github.com/facebook/rocksdb/commit/2296c624fa0fd72f61eb706c56bb4fc53ddf7ce6#diff-26d3ab8a3d764183d0ea3aea834fe481eec2347c334b918ebd7bdce4bcabcc19R35
This PR addresses that regression. Closes https://github.com/facebook/rocksdb/issues/12132

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12133

Reviewed By: jowlyzhang

Differential Revision: D52041736

Pulled By: ltamasi

fbshipit-source-id: 33db57035154c833ae00b5d921b17b3be80c8dd7
2023-12-11 11:21:52 -08:00
Alan Paxton 5a063ecd34 Java API consistency between RocksDB.put() , .merge() and Transaction.put() , .merge() (#11019)
Summary:
### Implement new Java API get()/put()/merge() methods, and transactional variants.

The Java API methods are very inconsistent in terms of how they pass parameters (byte[], ByteBuffer), and what variants and defaulted parameters they support. We try to bring some consistency to this.
 * All APIs should support calls with ByteBuffer parameters.
 * Similar methods (RocksDB.get() vs Transaction.get()) should support as similar as possible sets of parameters for predictability.
 * get()-like methods should provide variants where the caller supplies the target buffer, for the sake of efficiency. Allocation costs in Java can be significant when large buffers are repeatedly allocated and freed.

### API Additions

 1. RockDB.get implement indirect ByteBuffers. Added indirect ByteBuffers and supporting native methods for get().
 2. RocksDB.Iterator implement missing (byte[], offset, length) variants for key() and value() parameters.
 3. Transaction.get() implement missing methods, based on RocksDB.get. Added ByteBuffer.get with and without column family. Added byte[]-as-target get.
 4. Transaction.iterator() implement a getIterator() which defaults ReadOptions; as per RocksDB.iterator(). Rationalize support API for this and RocksDB.iterator()
 5. RocksDB.merge implement ByteBuffer methods; both direct and indirect buffers. Shadow the methods of RocksDB.put; RocksDB.put only offers ByteBuffer API with explicit WriteOptions. Duplicated this with RocksDB.merge
 6. Transaction.merge implement methods as per RocksDB.merge methods. Transaction is already constructed with WriteOptions, so no explicit WriteOptions methods required.
 7. Transaction.mergeUntracked implement the same API methods as Transaction.merge except the ones that use assumeTracked, because that’s not a feature of merge untracked.

### Support Changes (C++)

The current JNI code in C++ supports multiple variants of methods through a number of helper functions. There are numerous TODO suggestions in the code proposing that the helpers be re-factored/shared.

We have taken a different approach for the new methods; we have created wrapper classes `JDirectBufferSlice`, `JDirectBufferPinnableSlice`, `JByteArraySlice` and `JByteArrayPinnableSlice` RAII classes which construct slices from JNI parameters and can then be passed directly to RocksDB methods. For instance, the `Java_org_rocksdb_Transaction_getDirect` method is implemented like this:

```
  try {
    ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
                                              jkey_part_len);
    ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
                                                        jval_part_len);
    ROCKSDB_NAMESPACE::KVException::ThrowOnError(
        env, txn->Get(*read_options, column_family_handle, key.slice(),
                      &value.pinnable_slice()));
    return value.Fetch();
  } catch (const ROCKSDB_NAMESPACE::KVException& e) {
    return e.Code();
  }
```
Notice the try/catch mechanism with the `KVException` class, which combined with RAII and the wrapper classes means that there is no ad-hoc cleanup necessary in the JNI methods.

We propose to extend this mechanism to existing JNI methods as further work.

### Support Changes (Java)

Where there are multiple parameter-variant versions of the same method, we use fewer or just one supporting native method for all of them. This makes maintenance a bit easier and reduces the opportunity for coding errors mixing up (untyped) object handles.

In  order to support this efficiently, some classes need to have default values for column families and read options added and cached so that they are not re-constructed on every method call.

This PR closes https://github.com/facebook/rocksdb/issues/9776

Pull Request resolved: https://github.com/facebook/rocksdb/pull/11019

Reviewed By: ajkr

Differential Revision: D52039446

Pulled By: jowlyzhang

fbshipit-source-id: 45d0140a4887e42134d2e56520e9b8efbd349660
2023-12-11 11:03:17 -08:00
Richard Barnes 4f04f96742 Remove extra semi colon from infrasec/authorization/audit/AclAuditor.cpp
Summary:
`-Wextra-semi` or `-Wextra-semi-stmt`

If the code compiles, this is safe to land.

Reviewed By: palmje

Differential Revision: D51995065

fbshipit-source-id: 9b55a0d8abd0927b76376cb7751bf0fcab10518c
2023-12-08 17:21:52 -08:00
Kevin Mingtarja 44fd914128 Fix double counting of BYTES_WRITTEN ticker (#12111)
Summary:
Fixes https://github.com/facebook/rocksdb/issues/12061.

We were double counting the `BYTES_WRITTEN` ticker when doing writes with transactions. During transactions, after writing, a client can call `Prepare()`, which writes the values to WAL but not to the Memtable. After that, they can call `Commit()`, which writes a commit marker to the WAL and the values to Memtable.

The cause of this bug is previously during writes, we didn't take into account `writer->ShouldWriteToMemtable()` before adding to `total_byte_size`, so it is still added to during the `Prepare()` phase even though we're not writing to the Memtable, which was why we saw the value to be double of what's written to WAL.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12111

Test Plan: Added a test in `db/db_statistics_test.cc` that tests writes with and without transactions, by comparing the values of `BYTES_WRITTEN` and `WAL_FILE_BYTES` after doing writes.

Reviewed By: jaykorean

Differential Revision: D51954327

Pulled By: jowlyzhang

fbshipit-source-id: 57a0986a14e5b94eb5188715d819212529110d2c
2023-12-08 17:12:11 -08:00
Levi Tamasi a143f93236 Turn the default Timer in PeriodicTaskScheduler into a leaky Meyers singleton (#12128)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12128

The patch turns the `Timer` Meyers singleton in `PeriodicTaskScheduler::Default()` into one of the leaky variety in order to prevent static destruction order issues.

Reviewed By: akankshamahajan15

Differential Revision: D51963950

fbshipit-source-id: 0fc34113ad03c51fdc83bdb8c2cfb6c9f6913948
2023-12-08 10:34:07 -08:00
Hui Xiao 179d2c7646 Intensify "xxx_one_in"'s default value in crash test (#12127)
Summary:
**Context/Summary:**
My experimental stress runs with more frequent "xxx_one_in" surfaced a couple interesting bugs/issues with RocksDB or crash test framework in the past. We now consider changing the default value so they are run more frequently in production testing environment.

Increase frequency by 2 orders of magnitude for most parameters, except for error-prone features e.g, manual compaction and file ingestion (increased by 3 orders) and expensive features e.g, checksum verification (increased by 1 order)

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12127

Test Plan: Monitor CI to see if it did surface more interesting bugs/issues. If not, we may consider intensify even more.

Reviewed By: pdillinger

Differential Revision: D51954235

Pulled By: hx235

fbshipit-source-id: 92046cb7c52a37212f19ab7965b40f77b90b08b1
2023-12-08 10:22:14 -08:00
akankshamahajan c77b50a4fd Add AsyncIO support for tuning readahead_size by block cache lookup (#11936)
Summary:
Add support for tuning of readahead_size by block cache lookup for async_io.

**Design/ Implementation** -

**BlockBasedTableIterator.cc** -

`BlockCacheLookupForReadAheadSize` callback API lookups in the block cache and tries to reduce the start
and end offset passed. This function looks into the block cache for the blocks between `start_offset`
and `end_offset` and add all the handles in the queue.

It then iterates from the end in the handles to find first miss block and update the end offset to that block.
It also iterates from the start and find first miss block and update the start offset to that block.

```
_read_curr_block_ argument : True if this call was due to miss in the cache and caller wants to read that block
                             synchronously.
                             False if current call is to prefetch additional data in extra buffers
                            (due to ReadAsync call in FilePrefetchBuffer)
```
In case there is no data to be read in that callback (because of upper_bound or all blocks are in cache),
it updates start and end offset to be equal and that `FilePrefetchBuffer` interprets that as 0 length to be read.

**FilePrefetchBuffer.cc** -

FilePrefetchBuffer calls the callback - `ReadAheadSizeTuning` and pass the start and end offset to that
callback to get updated start and end offset to read based on cache hits/misses.

1. In case of Read calls (when offset passed to FilePrefetchBuffer is on cache miss and that data needs to be read), _read_curr_block_ is passed true.
2. In case of ReadAsync calls, when buffer is all consumed and can go for additional prefetching,  the start offset passed is the initial end offset of prev buffer (without any updated offset based on cache hit/miss).

Foreg. if following are the data blocks with cache hit/miss and start offset
and Read API found miss on DB1 and based on readahead_size (50)  it passes end offset to be 50.
 [DB1 - miss- 0 ] [DB2 - hit -10] [DB3 - miss -20] [DB4 - miss-30] [DB5 - hit-40]
 [DB6 - hit-50] [DB7 - miss-60] [DB8 - miss - 70] [DB9 - hit - 80] [DB6 - hit 90]

- For Read call - updated start offset remains 0 but end offset updates to DB4, as DB5 is in cache.
- Read calls saves initial end offset 50 as that was meant to be prefetched.
- Now for next ReadAsync call - the start offset will be 50 (previous buffer initial end offset) and based on readahead_size, end offset will be 100
- On callback, because of cache hits - callback will update the start offset to 60 and end offset to 80 to read only 2 data blocks (DB7 and DB8).
- And for that ReadAsync call - initial end offset will be set to 100 which will again used by next ReadAsync call as start offset.
-  `initial_end_offset_` in `BufferInfo` is used to save the initial end offset of that buffer.

- If let's say DB5 and DB6 overlaps in 2 buffers (because of alignment), `prev_buf_end_offset` is passed to make sure already prefetched data is not prefetched again in second buffer.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/11936

Test Plan:
- Ran crash_test several times.
-  New unit tests added.

Reviewed By: anand1976

Differential Revision: D50906217

Pulled By: akankshamahajan15

fbshipit-source-id: 0d75d3c98274e98aa34901b201b8fb05232139cf
2023-12-06 13:48:15 -08:00
Levi Tamasi 0ebe1614cb Eliminate some code duplication in MergeHelper (#12121)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12121

The patch eliminates some code duplication by unifying the two sets of `MergeHelper::TimedFullMerge` overloads using variadic templates. It also brings the order of parameters into sync when it comes to the various `TimedFullMerge*` methods.

Reviewed By: jaykorean

Differential Revision: D51862483

fbshipit-source-id: e3f832a6ff89ba34591451655cf11025d0a0d018
2023-12-05 14:07:42 -08:00
Levi Tamasi 2045fe4693 Mention PR 11892 in the changelog (#12118)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12118

Reviewed By: jaykorean

Differential Revision: D51820703

fbshipit-source-id: d2a86a4781618747c6b7c71971862d510a25e103
2023-12-04 13:20:28 -08:00
Yu Zhang ba8fa0f546 internal_repo_rocksdb (4372117296613874540) (#12117)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12117

Reviewed By: ajkr

Differential Revision: D51745846

Pulled By: jowlyzhang

fbshipit-source-id: 51c806a484b3b43d174b06d2cfe9499191d09914
2023-12-04 11:17:32 -08:00
Richard Barnes dce3ca5ab8 Remove extra semi colon from internal_repo_rocksdb/repo/monitoring/perf_context_imp.h
Summary:
`-Wextra-semi` or `-Wextra-semi-stmt`

If the code compiles, this is safe to land.

Reviewed By: dmm-fb

Differential Revision: D51778007

fbshipit-source-id: 5d1b20a3acc4bcc7cd7c204f2f73a14fc8f81883
2023-12-01 22:35:34 -08:00
Andrew Kryczka 06dc32ef25 internal_repo_rocksdb (435146444452818992) (#12115)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12115

Reviewed By: jowlyzhang

Differential Revision: D51745742

Pulled By: ajkr

fbshipit-source-id: 67000d07783b413924798dd9c1751da27e119d53
2023-12-01 11:15:17 -08:00
Andrew Kryczka be3bc36811 internal_repo_rocksdb (-8794174668376270091) (#12114)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12114

Reviewed By: jowlyzhang

Differential Revision: D51745613

Pulled By: ajkr

fbshipit-source-id: 27ca4bda275cab057d3a3ec99f0f92cdb9be5177
2023-12-01 11:10:30 -08:00
Yu Zhang 7eca51dfc3 Refactor crash test stderr parsing logic into a function (#12109)
Summary:
This is a simple refactor for the crash test script to put shared logic for parsing stderr into a function. There is no functional change.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12109

Test Plan: manually tested the script

Reviewed By: ajkr

Differential Revision: D51692172

Pulled By: jowlyzhang

fbshipit-source-id: d346d64e981d9c489c380ff6ce33296a224b5877
2023-12-01 11:01:29 -08:00
Levi Tamasi b760af321f Initial support for wide columns in WriteBatchWithIndex (#11982)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/11982

The patch constitutes the first phase of adding wide-column support to `WriteBatchWithIndex`. Namely, it implements the `PutEntity` API in `WriteBatchWithIndex` on the write path, and the `Iterator::columns()` API in `BaseDeltaIterator` on the read path. In addition, it updates all existing read APIs (`GetFromBatch`, `GetFromBatchAndDB`, `MultiGetFromBatchAndDB`, and `BaseDeltaIterator`) so that they handle wide-column entities correctly. This includes returning the value of the default column of entities as appropriate and correctly applying merges to wide-column base values. I plan to add the wide-column specific point lookup APIs (`GetEntityFromBatch`, `GetEntityFromBatchAndDB`, and `MultiGetEntityFromBatchAndDB`) in subsequent patches.

Reviewed By: jaykorean

Differential Revision: D50439231

fbshipit-source-id: 59fd0f12c45249fecde8af249c5d3f509ba58bbe
2023-11-30 14:10:13 -08:00
raffertyyu a7779458bd sst_dump support cuckoo table (#12098)
Summary:
https://github.com/facebook/rocksdb/issues/11446

Support Cuckoo Table format in sst_dump.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12098

Reviewed By: jowlyzhang

Differential Revision: D51594094

Pulled By: ajkr

fbshipit-source-id: ba9092818bc3cc0f207b000391aa21d564570df2
2023-11-30 08:06:37 -08:00
Yu Zhang d68f45e777 Flush buffered logs when FlushRequest is rescheduled (#12105)
Summary:
The optimization to not find and delete obsolete files when FlushRequest is re-scheduled also inadvertently skipped flushing the `LogBuffer`, resulting in missed logs. This PR fixes the issue.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12105

Test Plan:
manually check this test has the correct info log after the fix
`./column_family_test --gtest_filter=ColumnFamilyRetainUDTTest.NotAllKeysExpiredFlushRescheduled`

Reviewed By: ajkr

Differential Revision: D51671079

Pulled By: jowlyzhang

fbshipit-source-id: da0640e07e35c69c08988772ed611ec9e67f2e92
2023-11-29 11:35:59 -08:00
anand76 acc078f878 Add tiered cache options to db_bench (#12104)
Summary:
Add the option to have a 3-tier block cache (uncompressed RAM, compressed RAM, and local flash) in db_bench, as well as specifying secondary cache admission policy.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12104

Reviewed By: ajkr

Differential Revision: D51629092

Pulled By: anand1976

fbshipit-source-id: 6a208f853bc85d3d8b437d91cb1b0142d9a99e53
2023-11-28 14:54:08 -08:00
anand76 4d04138512 Add dynamic disabling of compressed cache to db_stress (#12102)
Summary:
We now support re-enabling the compressed portion of the `TieredCache` after dynamically disabling it. Add it to db_stress for testing purposes.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12102

Reviewed By: akankshamahajan15

Differential Revision: D51594259

Pulled By: anand1976

fbshipit-source-id: ea544e30a5ebd6290fc9ed46a241f09634764d2a
2023-11-27 13:00:15 -08:00
Alexander Kiel 6e7701d49b Fix JavaDoc of setCompactionReadaheadSize (#12090)
Summary:
Recently in https://github.com/facebook/rocksdb/issues/11762 the default of `compaction_readahead_size` changed from 0 to 2 MB.

Closes: https://github.com/facebook/rocksdb/issues/12088

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12090

Reviewed By: jaykorean

Differential Revision: D51531762

Pulled By: ajkr

fbshipit-source-id: a0b7145a1dca95ee90ffa3553f6eeacce6424aee
2023-11-27 11:50:53 -08:00
Peter Dillinger 4dd2bb8f70 Fix stack trace trimming with LLDB (#12101)
Summary:
I must have chosen trimming before frame 8 based on assertion failures, but that trims too many frame for a general segfault. So this changes to start printing at frame 4, as in this example where I've seeded a null deref:

```
Received signal 11 (Segmentation fault)
Invoking LLDB for stack trace...
Process 873208 stopped
* thread #1, name = 'db_stress', stop reason = signal SIGSTOP
    frame #0: 0x00007fb1fe8f1033 libc.so.6`__GI___wait4(pid=873478, stat_loc=0x00007fb1fb114030, options=0, usage=0x0000000000000000) at wait4.c:30:10
  thread #2, name = 'rocksdb:low', stop reason = signal SIGSTOP
    frame #0: 0x00007fb1fe8972a1 libc.so.6`__GI___futex_abstimed_wait_cancelable64 at futex-internal.c:57:12
Executable module set to "/data/users/peterd/rocksdb/db_stress".
Architecture set to: x86_64-unknown-linux-gnu.
True
frame #4: 0x00007fb1fe844540 libc.so.6`__restore_rt at libc_sigaction.c:13
frame #5: 0x0000000000608514 db_stress`rocksdb::StressTest::InitDb(rocksdb::SharedState*) at db_stress_test_base.cc:345:18
frame #6: 0x0000000000585d62 db_stress`rocksdb::RunStressTestImpl(rocksdb::SharedState*) at db_stress_driver.cc:84:17
frame #7: 0x000000000058dd69 db_stress`rocksdb::RunStressTest(shared=0x00006120000001c0) at db_stress_driver.cc:266:34
frame #8: 0x0000000000453b34 db_stress`rocksdb::db_stress_tool(int, char**) at db_stress_tool.cc:370:20
...
```

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12101

Test Plan: manual (see above)

Reviewed By: ajkr

Differential Revision: D51593217

Pulled By: pdillinger

fbshipit-source-id: 4a71eb8e516edbc32e682f9537bc77d073a7b4ed
2023-11-27 11:49:52 -08:00
Peter Dillinger f6fd4b9dbd Print stack traces more reliably with concurrency (#12086)
Summary:
It's been relatively easy to break our stack trace printer:
* If another thread reaches a signal condition such as a related SEGV or assertion failure while one is trying to print a stack trace from the signal handler, it seems to end the process abruptly without a stack trace.
* If the process exits normally in one thread (such as main finishing) while another is trying to print a stack trace from the signal handler, it seems the process will often end normally without a stack trace.

This change attempts to fix these issues, with
* Keep the custom signal handler installed as long as possible, so that other threads will most likely re-enter our custom handler. (We only switch back to default for triggering core dump or whatever after stack trace.)
* Use atomics and sleeps to implement a crude recursive mutex for ensuring all threads hitting the custom signal handler wait on the first that is trying to print a stack trace, while recursive signals in the same thread can still be handled cleanly.
* Use an atexit handler to hook into normal exit to (a) wait on a pending printing of stack trace when detectable and applicable, and (b) detect and warn when printing a stack trace might be interrupted by a process exit in progress. (I don't know how to pause that *after* our atexit handler has been called; the best I know how to do is warn, "In a race with process already exiting...".)

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12086

Test Plan:
manual, including with TSAN. I added this code to the end of a unit test file:
```
  for (size_t i = 0; i < 3; ++i) {
    std::thread t([]() { assert(false); });
    t.detach();
  }
```
Followed by either `sleep(100)` or `usleep(100)` or usual process exit. And for recursive signal testing, inject `abort()` at various places in the handler.

Reviewed By: cbi42

Differential Revision: D51531882

Pulled By: pdillinger

fbshipit-source-id: 3473b863a43e61b722dfb7a2ed12a8120949b09c
2023-11-22 11:55:10 -08:00
Peter Dillinger a140b519b1 Convert all but one windows job to nightly (#12089)
Summary:
... because they are expensive and rarely disagree with each other. Historical data indicates that the 2019 job is most sensitive to failure.

https://fburl.com/scuba/opensource_ci_jobs/ntq3ue3p https://fburl.com/scuba/opensource_ci_jobs/0xo91j5f

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12089

Test Plan: CI

Reviewed By: ajkr

Differential Revision: D51530386

Pulled By: pdillinger

fbshipit-source-id: 8b676d6e01096e359a0f465b59d81ac10f4f7969
2023-11-22 10:40:52 -08:00
cz2h 324453e579 Fix rowcache get returning incorrect timestamp (#11952)
Summary:
Fixes https://github.com/facebook/rocksdb/issues/7930.

When there is a timestamp associated with stored records, get from row cache will return the timestamp provided in query instead of the timestamp associated with the stored record.

## Cause of error:
Currently a row_handle is fetched using row_cache_key(contains a timestamp provided by user query) and the row_handle itself does not persist timestamp associated with the object. Hence the [GetContext::SaveValue()
](https://github.com/facebook/rocksdb/blob/6e3429b8a6a53d5e477074057b5f27218063b5f2/table/get_context.cc#L257) function will fetch the timestamp in row_cache_key and may return the incorrect timestamp value.

## Proposed Solution
If current cf enables ts, append a timestamp associated with stored records after the value in replay_log (equivalently the value of row cache entry).

When read, `replayGetContextLog()` will update parsed_key with the correct timestamp.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/11952

Reviewed By: ajkr

Differential Revision: D51501176

Pulled By: jowlyzhang

fbshipit-source-id: 808fc943a8ae95de56ae0e82ec59a2573a031f28
2023-11-21 20:39:33 -08:00
Jay Huh ddb7df10ef Update HISTORY.md and version.h for 8.9.fb release (#12074)
Summary:
Creating cut for 8.9 release

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12074

Test Plan: CI

Reviewed By: ajkr

Differential Revision: D51435289

Pulled By: jaykorean

fbshipit-source-id: 3918a8250032839e5b71f67f26c8ba01cbc17a41
2023-11-21 18:07:19 -08:00
232 changed files with 7154 additions and 2689 deletions

No files matched your search

+2 -2
View File
@@ -914,8 +914,6 @@ workflows:
- build-linux-mini-crashtest
jobs-windows:
jobs:
- build-windows-vs2022-avx2
- build-windows-vs2022
- build-windows-vs2019
- build-cmake-mingw
jobs-java:
@@ -964,3 +962,5 @@ workflows:
- build-linux-non-shm
- build-linux-clang-13-asan-ubsan-with-folly
- build-linux-valgrind
- build-windows-vs2022-avx2
- build-windows-vs2022
+68
View File
@@ -1,6 +1,74 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 8.10.2 (02/16/2024)
* Update zlib to 1.3.1 for Java builds
## 8.10.1 (01/16/2024)
### Bug Fixes
* Fix bug in auto_readahead_size that combined with IndexType::kBinarySearchWithFirstKey + fails or iterator lands at a wrong key
## 8.10.0 (12/15/2023)
### New Features
* Provide support for async_io to trim readahead_size by doing block cache lookup
* Added initial wide-column support in `WriteBatchWithIndex`. This includes the `PutEntity` API and support for wide columns in the existing read APIs (`GetFromBatch`, `GetFromBatchAndDB`, `MultiGetFromBatchAndDB`, and `BaseDeltaIterator`).
### Public API Changes
* Custom implementations of `TablePropertiesCollectorFactory` may now return a `nullptr` collector to decline processing a file, reducing callback overheads in such cases.
### Behavior Changes
* Make ReadOptions.auto_readahead_size default true which does prefetching optimizations for forward scans if iterate_upper_bound and block_cache is also specified.
* Compactions can be scheduled in parallel in an additional scenario: high compaction debt relative to the data size
* HyperClockCache now has built-in protection against excessive CPU consumption under the extreme stress condition of no (or very few) evictable cache entries, which can slightly increase memory usage such conditions. New option `HyperClockCacheOptions::eviction_effort_cap` controls the space-time trade-off of the response. The default should be generally well-balanced, with no measurable affect on normal operation.
### Bug Fixes
* Fix a corner case with auto_readahead_size where Prev Operation returns NOT SUPPORTED error when scans direction is changed from forward to backward.
* Avoid destroying the periodic task scheduler's default timer in order to prevent static destruction order issues.
* Fix double counting of BYTES_WRITTEN ticker when doing writes with transactions.
* Fix a WRITE_STALL counter that was reporting wrong value in few cases.
* A lookup by MultiGet in a TieredCache that goes to the local flash cache and finishes with very low latency, i.e before the subsequent call to WaitAll, is ignored, resulting in a false negative and a memory leak.
### Performance Improvements
* Java API extensions to improve consistency and completeness of APIs
1 Extended `RocksDB.get([ColumnFamilyHandle columnFamilyHandle,] ReadOptions opt, ByteBuffer key, ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
2 Extended `RocksDB.put( [ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
3 Added `RocksDB.merge([ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOptions, ByteBuffer key, ByteBuffer value)` methods with the same parameter options as `put(...)` - direct and indirect buffers are supported
4 Added `RocksIterator.key( byte[] key [, int offset, int len])` methods which retrieve the iterator key into the supplied buffer
5 Added `RocksIterator.value( byte[] value [, int offset, int len])` methods which retrieve the iterator value into the supplied buffer
6 Deprecated `get(final ColumnFamilyHandle columnFamilyHandle, final ReadOptions readOptions, byte[])` in favour of `get(final ReadOptions readOptions, final ColumnFamilyHandle columnFamilyHandle, byte[])` which has consistent parameter ordering with other methods in the same class
7 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value)` methods which retrieve the requested value into the supplied buffer
8 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which retrieve the requested value into the supplied buffer
9 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
10 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
11 Added `Transaction.getIterator()` method as a convenience which defaults the `ReadOptions` value supplied to existing `Transaction.iterator()` methods. This mirrors the existing `RocksDB.iterator()` method.
12 Added `Transaction.put([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written in a `ByteBuffer` parameter
13 Added `Transaction.merge([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
14 Added `Transaction.mergeUntracked([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
## 8.9.0 (11/17/2023)
### New Features
* Add GetEntity() and PutEntity() API implementation for Attribute Group support. Through the use of Column Families, AttributeGroup enables users to logically group wide-column entities.
### Public API Changes
* Added rocksdb_ratelimiter_create_auto_tuned API to create an auto-tuned GenericRateLimiter.
* Added clipColumnFamily() to the Java API to clip the entries in the CF according to the range [begin_key, end_key).
* Make the `EnableFileDeletion` API not default to force enabling. For users that rely on this default behavior and still
want to continue to use force enabling, they need to explicitly pass a `true` to `EnableFileDeletion`.
* Add new Cache APIs GetSecondaryCacheCapacity() and GetSecondaryCachePinnedUsage() to return the configured capacity, and cache reservation charged to the secondary cache.
### Behavior Changes
* During off-peak hours defined by `daily_offpeak_time_utc`, the compaction picker will select a larger number of files for periodic compaction. This selection will include files that are projected to expire by the next off-peak start time, ensuring that these files are not chosen for periodic compaction outside of off-peak hours.
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occured previously in its status message.
* Deleting stale files upon recovery are delegated to SstFileManger if available so they can be rate limited.
* Make RocksDB only call `TablePropertiesCollector::Finish()` once.
* When `WAL_ttl_seconds > 0`, we now process archived WALs for deletion at least every `WAL_ttl_seconds / 2` seconds. Previously it could be less frequent in case of small `WAL_ttl_seconds` values when size-based expiration (`WAL_size_limit_MB > 0 `) was simultaneously enabled.
### Bug Fixes
* Fixed a crash or assertion failure bug in experimental new HyperClockCache variant, especially when running with a SecondaryCache.
* Fix a race between flush error recovery and db destruction that can lead to db crashing.
* Fixed some bugs in the index builder/reader path for user-defined timestamps in Memtable only feature.
## 8.8.0 (10/23/2023)
### New Features
* Introduce AttributeGroup by adding the first AttributeGroup support API, MultiGetEntity(). Through the use of Column Families, AttributeGroup enables users to logically group wide-column entities. More APIs to support AttributeGroup will come soon, including GetEntity, PutEntity, and others.
+9 -5
View File
@@ -539,7 +539,7 @@ endif
ifdef USE_CLANG
# Used by some teams in Facebook
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator -Wimplicit-fallthrough
endif
ifeq ($(PLATFORM), OS_OPENBSD)
@@ -2060,7 +2060,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
ifeq ($(PLATFORM), OS_SOLARIS)
ARCH := $(shell isainfo -b)
else ifeq ($(PLATFORM), OS_OPENBSD)
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 sparc64 loongarch64, $(MACHINE)))
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
ARCH := 64
else
ARCH := 32
@@ -2081,7 +2081,7 @@ ifneq ($(origin JNI_LIBC), undefined)
endif
ifeq (,$(ROCKSDBJNILIB))
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64 loongarch64, $(MACHINE)))
ifneq (,$(filter ppc% s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
else
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
@@ -2094,8 +2094,8 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
SHA256_CMD = sha256sum
ZLIB_VER ?= 1.3
ZLIB_SHA256 ?= ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e
ZLIB_VER ?= 1.3.1
ZLIB_SHA256 ?= 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
@@ -2346,6 +2346,10 @@ rocksdbjavastaticdockers390x:
mkdir -p java/target
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
rocksdbjavastaticdockerriscv64:
mkdir -p java/target
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
rocksdbjavastaticdockerx86musl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
+5 -4
View File
@@ -647,8 +647,10 @@ if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
fi
COMMON_FLAGS="$COMMON_FLAGS"
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
RISC_ISA=$(cat /proc/cpuinfo | grep -E '^isa\s*:' | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
if [ -n "${RISCV_ISA}" ]; then
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
fi
elif [ "$TARGET_OS" == "IOS" ]; then
COMMON_FLAGS="$COMMON_FLAGS"
else
@@ -660,8 +662,7 @@ else
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
COMMON_FLAGS="$COMMON_FLAGS -march=rv64gc"
elif test "$USE_SSE"; then
# USE_SSE is DEPRECATED
# This is a rough approximation of the old USE_SSE behavior
+5
View File
@@ -48,6 +48,10 @@ DEFINE_uint64(cache_size, 1 * GiB,
"Number of bytes to use as a cache of uncompressed data.");
DEFINE_int32(num_shard_bits, -1,
"ShardedCacheOptions::shard_bits. Default = auto");
DEFINE_int32(
eviction_effort_cap,
ROCKSDB_NAMESPACE::HyperClockCacheOptions(1, 1).eviction_effort_cap,
"HyperClockCacheOptions::eviction_effort_cap");
DEFINE_double(resident_ratio, 0.25,
"Ratio of keys fitting in cache to keyspace.");
@@ -391,6 +395,7 @@ class CacheBench {
FLAGS_cache_size, /*estimated_entry_charge=*/0, FLAGS_num_shard_bits);
opts.hash_seed = BitwiseAnd(FLAGS_seed, INT32_MAX);
opts.memory_allocator = allocator;
opts.eviction_effort_cap = FLAGS_eviction_effort_cap;
if (FLAGS_cache_type == "fixed_hyper_clock_cache" ||
FLAGS_cache_type == "hyper_clock_cache") {
opts.estimated_entry_charge = FLAGS_value_bytes_estimate > 0
+98 -44
View File
@@ -15,6 +15,7 @@
#include <cassert>
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <functional>
#include <numeric>
@@ -51,14 +52,15 @@ inline uint64_t GetInitialCountdown(Cache::Priority priority) {
switch (priority) {
case Cache::Priority::HIGH:
return ClockHandle::kHighCountdown;
default:
assert(false);
FALLTHROUGH_INTENDED;
case Cache::Priority::LOW:
return ClockHandle::kLowCountdown;
case Cache::Priority::BOTTOM:
return ClockHandle::kBottomCountdown;
}
// Switch should have been exhaustive.
assert(false);
// For release build, fall back on something reasonable.
return ClockHandle::kLowCountdown;
}
inline void MarkEmpty(ClockHandle& h) {
@@ -93,7 +95,8 @@ inline void Unref(const ClockHandle& h, uint64_t count = 1) {
(void)old_meta;
}
inline bool ClockUpdate(ClockHandle& h, bool* purgeable = nullptr) {
inline bool ClockUpdate(ClockHandle& h, BaseClockTable::EvictionData* data,
bool* purgeable = nullptr) {
uint64_t meta;
if (purgeable) {
assert(*purgeable == false);
@@ -125,6 +128,7 @@ inline bool ClockUpdate(ClockHandle& h, bool* purgeable = nullptr) {
(meta >> ClockHandle::kReleaseCounterShift) & ClockHandle::kCounterMask;
if (acquire_count != release_count) {
// Only clock update entries with no outstanding refs
data->seen_pinned_count++;
return false;
}
if ((meta >> ClockHandle::kStateShift == ClockHandle::kStateVisible) &&
@@ -148,6 +152,8 @@ inline bool ClockUpdate(ClockHandle& h, bool* purgeable = nullptr) {
<< ClockHandle::kStateShift) |
(meta & ClockHandle::kHitBitMask))) {
// Took ownership.
data->freed_charge += h.GetTotalCharge();
data->freed_count += 1;
return true;
} else {
// Compare-exchange failing probably
@@ -355,6 +361,18 @@ void ConstApplyToEntriesRange(const Func& func, const HandleImpl* begin,
}
}
constexpr uint32_t kStrictCapacityLimitBit = 1u << 31;
uint32_t SanitizeEncodeEecAndScl(int eviction_effort_cap,
bool strict_capacit_limit) {
eviction_effort_cap = std::max(int{1}, eviction_effort_cap);
eviction_effort_cap =
std::min(static_cast<int>(~kStrictCapacityLimitBit), eviction_effort_cap);
uint32_t eec_and_scl = static_cast<uint32_t>(eviction_effort_cap);
eec_and_scl |= strict_capacit_limit ? kStrictCapacityLimitBit : 0;
return eec_and_scl;
}
} // namespace
void ClockHandleBasicData::FreeData(MemoryAllocator* allocator) const {
@@ -384,17 +402,20 @@ HandleImpl* BaseClockTable::StandaloneInsert(
template <class Table>
typename Table::HandleImpl* BaseClockTable::CreateStandalone(
ClockHandleBasicData& proto, size_t capacity, bool strict_capacity_limit,
ClockHandleBasicData& proto, size_t capacity, uint32_t eec_and_scl,
bool allow_uncharged) {
Table& derived = static_cast<Table&>(*this);
typename Table::InsertState state;
derived.StartInsert(state);
const size_t total_charge = proto.GetTotalCharge();
if (strict_capacity_limit) {
// NOTE: we can use eec_and_scl as eviction_effort_cap below because
// strict_capacity_limit=true is supposed to disable the limit on eviction
// effort, and a large value effectively does that.
if (eec_and_scl & kStrictCapacityLimitBit) {
Status s = ChargeUsageMaybeEvictStrict<Table>(
total_charge, capacity,
/*need_evict_for_occupancy=*/false, state);
/*need_evict_for_occupancy=*/false, eec_and_scl, state);
if (!s.ok()) {
if (allow_uncharged) {
proto.total_charge = 0;
@@ -406,7 +427,7 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
// Case strict_capacity_limit == false
bool success = ChargeUsageMaybeEvictNonStrict<Table>(
total_charge, capacity,
/*need_evict_for_occupancy=*/false, state);
/*need_evict_for_occupancy=*/false, eec_and_scl, state);
if (!success) {
// Force the issue
usage_.FetchAddRelaxed(total_charge);
@@ -419,7 +440,7 @@ typename Table::HandleImpl* BaseClockTable::CreateStandalone(
template <class Table>
Status BaseClockTable::ChargeUsageMaybeEvictStrict(
size_t total_charge, size_t capacity, bool need_evict_for_occupancy,
typename Table::InsertState& state) {
uint32_t eviction_effort_cap, typename Table::InsertState& state) {
if (total_charge > capacity) {
return Status::MemoryLimit(
"Cache entry too large for a single cache shard: " +
@@ -444,7 +465,8 @@ Status BaseClockTable::ChargeUsageMaybeEvictStrict(
}
if (request_evict_charge > 0) {
EvictionData data;
static_cast<Table*>(this)->Evict(request_evict_charge, state, &data);
static_cast<Table*>(this)->Evict(request_evict_charge, state, &data,
eviction_effort_cap);
occupancy_.FetchSub(data.freed_count);
if (LIKELY(data.freed_charge > need_evict_charge)) {
assert(data.freed_count > 0);
@@ -474,7 +496,7 @@ Status BaseClockTable::ChargeUsageMaybeEvictStrict(
template <class Table>
inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
size_t total_charge, size_t capacity, bool need_evict_for_occupancy,
typename Table::InsertState& state) {
uint32_t eviction_effort_cap, typename Table::InsertState& state) {
// For simplicity, we consider that either the cache can accept the insert
// with no evictions, or we must evict enough to make (at least) enough
// space. It could lead to unnecessary failures or excessive evictions in
@@ -510,7 +532,8 @@ inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
}
EvictionData data;
if (need_evict_charge > 0) {
static_cast<Table*>(this)->Evict(need_evict_charge, state, &data);
static_cast<Table*>(this)->Evict(need_evict_charge, state, &data,
eviction_effort_cap);
// Deal with potential occupancy deficit
if (UNLIKELY(need_evict_for_occupancy) && data.freed_count == 0) {
assert(data.freed_charge == 0);
@@ -529,11 +552,7 @@ inline bool BaseClockTable::ChargeUsageMaybeEvictNonStrict(
return true;
}
void BaseClockTable::TrackAndReleaseEvictedEntry(
ClockHandle* h, BaseClockTable::EvictionData* data) {
data->freed_charge += h->GetTotalCharge();
data->freed_count += 1;
void BaseClockTable::TrackAndReleaseEvictedEntry(ClockHandle* h) {
bool took_value_ownership = false;
if (eviction_callback_) {
// For key reconstructed from hash
@@ -550,11 +569,20 @@ void BaseClockTable::TrackAndReleaseEvictedEntry(
MarkEmpty(*h);
}
bool IsEvictionEffortExceeded(const BaseClockTable::EvictionData& data,
uint32_t eviction_effort_cap) {
// Basically checks whether the ratio of useful effort to wasted effort is
// too low, with a start-up allowance for wasted effort before any useful
// effort.
return (data.freed_count + 1U) * uint64_t{eviction_effort_cap} <=
data.seen_pinned_count;
}
template <class Table>
Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
typename Table::HandleImpl** handle,
Cache::Priority priority, size_t capacity,
bool strict_capacity_limit) {
uint32_t eec_and_scl) {
using HandleImpl = typename Table::HandleImpl;
Table& derived = static_cast<Table&>(*this);
@@ -572,9 +600,12 @@ Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
// strict_capacity_limit, but mostly pessimistic.
bool use_standalone_insert = false;
const size_t total_charge = proto.GetTotalCharge();
if (strict_capacity_limit) {
// NOTE: we can use eec_and_scl as eviction_effort_cap below because
// strict_capacity_limit=true is supposed to disable the limit on eviction
// effort, and a large value effectively does that.
if (eec_and_scl & kStrictCapacityLimitBit) {
Status s = ChargeUsageMaybeEvictStrict<Table>(
total_charge, capacity, need_evict_for_occupancy, state);
total_charge, capacity, need_evict_for_occupancy, eec_and_scl, state);
if (!s.ok()) {
// Revert occupancy
occupancy_.FetchSubRelaxed(1);
@@ -583,7 +614,7 @@ Status BaseClockTable::Insert(const ClockHandleBasicData& proto,
} else {
// Case strict_capacity_limit == false
bool success = ChargeUsageMaybeEvictNonStrict<Table>(
total_charge, capacity, need_evict_for_occupancy, state);
total_charge, capacity, need_evict_for_occupancy, eec_and_scl, state);
if (!success) {
// Revert occupancy
occupancy_.FetchSubRelaxed(1);
@@ -687,8 +718,7 @@ void BaseClockTable::TEST_ReleaseNMinus1(ClockHandle* h, size_t n) {
#endif
FixedHyperClockTable::FixedHyperClockTable(
size_t capacity, bool /*strict_capacity_limit*/,
CacheMetadataChargePolicy metadata_charge_policy,
size_t capacity, CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const Opts& opts)
@@ -1083,7 +1113,8 @@ inline void FixedHyperClockTable::ReclaimEntryUsage(size_t total_charge) {
}
inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
EvictionData* data) {
EvictionData* data,
uint32_t eviction_effort_cap) {
// precondition
assert(requested_charge > 0);
@@ -1104,10 +1135,10 @@ inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
for (;;) {
for (size_t i = 0; i < step_size; i++) {
HandleImpl& h = array_[ModTableSize(Lower32of64(old_clock_pointer + i))];
bool evicting = ClockUpdate(h);
bool evicting = ClockUpdate(h, data);
if (evicting) {
Rollback(h.hashed_key, &h);
TrackAndReleaseEvictedEntry(&h, data);
TrackAndReleaseEvictedEntry(&h);
}
}
@@ -1118,6 +1149,10 @@ inline void FixedHyperClockTable::Evict(size_t requested_charge, InsertState&,
if (old_clock_pointer >= max_clock_pointer) {
return;
}
if (IsEvictionEffortExceeded(*data, eviction_effort_cap)) {
eviction_effort_exceeded_count_.FetchAddRelaxed(1);
return;
}
// Advance clock pointer (concurrently)
old_clock_pointer = clock_pointer_.FetchAddRelaxed(step_size);
@@ -1132,10 +1167,11 @@ ClockCacheShard<Table>::ClockCacheShard(
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const typename Table::Opts& opts)
: CacheShardBase(metadata_charge_policy),
table_(capacity, strict_capacity_limit, metadata_charge_policy, allocator,
eviction_callback, hash_seed, opts),
table_(capacity, metadata_charge_policy, allocator, eviction_callback,
hash_seed, opts),
capacity_(capacity),
strict_capacity_limit_(strict_capacity_limit) {
eec_and_scl_(SanitizeEncodeEecAndScl(opts.eviction_effort_cap,
strict_capacity_limit)) {
// Initial charge metadata should not exceed capacity
assert(table_.GetUsage() <= capacity_.LoadRelaxed() ||
capacity_.LoadRelaxed() < sizeof(HandleImpl));
@@ -1211,7 +1247,11 @@ void ClockCacheShard<Table>::SetCapacity(size_t capacity) {
template <class Table>
void ClockCacheShard<Table>::SetStrictCapacityLimit(
bool strict_capacity_limit) {
strict_capacity_limit_.StoreRelaxed(strict_capacity_limit);
if (strict_capacity_limit) {
eec_and_scl_.FetchOrRelaxed(kStrictCapacityLimitBit);
} else {
eec_and_scl_.FetchAndRelaxed(~kStrictCapacityLimitBit);
}
// next Insert will take care of any necessary evictions
}
@@ -1233,7 +1273,7 @@ Status ClockCacheShard<Table>::Insert(const Slice& key,
proto.total_charge = charge;
return table_.template Insert<Table>(proto, handle, priority,
capacity_.LoadRelaxed(),
strict_capacity_limit_.LoadRelaxed());
eec_and_scl_.LoadRelaxed());
}
template <class Table>
@@ -1248,9 +1288,9 @@ typename Table::HandleImpl* ClockCacheShard<Table>::CreateStandalone(
proto.value = obj;
proto.helper = helper;
proto.total_charge = charge;
return table_.template CreateStandalone<Table>(
proto, capacity_.LoadRelaxed(), strict_capacity_limit_.LoadRelaxed(),
allow_uncharged);
return table_.template CreateStandalone<Table>(proto, capacity_.LoadRelaxed(),
eec_and_scl_.LoadRelaxed(),
allow_uncharged);
}
template <class Table>
@@ -1502,14 +1542,20 @@ void BaseHyperClockCache<Table>::ReportProblems(
const std::shared_ptr<Logger>& info_log) const {
if (info_log->GetInfoLogLevel() <= InfoLogLevel::DEBUG_LEVEL) {
LoadVarianceStats slot_stats;
uint64_t eviction_effort_exceeded_count = 0;
this->ForEachShard([&](const BaseHyperClockCache<Table>::Shard* shard) {
size_t count = shard->GetTableAddressCount();
for (size_t i = 0; i < count; ++i) {
slot_stats.Add(IsSlotOccupied(*shard->GetTable().HandlePtr(i)));
}
eviction_effort_exceeded_count +=
shard->GetTable().GetEvictionEffortExceededCount();
});
ROCKS_LOG_AT_LEVEL(info_log, InfoLogLevel::DEBUG_LEVEL,
"Slot occupancy stats: %s", slot_stats.Report().c_str());
ROCKS_LOG_AT_LEVEL(info_log, InfoLogLevel::DEBUG_LEVEL,
"Eviction effort exceeded: %" PRIu64,
eviction_effort_exceeded_count);
}
}
@@ -1907,8 +1953,7 @@ class AutoHyperClockTable::ChainRewriteLock {
};
AutoHyperClockTable::AutoHyperClockTable(
size_t capacity, bool /*strict_capacity_limit*/,
CacheMetadataChargePolicy metadata_charge_policy,
size_t capacity, CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
const Opts& opts)
@@ -2589,7 +2634,8 @@ using ClockUpdateChainLockedOpData =
template <class OpData>
void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
ChainRewriteLock& rewrite_lock,
size_t home) {
size_t home,
BaseClockTable::EvictionData* data) {
constexpr bool kIsPurge = std::is_same_v<OpData, PurgeLockedOpData>;
constexpr bool kIsClockUpdateChain =
std::is_same_v<OpData, ClockUpdateChainLockedOpData>;
@@ -2631,7 +2677,7 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
assert(home == BottomNBits(h->hashed_key[1], home_shift));
if constexpr (kIsClockUpdateChain) {
// Clock update and/or check for purgeable (under (de)construction)
if (ClockUpdate(*h, &purgeable)) {
if (ClockUpdate(*h, data, &purgeable)) {
// Remember for finishing eviction
op_data->push_back(h);
// Entries for eviction become purgeable
@@ -2641,6 +2687,7 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
}
} else {
(void)op_data;
(void)data;
purgeable = ((h->meta.Load() >> ClockHandle::kStateShift) &
ClockHandle::kStateShareableBit) == 0;
}
@@ -2718,7 +2765,8 @@ using PurgeOpData = const UniqueId64x2;
using ClockUpdateChainOpData = ClockUpdateChainLockedOpData;
template <class OpData>
void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home) {
void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home,
BaseClockTable::EvictionData* data) {
// Early efforts to make AutoHCC fully wait-free ran into too many problems
// that needed obscure and potentially inefficient work-arounds to have a
// chance at working.
@@ -2799,9 +2847,9 @@ void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home) {
if (!rewrite_lock.IsEnd()) {
if constexpr (kIsPurge) {
PurgeLockedOpData* locked_op_data{};
PurgeImplLocked(locked_op_data, rewrite_lock, home);
PurgeImplLocked(locked_op_data, rewrite_lock, home, data);
} else {
PurgeImplLocked(op_data, rewrite_lock, home);
PurgeImplLocked(op_data, rewrite_lock, home, data);
}
}
}
@@ -3404,7 +3452,8 @@ void AutoHyperClockTable::EraseUnRefEntries() {
}
void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
EvictionData* data) {
EvictionData* data,
uint32_t eviction_effort_cap) {
// precondition
assert(requested_charge > 0);
@@ -3462,12 +3511,12 @@ void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
if (home >= used_length) {
break;
}
PurgeImpl(&to_finish_eviction, home);
PurgeImpl(&to_finish_eviction, home, data);
}
}
for (HandleImpl* h : to_finish_eviction) {
TrackAndReleaseEvictedEntry(h, data);
TrackAndReleaseEvictedEntry(h);
// NOTE: setting likely_empty_slot here can cause us to reduce the
// portion of "at home" entries, probably because an evicted entry
// is more likely to come back than a random new entry and would be
@@ -3495,6 +3544,11 @@ void AutoHyperClockTable::Evict(size_t requested_charge, InsertState& state,
if (old_clock_pointer + step_size >= max_clock_pointer) {
return;
}
if (IsEvictionEffortExceeded(*data, eviction_effort_cap)) {
eviction_effort_exceeded_count_.FetchAddRelaxed(1);
return;
}
}
}
+49 -19
View File
@@ -11,6 +11,7 @@
#include <array>
#include <atomic>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <memory>
@@ -374,6 +375,14 @@ struct ClockHandle : public ClockHandleBasicData {
class BaseClockTable {
public:
struct BaseOpts {
explicit BaseOpts(int _eviction_effort_cap)
: eviction_effort_cap(_eviction_effort_cap) {}
explicit BaseOpts(const HyperClockCacheOptions& opts)
: BaseOpts(opts.eviction_effort_cap) {}
int eviction_effort_cap;
};
BaseClockTable(CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -386,13 +395,13 @@ class BaseClockTable {
template <class Table>
typename Table::HandleImpl* CreateStandalone(ClockHandleBasicData& proto,
size_t capacity,
bool strict_capacity_limit,
uint32_t eec_and_scl,
bool allow_uncharged);
template <class Table>
Status Insert(const ClockHandleBasicData& proto,
typename Table::HandleImpl** handle, Cache::Priority priority,
size_t capacity, bool strict_capacity_limit);
size_t capacity, uint32_t eec_and_scl);
void Ref(ClockHandle& handle);
@@ -406,12 +415,17 @@ class BaseClockTable {
uint64_t GetYieldCount() const { return yield_count_.LoadRelaxed(); }
uint64_t GetEvictionEffortExceededCount() const {
return eviction_effort_exceeded_count_.LoadRelaxed();
}
struct EvictionData {
size_t freed_charge = 0;
size_t freed_count = 0;
size_t seen_pinned_count = 0;
};
void TrackAndReleaseEvictedEntry(ClockHandle* h, EvictionData* data);
void TrackAndReleaseEvictedEntry(ClockHandle* h);
#ifndef NDEBUG
// Acquire N references
@@ -436,6 +450,7 @@ class BaseClockTable {
template <class Table>
Status ChargeUsageMaybeEvictStrict(size_t total_charge, size_t capacity,
bool need_evict_for_occupancy,
uint32_t eviction_effort_cap,
typename Table::InsertState& state);
// Helper for updating `usage_` for new entry with given `total_charge`
@@ -449,6 +464,7 @@ class BaseClockTable {
template <class Table>
bool ChargeUsageMaybeEvictNonStrict(size_t total_charge, size_t capacity,
bool need_evict_for_occupancy,
uint32_t eviction_effort_cap,
typename Table::InsertState& state);
protected: // data
@@ -461,9 +477,15 @@ class BaseClockTable {
RelaxedAtomic<uint64_t> clock_pointer_{};
// Counter for number of times we yield to wait on another thread.
// It is normal for this to occur rarely in normal operation.
// (Relaxed: a simple stat counter.)
RelaxedAtomic<uint64_t> yield_count_{};
// Counter for number of times eviction effort cap is exceeded.
// It is normal for this to occur rarely in normal operation.
// (Relaxed: a simple stat counter.)
RelaxedAtomic<uint64_t> eviction_effort_exceeded_count_{};
// TODO: is this separation needed if we don't do background evictions?
ALIGN_AS(CACHE_LINE_SIZE)
// Number of elements in the table.
@@ -517,17 +539,19 @@ class FixedHyperClockTable : public BaseClockTable {
inline void SetStandalone() { standalone = true; }
}; // struct HandleImpl
struct Opts {
explicit Opts(size_t _estimated_value_size)
: estimated_value_size(_estimated_value_size) {}
explicit Opts(const HyperClockCacheOptions& opts) {
struct Opts : public BaseOpts {
explicit Opts(size_t _estimated_value_size, int _eviction_effort_cap)
: BaseOpts(_eviction_effort_cap),
estimated_value_size(_estimated_value_size) {}
explicit Opts(const HyperClockCacheOptions& opts)
: BaseOpts(opts.eviction_effort_cap) {
assert(opts.estimated_entry_charge > 0);
estimated_value_size = opts.estimated_entry_charge;
}
size_t estimated_value_size;
};
FixedHyperClockTable(size_t capacity, bool strict_capacity_limit,
FixedHyperClockTable(size_t capacity,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -549,7 +573,8 @@ class FixedHyperClockTable : public BaseClockTable {
// Runs the clock eviction algorithm trying to reclaim at least
// requested_charge. Returns how much is evicted, which could be less
// if it appears impossible to evict the requested amount without blocking.
void Evict(size_t requested_charge, InsertState& state, EvictionData* data);
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
uint32_t eviction_effort_cap);
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
@@ -803,18 +828,20 @@ class AutoHyperClockTable : public BaseClockTable {
}
}; // struct HandleImpl
struct Opts {
explicit Opts(size_t _min_avg_value_size)
: min_avg_value_size(_min_avg_value_size) {}
struct Opts : public BaseOpts {
explicit Opts(size_t _min_avg_value_size, int _eviction_effort_cap)
: BaseOpts(_eviction_effort_cap),
min_avg_value_size(_min_avg_value_size) {}
explicit Opts(const HyperClockCacheOptions& opts) {
explicit Opts(const HyperClockCacheOptions& opts)
: BaseOpts(opts.eviction_effort_cap) {
assert(opts.estimated_entry_charge == 0);
min_avg_value_size = opts.min_avg_entry_charge;
}
size_t min_avg_value_size;
};
AutoHyperClockTable(size_t capacity, bool strict_capacity_limit,
AutoHyperClockTable(size_t capacity,
CacheMetadataChargePolicy metadata_charge_policy,
MemoryAllocator* allocator,
const Cache::EvictionCallback* eviction_callback,
@@ -841,7 +868,8 @@ class AutoHyperClockTable : public BaseClockTable {
// Runs the clock eviction algorithm trying to reclaim at least
// requested_charge. Returns how much is evicted, which could be less
// if it appears impossible to evict the requested amount without blocking.
void Evict(size_t requested_charge, InsertState& state, EvictionData* data);
void Evict(size_t requested_charge, InsertState& state, EvictionData* data,
uint32_t eviction_effort_cap);
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
@@ -906,7 +934,8 @@ class AutoHyperClockTable : public BaseClockTable {
// with proper handling to ensure all existing data is seen even in the
// presence of concurrent insertions, etc. (See implementation.)
template <class OpData>
void PurgeImpl(OpData* op_data, size_t home = SIZE_MAX);
void PurgeImpl(OpData* op_data, size_t home = SIZE_MAX,
EvictionData* data = nullptr);
// An RAII wrapper for locking a chain of entries for removals. See
// implementation.
@@ -916,7 +945,7 @@ class AutoHyperClockTable : public BaseClockTable {
// implementation.
template <class OpData>
void PurgeImplLocked(OpData* op_data, ChainRewriteLock& rewrite_lock,
size_t home);
size_t home, EvictionData* data);
// Update length_info_ as much as possible without waiting, given a known
// usable (ready for inserts and lookups) grow_home. (Previous grow_homes
@@ -1078,9 +1107,10 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
// (Relaxed: eventual consistency/update is OK)
RelaxedAtomic<size_t> capacity_;
// Whether to reject insertion if cache reaches its full capacity.
// Encodes eviction_effort_cap (bottom 31 bits) and strict_capacity_limit
// (top bit). See HyperClockCacheOptions::eviction_effort_cap etc.
// (Relaxed: eventual consistency/update is OK)
RelaxedAtomic<bool> strict_capacity_limit_;
RelaxedAtomic<uint32_t> eec_and_scl_;
}; // class ClockCacheShard
template <class Table>
+3 -1
View File
@@ -31,7 +31,7 @@ CompressedSecondaryCache::~CompressedSecondaryCache() {}
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
bool& kept_in_sec_cache) {
Statistics* stats, bool& kept_in_sec_cache) {
assert(helper);
// This is a minor optimization. Its ok to skip it in TSAN in order to
// avoid a false positive.
@@ -51,6 +51,7 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
void* handle_value = cache_->Value(lru_handle);
if (handle_value == nullptr) {
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_DUMMY_HITS);
return nullptr;
}
@@ -137,6 +138,7 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
}
handle.reset(new CompressedSecondaryCacheResultHandle(value, charge));
RecordTick(stats, COMPRESSED_SECONDARY_CACHE_HITS);
return handle;
}
+1 -1
View File
@@ -86,7 +86,7 @@ class CompressedSecondaryCache : public SecondaryCache {
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
bool& kept_in_sec_cache) override;
Statistics* stats, bool& kept_in_sec_cache) override;
bool SupportForceErase() const override { return true; }
+15 -13
View File
@@ -44,7 +44,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
// Lookup an non-existent key.
std::unique_ptr<SecondaryCacheResultHandle> handle0 =
sec_cache->Lookup(key0, GetHelper(), this, true, /*advise_erase=*/true,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle0, nullptr);
Random rnd(301);
@@ -59,7 +59,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle1_1, nullptr);
// Insert and Lookup the item k1 for the second time and advise erasing it.
@@ -68,7 +68,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle1_2, nullptr);
ASSERT_FALSE(kept_in_sec_cache);
if (sec_cache_is_compressed) {
@@ -89,7 +89,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
// Lookup the item k1 again.
std::unique_ptr<SecondaryCacheResultHandle> handle1_3 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle1_3, nullptr);
// Insert and Lookup the item k2.
@@ -99,7 +99,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 2);
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle2_1, nullptr);
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
@@ -115,7 +115,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
}
std::unique_ptr<SecondaryCacheResultHandle> handle2_2 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle2_2, nullptr);
std::unique_ptr<TestItem> val2 =
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2_2->Value()));
@@ -196,14 +196,14 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
bool kept_in_sec_cache{false};
std::unique_ptr<SecondaryCacheResultHandle> handle1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle1, nullptr);
// Insert k2 and k1 is evicted.
ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
std::unique_ptr<SecondaryCacheResultHandle> handle2 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle2, nullptr);
std::unique_ptr<TestItem> val2 =
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2->Value()));
@@ -215,14 +215,14 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle1_1, nullptr);
// Create Fails.
SetFailCreate(true);
std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/true,
kept_in_sec_cache);
/*stats=*/nullptr, kept_in_sec_cache);
ASSERT_EQ(handle2_1, nullptr);
// Save Fails.
@@ -912,9 +912,9 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1U);
bool kept_in_sec_cache{true};
std::unique_ptr<SecondaryCacheResultHandle> handle =
sec_cache->Lookup(ith_key, GetHelper(role), this, true,
/*advise_erase=*/true, kept_in_sec_cache);
std::unique_ptr<SecondaryCacheResultHandle> handle = sec_cache->Lookup(
ith_key, GetHelper(role), this, true,
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
ASSERT_NE(handle, nullptr);
// Lookup returns the right data
@@ -992,6 +992,8 @@ class CompressedSecCacheTestWithTiered
/*_capacity=*/0,
/*_estimated_entry_charge=*/256 << 10,
/*_num_shard_bits=*/0);
// eviction_effort_cap setting simply to avoid churn in existing test
hcc_opts.eviction_effort_cap = 100;
TieredCacheOptions opts;
lru_opts.capacity = 0;
lru_opts.num_shard_bits = 0;
+60 -3
View File
@@ -389,12 +389,13 @@ class ClockCacheTest : public testing::Test {
}
}
void NewShard(size_t capacity, bool strict_capacity_limit = true) {
void NewShard(size_t capacity, bool strict_capacity_limit = true,
int eviction_effort_cap = 30) {
DeleteShard();
shard_ =
reinterpret_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
TableOpts opts{1 /*value_size*/};
TableOpts opts{1 /*value_size*/, eviction_effort_cap};
new (shard_)
Shard(capacity, strict_capacity_limit, kDontChargeCacheMetadata,
/*allocator*/ nullptr, &eviction_callback_, &hash_seed_, opts);
@@ -445,12 +446,20 @@ class ClockCacheTest : public testing::Test {
return Slice(reinterpret_cast<const char*>(&hashed_key), 16U);
}
// A bad hash function for testing / stressing collision handling
static inline UniqueId64x2 TestHashedKey(char key) {
// For testing hash near-collision behavior, put the variance in
// hashed_key in bits that are unlikely to be used as hash bits.
return {(static_cast<uint64_t>(key) << 56) + 1234U, 5678U};
}
// A reasonable hash function, for testing "typical behavior" etc.
template <typename T>
static inline UniqueId64x2 CheapHash(T i) {
return {static_cast<uint64_t>(i) * uint64_t{0x85EBCA77C2B2AE63},
static_cast<uint64_t>(i) * uint64_t{0xC2B2AE3D27D4EB4F}};
}
Shard* shard_ = nullptr;
private:
@@ -683,6 +692,53 @@ TYPED_TEST(ClockCacheTest, ClockEvictionTest) {
}
}
TYPED_TEST(ClockCacheTest, ClockEvictionEffortCapTest) {
using HandleImpl = typename ClockCacheTest<TypeParam>::Shard::HandleImpl;
for (bool strict_capacity_limit : {true, false}) {
SCOPED_TRACE("strict_capacity_limit = " +
std::to_string(strict_capacity_limit));
for (int eec : {-42, 0, 1, 10, 100, 1000}) {
SCOPED_TRACE("eviction_effort_cap = " + std::to_string(eec));
constexpr size_t kCapacity = 1000;
// Start with much larger capacity to ensure that we can go way over
// capacity without reaching table occupancy limit.
this->NewShard(3 * kCapacity, strict_capacity_limit, eec);
auto& shard = *this->shard_;
shard.SetCapacity(kCapacity);
// Nearly fill the cache with pinned entries, then add a bunch of
// non-pinned entries. eviction_effort_cap should affect how many
// evictable entries are present beyond the cache capacity, despite
// being evictable.
constexpr size_t kCount = kCapacity - 1;
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [kCount] {} };
for (size_t i = 0; i < 2 * kCount; ++i) {
UniqueId64x2 hkey = this->CheapHash(i);
ASSERT_OK(shard.Insert(
this->TestKey(hkey), hkey, nullptr /*value*/, &kNoopCacheItemHelper,
1 /*charge*/, i < kCount ? &ha[i] : nullptr, Cache::Priority::LOW));
}
if (strict_capacity_limit) {
// If strict_capacity_limit is enabled, the cache will never exceed its
// capacity
EXPECT_EQ(shard.GetOccupancyCount(), kCapacity);
} else {
// Rough inverse relationship between cap and possible memory
// explosion, which shows up as increased table occupancy count.
int effective_eec = std::max(int{1}, eec) + 1;
EXPECT_NEAR(shard.GetOccupancyCount() * 1.0,
kCount * (1 + 1.4 / effective_eec),
kCount * (0.6 / effective_eec) + 1.0);
}
for (size_t i = 0; i < kCount; ++i) {
shard.Release(ha[i]);
}
}
}
}
namespace {
struct DeleteCounter {
int deleted = 0;
@@ -1035,7 +1091,8 @@ class TestSecondaryCache : public SecondaryCache {
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool /*wait*/,
bool /*advise_erase*/, bool& kept_in_sec_cache) override {
bool /*advise_erase*/, Statistics* /*stats*/,
bool& kept_in_sec_cache) override {
std::string key_str = key.ToString();
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
+8 -5
View File
@@ -294,7 +294,8 @@ Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
bool kept_in_sec_cache = false;
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
secondary_cache_->Lookup(key, helper, create_context, /*wait*/ true,
found_dummy_entry, /*out*/ kept_in_sec_cache);
found_dummy_entry, stats,
/*out*/ kept_in_sec_cache);
if (secondary_handle) {
result = Promote(std::move(secondary_handle), key, helper, priority,
stats, found_dummy_entry, kept_in_sec_cache);
@@ -348,10 +349,10 @@ void CacheWithSecondaryAdapter::StartAsyncLookupOnMySecondary(
assert(async_handle.result_handle == nullptr);
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
secondary_cache_->Lookup(async_handle.key, async_handle.helper,
async_handle.create_context, /*wait*/ false,
async_handle.found_dummy_entry,
/*out*/ async_handle.kept_in_sec_cache);
secondary_cache_->Lookup(
async_handle.key, async_handle.helper, async_handle.create_context,
/*wait*/ false, async_handle.found_dummy_entry, async_handle.stats,
/*out*/ async_handle.kept_in_sec_cache);
if (secondary_handle) {
// TODO with stacked secondaries: Check & process if already ready?
async_handle.pending_handle = secondary_handle.release();
@@ -683,12 +684,14 @@ std::shared_ptr<Cache> NewTieredCache(const TieredCacheOptions& _opts) {
*(static_cast_with_check<LRUCacheOptions, ShardedCacheOptions>(
opts.cache_opts));
cache_opts.capacity = opts.total_capacity;
cache_opts.secondary_cache = nullptr;
cache = cache_opts.MakeSharedCache();
} else if (opts.cache_type == PrimaryCacheType::kCacheTypeHCC) {
HyperClockCacheOptions cache_opts =
*(static_cast_with_check<HyperClockCacheOptions, ShardedCacheOptions>(
opts.cache_opts));
cache_opts.capacity = opts.total_capacity;
cache_opts.secondary_cache = nullptr;
cache = cache_opts.MakeSharedCache();
} else {
return nullptr;
+15 -9
View File
@@ -5,6 +5,8 @@
#include "cache/tiered_secondary_cache.h"
#include "monitoring/statistics_impl.h"
namespace ROCKSDB_NAMESPACE {
// Creation callback for use in the lookup path. It calls the upper layer
@@ -29,6 +31,9 @@ Status TieredSecondaryCache::MaybeInsertAndCreate(
// TODO: Don't hardcode the source
context->comp_sec_cache->InsertSaved(*context->key, data, type, source)
.PermitUncheckedError();
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTIONS);
} else {
RecordTick(context->stats, COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS);
}
// Primary cache will accept the object, so call its helper to create
// the object
@@ -43,10 +48,10 @@ Status TieredSecondaryCache::MaybeInsertAndCreate(
std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
bool& kept_in_sec_cache) {
Statistics* stats, bool& kept_in_sec_cache) {
bool dummy = false;
std::unique_ptr<SecondaryCacheResultHandle> result =
target()->Lookup(key, helper, create_context, wait, advise_erase,
target()->Lookup(key, helper, create_context, wait, advise_erase, stats,
/*kept_in_sec_cache=*/dummy);
// We never want the item to spill back into the secondary cache
kept_in_sec_cache = true;
@@ -66,9 +71,10 @@ std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
ctx.helper = helper;
ctx.inner_ctx = create_context;
ctx.comp_sec_cache = target();
ctx.stats = stats;
return nvm_sec_cache_->Lookup(key, outer_helper, &ctx, wait, advise_erase,
kept_in_sec_cache);
stats, kept_in_sec_cache);
}
// If wait is false, i.e its an async lookup, we have to allocate a result
@@ -80,8 +86,10 @@ std::unique_ptr<SecondaryCacheResultHandle> TieredSecondaryCache::Lookup(
handle->ctx()->helper = helper;
handle->ctx()->inner_ctx = create_context;
handle->ctx()->comp_sec_cache = target();
handle->SetInnerHandle(nvm_sec_cache_->Lookup(
key, outer_helper, handle->ctx(), wait, advise_erase, kept_in_sec_cache));
handle->ctx()->stats = stats;
handle->SetInnerHandle(
nvm_sec_cache_->Lookup(key, outer_helper, handle->ctx(), wait,
advise_erase, stats, kept_in_sec_cache));
if (!handle->inner_handle()) {
handle.reset();
} else {
@@ -109,10 +117,8 @@ void TieredSecondaryCache::WaitAll(
}
nvm_sec_cache_->WaitAll(nvm_handles);
for (auto handle : my_handles) {
assert(handle->IsReady());
auto nvm_handle = handle->inner_handle();
handle->SetSize(nvm_handle->Size());
handle->SetValue(nvm_handle->Value());
assert(handle->inner_handle()->IsReady());
handle->Complete();
}
}
+8 -3
View File
@@ -59,7 +59,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
bool& kept_in_sec_cache) override;
Statistics* stats, bool& kept_in_sec_cache) override;
virtual void WaitAll(
std::vector<SecondaryCacheResultHandle*> handles) override;
@@ -72,6 +72,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
Cache::CreateContext* inner_ctx;
std::shared_ptr<SecondaryCacheResultHandle> inner_handle;
SecondaryCache* comp_sec_cache;
Statistics* stats;
};
class ResultHandle : public SecondaryCacheResultHandle {
@@ -79,7 +80,10 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
~ResultHandle() override {}
bool IsReady() override {
return !inner_handle_ || inner_handle_->IsReady();
if (inner_handle_ && inner_handle_->IsReady()) {
Complete();
}
return ready_;
}
void Wait() override {
@@ -92,10 +96,10 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
Cache::ObjectPtr Value() override { return value_; }
void Complete() {
assert(IsReady());
size_ = inner_handle_->Size();
value_ = inner_handle_->Value();
inner_handle_.reset();
ready_ = true;
}
void SetInnerHandle(std::unique_ptr<SecondaryCacheResultHandle>&& handle) {
@@ -115,6 +119,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
CreateContext ctx_;
size_t size_;
Cache::ObjectPtr value_;
bool ready_ = false;
};
static void NoopDelete(Cache::ObjectPtr /*obj*/,
+130 -10
View File
@@ -15,10 +15,11 @@ namespace ROCKSDB_NAMESPACE {
class TestSecondaryCache : public SecondaryCache {
public:
explicit TestSecondaryCache(size_t capacity)
explicit TestSecondaryCache(size_t capacity, bool ready_before_wait)
: cache_(NewLRUCache(capacity, 0, false, 0.5 /* high_pri_pool_ratio */,
nullptr, kDefaultToAdaptiveMutex,
kDontChargeCacheMetadata)),
ready_before_wait_(ready_before_wait),
num_insert_saved_(0),
num_hits_(0),
num_misses_(0) {}
@@ -61,7 +62,7 @@ class TestSecondaryCache : public SecondaryCache {
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool /*advise_erase*/,
bool& kept_in_sec_cache) override {
Statistics* /*stats*/, bool& kept_in_sec_cache) override {
std::string key_str = key.ToString();
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
@@ -88,7 +89,8 @@ class TestSecondaryCache : public SecondaryCache {
/*alloc*/ nullptr, &value, &charge);
if (s.ok()) {
secondary_handle.reset(new TestSecondaryCacheResultHandle(
cache_.get(), handle, value, charge, /*ready=*/wait));
cache_.get(), handle, value, charge,
/*ready=*/wait || ready_before_wait_));
kept_in_sec_cache = true;
} else {
cache_.Release(handle);
@@ -168,6 +170,7 @@ class TestSecondaryCache : public SecondaryCache {
BasicTypedSharedCacheInterface<char[], CacheEntryRole::kMisc>;
using TypedHandle = SharedCache::TypedHandle;
SharedCache cache_;
bool ready_before_wait_;
uint32_t num_insert_saved_;
uint32_t num_hits_;
uint32_t num_misses_;
@@ -179,11 +182,10 @@ class DBTieredSecondaryCacheTest : public DBTestBase {
DBTieredSecondaryCacheTest()
: DBTestBase("db_tiered_secondary_cache_test", /*env_do_fsync=*/true) {}
std::shared_ptr<Cache> NewCache(size_t pri_capacity,
size_t compressed_capacity,
size_t nvm_capacity,
TieredAdmissionPolicy adm_policy =
TieredAdmissionPolicy::kAdmPolicyAuto) {
std::shared_ptr<Cache> NewCache(
size_t pri_capacity, size_t compressed_capacity, size_t nvm_capacity,
TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto,
bool ready_before_wait = false) {
LRUCacheOptions lru_opts;
TieredCacheOptions opts;
lru_opts.capacity = 0;
@@ -194,10 +196,11 @@ class DBTieredSecondaryCacheTest : public DBTestBase {
opts.comp_cache_opts.capacity = 0;
opts.comp_cache_opts.num_shard_bits = 0;
opts.total_capacity = pri_capacity + compressed_capacity;
opts.compressed_secondary_ratio =
opts.compressed_secondary_ratio = compressed_secondary_ratio_ =
(double)compressed_capacity / opts.total_capacity;
if (nvm_capacity > 0) {
nvm_sec_cache_.reset(new TestSecondaryCache(nvm_capacity));
nvm_sec_cache_.reset(
new TestSecondaryCache(nvm_capacity, ready_before_wait));
opts.nvm_sec_cache = nvm_sec_cache_;
}
opts.adm_policy = adm_policy;
@@ -207,6 +210,12 @@ class DBTieredSecondaryCacheTest : public DBTestBase {
return cache_;
}
void ClearPrimaryCache() {
ASSERT_EQ(UpdateTieredCache(cache_, -1, 1.0), Status::OK());
ASSERT_EQ(UpdateTieredCache(cache_, -1, compressed_secondary_ratio_),
Status::OK());
}
TestSecondaryCache* nvm_sec_cache() { return nvm_sec_cache_.get(); }
CompressedSecondaryCache* compressed_secondary_cache() {
@@ -218,6 +227,7 @@ class DBTieredSecondaryCacheTest : public DBTestBase {
private:
std::shared_ptr<Cache> cache_;
std::shared_ptr<TestSecondaryCache> nvm_sec_cache_;
double compressed_secondary_ratio_;
};
// In this test, the block size is set to 4096. Each value is 1007 bytes, so
@@ -582,6 +592,116 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
Destroy(options);
}
TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
if (!LZ4_Supported()) {
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
return;
}
BlockBasedTableOptions table_options;
table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024,
TieredAdmissionPolicy::kAdmPolicyAuto,
/*ready_before_wait=*/true);
table_options.block_size = 4 * 1024;
table_options.cache_index_and_filter_blocks = false;
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.statistics = CreateDBStatistics();
options.paranoid_file_checks = false;
DestroyAndReopen(options);
Random rnd(301);
const int N = 256;
for (int i = 0; i < N; i++) {
std::string p_v;
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
ASSERT_OK(Put(Key(i), p_v));
}
ASSERT_OK(Flush());
std::vector<std::string> keys;
std::vector<std::string> values;
keys.push_back(Key(0));
keys.push_back(Key(4));
keys.push_back(Key(8));
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
ASSERT_EQ(values.size(), keys.size());
for (auto value : values) {
ASSERT_EQ(1007, value.size());
}
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 3u);
keys.clear();
values.clear();
keys.push_back(Key(12));
keys.push_back(Key(16));
keys.push_back(Key(20));
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
ASSERT_EQ(values.size(), keys.size());
for (auto value : values) {
ASSERT_EQ(1007, value.size());
}
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
keys.clear();
values.clear();
keys.push_back(Key(0));
keys.push_back(Key(4));
keys.push_back(Key(8));
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
ASSERT_EQ(values.size(), keys.size());
for (auto value : values) {
ASSERT_EQ(1007, value.size());
}
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
ClearPrimaryCache();
keys.clear();
values.clear();
keys.push_back(Key(0));
keys.push_back(Key(32));
keys.push_back(Key(36));
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
ASSERT_EQ(values.size(), keys.size());
for (auto value : values) {
ASSERT_EQ(1007, value.size());
}
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
keys.clear();
values.clear();
keys.push_back(Key(0));
keys.push_back(Key(32));
keys.push_back(Key(36));
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
ASSERT_EQ(values.size(), keys.size());
for (auto value : values) {
ASSERT_EQ(1007, value.size());
}
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
Destroy(options);
}
// This test is for iteration. It iterates through a set of keys in two
// passes. First pass loads the compressed blocks into the nvm tier, and
// the second pass should hit all of those blocks.
+2 -2
View File
@@ -1220,7 +1220,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
auto sec_handle0 = secondary_cache->Lookup(
key0, BlobSource::SharedCacheInterface::GetFullHelper(),
/*context*/ nullptr, true,
/*advise_erase=*/true, kept_in_sec_cache);
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
ASSERT_FALSE(kept_in_sec_cache);
ASSERT_NE(sec_handle0, nullptr);
ASSERT_TRUE(sec_handle0->IsReady());
@@ -1248,7 +1248,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
auto sec_handle1 = secondary_cache->Lookup(
key1, BlobSource::SharedCacheInterface::GetFullHelper(),
/*context*/ nullptr, true,
/*advise_erase=*/true, kept_in_sec_cache);
/*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
ASSERT_FALSE(kept_in_sec_cache);
ASSERT_EQ(sec_handle1, nullptr);
+45 -15
View File
@@ -833,8 +833,8 @@ std::unique_ptr<WriteControllerToken> SetupDelay(
return write_controller->GetDelayToken(write_rate);
}
int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
int level0_slowdown_writes_trigger) {
int GetL0FileCountForCompactionSpeedup(int level0_file_num_compaction_trigger,
int level0_slowdown_writes_trigger) {
// SanitizeOptions() ensures it.
assert(level0_file_num_compaction_trigger <= level0_slowdown_writes_trigger);
@@ -864,6 +864,36 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
return static_cast<int>(res);
}
}
uint64_t GetPendingCompactionBytesForCompactionSpeedup(
const MutableCFOptions& mutable_cf_options,
const VersionStorageInfo* vstorage) {
// Compaction debt relatively large compared to the stable (bottommost) data
// size indicates compaction fell behind.
const uint64_t kBottommostSizeMultiplier = 8;
// Meaningful progress toward the slowdown trigger is another good indication.
const uint64_t kSlowdownTriggerDivisor = 4;
uint64_t bottommost_files_size = 0;
for (const auto& level_and_file : vstorage->BottommostFiles()) {
bottommost_files_size += level_and_file.second->fd.GetFileSize();
}
// Slowdown trigger might be zero but that means compaction speedup should
// always happen (undocumented/historical), so no special treatment is needed.
uint64_t slowdown_threshold =
mutable_cf_options.soft_pending_compaction_bytes_limit /
kSlowdownTriggerDivisor;
// Size of zero, however, should not be used to decide to speedup compaction.
if (bottommost_files_size == 0) {
return slowdown_threshold;
}
uint64_t size_threshold =
MultiplyCheckOverflow(bottommost_files_size, kBottommostSizeMultiplier);
return std::min(size_threshold, slowdown_threshold);
}
} // anonymous namespace
std::pair<WriteStallCondition, WriteStallCause>
@@ -1019,7 +1049,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
} else {
assert(write_stall_condition == WriteStallCondition::kNormal);
if (vstorage->l0_delay_trigger_count() >=
GetL0ThresholdSpeedupCompaction(
GetL0FileCountForCompactionSpeedup(
mutable_cf_options.level0_file_num_compaction_trigger,
mutable_cf_options.level0_slowdown_writes_trigger)) {
write_controller_token_ =
@@ -1029,22 +1059,22 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
"[%s] Increasing compaction threads because we have %d level-0 "
"files ",
name_.c_str(), vstorage->l0_delay_trigger_count());
} else if (vstorage->estimated_compaction_needed_bytes() >=
mutable_cf_options.soft_pending_compaction_bytes_limit / 4) {
// Increase compaction threads if bytes needed for compaction exceeds
// 1/4 of threshold for slowing down.
} else if (mutable_cf_options.soft_pending_compaction_bytes_limit == 0) {
// If soft pending compaction byte limit is not set, always speed up
// compaction.
write_controller_token_ =
write_controller->GetCompactionPressureToken();
if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0) {
ROCKS_LOG_INFO(
ioptions_.logger,
"[%s] Increasing compaction threads because of estimated pending "
"compaction "
"bytes %" PRIu64,
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
}
} else if (vstorage->estimated_compaction_needed_bytes() >=
GetPendingCompactionBytesForCompactionSpeedup(
mutable_cf_options, vstorage)) {
write_controller_token_ =
write_controller->GetCompactionPressureToken();
ROCKS_LOG_INFO(
ioptions_.logger,
"[%s] Increasing compaction threads because of estimated pending "
"compaction "
"bytes %" PRIu64,
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
} else {
write_controller_token_.reset();
}
+37
View File
@@ -2971,6 +2971,43 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
}
TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
db_options_.max_background_compactions = 6;
Open();
ColumnFamilyData* cfd =
static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())->cfd();
MutableCFOptions mutable_cf_options(column_family_options_);
mutable_cf_options.soft_pending_compaction_bytes_limit =
std::numeric_limits<uint64_t>::max();
{
// No bottommost data, so debt ratio cannot trigger speedup.
VersionStorageInfo* vstorage = cfd->current()->storage_info();
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
}
// Add a tiny amount of bottommost data.
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
ASSERT_OK(db_->Flush(FlushOptions()));
{
// 1MB debt is way bigger than bottommost data so definitely triggers
// speedup.
VersionStorageInfo* vstorage = cfd->current()->storage_info();
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
// Eight bytes is way smaller than bottommost data so definitely does not
// trigger speedup.
vstorage->TEST_set_estimated_compaction_needed_bytes(8);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
}
}
TEST_P(ColumnFamilyTest, CreateAndDestroyOptions) {
std::unique_ptr<ColumnFamilyOptions> cfo(new ColumnFamilyOptions());
ColumnFamilyHandle* cfh;
+19 -19
View File
@@ -3301,7 +3301,7 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FlushFinish:0");
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
// There is no need to do these clean up if the flush job is rescheduled
// There is no need to find obsolete files if the flush job is rescheduled
// to retain user-defined timestamps because the job doesn't get to the
// stage of actually flushing the MemTables.
if (!flush_rescheduled_to_retain_udt) {
@@ -3309,25 +3309,25 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
// have created. Thus, we force full scan in FindObsoleteFiles()
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
!s.IsColumnFamilyDropped());
// delete unnecessary files if any, this is done outside the mutex
if (job_context.HaveSomethingToClean() ||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
mutex_.Unlock();
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
// Have to flush the info logs before bg_flush_scheduled_--
// because if bg_flush_scheduled_ becomes 0 and the lock is
// released, the deconstructor of DB can kick in and destroy all the
// states of DB so info_log might not be available after that point.
// It also applies to access other states that DB owns.
log_buffer.FlushBufferToLog();
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
}
job_context.Clean();
mutex_.Lock();
}
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:ContextCleanedUp");
}
// delete unnecessary files if any, this is done outside the mutex
if (job_context.HaveSomethingToClean() ||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
mutex_.Unlock();
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
// Have to flush the info logs before bg_flush_scheduled_--
// because if bg_flush_scheduled_ becomes 0 and the lock is
// released, the deconstructor of DB can kick in and destroy all the
// states of DB so info_log might not be available after that point.
// It also applies to access other states that DB owns.
log_buffer.FlushBufferToLog();
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
}
job_context.Clean();
mutex_.Lock();
}
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:ContextCleanedUp");
assert(num_running_flushes_ > 0);
num_running_flushes_--;
+6 -5
View File
@@ -437,10 +437,10 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
valid_batches += writer->batch_cnt;
if (writer->ShouldWriteToMemtable()) {
total_count += WriteBatchInternal::Count(writer->batch);
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
parallel = parallel && !writer->batch->HasMerge();
}
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
if (writer->pre_release_callback) {
pre_release_callback_cnt++;
}
@@ -720,11 +720,11 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
if (writer->ShouldWriteToMemtable()) {
writer->sequence = next_sequence;
size_t count = WriteBatchInternal::Count(writer->batch);
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
next_sequence += count;
total_count += count;
}
total_byte_size = WriteBatchInternal::AppendedByteSize(
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
}
}
if (w.disable_wal) {
@@ -1839,11 +1839,12 @@ Status DBImpl::DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
delay = 0;
}
TEST_SYNC_POINT("DBImpl::DelayWrite:Start");
start_time = immutable_db_options_.clock->NowMicros();
if (delay > 0) {
if (write_options.no_slowdown) {
return Status::Incomplete("Write stall");
}
start_time = immutable_db_options_.clock->NowMicros();
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
// Notify write_thread about the stall so it can setup a barrier and
+16 -32
View File
@@ -83,8 +83,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
cfd_(cfd),
timestamp_ub_(read_options.timestamp),
timestamp_lb_(read_options.iter_start_ts),
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0),
auto_readahead_size_(read_options.auto_readahead_size) {
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
RecordTick(statistics_, NO_ITERATOR_CREATED);
if (pin_thru_lifetime_) {
pinned_iters_mgr_.StartPinning();
@@ -395,9 +394,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
case kTypeValue:
case kTypeBlobIndex:
case kTypeWideColumnEntity:
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
if (!PrepareValue()) {
return false;
}
if (timestamp_lb_) {
@@ -428,9 +425,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
return true;
break;
case kTypeMerge:
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
if (!PrepareValue()) {
return false;
}
saved_key_.SetUserKey(
@@ -575,8 +570,7 @@ bool DBIter::MergeValuesNewToOld() {
iter_.Next();
break;
}
if (!iter_.PrepareValue()) {
valid_ = false;
if (!PrepareValue()) {
return false;
}
@@ -747,22 +741,15 @@ bool DBIter::ReverseToBackward() {
// When current_entry_is_merged_ is true, iter_ may be positioned on the next
// key, which may not exist or may have prefix different from current.
// If that's the case, seek to saved_key_.
//
// In case of auto_readahead_size enabled, index_iter moves forward during
// forward scan for block cache lookup and points to different block. If Prev
// op is called, it needs to call SeekForPrev to point to right index_iter_ in
// BlockBasedTableIterator. This only happens when direction is changed from
// forward to backward.
if ((current_entry_is_merged_ &&
(!expect_total_order_inner_iter() || !iter_.Valid())) ||
auto_readahead_size_) {
if (current_entry_is_merged_ &&
(!expect_total_order_inner_iter() || !iter_.Valid())) {
IterKey last_key;
// Using kMaxSequenceNumber and kValueTypeForSeek
// (not kValueTypeForSeekForPrev) to seek to a key strictly smaller
// than saved_key_.
last_key.SetInternalKey(ParsedInternalKey(
saved_key_.GetUserKey(), kMaxSequenceNumber, kValueTypeForSeek));
if (!expect_total_order_inner_iter() || auto_readahead_size_) {
if (!expect_total_order_inner_iter()) {
iter_.SeekForPrev(last_key.GetInternalKey());
} else {
// Some iterators may not support SeekForPrev(), so we avoid using it
@@ -909,8 +896,7 @@ bool DBIter::FindValueForCurrentKey() {
return FindValueForCurrentKeyUsingSeek();
}
if (!iter_.PrepareValue()) {
valid_ = false;
if (!PrepareValue()) {
return false;
}
@@ -1149,8 +1135,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
}
return true;
}
if (!iter_.PrepareValue()) {
valid_ = false;
if (!PrepareValue()) {
return false;
}
if (timestamp_size_ > 0) {
@@ -1215,8 +1200,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
ikey.type == kTypeDeletionWithTimestamp) {
break;
}
if (!iter_.PrepareValue()) {
valid_ = false;
if (!PrepareValue()) {
return false;
}
@@ -1293,8 +1277,8 @@ bool DBIter::MergeWithNoBaseValue(const Slice& user_key) {
const Status s = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kNoBaseValue,
merge_context_.GetOperands(), logger_, statistics_, clock_,
/* update_num_ops_stats */ true, &saved_value_, &pinned_value_,
&result_type, /* op_failure_scope */ nullptr);
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
&saved_value_, &pinned_value_, &result_type);
return SetValueAndColumnsFromMergeResult(s, result_type);
}
@@ -1306,8 +1290,8 @@ bool DBIter::MergeWithPlainBaseValue(const Slice& value,
const Status s = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kPlainBaseValue, value,
merge_context_.GetOperands(), logger_, statistics_, clock_,
/* update_num_ops_stats */ true, &saved_value_, &pinned_value_,
&result_type, /* op_failure_scope */ nullptr);
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
&saved_value_, &pinned_value_, &result_type);
return SetValueAndColumnsFromMergeResult(s, result_type);
}
@@ -1319,8 +1303,8 @@ bool DBIter::MergeWithWideColumnBaseValue(const Slice& entity,
const Status s = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kWideBaseValue, entity,
merge_context_.GetOperands(), logger_, statistics_, clock_,
/* update_num_ops_stats */ true, &saved_value_, &pinned_value_,
&result_type, /* op_failure_scope */ nullptr);
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
&saved_value_, &pinned_value_, &result_type);
return SetValueAndColumnsFromMergeResult(s, result_type);
}
+16 -1
View File
@@ -328,6 +328,22 @@ class DBIter final : public Iterator {
bool MergeWithPlainBaseValue(const Slice& value, const Slice& user_key);
bool MergeWithWideColumnBaseValue(const Slice& entity, const Slice& user_key);
bool PrepareValue() {
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
return false;
}
// ikey_ could change as BlockBasedTableIterator does Block cache
// lookup and index_iter_ could point to different block resulting
// in ikey_ pointing to wrong key. So ikey_ needs to be updated in
// case of Seek/Next calls to point to right key again.
if (!ParseKey(&ikey_)) {
return false;
}
return true;
}
const SliceTransform* prefix_extractor_;
Env* const env_;
SystemClock* clock_;
@@ -402,7 +418,6 @@ class DBIter final : public Iterator {
const Slice* const timestamp_lb_;
const size_t timestamp_size_;
std::string saved_timestamp_;
bool auto_readahead_size_;
};
// Return a new iterator that converts internal keys (yielded by
+74
View File
@@ -6,9 +6,11 @@
#include <string>
#include "db/db_test_util.h"
#include "db/write_batch_internal.h"
#include "monitoring/thread_status_util.h"
#include "port/stack_trace.h"
#include "rocksdb/statistics.h"
#include "rocksdb/utilities/transaction_db.h"
#include "util/random.h"
namespace ROCKSDB_NAMESPACE {
@@ -283,6 +285,78 @@ TEST_F(DBStatisticsTest, BlockChecksumStats) {
options.statistics->getTickerCount(BLOCK_CHECKSUM_MISMATCH_COUNT));
}
TEST_F(DBStatisticsTest, BytesWrittenStats) {
Options options = CurrentOptions();
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.statistics->set_stats_level(StatsLevel::kExceptHistogramOrTimers);
Reopen(options);
EXPECT_EQ(0, options.statistics->getAndResetTickerCount(WAL_FILE_BYTES));
EXPECT_EQ(0, options.statistics->getAndResetTickerCount(BYTES_WRITTEN));
const int kNumKeysWritten = 100;
// Scenario 0: Not using transactions.
// This will write to WAL and memtable directly.
ASSERT_OK(options.statistics->Reset());
for (int i = 0; i < kNumKeysWritten; ++i) {
ASSERT_OK(Put(Key(i), "val"));
}
EXPECT_EQ(options.statistics->getAndResetTickerCount(WAL_FILE_BYTES),
options.statistics->getAndResetTickerCount(BYTES_WRITTEN));
// Scenario 1: Using transactions.
// This should not double count BYTES_WRITTEN (issue #12061).
for (bool enable_pipelined_write : {false, true}) {
ASSERT_OK(options.statistics->Reset());
// Destroy the DB to recreate as a TransactionDB.
Destroy(options, true);
// Create a TransactionDB.
TransactionDB* txn_db = nullptr;
TransactionDBOptions txn_db_opts;
txn_db_opts.write_policy = TxnDBWritePolicy::WRITE_COMMITTED;
options.enable_pipelined_write = enable_pipelined_write;
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, &txn_db));
ASSERT_NE(txn_db, nullptr);
db_ = txn_db->GetBaseDB();
WriteOptions wopts;
TransactionOptions txn_opts;
Transaction* txn = txn_db->BeginTransaction(wopts, txn_opts, nullptr);
ASSERT_NE(txn, nullptr);
ASSERT_OK(txn->SetName("txn1"));
for (int i = 0; i < kNumKeysWritten; ++i) {
ASSERT_OK(txn->Put(Key(i), "val"));
}
// Prepare() writes to WAL, but not to memtable. (WriteCommitted)
ASSERT_OK(txn->Prepare());
EXPECT_NE(0, options.statistics->getTickerCount(WAL_FILE_BYTES));
// BYTES_WRITTEN would have been non-zero previously (issue #12061).
EXPECT_EQ(0, options.statistics->getTickerCount(BYTES_WRITTEN));
// Commit() writes to memtable and also a commit marker to WAL.
ASSERT_OK(txn->Commit());
delete txn;
// The WAL has an extra header of size `kHeader` written to it,
// as we are writing twice to it (first during Prepare, second during
// Commit).
EXPECT_EQ(options.statistics->getAndResetTickerCount(WAL_FILE_BYTES),
options.statistics->getAndResetTickerCount(BYTES_WRITTEN) +
WriteBatchInternal::kHeader);
// Cleanup
db_ = nullptr;
delete txn_db;
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+66
View File
@@ -22,6 +22,7 @@
#include "table/table_properties_internal.h"
#include "test_util/testharness.h"
#include "test_util/testutil.h"
#include "util/atomic.h"
#include "util/random.h"
@@ -417,6 +418,71 @@ TEST_F(DBTablePropertiesTest, GetDbIdentifiersProperty) {
}
}
TEST_F(DBTablePropertiesTest, FactoryReturnsNull) {
struct JunkTablePropertiesCollector : public TablePropertiesCollector {
const char* Name() const override { return "JunkTablePropertiesCollector"; }
Status Finish(UserCollectedProperties* properties) override {
properties->insert({"Junk", "Junk"});
return Status::OK();
}
UserCollectedProperties GetReadableProperties() const override {
return {};
}
};
// Alternates between putting a "Junk" property and using `nullptr` to
// opt out.
static RelaxedAtomic<int> count{0};
struct SometimesTablePropertiesCollectorFactory
: public TablePropertiesCollectorFactory {
const char* Name() const override {
return "SometimesTablePropertiesCollectorFactory";
}
TablePropertiesCollector* CreateTablePropertiesCollector(
TablePropertiesCollectorFactory::Context /*context*/) override {
if (count.FetchAddRelaxed(1) & 1) {
return nullptr;
} else {
return new JunkTablePropertiesCollector();
}
}
};
Options options = CurrentOptions();
options.table_properties_collector_factories.emplace_back(
std::make_shared<SometimesTablePropertiesCollectorFactory>());
// For plain table
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
for (std::shared_ptr<TableFactory> tf :
{options.table_factory,
std::shared_ptr<TableFactory>(NewPlainTableFactory({}))}) {
SCOPED_TRACE("Table factory = " + std::string(tf->Name()));
options.table_factory = tf;
DestroyAndReopen(options);
ASSERT_OK(Put("key0", "value1"));
ASSERT_OK(Flush());
ASSERT_OK(Put("key0", "value2"));
ASSERT_OK(Flush());
TablePropertiesCollection props;
ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
int no_junk_count = 0;
int junk_count = 0;
for (const auto& item : props) {
if (item.second->user_collected_properties.find("Junk") !=
item.second->user_collected_properties.end()) {
junk_count++;
} else {
no_junk_count++;
}
}
EXPECT_EQ(1, no_junk_count);
EXPECT_EQ(1, junk_count);
}
}
class DBTableHostnamePropertyTest
: public DBTestBase,
public ::testing::WithParamInterface<std::tuple<int, std::string>> {
+153 -39
View File
@@ -1646,35 +1646,88 @@ TEST_F(DBBasicTestWithTimestamp, GetWithRowCache) {
const Snapshot* snap_with_nothing = db_->GetSnapshot();
ASSERT_OK(db_->Put(write_opts, "foo", ts_early, "bar"));
const Snapshot* snap_with_foo = db_->GetSnapshot();
ASSERT_OK(db_->Put(write_opts, "foo2", ts_early, "bar2"));
ASSERT_OK(db_->Put(write_opts, "foo3", ts_early, "bar3"));
// Ensure file has sequence number greater than snapshot_with_foo
for (int i = 0; i < 10; i++) {
std::string numStr = std::to_string(i);
ASSERT_OK(db_->Put(write_opts, numStr, ts_later, numStr));
}
const Snapshot* snap_with_foo = db_->GetSnapshot();
ASSERT_OK(Flush());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 0);
ReadOptions read_opts;
read_opts.timestamp = &ts_later_slice;
std::string read_value;
std::string read_ts;
Status s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
ASSERT_EQ(read_ts, ts_early);
Status s;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
// Row cache is not storing the ts when record is inserted/updated.
// To be fixed after enabling ROW_CACHE with timestamp.
// ASSERT_EQ(read_ts, ts_early);
int expected_hit_count = 0;
int expected_miss_count = 0;
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
{
read_opts.timestamp = nullptr;
s = db_->Get(read_opts, "foo", &read_value);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsInvalidArgument());
}
// Mix use of Get
{
read_opts.timestamp = &ts_later_slice;
// Use Get without ts first, expect cache entry to store the correct ts
s = db_->Get(read_opts, "foo2", &read_value);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_value, "bar2");
s = db_->Get(read_opts, "foo2", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), ++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar2");
// Use Get with ts first, expect the Get without ts can get correct record
s = db_->Get(read_opts, "foo3", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar3");
s = db_->Get(read_opts, "foo3", &read_value);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), ++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
ASSERT_EQ(read_value, "bar3");
}
{
// Test with consecutive calls of Get with ts.
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
// Test repeated get on cache entry
for (int i = 0; i < 3; i++) {
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT),
++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
}
}
{
std::string ts_nothing = Timestamp(0, 0);
@@ -1682,41 +1735,43 @@ TEST_F(DBBasicTestWithTimestamp, GetWithRowCache) {
read_opts.timestamp = &ts_nothing_slice;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_TRUE(s.IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
read_opts.timestamp = &ts_later_slice;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
}
{
read_opts.snapshot = snap_with_foo;
read_opts.timestamp = &ts_later_slice;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 3);
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), ++expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), expected_miss_count);
ASSERT_EQ(read_ts, ts_early);
ASSERT_EQ(read_value, "bar");
}
{
read_opts.snapshot = snap_with_nothing;
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_TRUE(s.IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 4);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_TRUE(s.IsNotFound());
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 5);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), expected_hit_count);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS),
++expected_miss_count);
}
db_->ReleaseSnapshot(snap_with_nothing);
@@ -1724,6 +1779,65 @@ TEST_F(DBBasicTestWithTimestamp, GetWithRowCache) {
Close();
}
TEST_F(DBBasicTestWithTimestamp, GetWithRowCacheMultiSST) {
BlockBasedTableOptions table_options;
table_options.block_size = 1;
Options options = CurrentOptions();
options.env = env_;
options.create_if_missing = true;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
LRUCacheOptions cache_options;
cache_options.capacity = 8192;
options.row_cache = cache_options.MakeSharedRowCache();
const size_t kTimestampSize = Timestamp(0, 0).size();
TestComparator test_cmp(kTimestampSize);
options.comparator = &test_cmp;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
options.disable_auto_compactions = true;
DestroyAndReopen(options);
std::string ts_early = Timestamp(1, 0);
std::string ts_later = Timestamp(10, 0);
Slice ts_later_slice = ts_later;
ASSERT_OK(db_->Put(WriteOptions(), "foo", ts_early, "v1"));
ASSERT_OK(Flush());
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
ASSERT_OK(
db_->Merge(WriteOptions(), default_cf, "foo", Timestamp(2, 0), "v2"));
ASSERT_OK(
db_->Merge(WriteOptions(), default_cf, "foo", Timestamp(3, 0), "v3"));
ASSERT_OK(Flush());
ReadOptions read_opts;
read_opts.timestamp = &ts_later_slice;
std::string read_value;
std::string read_ts;
Status s;
{
// Since there are two SST files, will trigger the table lookup twice.
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
ASSERT_EQ(read_ts, Timestamp(3, 0));
ASSERT_EQ(read_value, "v1,v2,v3");
s = db_->Get(read_opts, "foo", &read_value, &read_ts);
ASSERT_OK(s);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
ASSERT_EQ(read_ts, Timestamp(3, 0));
ASSERT_EQ(read_value, "v1,v2,v3");
}
}
TEST_P(DBBasicTestWithTimestampTableOptions, MultiGetPrefixFilter) {
Options options = CurrentOptions();
options.env = env_;
+2 -2
View File
@@ -326,7 +326,7 @@ TEST_F(ImportColumnFamilyTest, ImportSSTFileWriterFilesWithRangeTombstone) {
const SstFileMetaData* file_meta = nullptr;
for (const auto& level_meta : import_cf_meta.levels) {
if (!level_meta.files.empty()) {
file_meta = &(level_meta.files[0]);
file_meta = level_meta.files.data();
break;
}
}
@@ -389,7 +389,7 @@ TEST_F(ImportColumnFamilyTest, ImportExportedSSTFromAnotherCF) {
*metadata_ptr_, &import_cfh2_));
ASSERT_NE(import_cfh2_, nullptr);
delete metadata_ptr_;
metadata_ptr_ = NULL;
metadata_ptr_ = nullptr;
std::string value1, value2;
+4 -4
View File
@@ -132,8 +132,8 @@ class TestCompactionListener : public EventListener {
ASSERT_EQ(db->GetEnv()->GetThreadID(), ci.thread_id);
ASSERT_GT(ci.thread_id, 0U);
for (auto fl : {ci.input_files, ci.output_files}) {
for (auto fn : fl) {
for (const auto& fl : {ci.input_files, ci.output_files}) {
for (const auto& fn : fl) {
auto it = ci.table_properties.find(fn);
ASSERT_NE(it, ci.table_properties.end());
auto tp = it->second;
@@ -237,7 +237,7 @@ class TestFlushListener : public EventListener {
std::vector<ThreadStatus> thread_list;
ASSERT_OK(env_->GetThreadList(&thread_list));
bool found_match = false;
for (auto thread_status : thread_list) {
for (const auto& thread_status : thread_list) {
if (thread_status.operation_type == ThreadStatus::OP_FLUSH ||
thread_status.operation_type == ThreadStatus::OP_COMPACTION) {
if (thread_id == thread_status.thread_id) {
@@ -893,7 +893,7 @@ class MemTableSealedListener : public EventListener {
SequenceNumber latest_seq_number_;
public:
MemTableSealedListener() {}
MemTableSealedListener() = default;
void OnMemTableSealed(const MemTableInfo& info) override {
latest_seq_number_ = info.first_seqno;
}
+2 -2
View File
@@ -9,7 +9,7 @@
#include "db/log_reader.h"
#include <stdio.h>
#include <cstdio>
#include "file/sequence_file_reader.h"
#include "port/lang.h"
@@ -21,7 +21,7 @@
namespace ROCKSDB_NAMESPACE {
namespace log {
Reader::Reporter::~Reporter() {}
Reader::Reporter::~Reporter() = default;
Reader::Reader(std::shared_ptr<Logger> info_log,
std::unique_ptr<SequentialFileReader>&& _file,
+1 -1
View File
@@ -1167,7 +1167,7 @@ TEST_P(StreamingCompressionTest, Basic) {
}
allocator->Deallocate((void*)output_buffer);
} while (remaining > 0);
std::string uncompressed_buffer = "";
std::string uncompressed_buffer;
int ret_val = 0;
size_t output_pos;
char* uncompressed_output_buffer = (char*)allocator->Allocate(kBlockSize);
+1 -1
View File
@@ -9,7 +9,7 @@
#include "db/log_writer.h"
#include <stdint.h>
#include <cstdint>
#include "file/writable_file_writer.h"
#include "rocksdb/env.h"
+1 -2
View File
@@ -9,8 +9,7 @@
#include "db/malloc_stats.h"
#include <string.h>
#include <cstring>
#include <memory>
#include "port/jemalloc_helper.h"
+1 -1
View File
@@ -58,7 +58,7 @@ class ManualCompactionTest : public testing::Test {
class DestroyAllCompactionFilter : public CompactionFilter {
public:
DestroyAllCompactionFilter() {}
DestroyAllCompactionFilter() = default;
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& existing_value,
std::string* /*new_value*/,
+9 -9
View File
@@ -1061,8 +1061,8 @@ static bool SaveValue(void* arg, const char* entry) {
merge_operator, s->key->user_key(),
MergeHelper::kPlainBaseValue, v, merge_context->GetOperands(),
s->logger, s->statistics, s->clock,
/* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
s->value, s->columns);
}
} else if (s->value) {
s->value->assign(v.data(), v.size());
@@ -1114,8 +1114,8 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = MergeHelper::TimedFullMerge(
merge_operator, s->key->user_key(), MergeHelper::kWideBaseValue,
v, merge_context->GetOperands(), s->logger, s->statistics,
s->clock, /* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
s->clock, /* update_num_ops_stats */ true,
/* op_failure_scope */ nullptr, s->value, s->columns);
}
} else if (s->value) {
Slice value_of_default;
@@ -1152,8 +1152,8 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = MergeHelper::TimedFullMerge(
merge_operator, s->key->user_key(), MergeHelper::kNoBaseValue,
merge_context->GetOperands(), s->logger, s->statistics,
s->clock, /* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
s->clock, /* update_num_ops_stats */ true,
/* op_failure_scope */ nullptr, s->value, s->columns);
} else {
// We have found a final value (a base deletion) and have newer
// merge operands that we do not intend to merge. Nothing remains
@@ -1192,8 +1192,8 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = MergeHelper::TimedFullMerge(
merge_operator, s->key->user_key(), MergeHelper::kNoBaseValue,
merge_context->GetOperands(), s->logger, s->statistics,
s->clock, /* update_num_ops_stats */ true, s->value, s->columns,
/* op_failure_scope */ nullptr);
s->clock, /* update_num_ops_stats */ true,
/* op_failure_scope */ nullptr, s->value, s->columns);
}
*(s->found_final_value) = true;
@@ -1362,7 +1362,7 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
range_indexes[num_keys++] = iter.index();
}
}
bloom_filter_->MayContain(num_keys, &bloom_keys[0], &may_match[0]);
bloom_filter_->MayContain(num_keys, bloom_keys.data(), may_match.data());
for (int i = 0; i < num_keys; ++i) {
if (!may_match[i]) {
temp_range.SkipIndex(range_indexes[i]);
+22 -156
View File
@@ -12,7 +12,6 @@
#include "db/blob/prefetch_buffer_collection.h"
#include "db/compaction/compaction_iteration_stats.h"
#include "db/dbformat.h"
#include "db/wide/wide_column_serialization.h"
#include "db/wide/wide_columns_helper.h"
#include "logging/logging.h"
#include "monitoring/perf_context_imp.h"
@@ -111,9 +110,9 @@ Status MergeHelper::TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats, std::string* result,
Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, std::string* result,
Slice* result_operand, ValueType* result_type) {
assert(result);
assert(result_type);
@@ -173,9 +172,9 @@ Status MergeHelper::TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats, std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, std::string* result_value,
PinnableWideColumns* result_entity) {
assert(result_value || result_entity);
assert(!result_value || !result_entity);
@@ -245,141 +244,6 @@ Status MergeHelper::TimedFullMergeImpl(
op_failure_scope, std::move(visitor));
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats, std::string* result,
Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(value);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
Slice entity_copy(entity);
WideColumns existing_columns;
const Status s =
WideColumnSerialization::Deserialize(entity_copy, existing_columns);
if (!s.ok()) {
return s;
}
existing_value = std::move(existing_columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const WideColumns& columns, const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics, SystemClock* clock,
bool update_num_ops_stats, std::string* result, Slice* result_operand,
ValueType* result_type, MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result, result_operand,
result_type, op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands, Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats, std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(value);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
Slice entity_copy(entity);
WideColumns existing_columns;
const Status s =
WideColumnSerialization::Deserialize(entity_copy, existing_columns);
if (!s.ok()) {
return s;
}
existing_value = std::move(existing_columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
Status MergeHelper::TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const WideColumns& columns, const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics, SystemClock* clock,
bool update_num_ops_stats, std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(columns);
return TimedFullMergeImpl(merge_operator, key, std::move(existing_value),
operands, logger, statistics, clock,
update_num_ops_stats, result_value, result_entity,
op_failure_scope);
}
// PRE: iter points to the first merge type entry
// POST: iter points to the first entry beyond the merge process (or the end)
// keys_, operands_ are updated to reflect the merge result.
@@ -428,7 +292,9 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
Status s = ParseInternalKey(original_key, &orig_ikey, allow_data_in_errors);
assert(s.ok());
if (!s.ok()) return s;
if (!s.ok()) {
return s;
}
assert(kTypeMerge == orig_ikey.type);
@@ -517,14 +383,14 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kNoBaseValue,
merge_context_.GetOperands(), logger_, stats_,
clock_, /* update_num_ops_stats */ false,
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
} else if (ikey.type == kTypeValue) {
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kPlainBaseValue,
iter->value(), merge_context_.GetOperands(), logger_,
stats_, clock_, /* update_num_ops_stats */ false,
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
} else if (ikey.type == kTypeBlobIndex) {
BlobIndex blob_index;
@@ -557,20 +423,20 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kPlainBaseValue,
blob_value, merge_context_.GetOperands(), logger_,
stats_, clock_, /* update_num_ops_stats */ false,
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
} else if (ikey.type == kTypeWideColumnEntity) {
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kWideBaseValue,
iter->value(), merge_context_.GetOperands(), logger_,
stats_, clock_, /* update_num_ops_stats */ false,
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
} else {
s = TimedFullMerge(user_merge_operator_, ikey.user_key, kNoBaseValue,
merge_context_.GetOperands(), logger_, stats_,
clock_, /* update_num_ops_stats */ false,
&merge_result, /* result_operand */ nullptr,
&merge_result_type, &op_failure_scope);
&op_failure_scope, &merge_result,
/* result_operand */ nullptr, &merge_result_type);
}
// We store the result in keys_.back() and operands_.back()
@@ -712,9 +578,9 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
MergeOperator::OpFailureScope op_failure_scope;
s = TimedFullMerge(user_merge_operator_, orig_ikey.user_key, kNoBaseValue,
merge_context_.GetOperands(), logger_, stats_, clock_,
/* update_num_ops_stats */ false, &merge_result,
/* result_operand */ nullptr, &merge_result_type,
&op_failure_scope);
/* update_num_ops_stats */ false, &op_failure_scope,
&merge_result,
/* result_operand */ nullptr, &merge_result_type);
if (s.ok()) {
// The original key encountered
// We are certain that keys_ is not empty here (see assertions couple of
+52 -46
View File
@@ -12,6 +12,7 @@
#include "db/merge_context.h"
#include "db/range_del_aggregator.h"
#include "db/snapshot_checker.h"
#include "db/wide/wide_column_serialization.h"
#include "rocksdb/compaction_filter.h"
#include "rocksdb/env.h"
#include "rocksdb/merge_operator.h"
@@ -60,74 +61,73 @@ class MergeHelper {
struct WideBaseValueTag {};
static constexpr WideBaseValueTag kWideBaseValue{};
// Variants that expose the merge result directly (in serialized form for wide
// columns) as well as its value type. Used by iterator and compaction.
template <typename... ResultTs>
static Status TimedFullMerge(const MergeOperator* merge_operator,
const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand,
ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
MergeOperator::OpFailureScope* op_failure_scope,
ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
template <typename... ResultTs>
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
MergeOperator::OpFailureScope* op_failure_scope, ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(value);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
template <typename... ResultTs>
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
MergeOperator::OpFailureScope* op_failure_scope, ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value;
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const WideColumns& columns, const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics, SystemClock* clock,
bool update_num_ops_stats, std::string* result, Slice* result_operand,
ValueType* result_type, MergeOperator::OpFailureScope* op_failure_scope);
Slice entity_copy(entity);
WideColumns existing_columns;
// Variants that expose the merge result translated to the form requested by
// the client. (For example, if the result is a wide-column structure but the
// client requested the results in plain-value form, the value of the default
// column is returned.) Used by point lookups.
static Status TimedFullMerge(const MergeOperator* merge_operator,
const Slice& key, NoBaseValueTag,
const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
const Status s =
WideColumnSerialization::Deserialize(entity_copy, existing_columns);
if (!s.ok()) {
return s;
}
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, PlainBaseValueTag,
const Slice& value, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
existing_value = std::move(existing_columns);
static Status TimedFullMerge(
const MergeOperator* merge_operator, const Slice& key, WideBaseValueTag,
const Slice& entity, const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
template <typename... ResultTs>
static Status TimedFullMerge(const MergeOperator* merge_operator,
const Slice& key, WideBaseValueTag,
const WideColumns& columns,
const std::vector<Slice>& operands,
Logger* logger, Statistics* statistics,
SystemClock* clock, bool update_num_ops_stats,
std::string* result_value,
PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
MergeOperator::OpFailureScope* op_failure_scope,
ResultTs... results) {
MergeOperator::MergeOperationInputV3::ExistingValue existing_value(columns);
return TimedFullMergeImpl(
merge_operator, key, std::move(existing_value), operands, logger,
statistics, clock, update_num_ops_stats, op_failure_scope, results...);
}
// During compaction, merge entries until we hit
// - a corrupted key
@@ -271,21 +271,27 @@ class MergeHelper {
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
MergeOperator::OpFailureScope* op_failure_scope, Visitor&& visitor);
// Variant that exposes the merge result directly (in serialized form for wide
// columns) as well as its value type. Used by iterator and compaction.
static Status TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result, Slice* result_operand, ValueType* result_type,
MergeOperator::OpFailureScope* op_failure_scope);
MergeOperator::OpFailureScope* op_failure_scope, std::string* result,
Slice* result_operand, ValueType* result_type);
// Variant that exposes the merge result translated into the form requested by
// the client. (For example, if the result is a wide-column structure but the
// client requested the results in plain-value form, the value of the default
// column is returned.) Used by point lookups.
static Status TimedFullMergeImpl(
const MergeOperator* merge_operator, const Slice& key,
MergeOperator::MergeOperationInputV3::ExistingValue&& existing_value,
const std::vector<Slice>& operands, Logger* logger,
Statistics* statistics, SystemClock* clock, bool update_num_ops_stats,
std::string* result_value, PinnableWideColumns* result_entity,
MergeOperator::OpFailureScope* op_failure_scope);
MergeOperator::OpFailureScope* op_failure_scope,
std::string* result_value, PinnableWideColumns* result_entity);
};
// MergeOutputIterator can be used to iterate over the result of a merge.
+13 -8
View File
@@ -3,8 +3,7 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
#include <assert.h>
#include <cassert>
#include <iostream>
#include <memory>
@@ -87,7 +86,9 @@ class EnvMergeTest : public EnvWrapper {
static std::unique_ptr<EnvMergeTest> singleton_;
static EnvMergeTest* GetInstance() {
if (nullptr == singleton_) singleton_.reset(new EnvMergeTest);
if (nullptr == singleton_) {
singleton_.reset(new EnvMergeTest);
}
return singleton_.get();
}
};
@@ -145,7 +146,7 @@ class Counters {
assert(db_);
}
virtual ~Counters() {}
virtual ~Counters() = default;
// public interface of Counters.
// All four functions return false
@@ -194,7 +195,7 @@ class Counters {
std::cerr << "value corruption\n";
return false;
}
*value = DecodeFixed64(&str[0]);
*value = DecodeFixed64(str.data());
return true;
} else {
std::cerr << s.ToString() << std::endl;
@@ -220,14 +221,18 @@ class Counters {
uint64_t value = default_;
int result = get(key, &value);
assert(result);
if (result == 0) exit(1); // Disable unused variable warning.
if (result == 0) {
exit(1); // Disable unused variable warning.
}
return value;
}
void assert_add(const std::string& key, uint64_t value) {
int result = add(key, value);
assert(result);
if (result == 0) exit(1); // Disable unused variable warning.
if (result == 0) {
exit(1); // Disable unused variable warning.
}
}
};
@@ -496,7 +501,7 @@ void testSingleBatchSuccessiveMerge(DB* db, size_t max_num_merges,
std::string get_value_str;
ASSERT_OK(db->Get(ReadOptions(), key, &get_value_str));
assert(get_value_str.size() == sizeof(uint64_t));
uint64_t get_value = DecodeFixed64(&get_value_str[0]);
uint64_t get_value = DecodeFixed64(get_value_str.data());
ASSERT_EQ(get_value, num_merges * merge_value);
ASSERT_EQ(num_merge_operator_calls,
static_cast<size_t>((num_merges % (max_num_merges + 1))));
+2 -4
View File
@@ -7,10 +7,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include <stdlib.h>
#include <algorithm>
#include <cstdlib>
#include <map>
#include <string>
#include <vector>
@@ -68,7 +66,7 @@ class ObsoleteFilesTest : public DBTestBase {
int log_cnt = 0;
int sst_cnt = 0;
int manifest_cnt = 0;
for (auto file : filenames) {
for (const auto& file : filenames) {
uint64_t number;
FileType type;
if (ParseFileName(file, &number, &type)) {
+4 -4
View File
@@ -28,7 +28,7 @@ void UpdateOptionsFiles(DB* db,
uint64_t number;
FileType type;
*options_files_count = 0;
for (auto filename : filenames) {
for (const auto& filename : filenames) {
if (ParseFileName(filename, &number, &type) && type == kOptionsFile) {
filename_history->insert(filename);
(*options_files_count)++;
@@ -44,16 +44,16 @@ void VerifyOptionsFileName(
EXPECT_OK(db->GetEnv()->GetChildren(db->GetName(), &filenames));
uint64_t number;
FileType type;
for (auto filename : filenames) {
for (const auto& filename : filenames) {
if (ParseFileName(filename, &number, &type) && type == kOptionsFile) {
current_filenames.insert(filename);
}
}
for (auto past_filename : past_filenames) {
for (const auto& past_filename : past_filenames) {
if (current_filenames.find(past_filename) != current_filenames.end()) {
continue;
}
for (auto filename : current_filenames) {
for (const auto& filename : current_filenames) {
ASSERT_GT(filename, past_filename);
}
}
+2 -2
View File
@@ -1050,7 +1050,7 @@ TEST_F(PerfContextTest, MergeOperandCount) {
std::vector<Status> statuses(num_keys);
db->MultiGet(ReadOptions(), db->DefaultColumnFamily(), num_keys,
&key_slices[0], &results[0], &statuses[0]);
key_slices.data(), results.data(), statuses.data());
for (size_t i = 0; i < num_keys; ++i) {
ASSERT_OK(statuses[i]);
@@ -1068,7 +1068,7 @@ TEST_F(PerfContextTest, MergeOperandCount) {
std::vector<Status> statuses(num_keys);
db->MultiGetEntity(ReadOptions(), db->DefaultColumnFamily(), num_keys,
&key_slices[0], &results[0], &statuses[0]);
key_slices.data(), results.data(), statuses.data());
for (size_t i = 0; i < num_keys; ++i) {
ASSERT_OK(statuses[i]);
+1 -2
View File
@@ -94,7 +94,7 @@ Status PeriodicTaskScheduler::Unregister(PeriodicTaskType task_type) {
}
Timer* PeriodicTaskScheduler::Default() {
static Timer timer(SystemClock::Default().get());
STATIC_AVOID_DESTRUCTION(Timer, timer)(SystemClock::Default().get());
return &timer;
}
@@ -108,4 +108,3 @@ void PeriodicTaskScheduler::TEST_OverrideTimer(SystemClock* clock) {
#endif // NDEBUG
} // namespace ROCKSDB_NAMESPACE
+1 -1
View File
@@ -292,7 +292,7 @@ class TestPlainTableReader : public PlainTableReader {
table_properties_ = std::move(props);
}
~TestPlainTableReader() override {}
~TestPlainTableReader() override = default;
private:
bool MatchBloom(uint32_t hash) const override {
+21 -8
View File
@@ -89,8 +89,12 @@ class TestKeyComparator : public Comparator {
const TestKey* key_a = &kkey_a;
const TestKey* key_b = &kkey_b;
if (key_a->prefix != key_b->prefix) {
if (key_a->prefix < key_b->prefix) return -1;
if (key_a->prefix > key_b->prefix) return 1;
if (key_a->prefix < key_b->prefix) {
return -1;
}
if (key_a->prefix > key_b->prefix) {
return 1;
}
} else {
EXPECT_TRUE(key_a->prefix == key_b->prefix);
// note, both a and b could be prefix only
@@ -99,8 +103,12 @@ class TestKeyComparator : public Comparator {
EXPECT_TRUE(
(a.size() == sizeof(uint64_t) && b.size() == sizeof(TestKey)) ||
(b.size() == sizeof(uint64_t) && a.size() == sizeof(TestKey)));
if (a.size() < b.size()) return -1;
if (a.size() > b.size()) return 1;
if (a.size() < b.size()) {
return -1;
}
if (a.size() > b.size()) {
return 1;
}
} else {
// both a and b are prefix
if (a.size() == sizeof(uint64_t)) {
@@ -109,9 +117,15 @@ class TestKeyComparator : public Comparator {
// both a and b are whole key
EXPECT_TRUE(a.size() == sizeof(TestKey) && b.size() == sizeof(TestKey));
if (key_a->sorted < key_b->sorted) return -1;
if (key_a->sorted > key_b->sorted) return 1;
if (key_a->sorted == key_b->sorted) return 0;
if (key_a->sorted < key_b->sorted) {
return -1;
}
if (key_a->sorted > key_b->sorted) {
return 1;
}
if (key_a->sorted == key_b->sorted) {
return 0;
}
}
}
return 0;
@@ -892,4 +906,3 @@ int main(int argc, char** argv) {
}
#endif // GFLAGS
-1
View File
@@ -8,7 +8,6 @@
#include "db/compaction/compaction_iteration_stats.h"
#include "db/dbformat.h"
#include "db/pinned_iterators_manager.h"
#include "db/range_del_aggregator.h"
#include "db/range_tombstone_fragmenter.h"
#include "db/version_edit.h"
#include "rocksdb/comparator.h"
+1 -2
View File
@@ -378,8 +378,7 @@ TEST_P(RepairTestWithTimestamp, UnflushedSst) {
ColumnFamilyOptions cf_options(options);
std::vector<ColumnFamilyDescriptor> column_families;
column_families.push_back(
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
ASSERT_OK(DB::Open(options, dbname_, column_families, &handles_, &db_));
+1 -1
View File
@@ -85,7 +85,7 @@ TableCache::TableCache(const ImmutableOptions& ioptions,
}
}
TableCache::~TableCache() {}
TableCache::~TableCache() = default;
Status TableCache::GetTableReader(
const ReadOptions& ro, const FileOptions& file_options,
+7 -2
View File
@@ -98,8 +98,13 @@ class UserKeyTablePropertiesCollectorFactory
TablePropertiesCollectorFactory::Context context;
context.column_family_id = column_family_id;
context.level_at_creation = level_at_creation;
return new UserKeyTablePropertiesCollector(
user_collector_factory_->CreateTablePropertiesCollector(context));
TablePropertiesCollector* collector =
user_collector_factory_->CreateTablePropertiesCollector(context);
if (collector) {
return new UserKeyTablePropertiesCollector(collector);
} else {
return nullptr;
}
}
virtual const char* Name() const override {
+1 -2
View File
@@ -180,7 +180,6 @@ class RegularKeysStartWithAInternal : public IntTblPropCollector {
uint64_t /* block_compressed_bytes_fast */,
uint64_t /* block_compressed_bytes_slow */) override {
// Nothing to do.
return;
}
UserCollectedProperties GetReadableProperties() const override {
@@ -227,7 +226,7 @@ class FlushBlockEveryThreePolicy : public FlushBlockPolicy {
class FlushBlockEveryThreePolicyFactory : public FlushBlockPolicyFactory {
public:
explicit FlushBlockEveryThreePolicyFactory() {}
explicit FlushBlockEveryThreePolicyFactory() = default;
const char* Name() const override {
return "FlushBlockEveryThreePolicyFactory";
-1
View File
@@ -484,7 +484,6 @@ void VersionEdit::EncodeFileBoundaries(std::string* dst,
StripTimestampFromInternalKey(&largest_buf, meta.largest.Encode(), ts_sz);
PutLengthPrefixedSlice(dst, smallest_buf);
PutLengthPrefixedSlice(dst, largest_buf);
return;
};
Status VersionEdit::DecodeFrom(const Slice& src) {
+3 -1
View File
@@ -562,7 +562,9 @@ TEST_F(VersionEditTest, AddWalDebug) {
std::stringstream ss;
ss << "{\"LogNumber\": " << kLogNumbers[i] << ", "
<< "\"SyncedSizeInBytes\": " << kSizeInBytes[i] << "}";
if (i < n - 1) ss << ", ";
if (i < n - 1) {
ss << ", ";
}
expected_json += ss.str();
}
expected_json += "], \"ColumnFamily\": 0}";
+23 -18
View File
@@ -122,7 +122,9 @@ Status OverlapWithIterator(const Comparator* ucmp,
ParsedInternalKey seek_result;
Status s = ParseInternalKey(iter->key(), &seek_result,
false /* log_err_key */); // TODO
if (!s.ok()) return s;
if (!s.ok()) {
return s;
}
if (ucmp->CompareWithoutTimestamp(seek_result.user_key, largest_user_key) <=
0) {
@@ -835,9 +837,9 @@ Version::~Version() {
assert(cfd_ != nullptr);
uint32_t path_id = f->fd.GetPathId();
assert(path_id < cfd_->ioptions()->cf_paths.size());
vset_->obsolete_files_.push_back(
ObsoleteFileInfo(f, cfd_->ioptions()->cf_paths[path_id].path,
cfd_->GetFileMetadataCacheReservationManager()));
vset_->obsolete_files_.emplace_back(
f, cfd_->ioptions()->cf_paths[path_id].path,
cfd_->GetFileMetadataCacheReservationManager());
}
}
}
@@ -2534,8 +2536,8 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
*status = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kNoBaseValue,
merge_context->GetOperands(), info_log_, db_statistics_, clock_,
/* update_num_ops_stats */ true, value ? value->GetSelf() : nullptr,
columns, /* op_failure_scope */ nullptr);
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
value ? value->GetSelf() : nullptr, columns);
if (status->ok()) {
if (LIKELY(value != nullptr)) {
value->PinSelf();
@@ -2780,9 +2782,8 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
*status = MergeHelper::TimedFullMerge(
merge_operator_, user_key, MergeHelper::kNoBaseValue,
iter->merge_context.GetOperands(), info_log_, db_statistics_, clock_,
/* update_num_ops_stats */ true,
iter->value ? iter->value->GetSelf() : nullptr, iter->columns,
/* op_failure_scope */ nullptr);
/* update_num_ops_stats */ true, /* op_failure_scope */ nullptr,
iter->value ? iter->value->GetSelf() : nullptr, iter->columns);
if (LIKELY(iter->value != nullptr)) {
iter->value->PinSelf();
range->AddValueSize(iter->value->size());
@@ -3066,9 +3067,7 @@ void VersionStorageInfo::PrepareForVersionAppend(
GenerateFileIndexer();
GenerateLevelFilesBrief();
GenerateLevel0NonOverlapping();
if (!immutable_options.allow_ingest_behind) {
GenerateBottommostFiles();
}
GenerateBottommostFiles();
GenerateFileLocationIndex();
}
@@ -3101,7 +3100,9 @@ bool Version::MaybeInitializeFileMetaData(const ReadOptions& read_options,
file_meta->fd.GetNumber(), s.ToString().c_str());
return false;
}
if (tp.get() == nullptr) return false;
if (tp.get() == nullptr) {
return false;
}
file_meta->num_entries = tp->num_entries;
file_meta->num_deletions = tp->num_deletions;
file_meta->raw_value_size = tp->raw_value_size;
@@ -4515,7 +4516,9 @@ const char* VersionStorageInfo::LevelSummary(
for (int i = 0; i < num_levels(); i++) {
int sz = sizeof(scratch->buffer) - len;
int ret = snprintf(scratch->buffer + len, sz, "%d ", int(files_[i].size()));
if (ret < 0 || ret >= sz) break;
if (ret < 0 || ret >= sz) {
break;
}
len += ret;
}
if (len > 0) {
@@ -4545,7 +4548,9 @@ const char* VersionStorageInfo::LevelFileSummary(FileSummaryStorage* scratch,
"#%" PRIu64 "(seq=%" PRIu64 ",sz=%s,%d) ",
f->fd.GetNumber(), f->fd.smallest_seqno, sztxt,
static_cast<int>(f->being_compacted));
if (ret < 0 || ret >= sz) break;
if (ret < 0 || ret >= sz) {
break;
}
len += ret;
}
// overwrite the last space (only if files_[level].size() is non-zero)
@@ -5384,9 +5389,9 @@ Status VersionSet::ProcessManifestWrites(
}
for (const auto* cfd : *column_family_set_) {
assert(curr_state.find(cfd->GetID()) == curr_state.end());
curr_state.emplace(std::make_pair(
curr_state.emplace(
cfd->GetID(),
MutableCFState(cfd->GetLogNumber(), cfd->GetFullHistoryTsLow())));
MutableCFState(cfd->GetLogNumber(), cfd->GetFullHistoryTsLow()));
}
for (const auto& wal : wals_.GetWals()) {
@@ -7277,7 +7282,7 @@ ReactiveVersionSet::ReactiveVersionSet(
/*db_session_id*/ "", /*daily_offpeak_time_utc*/ "",
/*error_handler=*/nullptr) {}
ReactiveVersionSet::~ReactiveVersionSet() {}
ReactiveVersionSet::~ReactiveVersionSet() = default;
Status ReactiveVersionSet::Recover(
const std::vector<ColumnFamilyDescriptor>& column_families,
+6
View File
@@ -490,6 +490,12 @@ class VersionStorageInfo {
files_marked_for_periodic_compaction_.emplace_back(level, f);
}
// REQUIRES: PrepareForVersionAppend has been called
const autovector<std::pair<int, FileMetaData*>>& BottommostFiles() const {
assert(finalized_);
return bottommost_files_;
}
// REQUIRES: ComputeCompactionScore has been called
// REQUIRES: DB mutex held during access
const autovector<std::pair<int, FileMetaData*>>&
+54 -5
View File
@@ -34,7 +34,7 @@ class GenerateLevelFilesBriefTest : public testing::Test {
LevelFilesBrief file_level_;
Arena arena_;
GenerateLevelFilesBriefTest() {}
GenerateLevelFilesBriefTest() = default;
~GenerateLevelFilesBriefTest() override {
for (size_t i = 0; i < files_.size(); i++) {
@@ -213,7 +213,7 @@ class VersionStorageInfoTest : public VersionStorageInfoTestBase {
public:
VersionStorageInfoTest() : VersionStorageInfoTestBase(BytewiseComparator()) {}
~VersionStorageInfoTest() override {}
~VersionStorageInfoTest() override = default;
};
TEST_F(VersionStorageInfoTest, MaxBytesForLevelStatic) {
@@ -520,6 +520,55 @@ TEST_F(VersionStorageInfoTest, EstimateLiveDataSize2) {
ASSERT_EQ(4U, vstorage_.EstimateLiveDataSize());
}
TEST_F(VersionStorageInfoTest, SingleLevelBottommostData) {
// In case of a single level, the oldest L0 file is bottommost. This could be
// improved in case the L0 files cover disjoint key-ranges.
Add(0 /* level */, 1U /* file_number */, "A" /* smallest */,
"Z" /* largest */, 1U /* file_size */);
Add(0 /* level */, 2U /* file_number */, "A" /* smallest */,
"Z" /* largest */, 1U /* file_size */);
Add(0 /* level */, 3U /* file_number */, "0" /* smallest */,
"9" /* largest */, 1U /* file_size */);
UpdateVersionStorageInfo();
ASSERT_EQ(1, vstorage_.BottommostFiles().size());
ASSERT_EQ(0, vstorage_.BottommostFiles()[0].first);
ASSERT_EQ(3U, vstorage_.BottommostFiles()[0].second->fd.GetNumber());
}
TEST_F(VersionStorageInfoTest, MultiLevelBottommostData) {
// In case of multiple levels, the oldest file for a key-range from each L1+
// level is bottommost. This could be improved in case an L0 file contains the
// oldest data for some range of keys.
Add(0 /* level */, 1U /* file_number */, "A" /* smallest */,
"Z" /* largest */, 1U /* file_size */);
Add(0 /* level */, 2U /* file_number */, "0" /* smallest */,
"9" /* largest */, 1U /* file_size */);
Add(1 /* level */, 3U /* file_number */, "A" /* smallest */,
"D" /* largest */, 1U /* file_size */);
Add(2 /* level */, 4U /* file_number */, "E" /* smallest */,
"H" /* largest */, 1U /* file_size */);
Add(2 /* level */, 5U /* file_number */, "I" /* smallest */,
"L" /* largest */, 1U /* file_size */);
UpdateVersionStorageInfo();
autovector<std::pair<int, FileMetaData*>> bottommost_files =
vstorage_.BottommostFiles();
std::sort(bottommost_files.begin(), bottommost_files.end(),
[](const std::pair<int, FileMetaData*>& lhs,
const std::pair<int, FileMetaData*>& rhs) {
assert(lhs.second);
assert(rhs.second);
return lhs.second->fd.GetNumber() < rhs.second->fd.GetNumber();
});
ASSERT_EQ(3, bottommost_files.size());
ASSERT_EQ(3U, bottommost_files[0].second->fd.GetNumber());
ASSERT_EQ(4U, bottommost_files[1].second->fd.GetNumber());
ASSERT_EQ(5U, bottommost_files[2].second->fd.GetNumber());
}
TEST_F(VersionStorageInfoTest, GetOverlappingInputs) {
// Two files that overlap at the range deletion tombstone sentinel.
Add(1, 1U, {"a", 0, kTypeValue},
@@ -928,7 +977,7 @@ class VersionStorageInfoTimestampTest : public VersionStorageInfoTestBase {
VersionStorageInfoTimestampTest()
: VersionStorageInfoTestBase(test::BytewiseComparatorWithU64TsWrapper()) {
}
~VersionStorageInfoTimestampTest() override {}
~VersionStorageInfoTimestampTest() override = default;
std::string Timestamp(uint64_t ts) const {
std::string ret;
PutFixed64(&ret, ts);
@@ -982,7 +1031,7 @@ class FindLevelFileTest : public testing::Test {
FindLevelFileTest() : disjoint_sorted_files_(true) {}
~FindLevelFileTest() override {}
~FindLevelFileTest() override = default;
void LevelFileInit(size_t num = 0) {
char* mem = arena_.AllocateAligned(num * sizeof(FdWithKeyRange));
@@ -3662,7 +3711,7 @@ class ChargeFileMetadataTestWithParam
: public ChargeFileMetadataTest,
public testing::WithParamInterface<CacheEntryRoleOptions::Decision> {
public:
ChargeFileMetadataTestWithParam() {}
ChargeFileMetadataTestWithParam() = default;
};
INSTANTIATE_TEST_CASE_P(
+11 -11
View File
@@ -94,7 +94,7 @@ TEST_F(DBWideBasicTest, PutEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
&keys[0], &values[0], &statuses[0]);
keys.data(), values.data(), statuses.data());
ASSERT_OK(statuses[0]);
ASSERT_EQ(values[0], first_value_of_default_column);
@@ -114,7 +114,7 @@ TEST_F(DBWideBasicTest, PutEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
&keys[0], &results[0], &statuses[0]);
keys.data(), results.data(), statuses.data());
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), first_columns);
@@ -398,8 +398,8 @@ TEST_F(DBWideBasicTest, MultiCFMultiGetEntity) {
std::array<PinnableWideColumns, num_keys> results;
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), num_keys, &column_families[0], &keys[0],
&results[0], &statuses[0]);
db_->MultiGetEntity(ReadOptions(), num_keys, column_families.data(),
keys.data(), results.data(), statuses.data());
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), first_columns);
@@ -642,7 +642,7 @@ TEST_F(DBWideBasicTest, MergePlainKeyValue) {
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
&keys[0], &results[0], &statuses[0]);
keys.data(), results.data(), statuses.data());
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), expected_first_columns);
@@ -822,7 +822,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
&keys[0], &values[0], &statuses[0]);
keys.data(), values.data(), statuses.data());
ASSERT_EQ(values[0], first_expected_default);
ASSERT_OK(statuses[0]);
@@ -839,7 +839,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
std::array<Status, num_keys> statuses;
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
&keys[0], &results[0], &statuses[0]);
keys.data(), results.data(), statuses.data());
ASSERT_OK(statuses[0]);
ASSERT_EQ(results[0].columns(), first_expected_columns);
@@ -900,7 +900,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
first_key, &merge_operands[0],
first_key, merge_operands.data(),
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
@@ -913,7 +913,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
second_key, &merge_operands[0],
second_key, merge_operands.data(),
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
@@ -933,7 +933,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
first_key, &merge_operands[0],
first_key, merge_operands.data(),
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
@@ -945,7 +945,7 @@ TEST_F(DBWideBasicTest, MergeEntity) {
int number_of_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
second_key, &merge_operands[0],
second_key, merge_operands.data(),
&get_merge_opts, &number_of_operands));
ASSERT_EQ(number_of_operands, num_merge_operands);
+11 -11
View File
@@ -233,9 +233,9 @@ WriteBatch& WriteBatch::operator=(WriteBatch&& src) {
return *this;
}
WriteBatch::~WriteBatch() {}
WriteBatch::~WriteBatch() = default;
WriteBatch::Handler::~Handler() {}
WriteBatch::Handler::~Handler() = default;
void WriteBatch::Handler::LogData(const Slice& /*blob*/) {
// If the user has not specified something to do with blobs, then we ignore
@@ -741,7 +741,7 @@ SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) {
}
void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) {
EncodeFixed64(&b->rep_[0], seq);
EncodeFixed64(b->rep_.data(), seq);
}
size_t WriteBatchInternal::GetFirstOffset(WriteBatch* /*b*/) {
@@ -1856,7 +1856,9 @@ class MemTableInserter : public WriteBatch::Handler {
}
void DecrementProtectionInfoIdxForTryAgain() {
if (prot_info_ != nullptr) --prot_info_idx_;
if (prot_info_ != nullptr) {
--prot_info_idx_;
}
}
void ResetProtectionInfo() {
@@ -2543,9 +2545,8 @@ class MemTableInserter : public WriteBatch::Handler {
WideColumnsHelper::GetDefaultColumn(columns), {value},
moptions->info_log, moptions->statistics,
SystemClock::Default().get(),
/* update_num_ops_stats */ false, &new_value,
/* result_operand */ nullptr, &new_value_type,
/* op_failure_scope */ nullptr);
/* update_num_ops_stats */ false, /* op_failure_scope */ nullptr,
&new_value, /* result_operand */ nullptr, &new_value_type);
} else {
// `op_failure_scope` (an output parameter) is not provided (set to
// nullptr) since a failure must be propagated regardless of its
@@ -2554,9 +2555,8 @@ class MemTableInserter : public WriteBatch::Handler {
merge_operator, key, MergeHelper::kWideBaseValue, columns,
{value}, moptions->info_log, moptions->statistics,
SystemClock::Default().get(),
/* update_num_ops_stats */ false, &new_value,
/* result_operand */ nullptr, &new_value_type,
/* op_failure_scope */ nullptr);
/* update_num_ops_stats */ false, /* op_failure_scope */ nullptr,
&new_value, /* result_operand */ nullptr, &new_value_type);
}
if (!merge_status.ok()) {
@@ -3018,7 +3018,7 @@ class ProtectionInfoUpdater : public WriteBatch::Handler {
explicit ProtectionInfoUpdater(WriteBatch::ProtectionInfo* prot_info)
: prot_info_(prot_info) {}
~ProtectionInfoUpdater() override {}
~ProtectionInfoUpdater() override = default;
Status PutCF(uint32_t cf, const Slice& key, const Slice& val) override {
return UpdateProtInfo(cf, key, val, kTypeValue);
+3 -4
View File
@@ -64,7 +64,7 @@ class MockWriteCallback : public WriteCallback {
bool allow_batching_ = false;
std::atomic<bool> was_called_{false};
MockWriteCallback() {}
MockWriteCallback() = default;
MockWriteCallback(const MockWriteCallback& other) {
should_fail_ = other.should_fail_;
@@ -111,7 +111,7 @@ TEST_P(WriteCallbackPTest, WriteWithCallbackTest) {
WriteOP(bool should_fail = false) { callback_.should_fail_ = should_fail; }
void Put(const string& key, const string& val) {
kvs_.push_back(std::make_pair(key, val));
kvs_.emplace_back(key, val);
ASSERT_OK(write_batch_.Put(key, val));
}
@@ -176,8 +176,7 @@ TEST_P(WriteCallbackPTest, WriteWithCallbackTest) {
DBOptions db_options(options);
ColumnFamilyOptions cf_options(options);
std::vector<ColumnFamilyDescriptor> column_families;
column_families.push_back(
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
std::vector<ColumnFamilyHandle*> handles;
auto open_s = DBImpl::Open(db_options, dbname, column_families, &handles,
&db, seq_per_batch_, true /* batch_per_txn */);
+2 -2
View File
@@ -13,8 +13,8 @@
namespace ROCKSDB_NAMESPACE {
class BatchedOpsStressTest : public StressTest {
public:
BatchedOpsStressTest() {}
virtual ~BatchedOpsStressTest() {}
BatchedOpsStressTest() = default;
virtual ~BatchedOpsStressTest() = default;
bool IsStateTracked() const override { return false; }
+2 -2
View File
@@ -16,7 +16,7 @@ class CfConsistencyStressTest : public StressTest {
public:
CfConsistencyStressTest() : batch_id_(0) {}
~CfConsistencyStressTest() override {}
~CfConsistencyStressTest() override = default;
bool IsStateTracked() const override { return false; }
@@ -232,7 +232,7 @@ class CfConsistencyStressTest : public StressTest {
}
db_->MultiGet(readoptionscopy, cfh, num_keys, keys.data(), values.data(),
statuses.data());
for (auto s : statuses) {
for (const auto& s : statuses) {
if (s.ok()) {
// found case
thread->stats.AddGets(1, 1);
-3
View File
@@ -210,9 +210,6 @@ void CompressedCacheSetCapacityThread(void* v) {
(double)thread->rand.Uniform(
FLAGS_compressed_secondary_cache_ratio * 100) /
100;
if (new_comp_cache_ratio == 0.0) {
new_comp_cache_ratio = 0.05;
}
fprintf(stderr, "New comp cache ratio = %f\n", new_comp_cache_ratio);
s = UpdateTieredCache(block_cache, /*capacity*/ -1,
+1
View File
@@ -313,6 +313,7 @@ DECLARE_uint32(memtable_protection_bytes_per_key);
DECLARE_uint32(block_protection_bytes_per_key);
DECLARE_uint64(user_timestamp_size);
DECLARE_bool(persist_user_defined_timestamps);
DECLARE_string(secondary_cache_uri);
DECLARE_int32(secondary_cache_fault_one_in);
+4
View File
@@ -1025,6 +1025,10 @@ DEFINE_uint64(user_timestamp_size, 0,
"Number of bytes for a user-defined timestamp. Currently, only "
"8-byte is supported");
DEFINE_bool(persist_user_defined_timestamps, true,
"Flag to indicate whether user-defined timestamps will be persisted"
" during Flush");
DEFINE_int32(open_metadata_write_fault_one_in, 0,
"On non-zero, enables fault injection on file metadata write "
"during DB reopen.");
+1 -1
View File
@@ -67,7 +67,7 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name, Env* env)
std::string id(24U, '\0');
Slice result;
for (;;) {
s = reader->Read(id.size(), opts, &result, &id[0], /*dbg*/ nullptr);
s = reader->Read(id.size(), opts, &result, id.data(), /*dbg*/ nullptr);
if (!s.ok()) {
fprintf(stderr, "Error reading unique id file: %s\n",
s.ToString().c_str());
+25 -4
View File
@@ -430,6 +430,13 @@ Status StressTest::AssertSame(DB* db, ColumnFamilyHandle* cf,
PinnableSlice v;
s = db->Get(ropt, cf, snap_state.key, &v);
if (!s.ok() && !s.IsNotFound()) {
// When `persist_user_defined_timestamps` is false, a repeated read with
// both a read timestamp and an explicitly taken snapshot cannot guarantee
// consistent result all the time. When it cannot return consistent result,
// it will return an `InvalidArgument` status.
if (s.IsInvalidArgument() && !FLAGS_persist_user_defined_timestamps) {
return Status::OK();
}
return s;
}
if (snap_state.status != s) {
@@ -1784,7 +1791,7 @@ Status StressTest::TestBackupRestore(
// `ListColumnFamilies` to get names because it won't necessarily give
// the same order as `column_family_names_`.
assert(FLAGS_clear_column_family_one_in == 0);
for (auto name : column_family_names_) {
for (const auto& name : column_family_names_) {
cf_descriptors.emplace_back(name, ColumnFamilyOptions(restore_options));
}
if (inplace_not_restore) {
@@ -2668,6 +2675,8 @@ void StressTest::PrintEnv() const {
static_cast<int>(FLAGS_fail_if_options_file_error));
fprintf(stdout, "User timestamp size bytes : %d\n",
static_cast<int>(FLAGS_user_timestamp_size));
fprintf(stdout, "Persist user defined timestamps : %d\n",
FLAGS_persist_user_defined_timestamps);
fprintf(stdout, "WAL compression : %s\n",
FLAGS_wal_compression.c_str());
fprintf(stdout, "Try verify sst unique id : %d\n",
@@ -2762,12 +2771,12 @@ void StressTest::Open(SharedState* shared, bool reopen) {
if (sorted_cfn != existing_column_families) {
fprintf(stderr, "Expected column families differ from the existing:\n");
fprintf(stderr, "Expected: {");
for (auto cf : sorted_cfn) {
for (const auto& cf : sorted_cfn) {
fprintf(stderr, "%s ", cf.c_str());
}
fprintf(stderr, "}\n");
fprintf(stderr, "Existing: {");
for (auto cf : existing_column_families) {
for (const auto& cf : existing_column_families) {
fprintf(stderr, "%s ", cf.c_str());
}
fprintf(stderr, "}\n");
@@ -2775,7 +2784,7 @@ void StressTest::Open(SharedState* shared, bool reopen) {
assert(sorted_cfn == existing_column_families);
}
std::vector<ColumnFamilyDescriptor> cf_descriptors;
for (auto name : column_family_names_) {
for (const auto& name : column_family_names_) {
if (name != kDefaultColumnFamilyName) {
new_column_family_name_ =
std::max(new_column_family_name_.load(), std::stoi(name) + 1);
@@ -3087,6 +3096,11 @@ bool StressTest::MaybeUseOlderTimestampForPointLookup(ThreadState* thread,
return false;
}
if (!FLAGS_persist_user_defined_timestamps) {
// Not read with older timestamps to avoid get InvalidArgument.
return false;
}
assert(thread);
if (!thread->rand.OneInOpt(3)) {
return false;
@@ -3116,6 +3130,11 @@ void StressTest::MaybeUseOlderTimestampForRangeScan(ThreadState* thread,
return;
}
if (!FLAGS_persist_user_defined_timestamps) {
// Not read with older timestamps to avoid get InvalidArgument.
return;
}
assert(thread);
if (!thread->rand.OneInOpt(3)) {
return;
@@ -3175,6 +3194,8 @@ void CheckAndSetOptionsForUserTimestamp(Options& options) {
exit(1);
}
options.comparator = cmp;
options.persist_user_defined_timestamps =
FLAGS_persist_user_defined_timestamps;
}
bool InitializeOptionsFromFile(Options& options) {
+1 -1
View File
@@ -185,7 +185,7 @@ ExpectedStateManager::ExpectedStateManager(size_t max_key,
num_column_families_(num_column_families),
latest_(nullptr) {}
ExpectedStateManager::~ExpectedStateManager() {}
ExpectedStateManager::~ExpectedStateManager() = default;
const std::string FileExpectedStateManager::kLatestBasename = "LATEST";
const std::string FileExpectedStateManager::kStateFilenameSuffix = ".state";
+3 -3
View File
@@ -150,7 +150,7 @@ std::string MultiOpsTxnsStressTest::Record::EncodePrimaryKey(uint32_t a) {
PutFixed32(&ret, kPrimaryIndexId);
PutFixed32(&ret, a);
char* const buf = &ret[0];
char* const buf = ret.data();
std::reverse(buf, buf + sizeof(kPrimaryIndexId));
std::reverse(buf + sizeof(kPrimaryIndexId),
buf + sizeof(kPrimaryIndexId) + sizeof(a));
@@ -162,7 +162,7 @@ std::string MultiOpsTxnsStressTest::Record::EncodeSecondaryKey(uint32_t c) {
PutFixed32(&ret, kSecondaryIndexId);
PutFixed32(&ret, c);
char* const buf = &ret[0];
char* const buf = ret.data();
std::reverse(buf, buf + sizeof(kSecondaryIndexId));
std::reverse(buf + sizeof(kSecondaryIndexId),
buf + sizeof(kSecondaryIndexId) + sizeof(c));
@@ -176,7 +176,7 @@ std::string MultiOpsTxnsStressTest::Record::EncodeSecondaryKey(uint32_t c,
PutFixed32(&ret, c);
PutFixed32(&ret, a);
char* const buf = &ret[0];
char* const buf = ret.data();
std::reverse(buf, buf + sizeof(kSecondaryIndexId));
std::reverse(buf + sizeof(kSecondaryIndexId),
buf + sizeof(kSecondaryIndexId) + sizeof(c));
+4 -4
View File
@@ -17,9 +17,9 @@
namespace ROCKSDB_NAMESPACE {
class NonBatchedOpsStressTest : public StressTest {
public:
NonBatchedOpsStressTest() {}
NonBatchedOpsStressTest() = default;
virtual ~NonBatchedOpsStressTest() {}
virtual ~NonBatchedOpsStressTest() = default;
void VerifyDb(ThreadState* thread) const override {
// This `ReadOptions` is for validation purposes. Ignore
@@ -624,7 +624,7 @@ class NonBatchedOpsStressTest : public StressTest {
if (!shared->AllowsOverwrite(rand_key) &&
shared->Exists(column_family, rand_key)) {
// Just do read your write checks for keys that allow overwrites.
ryw_expected_values.push_back(std::nullopt);
ryw_expected_values.emplace_back(std::nullopt);
continue;
}
// With a 1 in 10 probability, insert the just added key in the batch
@@ -667,7 +667,7 @@ class NonBatchedOpsStressTest : public StressTest {
thread->shared->SafeTerminate();
}
} else {
ryw_expected_values.push_back(std::nullopt);
ryw_expected_values.emplace_back(std::nullopt);
}
}
}
+1 -1
View File
@@ -504,7 +504,7 @@ EnvWrapper::EnvWrapper(const std::shared_ptr<Env>& t) : target_(t) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::~EnvWrapper() {}
EnvWrapper::~EnvWrapper() = default;
Status EnvWrapper::PrepareOptions(const ConfigOptions& options) {
target_.Prepare();
Vendored
+9 -9
View File
@@ -359,7 +359,7 @@ class LegacyFileSystemWrapper : public FileSystem {
public:
// Initialize an EnvWrapper that delegates all calls to *t
explicit LegacyFileSystemWrapper(Env* t) : target_(t) {}
~LegacyFileSystemWrapper() override {}
~LegacyFileSystemWrapper() override = default;
static const char* kClassName() { return "LegacyFileSystem"; }
const char* Name() const override { return kClassName(); }
@@ -624,7 +624,7 @@ Env::Env(const std::shared_ptr<FileSystem>& fs,
const std::shared_ptr<SystemClock>& clock)
: thread_status_updater_(nullptr), file_system_(fs), system_clock_(clock) {}
Env::~Env() {}
Env::~Env() = default;
Status Env::NewLogger(const std::string& fname,
std::shared_ptr<Logger>* result) {
@@ -797,7 +797,7 @@ std::string Env::GenerateUniqueId() {
// Use 36 character format of RFC 4122
result.resize(36U);
char* buf = &result[0];
char* buf = result.data();
PutBaseChars<16>(&buf, 8, upper >> 32, /*!uppercase*/ false);
*(buf++) = '-';
PutBaseChars<16>(&buf, 4, upper >> 16, /*!uppercase*/ false);
@@ -817,15 +817,15 @@ std::string Env::GenerateUniqueId() {
return result;
}
SequentialFile::~SequentialFile() {}
SequentialFile::~SequentialFile() = default;
RandomAccessFile::~RandomAccessFile() {}
RandomAccessFile::~RandomAccessFile() = default;
WritableFile::~WritableFile() {}
WritableFile::~WritableFile() = default;
MemoryMappedFileBuffer::~MemoryMappedFileBuffer() {}
MemoryMappedFileBuffer::~MemoryMappedFileBuffer() = default;
Logger::~Logger() {}
Logger::~Logger() = default;
Status Logger::Close() {
if (!closed_) {
@@ -838,7 +838,7 @@ Status Logger::Close() {
Status Logger::CloseImpl() { return Status::NotSupported(); }
FileLock::~FileLock() {}
FileLock::~FileLock() = default;
void LogFlush(Logger* info_log) {
if (info_log) {
+1 -1
View File
@@ -342,7 +342,7 @@ TEST_P(EnvMoreTestWithParam, GetChildren) {
ASSERT_OK(env_->GetChildrenFileAttributes(test_dir_, &childAttr));
ASSERT_EQ(3U, children.size());
ASSERT_EQ(3U, childAttr.size());
for (auto each : children) {
for (const auto& each : children) {
env_->DeleteDir(test_dir_ + "/" + each).PermitUncheckedError();
} // necessary for default POSIX env
+3 -2
View File
@@ -7,10 +7,11 @@
#include "env/env_chroot.h"
#include <errno.h> // errno
#include <stdlib.h> // realpath, free
#include <unistd.h> // geteuid
#include <cerrno> // errno
#include <cstdlib> // realpath, free
#include "env/composite_env_wrapper.h"
#include "env/fs_remap.h"
#include "rocksdb/utilities/options_type.h"
+2 -2
View File
@@ -827,7 +827,7 @@ Status BlockAccessCipherStream::Encrypt(uint64_t fileOffset, char* data,
AllocateScratch(scratch);
// Encrypt individual blocks.
while (1) {
while (true) {
char* block = data;
size_t n = std::min(dataSize, blockSize - blockOffset);
if (n != blockSize) {
@@ -871,7 +871,7 @@ Status BlockAccessCipherStream::Decrypt(uint64_t fileOffset, char* data,
AllocateScratch(scratch);
// Decrypt individual blocks.
while (1) {
while (true) {
char* block = data;
size_t n = std::min(dataSize, blockSize - blockOffset);
if (n != blockSize) {
+9 -7
View File
@@ -14,19 +14,21 @@
#ifndef ROCKSDB_NO_DYNAMIC_EXTENSION
#include <dlfcn.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <cerrno>
#if defined(ROCKSDB_IOURING_PRESENT)
#include <liburing.h>
#endif
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(OS_LINUX) || defined(OS_SOLARIS) || defined(OS_ANDROID)
#include <sys/statfs.h>
#endif
@@ -36,10 +38,10 @@
#if defined(ROCKSDB_IOURING_PRESENT)
#include <sys/uio.h>
#endif
#include <time.h>
#include <unistd.h>
#include <algorithm>
#include <ctime>
// Get nano time includes
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_GNU_KFREEBSD)
#elif defined(__MACH__)
@@ -199,7 +201,7 @@ class PosixClock : public SystemClock {
std::string dummy;
dummy.reserve(maxsize);
dummy.resize(maxsize);
char* p = &dummy[0];
char* p = dummy.data();
port::LocalTimeR(&seconds, &t);
snprintf(p, maxsize, "%04d/%02d/%02d-%02d:%02d:%02d ", t.tm_year + 1900,
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+4 -3
View File
@@ -26,13 +26,14 @@
#ifdef OS_LINUX
#include <fcntl.h>
#include <linux/fs.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdlib>
#endif
#ifdef ROCKSDB_FALLOCATE_PRESENT
#include <errno.h>
#include <cerrno>
#endif
#include "db/db_impl/db_impl.h"
@@ -2955,7 +2956,7 @@ struct NoDuplicateMiniStressTest {
NoDuplicateMiniStressTest() { env = Env::Default(); }
virtual ~NoDuplicateMiniStressTest() {}
virtual ~NoDuplicateMiniStressTest() = default;
void Run() {
std::array<std::thread, kThreads> threads;
+2 -2
View File
@@ -22,9 +22,9 @@
namespace ROCKSDB_NAMESPACE {
FileSystem::FileSystem() {}
FileSystem::FileSystem() = default;
FileSystem::~FileSystem() {}
FileSystem::~FileSystem() = default;
static int RegisterBuiltinFileSystems(ObjectLibrary& library,
const std::string& /*arg*/) {
+8 -7
View File
@@ -13,16 +13,17 @@
#ifndef ROCKSDB_NO_DYNAMIC_EXTENSION
#include <dlfcn.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(OS_LINUX) || defined(OS_SOLARIS) || defined(OS_ANDROID)
#include <sys/statfs.h>
#include <sys/sysmacros.h>
@@ -30,9 +31,9 @@
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <algorithm>
#include <ctime>
// Get nano time includes
#if defined(OS_LINUX) || defined(OS_FREEBSD)
#elif defined(__MACH__)
@@ -143,7 +144,7 @@ class PosixFileSystem : public FileSystem {
const char* Name() const override { return kClassName(); }
const char* NickName() const override { return kDefaultName(); }
~PosixFileSystem() override {}
~PosixFileSystem() override = default;
bool IsInstanceOf(const std::string& name) const override {
if (name == "posix") {
return true;
+9 -6
View File
@@ -10,23 +10,24 @@
#ifdef ROCKSDB_LIB_IO_POSIX
#include "env/io_posix.h"
#include <errno.h>
#include <fcntl.h>
#include <algorithm>
#include <cerrno>
#if defined(OS_LINUX)
#include <linux/fs.h>
#ifndef FALLOC_FL_KEEP_SIZE
#include <linux/falloc.h>
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifdef OS_LINUX
#include <sys/statfs.h>
#include <sys/sysmacros.h>
@@ -437,7 +438,7 @@ void LogicalBlockSizeCache::UnrefAndTryRemoveCachedLogicalBlockSize(
size_t LogicalBlockSizeCache::GetLogicalBlockSize(const std::string& fname,
int fd) {
std::string dir = fname.substr(0, fname.find_last_of("/"));
std::string dir = fname.substr(0, fname.find_last_of('/'));
if (dir.empty()) {
dir = "/";
}
@@ -654,7 +655,9 @@ IOStatus PosixRandomAccessFile::MultiRead(FSReadRequest* reqs, size_t num_reqs,
size_t this_reqs = (num_reqs - reqs_off) + incomplete_rq_list.size();
// If requests exceed depth, split it into batches
if (this_reqs > kIoUringDepth) this_reqs = kIoUringDepth;
if (this_reqs > kIoUringDepth) {
this_reqs = kIoUringDepth;
}
assert(incomplete_rq_list.size() <= this_reqs);
for (size_t i = 0; i < this_reqs; i++) {
+1 -1
View File
@@ -483,7 +483,7 @@ class TestMemLogger : public Logger {
options_(options),
dbg_(dbg),
flush_pending_(false) {}
~TestMemLogger() override {}
~TestMemLogger() override = default;
void Flush() override {
if (flush_pending_) {
+6 -6
View File
@@ -37,28 +37,28 @@ TEST_F(MockEnvTest, Corrupt) {
Slice result;
std::unique_ptr<RandomAccessFile> rand_file;
ASSERT_OK(env_->NewRandomAccessFile(kFileName, &rand_file, soptions_));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, &(scratch[0])));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, scratch.data()));
ASSERT_EQ(result.compare(kGood), 0);
// Sync + corrupt => no change
ASSERT_OK(writable_file->Fsync());
ASSERT_OK(dynamic_cast<MockEnv*>(env_)->CorruptBuffer(kFileName));
result.clear();
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, &(scratch[0])));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, scratch.data()));
ASSERT_EQ(result.compare(kGood), 0);
// Add new data and corrupt it
ASSERT_OK(writable_file->Append(kCorrupted));
ASSERT_TRUE(writable_file->GetFileSize() == kGood.size() + kCorrupted.size());
result.clear();
ASSERT_OK(
rand_file->Read(kGood.size(), kCorrupted.size(), &result, &(scratch[0])));
ASSERT_OK(rand_file->Read(kGood.size(), kCorrupted.size(), &result,
scratch.data()));
ASSERT_EQ(result.compare(kCorrupted), 0);
// Corrupted
ASSERT_OK(dynamic_cast<MockEnv*>(env_)->CorruptBuffer(kFileName));
result.clear();
ASSERT_OK(
rand_file->Read(kGood.size(), kCorrupted.size(), &result, &(scratch[0])));
ASSERT_OK(rand_file->Read(kGood.size(), kCorrupted.size(), &result,
scratch.data()));
ASSERT_NE(result.compare(kCorrupted), 0);
}
+1 -1
View File
@@ -177,7 +177,7 @@ Status DeleteScheduler::CleanupDirectory(Env* env, SstFileManagerImpl* sfm,
Status DeleteScheduler::MarkAsTrash(const std::string& file_path,
std::string* trash_file) {
// Sanity check of the path
size_t idx = file_path.rfind("/");
size_t idx = file_path.rfind('/');
if (idx == std::string::npos || idx == file_path.size() - 1) {
return Status::InvalidArgument("file_path is corrupted");
}
+233 -169
View File
@@ -82,9 +82,9 @@ void FilePrefetchBuffer::CalculateOffsetAndLen(size_t alignment,
Status FilePrefetchBuffer::Read(const IOOptions& opts,
RandomAccessFileReader* reader,
uint64_t read_len, uint64_t chunk_len,
uint64_t rounddown_start, uint32_t index) {
uint64_t start_offset, uint32_t index) {
Slice result;
Status s = reader->Read(opts, rounddown_start + chunk_len, read_len, &result,
Status s = reader->Read(opts, start_offset + chunk_len, read_len, &result,
bufs_[index].buffer_.BufferStart() + chunk_len,
/*aligned_buf=*/nullptr);
#ifndef NDEBUG
@@ -102,15 +102,15 @@ Status FilePrefetchBuffer::Read(const IOOptions& opts,
RecordTick(stats_, PREFETCH_BYTES, read_len);
}
// Update the buffer offset and size.
bufs_[index].offset_ = rounddown_start;
bufs_[index].offset_ = start_offset;
bufs_[index].buffer_.Size(static_cast<size_t>(chunk_len) + result.size());
return s;
}
Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
RandomAccessFileReader* reader,
uint64_t read_len,
uint64_t rounddown_start, uint32_t index) {
uint64_t read_len, uint64_t start_offset,
uint32_t index) {
TEST_SYNC_POINT("FilePrefetchBuffer::ReadAsync");
// callback for async read request.
auto fp = std::bind(&FilePrefetchBuffer::PrefetchAsyncCallback, this,
@@ -118,7 +118,7 @@ Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
FSReadRequest req;
Slice result;
req.len = read_len;
req.offset = rounddown_start;
req.offset = start_offset;
req.result = result;
req.scratch = bufs_[index].buffer_.BufferStart();
bufs_[index].async_req_len_ = req.len;
@@ -129,6 +129,7 @@ Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
/*aligned_buf=*/nullptr);
req.status.PermitUncheckedError();
if (s.ok()) {
RecordTick(stats_, PREFETCH_BYTES, read_len);
bufs_[index].async_read_in_progress_ = true;
}
return s;
@@ -149,19 +150,17 @@ Status FilePrefetchBuffer::Prefetch(const IOOptions& opts,
}
size_t alignment = reader->file()->GetRequiredBufferAlignment();
size_t offset_ = static_cast<size_t>(offset);
uint64_t rounddown_offset = Rounddown(offset_, alignment);
uint64_t roundup_end = Roundup(offset_ + n, alignment);
uint64_t roundup_len = roundup_end - rounddown_offset;
assert(roundup_len >= alignment);
assert(roundup_len % alignment == 0);
uint64_t rounddown_offset = offset, roundup_end = 0, chunk_len = 0;
size_t read_len = 0;
uint64_t chunk_len = 0;
CalculateOffsetAndLen(alignment, offset, roundup_len, curr_,
true /*refit_tail*/, chunk_len);
size_t read_len = static_cast<size_t>(roundup_len - chunk_len);
ReadAheadSizeTuning(/*read_curr_block=*/true, /*refit_tail=*/true,
rounddown_offset, curr_, alignment, 0, n,
rounddown_offset, roundup_end, read_len, chunk_len);
Status s = Read(opts, reader, read_len, chunk_len, rounddown_offset, curr_);
Status s;
if (read_len > 0) {
s = Read(opts, reader, read_len, chunk_len, rounddown_offset, curr_);
}
if (usage_ == FilePrefetchBufferUsage::kTableOpenPrefetchTail && s.ok()) {
RecordInHistogram(stats_, TABLE_OPEN_PREFETCH_TAIL_READ_BYTES, read_len);
@@ -196,7 +195,7 @@ void FilePrefetchBuffer::CopyDataToBuffer(uint32_t src, uint64_t& offset,
// length > 0 indicates it has consumed all data from the src buffer and it
// still needs to read more other buffer.
if (length > 0) {
bufs_[src].buffer_.Clear();
bufs_[src].ClearBuffer();
}
}
@@ -266,28 +265,35 @@ void FilePrefetchBuffer::AbortAllIOs() {
// Clear the buffers if it contains outdated data. Outdated data can be
// because previous sequential reads were read from the cache instead of these
// buffer.
void FilePrefetchBuffer::UpdateBuffersIfNeeded(uint64_t offset) {
void FilePrefetchBuffer::UpdateBuffersIfNeeded(uint64_t offset, size_t length) {
uint32_t second = curr_ ^ 1;
if (IsBufferOutdated(offset, curr_)) {
bufs_[curr_].buffer_.Clear();
bufs_[curr_].ClearBuffer();
}
if (IsBufferOutdated(offset, second)) {
bufs_[second].buffer_.Clear();
bufs_[second].ClearBuffer();
}
{
// In case buffers do not align, reset second buffer. This can happen in
// case readahead_size is set.
// In case buffers do not align, reset second buffer if requested data needs
// to be read in second buffer.
if (!bufs_[second].async_read_in_progress_ &&
!bufs_[curr_].async_read_in_progress_) {
if (DoesBufferContainData(curr_)) {
if (bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize() !=
bufs_[second].offset_) {
bufs_[second].buffer_.Clear();
if (DoesBufferContainData(second) &&
IsOffsetInBuffer(offset, curr_) &&
(offset + length >
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize())) {
bufs_[second].ClearBuffer();
}
}
} else {
if (!IsOffsetInBuffer(offset, second)) {
bufs_[second].buffer_.Clear();
if (DoesBufferContainData(second) &&
!IsOffsetInBuffer(offset, second)) {
bufs_[second].ClearBuffer();
}
}
}
@@ -308,7 +314,8 @@ void FilePrefetchBuffer::UpdateBuffersIfNeeded(uint64_t offset) {
}
}
void FilePrefetchBuffer::PollAndUpdateBuffersIfNeeded(uint64_t offset) {
void FilePrefetchBuffer::PollAndUpdateBuffersIfNeeded(uint64_t offset,
size_t length) {
if (bufs_[curr_].async_read_in_progress_ && fs_ != nullptr) {
if (bufs_[curr_].io_handle_ != nullptr) {
// Wait for prefetch data to complete.
@@ -324,7 +331,87 @@ void FilePrefetchBuffer::PollAndUpdateBuffersIfNeeded(uint64_t offset) {
// completed.
DestroyAndClearIOHandle(curr_);
}
UpdateBuffersIfNeeded(offset);
UpdateBuffersIfNeeded(offset, length);
}
// ReadAheadSizeTuning API calls readaheadsize_cb_
// (BlockBasedTableIterator::BlockCacheLookupForReadAheadSize) to lookup in the
// cache and tune the start and end offsets based on cache hits/misses.
//
// Arguments -
// read_curr_block : True if this call was due to miss in the cache and
// FilePrefetchBuffer wants to read that block
// synchronously.
// False if current call is to prefetch additional data in
// extra buffers through ReadAsync API.
// prev_buf_end_offset : End offset of the previous buffer. It's used in case
// of ReadAsync to make sure it doesn't read anything from
// previous buffer which is already prefetched.
void FilePrefetchBuffer::ReadAheadSizeTuning(
bool read_curr_block, bool refit_tail, uint64_t prev_buf_end_offset,
uint32_t index, size_t alignment, size_t length, size_t readahead_size,
uint64_t& start_offset, uint64_t& end_offset, size_t& read_len,
uint64_t& chunk_len) {
uint64_t updated_start_offset = Rounddown(start_offset, alignment);
uint64_t updated_end_offset =
Roundup(start_offset + length + readahead_size, alignment);
uint64_t initial_end_offset = updated_end_offset;
uint64_t initial_start_offset = updated_start_offset;
// Callback to tune the start and end offsets.
if (readaheadsize_cb_ != nullptr && readahead_size > 0) {
readaheadsize_cb_(read_curr_block, updated_start_offset,
updated_end_offset);
}
// read_len will be 0 and there is nothing to read/prefetch.
if (updated_start_offset == updated_end_offset) {
UpdateReadAheadTrimmedStat((initial_end_offset - initial_start_offset),
(updated_end_offset - updated_start_offset));
return;
}
assert(updated_start_offset < updated_end_offset);
if (!read_curr_block) {
// Handle the case when callback added block handles which are already
// prefetched and nothing new needs to be prefetched. In that case end
// offset updated by callback will be less than prev_buf_end_offset which
// means data has been already prefetched.
if (updated_end_offset <= prev_buf_end_offset) {
start_offset = end_offset = prev_buf_end_offset;
UpdateReadAheadTrimmedStat((initial_end_offset - initial_start_offset),
(end_offset - start_offset));
return;
}
}
// Realign if start and end offsets are not aligned after tuning.
start_offset = Rounddown(updated_start_offset, alignment);
end_offset = Roundup(updated_end_offset, alignment);
if (!read_curr_block && start_offset < prev_buf_end_offset) {
// Previous buffer already contains the data till prev_buf_end_offset
// because of alignment. Update the start offset after that to avoid
// prefetching it again.
start_offset = prev_buf_end_offset;
}
uint64_t roundup_len = end_offset - start_offset;
CalculateOffsetAndLen(alignment, start_offset, roundup_len, index, refit_tail,
chunk_len);
assert(roundup_len >= chunk_len);
// Update the buffer offset.
bufs_[index].offset_ = start_offset;
// Update the initial end offset of this buffer which will be the starting
// offset of next prefetch.
bufs_[index].initial_end_offset_ = initial_end_offset;
read_len = static_cast<size_t>(roundup_len - chunk_len);
UpdateReadAheadTrimmedStat((initial_end_offset - initial_start_offset),
(end_offset - start_offset));
}
Status FilePrefetchBuffer::HandleOverlappingData(
@@ -340,7 +427,7 @@ Status FilePrefetchBuffer::HandleOverlappingData(
// by Seek, but the next access is at another offset.
if (bufs_[curr_].async_read_in_progress_ &&
IsOffsetInBufferWithAsyncProgress(offset, curr_)) {
PollAndUpdateBuffersIfNeeded(offset);
PollAndUpdateBuffersIfNeeded(offset, length);
}
second = curr_ ^ 1;
@@ -354,7 +441,7 @@ Status FilePrefetchBuffer::HandleOverlappingData(
(bufs_[second].async_read_in_progress_ ||
DoesBufferContainData(second)))) {
// Allocate new buffer to third buffer;
bufs_[2].buffer_.Clear();
bufs_[2].ClearBuffer();
bufs_[2].buffer_.Alignment(alignment);
bufs_[2].buffer_.AllocateNewBuffer(length);
bufs_[2].offset_ = offset;
@@ -363,31 +450,28 @@ Status FilePrefetchBuffer::HandleOverlappingData(
CopyDataToBuffer(curr_, tmp_offset, tmp_length);
// Call async prefetching on curr_ since data has been consumed in curr_
// only if data lies within second buffer.
// only if requested data lies within second buffer.
size_t second_size = bufs_[second].async_read_in_progress_
? bufs_[second].async_req_len_
: bufs_[second].buffer_.CurrentSize();
uint64_t rounddown_start = bufs_[second].offset_ + second_size;
uint64_t start_offset = bufs_[second].initial_end_offset_;
// Second buffer might be out of bound if first buffer already prefetched
// that data.
if (tmp_offset + tmp_length <= bufs_[second].offset_ + second_size &&
!IsOffsetOutOfBound(rounddown_start)) {
uint64_t roundup_end =
Roundup(rounddown_start + readahead_size, alignment);
uint64_t roundup_len = roundup_end - rounddown_start;
uint64_t chunk_len = 0;
CalculateOffsetAndLen(alignment, rounddown_start, roundup_len, curr_,
false, chunk_len);
assert(chunk_len == 0);
assert(roundup_len >= chunk_len);
if (tmp_offset + tmp_length <= bufs_[second].offset_ + second_size) {
size_t read_len = 0;
uint64_t end_offset = start_offset, chunk_len = 0;
bufs_[curr_].offset_ = rounddown_start;
uint64_t read_len = static_cast<size_t>(roundup_len - chunk_len);
s = ReadAsync(opts, reader, read_len, rounddown_start, curr_);
if (!s.ok()) {
DestroyAndClearIOHandle(curr_);
bufs_[curr_].buffer_.Clear();
return s;
ReadAheadSizeTuning(/*read_curr_block=*/false, /*refit_tail=*/false,
bufs_[second].offset_ + second_size, curr_, alignment,
/*length=*/0, readahead_size, start_offset,
end_offset, read_len, chunk_len);
if (read_len > 0) {
s = ReadAsync(opts, reader, read_len, start_offset, curr_);
if (!s.ok()) {
DestroyAndClearIOHandle(curr_);
bufs_[curr_].ClearBuffer();
return s;
}
}
}
curr_ = curr_ ^ 1;
@@ -430,13 +514,14 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
Status s;
uint64_t tmp_offset = offset;
size_t tmp_length = length;
size_t original_length = length;
// 1. Abort IO and swap buffers if needed to point curr_ to first buffer with
// data.
if (!explicit_prefetch_submitted_) {
AbortIOIfNeeded(offset);
}
UpdateBuffersIfNeeded(offset);
UpdateBuffersIfNeeded(offset, length);
// 2. Handle overlapping data over two buffers. If data is overlapping then
// during this call:
@@ -458,14 +543,15 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
if (!bufs_[curr_].async_read_in_progress_ && DoesBufferContainData(curr_) &&
IsDataBlockInBuffer(offset, length, curr_)) {
// Whole data is in curr_.
UpdateBuffersIfNeeded(offset);
UpdateBuffersIfNeeded(offset, length);
if (!IsSecondBuffEligibleForPrefetching()) {
UpdateStats(/*found_in_buffer=*/true, original_length);
return s;
}
} else {
// After poll request, curr_ might be empty because of IOError in
// callback while reading or may contain required data.
PollAndUpdateBuffersIfNeeded(offset);
PollAndUpdateBuffersIfNeeded(offset, length);
}
if (copy_to_third_buffer) {
@@ -488,6 +574,7 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
return s;
}
if (!IsSecondBuffEligibleForPrefetching()) {
UpdateStats(/*found_in_buffer=*/true, original_length);
return s;
}
}
@@ -509,7 +596,7 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
}
}
DestroyAndClearIOHandle(second);
bufs_[second].buffer_.Clear();
bufs_[second].ClearBuffer();
}
// 5. Data is overlapping i.e. some of the data has been copied to third
@@ -524,73 +611,57 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
// and sync prefetching and copy the remaining data to third buffer in the
// end.
if (length == 0) {
UpdateStats(/*found_in_buffer=*/true, original_length);
return s;
}
}
// 6. Go for ReadAsync and Read (if needed).
size_t prefetch_size = length + readahead_size;
size_t _offset = static_cast<size_t>(offset);
// offset and size alignment for curr_ buffer with synchronous prefetching
uint64_t rounddown_start1 = Rounddown(_offset, alignment);
uint64_t roundup_end1 = Roundup(_offset + prefetch_size, alignment);
uint64_t roundup_len1 = roundup_end1 - rounddown_start1;
assert(roundup_len1 >= alignment);
assert(roundup_len1 % alignment == 0);
uint64_t chunk_len1 = 0;
uint64_t read_len1 = 0;
assert(!bufs_[second].async_read_in_progress_ &&
!DoesBufferContainData(second));
// offset and size alignment for curr_ buffer with synchronous prefetching
uint64_t start_offset1 = offset, end_offset1 = 0, chunk_len1 = 0;
size_t read_len1 = 0;
// For length == 0, skip the synchronous prefetching. read_len1 will be 0.
if (length > 0) {
CalculateOffsetAndLen(alignment, offset, roundup_len1, curr_,
false /*refit_tail*/, chunk_len1);
assert(roundup_len1 >= chunk_len1);
read_len1 = static_cast<size_t>(roundup_len1 - chunk_len1);
ReadAheadSizeTuning(/*read_curr_block=*/true, /*refit_tail=*/false,
start_offset1, curr_, alignment, length, readahead_size,
start_offset1, end_offset1, read_len1, chunk_len1);
UpdateStats(/*found_in_buffer=*/false,
/*length_found=*/original_length - length);
} else {
end_offset1 = bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
UpdateStats(/*found_in_buffer=*/true, original_length);
}
// Prefetch in second buffer only if readahead_size_ > 0.
if (readahead_size_ > 0) {
// Prefetch in second buffer only if readahead_size > 0.
if (readahead_size > 0) {
// offset and size alignment for second buffer for asynchronous
// prefetching
uint64_t rounddown_start2 = roundup_end1;
uint64_t roundup_end2 =
Roundup(rounddown_start2 + readahead_size, alignment);
// prefetching.
uint64_t start_offset2 = bufs_[curr_].initial_end_offset_;
// For length == 0, do the asynchronous prefetching in second instead of
// synchronous prefetching in curr_.
if (length == 0) {
rounddown_start2 =
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
roundup_end2 = Roundup(rounddown_start2 + prefetch_size, alignment);
}
// Second buffer might be out of bound if first buffer already prefetched
// that data.
if (!IsOffsetOutOfBound(rounddown_start2)) {
uint64_t roundup_len2 = roundup_end2 - rounddown_start2;
uint64_t chunk_len2 = 0;
CalculateOffsetAndLen(alignment, rounddown_start2, roundup_len2, second,
false /*refit_tail*/, chunk_len2);
assert(chunk_len2 == 0);
// Update the buffer offset.
bufs_[second].offset_ = rounddown_start2;
assert(roundup_len2 >= chunk_len2);
uint64_t read_len2 = static_cast<size_t>(roundup_len2 - chunk_len2);
s = ReadAsync(opts, reader, read_len2, rounddown_start2, second);
if (!s.ok()) {
DestroyAndClearIOHandle(second);
bufs_[second].buffer_.Clear();
return s;
}
// Find updated readahead size after tuning
size_t read_len2 = 0;
uint64_t end_offset2 = start_offset2, chunk_len2 = 0;
ReadAheadSizeTuning(/*read_curr_block=*/false, /*refit_tail=*/false,
/*prev_buf_end_offset=*/end_offset1, second,
alignment,
/*length=*/0, readahead_size, start_offset2,
end_offset2, read_len2, chunk_len2);
if (read_len2 > 0) {
s = ReadAsync(opts, reader, read_len2, start_offset2, second);
if (!s.ok()) {
DestroyAndClearIOHandle(second);
bufs_[second].ClearBuffer();
return s;
}
}
}
if (read_len1 > 0) {
s = Read(opts, reader, read_len1, chunk_len1, rounddown_start1, curr_);
s = Read(opts, reader, read_len1, chunk_len1, start_offset1, curr_);
if (!s.ok()) {
if (bufs_[second].io_handle_ != nullptr) {
std::vector<void*> handles;
@@ -602,11 +673,12 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(const IOOptions& opts,
}
}
DestroyAndClearIOHandle(second);
bufs_[second].buffer_.Clear();
bufs_[curr_].buffer_.Clear();
bufs_[second].ClearBuffer();
bufs_[curr_].ClearBuffer();
return s;
}
}
// Copy remaining requested bytes to third_buffer.
if (copy_to_third_buffer && length > 0) {
CopyDataToBuffer(curr_, offset, length);
@@ -668,8 +740,7 @@ bool FilePrefetchBuffer::TryReadFromCacheUntracked(
return false;
}
}
size_t current_readahead_size = ReadAheadSizeTuning(offset, n);
s = Prefetch(opts, reader, offset, n + current_readahead_size);
s = Prefetch(opts, reader, offset, n + readahead_size_);
}
if (!s.ok()) {
if (status) {
@@ -729,8 +800,8 @@ bool FilePrefetchBuffer::TryReadFromCacheAsyncUntracked(
// Random offset called. So abort the IOs.
if (prev_offset_ != offset) {
AbortAllIOs();
bufs_[curr_].buffer_.Clear();
bufs_[curr_ ^ 1].buffer_.Clear();
bufs_[curr_].ClearBuffer();
bufs_[curr_ ^ 1].ClearBuffer();
explicit_prefetch_submitted_ = false;
return false;
}
@@ -768,8 +839,6 @@ bool FilePrefetchBuffer::TryReadFromCacheAsyncUntracked(
}
}
UpdateReadAheadSizeForUpperBound(offset, n);
// Prefetch n + readahead_size_/2 synchronously as remaining
// readahead_size_/2 will be prefetched asynchronously.
s = PrefetchAsyncInternal(opts, reader, offset, n, readahead_size_ / 2,
@@ -788,6 +857,8 @@ bool FilePrefetchBuffer::TryReadFromCacheAsyncUntracked(
} else {
return false;
}
} else {
UpdateStats(/*found_in_buffer=*/true, n);
}
UpdateReadPattern(offset, n, false /*decrease_readaheadsize*/);
@@ -848,7 +919,6 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
explicit_prefetch_submitted_ = false;
bool is_eligible_for_prefetching = false;
UpdateReadAheadSizeForUpperBound(offset, n);
if (readahead_size_ > 0 &&
(!implicit_auto_readahead_ ||
num_file_reads_ >= num_file_reads_for_auto_readahead_)) {
@@ -860,14 +930,19 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
AbortAllIOs();
// 2. Clear outdated data.
UpdateBuffersIfNeeded(offset);
UpdateBuffersIfNeeded(offset, n);
uint32_t second = curr_ ^ 1;
// Since PrefetchAsync can be called on non sequential reads. So offset can
// be less than curr_ buffers' offset. In that case also it clears both
// buffers.
if (DoesBufferContainData(curr_) && !IsOffsetInBuffer(offset, curr_)) {
bufs_[curr_].buffer_.Clear();
bufs_[second].buffer_.Clear();
// - Since PrefetchAsync can be called on non sequential reads. So offset can
// be less than curr_ buffers' offset. In that case it clears both
// buffers.
// - In case of tuning of readahead_size, on Reseek, we have to clear both
// buffers otherwise, we may end up with inconsistent BlockHandles in queue
// and data in buffer.
if (readaheadsize_cb_ != nullptr ||
(DoesBufferContainData(curr_) && !IsOffsetInBuffer(offset, curr_))) {
bufs_[curr_].ClearBuffer();
bufs_[second].ClearBuffer();
}
UpdateReadPattern(offset, n, /*decrease_readaheadsize=*/false);
@@ -879,6 +954,8 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
uint64_t offset_in_buffer = offset - bufs_[curr_].offset_;
*result = Slice(bufs_[curr_].buffer_.BufferStart() + offset_in_buffer, n);
data_found = true;
UpdateStats(/*found_in_buffer=*/true, n);
// Update num_file_reads_ as TryReadFromCacheAsync won't be called for
// poll and update num_file_reads_ if data is found.
num_file_reads_++;
@@ -890,91 +967,78 @@ Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
}
} else {
// Partial data in curr_.
bufs_[curr_].buffer_.Clear();
bufs_[curr_].ClearBuffer();
}
bufs_[second].buffer_.Clear();
bufs_[second].ClearBuffer();
std::string msg;
Status s;
size_t alignment = reader->file()->GetRequiredBufferAlignment();
size_t prefetch_size = is_eligible_for_prefetching ? readahead_size_ / 2 : 0;
size_t readahead_size = is_eligible_for_prefetching ? readahead_size_ / 2 : 0;
size_t offset_to_read = static_cast<size_t>(offset);
uint64_t rounddown_start1 = 0;
uint64_t roundup_end1 = 0;
uint64_t rounddown_start2 = 0;
uint64_t roundup_end2 = 0;
uint64_t chunk_len1 = 0;
uint64_t chunk_len2 = 0;
size_t read_len1 = 0;
size_t read_len2 = 0;
uint64_t start_offset1 = offset, end_offset1 = 0, start_offset2 = 0,
chunk_len1 = 0;
size_t read_len1 = 0, read_len2 = 0;
// - If curr_ is empty.
// - Call async read for full data + prefetch_size on curr_.
// - Call async read for prefetch_size on second if eligible.
// - Call async read for full data + readahead_size on curr_.
// - Call async read for readahead_size on second if eligible.
// - If curr_ is filled.
// - prefetch_size on second.
// - readahead_size on second.
// Calculate length and offsets for reading.
if (!DoesBufferContainData(curr_)) {
uint64_t roundup_len1;
// Prefetch full data + prefetch_size in curr_.
// Prefetch full data + readahead_size in curr_.
if (is_eligible_for_prefetching || reader->use_direct_io()) {
rounddown_start1 = Rounddown(offset_to_read, alignment);
roundup_end1 = Roundup(offset_to_read + n + prefetch_size, alignment);
roundup_len1 = roundup_end1 - rounddown_start1;
assert(roundup_len1 >= alignment);
assert(roundup_len1 % alignment == 0);
ReadAheadSizeTuning(/*read_curr_block=*/true, /*refit_tail=*/false,
/*prev_buf_end_offset=*/start_offset1, curr_,
alignment, n, readahead_size, start_offset1,
end_offset1, read_len1, chunk_len1);
} else {
rounddown_start1 = offset_to_read;
roundup_end1 = offset_to_read + n;
roundup_len1 = roundup_end1 - rounddown_start1;
// No alignment or extra prefetching.
start_offset1 = offset_to_read;
end_offset1 = offset_to_read + n;
roundup_len1 = end_offset1 - start_offset1;
CalculateOffsetAndLen(alignment, start_offset1, roundup_len1, curr_,
false, chunk_len1);
assert(chunk_len1 == 0);
assert(roundup_len1 >= chunk_len1);
read_len1 = static_cast<size_t>(roundup_len1);
bufs_[curr_].offset_ = start_offset1;
}
CalculateOffsetAndLen(alignment, rounddown_start1, roundup_len1, curr_,
false, chunk_len1);
assert(chunk_len1 == 0);
assert(roundup_len1 >= chunk_len1);
read_len1 = static_cast<size_t>(roundup_len1);
bufs_[curr_].offset_ = rounddown_start1;
}
if (is_eligible_for_prefetching) {
if (DoesBufferContainData(curr_)) {
rounddown_start2 =
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
} else {
rounddown_start2 = roundup_end1;
}
start_offset2 = bufs_[curr_].initial_end_offset_;
// Second buffer might be out of bound if first buffer already prefetched
// that data.
if (!IsOffsetOutOfBound(rounddown_start2)) {
roundup_end2 = Roundup(rounddown_start2 + prefetch_size, alignment);
uint64_t roundup_len2 = roundup_end2 - rounddown_start2;
CalculateOffsetAndLen(alignment, rounddown_start2, roundup_len2, second,
false, chunk_len2);
assert(chunk_len2 == 0);
assert(roundup_len2 >= chunk_len2);
read_len2 = static_cast<size_t>(roundup_len2 - chunk_len2);
// Update the buffer offset.
bufs_[second].offset_ = rounddown_start2;
}
uint64_t end_offset2 = start_offset2, chunk_len2 = 0;
ReadAheadSizeTuning(/*read_curr_block=*/false, /*refit_tail=*/false,
/*prev_buf_end_offset=*/end_offset1, second,
alignment,
/*length=*/0, readahead_size, start_offset2,
end_offset2, read_len2, chunk_len2);
}
if (read_len1) {
s = ReadAsync(opts, reader, read_len1, rounddown_start1, curr_);
s = ReadAsync(opts, reader, read_len1, start_offset1, curr_);
if (!s.ok()) {
DestroyAndClearIOHandle(curr_);
bufs_[curr_].buffer_.Clear();
bufs_[curr_].ClearBuffer();
return s;
}
explicit_prefetch_submitted_ = true;
prev_len_ = 0;
}
if (read_len2) {
TEST_SYNC_POINT("FilePrefetchBuffer::PrefetchAsync:ExtraPrefetching");
s = ReadAsync(opts, reader, read_len2, rounddown_start2, second);
s = ReadAsync(opts, reader, read_len2, start_offset2, second);
if (!s.ok()) {
DestroyAndClearIOHandle(second);
bufs_[second].buffer_.Clear();
bufs_[second].ClearBuffer();
return s;
}
readahead_size_ = std::min(max_readahead_size_, readahead_size_ * 2);
+46 -48
View File
@@ -32,6 +32,11 @@ struct IOOptions;
class RandomAccessFileReader;
struct BufferInfo {
void ClearBuffer() {
buffer_.Clear();
initial_end_offset_ = 0;
}
AlignedBuffer buffer_;
uint64_t offset_ = 0;
@@ -52,6 +57,18 @@ struct BufferInfo {
// pos represents the index of this buffer in vector of BufferInfo.
uint32_t pos_ = 0;
// initial_end_offset is used to keep track of the end offset of the buffer
// that was originally called. It's helpful in case of autotuning of readahead
// size when callback is made to BlockBasedTableIterator.
// initial end offset of this buffer which will be the starting
// offset of next prefetch.
//
// For example - if end offset of previous buffer was 100 and because of
// readahead_size optimization, end_offset was trimmed to 60. Then for next
// prefetch call, start_offset should be intialized to 100 i.e start_offset =
// buf->initial_end_offset_.
uint64_t initial_end_offset_ = 0;
};
enum class FilePrefetchBufferUsage {
@@ -88,10 +105,9 @@ class FilePrefetchBuffer {
size_t readahead_size = 0, size_t max_readahead_size = 0,
bool enable = true, bool track_min_offset = false,
bool implicit_auto_readahead = false, uint64_t num_file_reads = 0,
uint64_t num_file_reads_for_auto_readahead = 0,
uint64_t upper_bound_offset = 0, FileSystem* fs = nullptr,
uint64_t num_file_reads_for_auto_readahead = 0, FileSystem* fs = nullptr,
SystemClock* clock = nullptr, Statistics* stats = nullptr,
const std::function<void(uint64_t, size_t, size_t&)>& cb = nullptr,
const std::function<void(bool, uint64_t&, uint64_t&)>& cb = nullptr,
FilePrefetchBufferUsage usage = FilePrefetchBufferUsage::kUnknown)
: curr_(0),
readahead_size_(readahead_size),
@@ -110,7 +126,6 @@ class FilePrefetchBuffer {
clock_(clock),
stats_(stats),
usage_(usage),
upper_bound_offset_(upper_bound_offset),
readaheadsize_cb_(cb) {
assert((num_file_reads_ >= num_file_reads_for_auto_readahead_ + 1) ||
(num_file_reads_ == 0));
@@ -239,9 +254,6 @@ class FilePrefetchBuffer {
void UpdateReadPattern(const uint64_t& offset, const size_t& len,
bool decrease_readaheadsize) {
if (decrease_readaheadsize) {
// Since this block was eligible for prefetch but it was found in
// cache, so check and decrease the readahead_size by 8KB (default)
// if eligible.
DecreaseReadAheadIfEligible(offset, len);
}
prev_offset_ = offset;
@@ -282,9 +294,10 @@ class FilePrefetchBuffer {
// Callback function passed to underlying FS in case of asynchronous reads.
void PrefetchAsyncCallback(const FSReadRequest& req, void* cb_arg);
void ResetUpperBoundOffset(uint64_t upper_bound_offset) {
upper_bound_offset_ = upper_bound_offset;
readahead_size_ = initial_auto_readahead_size_;
void TEST_GetBufferOffsetandSize(uint32_t index, uint64_t& offset,
size_t& len) {
offset = bufs_[index].offset_;
len = bufs_[index].buffer_.CurrentSize();
}
private:
@@ -299,12 +312,12 @@ class FilePrefetchBuffer {
void AbortAllIOs();
void UpdateBuffersIfNeeded(uint64_t offset);
void UpdateBuffersIfNeeded(uint64_t offset, size_t len);
// It calls Poll API if any there is any pending asynchronous request. It then
// checks if data is in any buffer. It clears the outdated data and swaps the
// buffers if required.
void PollAndUpdateBuffersIfNeeded(uint64_t offset);
void PollAndUpdateBuffersIfNeeded(uint64_t offset, size_t len);
Status PrefetchAsyncInternal(const IOOptions& opts,
RandomAccessFileReader* reader, uint64_t offset,
@@ -312,11 +325,11 @@ class FilePrefetchBuffer {
bool& copy_to_third_buffer);
Status Read(const IOOptions& opts, RandomAccessFileReader* reader,
uint64_t read_len, uint64_t chunk_len, uint64_t rounddown_start,
uint64_t read_len, uint64_t chunk_len, uint64_t start_offset,
uint32_t index);
Status ReadAsync(const IOOptions& opts, RandomAccessFileReader* reader,
uint64_t read_len, uint64_t rounddown_start, uint32_t index);
uint64_t read_len, uint64_t start_offset, uint32_t index);
// Copy the data from src to third buffer.
void CopyDataToBuffer(uint32_t src, uint64_t& offset, size_t& length);
@@ -402,7 +415,7 @@ class FilePrefetchBuffer {
return false;
}
bufs_[second].buffer_.Clear();
bufs_[second].ClearBuffer();
return true;
}
@@ -432,38 +445,27 @@ class FilePrefetchBuffer {
uint64_t offset, size_t n, Slice* result,
Status* status);
void UpdateReadAheadSizeForUpperBound(uint64_t offset, size_t n) {
// Adjust readhahead_size till upper_bound if upper_bound_offset_ is
// set.
if (readahead_size_ > 0 && upper_bound_offset_ > 0 &&
upper_bound_offset_ > offset) {
if (upper_bound_offset_ < offset + n + readahead_size_) {
readahead_size_ = (upper_bound_offset_ - offset) - n;
RecordTick(stats_, READAHEAD_TRIMMED);
}
void ReadAheadSizeTuning(bool read_curr_block, bool refit_tail,
uint64_t prev_buf_end_offset, uint32_t index,
size_t alignment, size_t length,
size_t readahead_size, uint64_t& offset,
uint64_t& end_offset, size_t& read_len,
uint64_t& chunk_len);
void UpdateStats(bool found_in_buffer, size_t length_found) {
if (found_in_buffer) {
RecordTick(stats_, PREFETCH_HITS);
}
if (length_found > 0) {
RecordTick(stats_, PREFETCH_BYTES_USEFUL, length_found);
}
}
inline bool IsOffsetOutOfBound(uint64_t offset) {
if (upper_bound_offset_ > 0) {
return (offset >= upper_bound_offset_);
void UpdateReadAheadTrimmedStat(size_t initial_length,
size_t updated_length) {
if (initial_length != updated_length) {
RecordTick(stats_, READAHEAD_TRIMMED);
}
return false;
}
// Performs tuning to calculate readahead_size.
size_t ReadAheadSizeTuning(uint64_t offset, size_t n) {
UpdateReadAheadSizeForUpperBound(offset, n);
if (readaheadsize_cb_ != nullptr && readahead_size_ > 0) {
size_t updated_readahead_size = 0;
readaheadsize_cb_(offset, readahead_size_, updated_readahead_size);
if (readahead_size_ != updated_readahead_size) {
RecordTick(stats_, READAHEAD_TRIMMED);
}
return updated_readahead_size;
}
return readahead_size_;
}
std::vector<BufferInfo> bufs_;
@@ -508,10 +510,6 @@ class FilePrefetchBuffer {
FilePrefetchBufferUsage usage_;
// upper_bound_offset_ is set when ReadOptions.iterate_upper_bound and
// ReadOptions.auto_readahead_size are set to trim readahead_size upto
// upper_bound_offset_ during prefetching.
uint64_t upper_bound_offset_ = 0;
std::function<void(uint64_t, size_t, size_t&)> readaheadsize_cb_;
std::function<void(bool, uint64_t&, uint64_t&)> readaheadsize_cb_;
};
} // namespace ROCKSDB_NAMESPACE
+2 -3
View File
@@ -8,10 +8,9 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "file/filename.h"
#include <ctype.h>
#include <stdio.h>
#include <cctype>
#include <cinttypes>
#include <cstdio>
#include <vector>
#include "file/writable_file_writer.h"
+9 -381
View File
@@ -1325,6 +1325,10 @@ TEST_P(PrefetchTest, PrefetchWithBlockLookupAutoTuneTest) {
ropts.readahead_size = cmp_ro.readahead_size = 32768;
}
if (std::get<1>(GetParam())) {
ropts.async_io = true;
}
// With and without tuning readahead_size.
{
ASSERT_OK(options.statistics->Reset());
@@ -1351,10 +1355,6 @@ TEST_P(PrefetchTest, PrefetchWithBlockLookupAutoTuneTest) {
cmp_iter->Next();
}
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
ASSERT_OK(cmp_iter->status());
ASSERT_OK(iter->status());
}
@@ -1381,10 +1381,6 @@ TEST_P(PrefetchTest, PrefetchWithBlockLookupAutoTuneTest) {
cmp_iter->Next();
}
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
ASSERT_OK(cmp_iter->status());
ASSERT_OK(iter->status());
}
@@ -2353,314 +2349,6 @@ TEST_P(PrefetchTest1, SeekParallelizationTest) {
Close();
}
// This test checks if readahead_size is trimmed when upper_bound is reached.
// It tests with different combinations of async_io disabled/enabled,
// readahead_size (implicit and explicit), and num_file_reads_for_auto_readahead
// from 0 to 2.
TEST_P(PrefetchTest, IterReadAheadSizeWithUpperBound) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
// First param is if the mockFS support_prefetch or not
std::shared_ptr<MockFS> fs =
std::make_shared<MockFS>(FileSystem::Default(), false);
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
Options options;
SetGenericOptions(env.get(), /*use_direct_io=*/false, options);
options.statistics = CreateDBStatistics();
BlockBasedTableOptions table_options;
SetBlockBasedTableOptions(table_options);
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Status s = TryReopen(options);
ASSERT_OK(s);
Random rnd(309);
WriteBatch batch;
for (int i = 0; i < 26; i++) {
std::string key = "my_key_";
for (int j = 0; j < 10; j++) {
key += char('a' + i);
ASSERT_OK(batch.Put(key, rnd.RandomString(1000)));
}
}
ASSERT_OK(db_->Write(WriteOptions(), &batch));
std::string start_key = "my_key_a";
std::string end_key = "my_key_";
for (int j = 0; j < 10; j++) {
end_key += char('a' + 25);
}
Slice least(start_key.data(), start_key.size());
Slice greatest(end_key.data(), end_key.size());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &least, &greatest));
int buff_prefetch_count = 0;
// Try with different num_file_reads_for_auto_readahead from 0 to 3.
for (size_t i = 0; i < 3; i++) {
table_options.num_file_reads_for_auto_readahead = i;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
s = TryReopen(options);
ASSERT_OK(s);
int buff_count_with_tuning = 0, buff_count_without_tuning = 0;
int keys_with_tuning = 0, keys_without_tuning = 0;
int reseek_keys_with_tuning = 0, reseek_keys_without_tuning = 0;
buff_prefetch_count = 0;
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::Prefetch:Start",
[&](void*) { buff_prefetch_count++; });
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::PrefetchAsyncInternal:Start",
[&](void*) { buff_prefetch_count++; });
SyncPoint::GetInstance()->EnableProcessing();
ReadOptions ropts;
if (std::get<0>(GetParam())) {
ropts.readahead_size = 32768;
}
if (std::get<1>(GetParam())) {
ropts.async_io = true;
}
// With tuning readahead_size.
{
ASSERT_OK(options.statistics->Reset());
Slice ub = Slice("my_key_uuu");
Slice* ub_ptr = &ub;
ropts.iterate_upper_bound = ub_ptr;
ropts.auto_readahead_size = true;
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ropts));
// Seek.
{
Slice seek_key = Slice("my_key_aaa");
iter->Seek(seek_key);
while (iter->Valid()) {
keys_with_tuning++;
iter->Next();
}
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
buff_count_with_tuning = buff_prefetch_count;
}
// Reseek with new upper_bound_iterator.
{
ub = Slice("my_key_y");
Slice reseek_key = Slice("my_key_v");
iter->Seek(reseek_key);
while (iter->Valid()) {
iter->Next();
reseek_keys_with_tuning++;
}
ASSERT_OK(iter->status());
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_GT(readahead_trimmed, 0);
ASSERT_GT(reseek_keys_with_tuning, 0);
}
}
// Without tuning readahead_size
{
Slice ub = Slice("my_key_uuu");
Slice* ub_ptr = &ub;
ropts.iterate_upper_bound = ub_ptr;
buff_prefetch_count = 0;
ASSERT_OK(options.statistics->Reset());
ropts.auto_readahead_size = false;
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ropts));
// Seek.
{
Slice seek_key = Slice("my_key_aaa");
iter->Seek(seek_key);
while (iter->Valid()) {
keys_without_tuning++;
iter->Next();
}
buff_count_without_tuning = buff_prefetch_count;
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_EQ(readahead_trimmed, 0);
}
// Reseek with new upper_bound_iterator.
{
ub = Slice("my_key_y");
Slice reseek_key = Slice("my_key_v");
iter->Seek(reseek_key);
while (iter->Valid()) {
iter->Next();
reseek_keys_without_tuning++;
}
ASSERT_OK(iter->status());
uint64_t readahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
ASSERT_EQ(readahead_trimmed, 0);
ASSERT_GT(reseek_keys_without_tuning, 0);
}
}
{
// Verify results with and without tuning.
if (std::get<1>(GetParam())) {
// In case of async_io.
ASSERT_GE(buff_count_with_tuning, buff_count_without_tuning);
} else {
ASSERT_EQ(buff_count_without_tuning, buff_count_with_tuning);
}
// Prefetching should happen.
ASSERT_GT(buff_count_without_tuning, 0);
ASSERT_GT(buff_count_with_tuning, 0);
// No of keys should be equal.
ASSERT_EQ(keys_without_tuning, keys_with_tuning);
// No of keys after reseek with new upper bound should be equal.
ASSERT_EQ(reseek_keys_without_tuning, reseek_keys_with_tuning);
}
Close();
}
}
// This test checks if readahead_size is trimmed when upper_bound is reached
// during Seek in async_io and it goes for polling without any extra
// prefetching.
TEST_P(PrefetchTest, IterReadAheadSizeWithUpperBoundSeekOnly) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
// First param is if the mockFS support_prefetch or not
std::shared_ptr<MockFS> fs =
std::make_shared<MockFS>(FileSystem::Default(), false);
bool use_direct_io = false;
if (std::get<0>(GetParam())) {
use_direct_io = true;
}
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
Options options;
SetGenericOptions(env.get(), use_direct_io, options);
options.statistics = CreateDBStatistics();
BlockBasedTableOptions table_options;
SetBlockBasedTableOptions(table_options);
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Status s = TryReopen(options);
if (use_direct_io && (s.IsNotSupported() || s.IsInvalidArgument())) {
// If direct IO is not supported, skip the test
return;
} else {
ASSERT_OK(s);
}
Random rnd(309);
WriteBatch batch;
for (int i = 0; i < 26; i++) {
std::string key = "my_key_";
for (int j = 0; j < 10; j++) {
key += char('a' + i);
ASSERT_OK(batch.Put(key, rnd.RandomString(1000)));
}
}
ASSERT_OK(db_->Write(WriteOptions(), &batch));
std::string start_key = "my_key_a";
std::string end_key = "my_key_";
for (int j = 0; j < 10; j++) {
end_key += char('a' + 25);
}
Slice least(start_key.data(), start_key.size());
Slice greatest(end_key.data(), end_key.size());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &least, &greatest));
s = TryReopen(options);
ASSERT_OK(s);
int buff_count_with_tuning = 0;
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::PrefetchAsyncInternal:Start",
[&](void*) { buff_count_with_tuning++; });
bool read_async_called = false;
SyncPoint::GetInstance()->SetCallBack(
"UpdateResults::io_uring_result",
[&](void* /*arg*/) { read_async_called = true; });
SyncPoint::GetInstance()->EnableProcessing();
SyncPoint::GetInstance()->EnableProcessing();
ReadOptions ropts;
if (std::get<1>(GetParam())) {
ropts.readahead_size = 32768;
}
ropts.async_io = true;
Slice ub = Slice("my_key_aaa");
ropts.iterate_upper_bound = &ub;
Slice seek_key = Slice("my_key_aaa");
// With tuning readahead_size.
{
ASSERT_OK(options.statistics->Reset());
ropts.auto_readahead_size = true;
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ropts));
iter->Seek(seek_key);
ASSERT_OK(iter->status());
// Verify results.
uint64_t readhahead_trimmed =
options.statistics->getAndResetTickerCount(READAHEAD_TRIMMED);
// Readahead got trimmed.
if (read_async_called) {
ASSERT_GT(readhahead_trimmed, 0);
// Seek called PrefetchAsync to poll the data.
ASSERT_EQ(1, buff_count_with_tuning);
} else {
// async_io disabled.
ASSERT_GE(readhahead_trimmed, 0);
ASSERT_EQ(0, buff_count_with_tuning);
}
}
Close();
}
namespace {
#ifdef GFLAGS
const int kMaxArgCount = 100;
@@ -3239,7 +2927,7 @@ TEST_F(FilePrefetchBufferTest, SeekWithBlockCacheHit) {
std::unique_ptr<RandomAccessFileReader> r;
Read(fname, opts, &r);
FilePrefetchBuffer fpb(16384, 16384, true, false, false, 0, 0, 0, fs());
FilePrefetchBuffer fpb(16384, 16384, true, false, false, 0, 0, fs());
Slice result;
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
// it will do two reads of 4096+8192 and 8192
@@ -3288,8 +2976,7 @@ TEST_F(FilePrefetchBufferTest, SeekWithoutAlignment) {
FilePrefetchBuffer fpb(
/*readahead_size=*/8192, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/true,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2,
/*upper_bound_offset=*/0, fs());
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2, fs());
Slice result;
// Simulate a seek of half of alignment bytes at offset n. Due to the
@@ -3320,8 +3007,7 @@ TEST_F(FilePrefetchBufferTest, SeekWithoutAlignment) {
FilePrefetchBuffer fpb(
/*readahead_size=*/16384, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/false,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2,
/*upper_bound_offset=*/0, fs());
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/2, fs());
Slice result;
// Simulate a seek of half of alignment bytes at offset n.
@@ -3357,8 +3043,7 @@ TEST_F(FilePrefetchBufferTest, NoSyncWithAsyncIO) {
FilePrefetchBuffer fpb(
/*readahead_size=*/8192, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/false,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/0,
/*upper_bound_offset=*/0, fs());
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/0, fs());
int read_async_called = 0;
SyncPoint::GetInstance()->SetCallBack(
@@ -3396,63 +3081,6 @@ TEST_F(FilePrefetchBufferTest, NoSyncWithAsyncIO) {
ASSERT_EQ(result, async_result);
}
// This test checks if during seek in async_io, if first buffer already
// prefetched the data till upper_bound offset, second buffer shouldn't go for
// prefetching.
TEST_F(FilePrefetchBufferTest, IterateUpperBoundTest1) {
std::string fname = "iterate-upperbound-test1";
Random rand(0);
std::string content = rand.RandomString(32768);
Write(fname, content);
FileOptions opts;
std::unique_ptr<RandomAccessFileReader> r;
Read(fname, opts, &r);
FilePrefetchBuffer fpb(
/*readahead_size=*/8192, /*max_readahead_size=*/16384, /*enable=*/true,
/*track_min_offset=*/false, /*implicit_auto_readahead=*/false,
/*num_file_reads=*/0, /*num_file_reads_for_auto_readahead=*/0,
/*upper_bound_offset=*/8000, fs());
int read_async_called = 0;
SyncPoint::GetInstance()->SetCallBack(
"FilePrefetchBuffer::ReadAsync",
[&](void* /*arg*/) { read_async_called++; });
SyncPoint::GetInstance()->EnableProcessing();
Slice async_result;
// Simulate a seek of 4000 bytes at offset 3000. Due to the readahead
// settings, it will do 1 read of 4000+1000 (till 8000 - upper bound).
Status s = fpb.PrefetchAsync(IOOptions(), r.get(), 3000, 4000, &async_result);
// Platforms that don't have IO uring may not support async IO
if (s.IsNotSupported()) {
return;
}
ASSERT_TRUE(s.IsTryAgain());
IOOptions io_opts;
io_opts.rate_limiter_priority = Env::IOPriority::IO_LOW;
ASSERT_TRUE(fpb.TryReadFromCacheAsync(io_opts, r.get(), /*offset=*/3000,
/*length=*/4000, &async_result, &s));
// No sync call should be made.
HistogramData sst_read_micros;
stats()->histogramData(SST_READ_MICROS, &sst_read_micros);
ASSERT_EQ(sst_read_micros.count, 0);
// Number of async calls should be 1.
// No Prefetching should happen in second buffer as first buffer has already
// prefetched till offset.
ASSERT_EQ(read_async_called, 1);
// Length should be 4000.
ASSERT_EQ(async_result.size(), 4000);
// Data correctness.
Slice result(&content[3000], 4000);
ASSERT_EQ(result.size(), 4000);
ASSERT_EQ(result, async_result);
}
TEST_F(FilePrefetchBufferTest, SyncReadaheadStats) {
std::string fname = "seek-with-block-cache-hit";
Random rand(0);
@@ -3464,7 +3092,7 @@ TEST_F(FilePrefetchBufferTest, SyncReadaheadStats) {
Read(fname, opts, &r);
std::shared_ptr<Statistics> stats = CreateDBStatistics();
FilePrefetchBuffer fpb(8192, 8192, true, false, false, 0, 0, 0, fs(), nullptr,
FilePrefetchBuffer fpb(8192, 8192, true, false, false, 0, 0, fs(), nullptr,
stats.get());
Slice result;
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
+3 -2
View File
@@ -492,8 +492,9 @@ IOStatus RandomAccessFileReader::ReadAsync(
auto read_async_callback =
std::bind(&RandomAccessFileReader::ReadAsyncCallback, this,
std::placeholders::_1, std::placeholders::_2);
ReadAsyncInfo* read_async_info =
new ReadAsyncInfo(cb, cb_arg, clock_->NowMicros());
ReadAsyncInfo* read_async_info = new ReadAsyncInfo(
cb, cb_arg, (clock_ != nullptr ? clock_->NowMicros() : 0));
if (ShouldNotifyListeners()) {
read_async_info->fs_start_ts_ = FileOperationInfo::StartNow();
+9 -3
View File
@@ -425,7 +425,9 @@ TEST(FSReadRequest, TryMerge) {
src.scratch = nullptr;
ASSERT_OK(src.status);
if (reverse) std::swap(dest, src);
if (reverse) {
std::swap(dest, src);
}
ASSERT_TRUE(TryMerge(&dest, src));
ASSERT_EQ(dest.offset, 0);
ASSERT_EQ(dest.len, 10);
@@ -448,7 +450,9 @@ TEST(FSReadRequest, TryMerge) {
src.scratch = nullptr;
ASSERT_OK(src.status);
if (reverse) std::swap(dest, src);
if (reverse) {
std::swap(dest, src);
}
ASSERT_TRUE(TryMerge(&dest, src));
ASSERT_EQ(dest.offset, 0);
ASSERT_EQ(dest.len, 10);
@@ -471,7 +475,9 @@ TEST(FSReadRequest, TryMerge) {
src.scratch = nullptr;
ASSERT_OK(src.status);
if (reverse) std::swap(dest, src);
if (reverse) {
std::swap(dest, src);
}
ASSERT_TRUE(TryMerge(&dest, src));
ASSERT_EQ(dest.offset, 0);
ASSERT_EQ(dest.len, 10);
+41 -3
View File
@@ -380,9 +380,6 @@ inline std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
// to find the appropriate balance automatically.
// * Cache priorities are less aggressively enforced, which could cause
// cache dilution from long range scans (unless they use fill_cache=false).
// * Can be worse for small caches, because if almost all of a cache shard is
// pinned (more likely with non-partitioned filters), then CLOCK eviction
// becomes very CPU intensive.
//
// See internal cache/clock_cache.h for full description.
struct HyperClockCacheOptions : public ShardedCacheOptions {
@@ -441,6 +438,43 @@ struct HyperClockCacheOptions : public ShardedCacheOptions {
// load factor for efficient Lookup, Insert, etc.
size_t min_avg_entry_charge = 450;
// A tuning parameter to cap eviction CPU usage in a "thrashing" situation
// by allowing the memory capacity to be exceeded slightly as needed. The
// default setting should offer balanced protection against excessive CPU
// and memory usage under extreme stress conditions, with no effect on
// normal operation. Such stress conditions are proportionally more likely
// with small caches (10s of MB or less) vs. large caches (GB-scale).
// (NOTE: With the unusual setting of strict_capacity_limit=true, this
// parameter is ignored.)
//
// BACKGROUND: Without some kind of limiter, inserting into a CLOCK-based
// cache with no evictable entries (all "pinned") requires scanning the
// entire cache to determine that nothing can be evicted. (By contrast,
// LRU caches can determine no entries are evictable in O(1) time, but
// require more synchronization/coordination on that eviction metadata.)
// This aspect of a CLOCK cache can make a stressed situation worse by
// bogging down the CPU with repeated scans of the cache. And with
// strict_capacity_limit=false (normal setting), finding something evictable
// doesn't change the outcome of insertion: the entry is inserted anyway
// and the cache is allowed to exceed its target capacity if necessary.
//
// SOLUTION: Eviction is aborted upon seeing some number of pinned
// entries before evicting anything, or if the ratio of pinned to evicted
// is too high. This setting `eviction_effort_cap` essentially controls both
// that allowed initial number of pinned entries and the maximum allowed
// ratio. As the pinned size approaches the target cache capacity, roughly
// 1/eviction_effort_cap additional portion of the capacity might be kept
// in memory and evictable in order to keep CLOCK eviction reasonably
// performant. Under the default setting and high stress conditions, this
// memory overhead is around 3-5%. Under normal or even moderate stress
// conditions, the memory overhead is negligible to zero.
//
// A large value like 1000 offers some protection with essentially no
// memory overhead, while the minimum value of 1 could be useful for a
// small cache where roughly doubling in size under stress could be OK to
// keep operations very fast.
int eviction_effort_cap = 30;
HyperClockCacheOptions(
size_t _capacity, size_t _estimated_entry_charge,
int _num_shard_bits = -1, bool _strict_capacity_limit = false,
@@ -499,6 +533,10 @@ enum TieredAdmissionPolicy {
// allocations costed to the block cache, will be distributed
// proportionally across both the primary and secondary.
struct TieredCacheOptions {
// This should point to an instance of either LRUCacheOptions or
// HyperClockCacheOptions, depending on the cache_type. In either
// case, the capacity and secondary_cache fields in those options
// should not be set. If set, they will be ignored by NewTieredCache.
ShardedCacheOptions* cache_opts = nullptr;
PrimaryCacheType cache_type = PrimaryCacheType::kCacheTypeLRU;
TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto;
+4 -10
View File
@@ -1737,23 +1737,17 @@ struct ReadOptions {
// Default: empty (every table will be scanned)
std::function<bool(const TableProperties&)> table_filter;
// Experimental
//
// If auto_readahead_size is set to true, it will auto tune the readahead_size
// during scans internally.
// For this feature to enabled, iterate_upper_bound must also be specified.
//
// NOTE: - Recommended for forward Scans only.
// - In case of backward scans like Prev or SeekForPrev, the
// cost of these backward operations might increase and affect the
// performace. So this option should not be enabled if workload
// contains backward scans.
// - If there is a backward scans, this option will be
// disabled internally and won't be reset if forward scan is done
// again.
// disabled internally and won't be enabled again if the forward scan
// is issued again.
//
// Default: false
bool auto_readahead_size = false;
// Default: true
bool auto_readahead_size = true;
// *** END options only relevant to iterators or scans ***
+3 -3
View File
@@ -114,7 +114,7 @@ class SecondaryCache : public Customizable {
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
bool& kept_in_sec_cache) = 0;
Statistics* stats, bool& kept_in_sec_cache) = 0;
// Indicate whether a handle can be erased in this secondary cache.
[[nodiscard]] virtual bool SupportForceErase() const = 0;
@@ -176,9 +176,9 @@ class SecondaryCacheWrapper : public SecondaryCache {
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
const Slice& key, const Cache::CacheItemHelper* helper,
Cache::CreateContext* create_context, bool wait, bool advise_erase,
bool& kept_in_sec_cache) override {
Statistics* stats, bool& kept_in_sec_cache) override {
return target()->Lookup(key, helper, create_context, wait, advise_erase,
kept_in_sec_cache);
stats, kept_in_sec_cache);
}
virtual bool SupportForceErase() const override {
+6
View File
@@ -531,6 +531,12 @@ enum Tickers : uint32_t {
// Number of FS reads avoided due to scan prefetching
PREFETCH_HITS,
// Compressed secondary cache related stats
COMPRESSED_SECONDARY_CACHE_DUMMY_HITS,
COMPRESSED_SECONDARY_CACHE_HITS,
COMPRESSED_SECONDARY_CACHE_PROMOTIONS,
COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS,
TICKER_ENUM_MAX
};
+7 -3
View File
@@ -140,8 +140,7 @@ class TablePropertiesCollector {
virtual bool NeedCompact() const { return false; }
};
// Constructs TablePropertiesCollector. Internals create a new
// TablePropertiesCollector for each new table
// Constructs TablePropertiesCollector instances for each table file creation.
//
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
// because RocksDB is not exception-safe. This could cause undefined behavior
@@ -163,7 +162,12 @@ class TablePropertiesCollectorFactory : public Customizable {
const ConfigOptions& options, const std::string& value,
std::shared_ptr<TablePropertiesCollectorFactory>* result);
// has to be thread-safe
// To collect properties of a table with the given context, returns
// a new object inheriting from TablePropertiesCollector. The caller
// is responsible for deleting the object returned. Alternatively,
// nullptr may be returned to decline collecting properties for the
// file (and reduce callback overheads).
// MUST be thread-safe.
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
TablePropertiesCollectorFactory::Context context) = 0;
@@ -10,7 +10,6 @@
// inserted.
#pragma once
#include <memory>
#include <string>
#include <vector>
@@ -30,6 +29,7 @@ class DB;
class ReadCallback;
struct ReadOptions;
struct DBOptions;
class MergeContext;
enum WriteType {
kPutRecord,
@@ -39,11 +39,12 @@ enum WriteType {
kDeleteRangeRecord,
kLogDataRecord,
kXIDRecord,
kPutEntityRecord,
kUnknownRecord,
};
// an entry for Put, Merge, Delete, or SingleDelete entry for write batches.
// Used in WBWIIterator.
// An entry for Put, PutEntity, Merge, Delete, or SingleDelete for write
// batches. Used in WBWIIterator.
struct WriteEntry {
WriteType type = kUnknownRecord;
Slice key;
@@ -77,12 +78,11 @@ class WBWIIterator {
};
// A WriteBatchWithIndex with a binary searchable index built for all the keys
// inserted.
// In Put(), Merge() Delete(), or SingleDelete(), the same function of the
// wrapped will be called. At the same time, indexes will be built.
// By calling GetWriteBatch(), a user will get the WriteBatch for the data
// they inserted, which can be used for DB::Write().
// A user can call NewIterator() to create an iterator.
// inserted. In Put(), PutEntity(), Merge(), Delete(), or SingleDelete(), the
// corresponding function of the wrapped WriteBatch will be called. At the same
// time, indexes will be built. By calling GetWriteBatch(), a user will get the
// WriteBatch for the data they inserted, which can be used for DB::Write(). A
// user can call NewIterator() to create an iterator.
class WriteBatchWithIndex : public WriteBatchBase {
public:
// backup_index_comparator: the backup comparator used to compare keys
@@ -121,14 +121,7 @@ class WriteBatchWithIndex : public WriteBatchBase {
}
Status PutEntity(ColumnFamilyHandle* column_family, const Slice& /* key */,
const WideColumns& /* columns */) override {
if (!column_family) {
return Status::InvalidArgument(
"Cannot call this method without a column family handle");
}
return Status::NotSupported(
"PutEntity not supported by WriteBatchWithIndex");
}
const WideColumns& /* columns */) override;
Status PutEntity(const Slice& /* key */,
const AttributeGroups& attribute_groups) override {
@@ -236,6 +229,8 @@ class WriteBatchWithIndex : public WriteBatchBase {
return GetFromBatch(nullptr, options, key, value);
}
// TODO: implement GetEntityFromBatch
// Similar to DB::Get() but will also read writes from this batch.
//
// This function will query both this batch and the DB and then merge
@@ -262,21 +257,24 @@ class WriteBatchWithIndex : public WriteBatchBase {
ColumnFamilyHandle* column_family, const Slice& key,
PinnableSlice* value);
// TODO: implement GetEntityFromBatchAndDB
void MultiGetFromBatchAndDB(DB* db, const ReadOptions& read_options,
ColumnFamilyHandle* column_family,
const size_t num_keys, const Slice* keys,
PinnableSlice* values, Status* statuses,
bool sorted_input);
// TODO: implement MultiGetEntityFromBatchAndDB
// Records the state of the batch for future calls to RollbackToSavePoint().
// May be called multiple times to set multiple save points.
void SetSavePoint() override;
// Remove all entries in this batch (Put, Merge, Delete, SingleDelete,
// PutLogData) since the most recent call to SetSavePoint() and removes the
// most recent save point.
// If there is no previous call to SetSavePoint(), behaves the same as
// Clear().
// Remove all entries in this batch (Put, PutEntity, Merge, Delete,
// SingleDelete, PutLogData) since the most recent call to SetSavePoint() and
// removes the most recent save point. If there is no previous call to
// SetSavePoint(), behaves the same as Clear().
//
// Calling RollbackToSavePoint invalidates any open iterators on this batch.
//
@@ -305,6 +303,11 @@ class WriteBatchWithIndex : public WriteBatchBase {
// last sub-batch.
size_t SubBatchCnt();
void MergeAcrossBatchAndDB(ColumnFamilyHandle* column_family,
const Slice& key,
const PinnableWideColumns& existing,
const MergeContext& merge_context,
PinnableSlice* value, Status* status);
Status GetFromBatchAndDB(DB* db, const ReadOptions& read_options,
ColumnFamilyHandle* column_family, const Slice& key,
PinnableSlice* value, ReadCallback* callback);
+2 -2
View File
@@ -12,8 +12,8 @@
// NOTE: in 'main' development branch, this should be the *next*
// minor or major version number planned for release.
#define ROCKSDB_MAJOR 8
#define ROCKSDB_MINOR 9
#define ROCKSDB_PATCH 0
#define ROCKSDB_MINOR 10
#define ROCKSDB_PATCH 2
// Do not use these. We made the mistake of declaring macros starting with
// double underscore. Now we have to live with our choice. We'll deprecate these
+2
View File
@@ -168,6 +168,7 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/FlushJobInfo.java
src/main/java/org/rocksdb/FlushReason.java
src/main/java/org/rocksdb/FlushOptions.java
src/main/java/org/rocksdb/GetStatus.java
src/main/java/org/rocksdb/HashLinkedListMemTableConfig.java
src/main/java/org/rocksdb/HashSkipListMemTableConfig.java
src/main/java/org/rocksdb/HistogramData.java
@@ -285,6 +286,7 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/WriteBufferManager.java
src/main/java/org/rocksdb/WriteStallCondition.java
src/main/java/org/rocksdb/WriteStallInfo.java
src/main/java/org/rocksdb/util/BufferUtil.java
src/main/java/org/rocksdb/util/ByteUtil.java
src/main/java/org/rocksdb/util/BytewiseComparator.java
src/main/java/org/rocksdb/util/Environment.java
@@ -8,16 +8,16 @@ Mac
```
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava
(cd java/target; cp rocksdbjni-7.9.0-osx.jar rocksdbjni-7.9.0-SNAPSHOT-osx.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.9.0-SNAPSHOT-osx.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.9.0-SNAPSHOT -Dpackaging=jar
(cd java/target; cp rocksdbjni-7.10.0-osx.jar rocksdbjni-7.10.0-SNAPSHOT-osx.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.10.0-SNAPSHOT-osx.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar
```
Linux
```
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava
(cd java/target; cp rocksdbjni-7.9.0-linux64.jar rocksdbjni-7.9.0-SNAPSHOT-linux64.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.9.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.9.0-SNAPSHOT -Dpackaging=jar
(cd java/target; cp rocksdbjni-7.10.0-linux64.jar rocksdbjni-7.10.0-SNAPSHOT-linux64.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.10.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar
```
Build jmh test package, on either platform
@@ -35,31 +35,10 @@ The long performance run (as big as we can make it on our Ubuntu box without fil
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,16384 -p columnFamilyTestType="1_column_family","20_column_families" GetBenchmarks.get GetBenchmarks.preallocatedByteBufferGet GetBenchmarks.preallocatedGet
```
## Results (small runs, Mac)
These are run on a 10-core M1 with 64GB of memory and 2TB of SSD.
They probably reflect the absolute best case for this optimization, hitting in-memory buffers and completely eliminating a buffer copy.
### Before
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 43496.578 ± 5743.090 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 70765.578 ± 697.548 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 69883.554 ± 944.184 ops/s
### After fixing byte[] (.get and .preallocatedGet)
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 149207.681 ± 2261.671 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 68920.489 ± 1574.664 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 177399.022 ± 2107.375 ops/s
### After fixing ByteBuffer (.preallocatedByteBufferGet)
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 150389.259 ± 1371.473 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 179919.468 ± 1670.714 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 178261.938 ± 2630.571 ops/s
## Results (Ubuntu, big runs)
NB - we have removed some test results we initially observed on Mac which were not later reproducible.
These take 3-4 hours
```
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,16384 -p columnFamilyTestType="1_column_family","20_column_families" GetBenchmarks.get GetBenchmarks.preallocatedByteBufferGet GetBenchmarks.preallocatedGet
@@ -67,6 +46,13 @@ java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,500
It's clear that all `get()` variants have noticeably improved performance, though not the spectacular gains of the M1.
### With fixes for all of the `get()` instances
The tests which use methods which have had performance improvements applied are:
```java
get()
preallocatedGet()
preallocatedByteBufferGet()
```
Benchmark (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get 1_column_family 1000 128 1024 thrpt 25 935648.793 ± 22879.910 ops/s
GetBenchmarks.get 1_column_family 1000 128 16384 thrpt 25 204366.301 ± 1326.570 ops/s
@@ -159,3 +145,60 @@ GetBenchmarks.preallocatedGet no_column_families 1000
The performance improvement is real.
# Put Performance Benchmarks
Results associated with [Java API consistency between RocksDB.put() , .merge() and Transaction.put() , .merge()](https://github.com/facebook/rocksdb/pull/11019)
This work was not designed specifically as a performance optimization, but we want to confirm that it has not regressed what it has changed, and to provide
a baseline for future possible performance work.
## Build/Run
Building is as above. Running is a different invocation of the same JMH jar.
```
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,32768 -p columnFamilyTestType="no_column_family" PutBenchmarks
```
## Before Changes
These results were generated in a private branch with the `PutBenchmarks` from the PR backported onto the current *main*.
Benchmark (bufferListSize) (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
PutBenchmarks.put 16 no_column_family 1000 128 1024 thrpt 25 76670.200 ± 2555.248 ops/s
PutBenchmarks.put 16 no_column_family 1000 128 32768 thrpt 25 3913.692 ± 225.690 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 1024 thrpt 25 74479.589 ± 988.361 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 32768 thrpt 25 4070.800 ± 194.838 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 1024 thrpt 25 72150.853 ± 1744.216 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 32768 thrpt 25 3896.646 ± 188.629 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 1024 thrpt 25 71753.287 ± 1053.904 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 32768 thrpt 25 3928.503 ± 264.443 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 1024 thrpt 25 72595.105 ± 1027.258 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 32768 thrpt 25 3890.100 ± 199.131 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 1024 thrpt 25 70878.133 ± 1181.601 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 32768 thrpt 25 3863.181 ± 215.888 ops/s
## After Changes
These results were generated on the PR branch.
Benchmark (bufferListSize) (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
PutBenchmarks.put 16 no_column_family 1000 128 1024 thrpt 25 75178.751 ± 2644.775 ops/s
PutBenchmarks.put 16 no_column_family 1000 128 32768 thrpt 25 3937.175 ± 257.039 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 1024 thrpt 25 74375.519 ± 1776.654 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 32768 thrpt 25 4013.413 ± 257.706 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 1024 thrpt 25 71418.303 ± 1610.977 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 32768 thrpt 25 4027.581 ± 227.900 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 1024 thrpt 25 71229.107 ± 2720.083 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 32768 thrpt 25 4022.635 ± 212.540 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 1024 thrpt 25 71718.501 ± 787.537 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 32768 thrpt 25 4078.050 ± 176.331 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 1024 thrpt 25 72736.754 ± 828.971 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 32768 thrpt 25 3987.232 ± 205.577 ops/s
## Discussion
The changes don't appear to have had a material effect on performance. We are happy with this.
* We would obviously advise running future changes before and after to confirm they have no adverse effects.
+4
View File
@@ -150,7 +150,9 @@ JAVA_TESTS = \
org.rocksdb.LRUCacheTest\
org.rocksdb.MemoryUtilTest\
org.rocksdb.MemTableTest\
org.rocksdb.MergeCFVariantsTest\
org.rocksdb.MergeTest\
org.rocksdb.MergeVariantsTest\
org.rocksdb.MultiColumnRegressionTest \
org.rocksdb.MultiGetManyKeysTest\
org.rocksdb.MultiGetTest\
@@ -167,6 +169,8 @@ JAVA_TESTS = \
org.rocksdb.OptionsTest\
org.rocksdb.PerfLevelTest \
org.rocksdb.PerfContextTest \
org.rocksdb.PutCFVariantsTest\
org.rocksdb.PutVariantsTest\
org.rocksdb.PlainTableConfigTest\
org.rocksdb.RateLimiterTest\
org.rocksdb.ReadOnlyTest\
@@ -6,18 +6,19 @@
*/
package org.rocksdb.jmh;
import org.openjdk.jmh.annotations.*;
import org.rocksdb.*;
import org.rocksdb.util.FileUtils;
import static org.rocksdb.util.KVUtils.ba;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.rocksdb.util.KVUtils.ba;
import org.openjdk.jmh.annotations.*;
import org.rocksdb.*;
import org.rocksdb.util.FileUtils;
@State(Scope.Benchmark)
public class PutBenchmarks {
@@ -30,12 +31,24 @@ public class PutBenchmarks {
})
String columnFamilyTestType;
@Param({"1000", "100000"}) int keyCount;
@Param({"12", "64", "128"}) int keySize;
@Param({"64", "1024", "65536"}) int valueSize;
@Param({"16"}) int bufferListSize;
Path dbDir;
DBOptions options;
int cfs = 0; // number of column families
private AtomicInteger cfHandlesIdx;
ColumnFamilyHandle[] cfHandles;
RocksDB db;
List<byte[]> keyBuffers = new ArrayList<>(bufferListSize);
List<byte[]> valueBuffers = new ArrayList<>(bufferListSize);
List<ByteBuffer> keyBuffersBB = new ArrayList<>(bufferListSize);
List<ByteBuffer> valueBuffersBB = new ArrayList<>(bufferListSize);
@Setup(Level.Trial)
public void setup() throws IOException, RocksDBException {
@@ -68,6 +81,34 @@ public class PutBenchmarks {
final List<ColumnFamilyHandle> cfHandlesList = new ArrayList<>(cfDescriptors.size());
db = RocksDB.open(options, dbDir.toAbsolutePath().toString(), cfDescriptors, cfHandlesList);
cfHandles = cfHandlesList.toArray(new ColumnFamilyHandle[0]);
for (int i = 0; i < bufferListSize; i++) {
final byte[] keyArr = new byte[keySize];
Arrays.fill(keyArr, (byte) 0x30);
keyBuffers.add(keyArr);
}
for (int i = 0; i < bufferListSize; i++) {
final byte[] valueArr = new byte[valueSize];
Arrays.fill(valueArr, (byte) 0x30);
valueBuffers.add(valueArr);
}
for (int i = 0; i < bufferListSize; i++) {
final ByteBuffer keyBB = ByteBuffer.allocateDirect(keySize);
byte[] keyArr = new byte[keySize];
Arrays.fill(keyArr, (byte) 0x30);
keyBB.put(keyArr);
keyBuffersBB.add(keyBB);
}
for (int i = 0; i < bufferListSize; i++) {
final ByteBuffer valueBB = ByteBuffer.allocateDirect(valueSize);
byte[] valueArr = new byte[valueSize];
Arrays.fill(valueArr, (byte) 0x30);
valueBB.put(valueArr);
valueBuffersBB.add(valueBB);
}
}
@TearDown(Level.Trial)
@@ -104,9 +145,79 @@ public class PutBenchmarks {
}
}
private <T> T borrow(final List<T> buffers) {
synchronized (buffers) {
while (true) {
if (buffers.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
return null;
}
continue;
}
return buffers.remove(0);
}
}
}
private <T> void repay(final List<T> buffers, final T buffer) {
synchronized (buffers) {
buffers.add(buffer);
}
}
@Benchmark
public void put(final ComparatorBenchmarks.Counter counter) throws RocksDBException {
public void put(final Counter counter) throws RocksDBException {
byte[] keyBuf = borrow(keyBuffers);
byte[] valueBuf = borrow(valueBuffers);
final int i = counter.next();
db.put(getColumnFamily(), ba("key" + i), ba("value" + i));
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
System.arraycopy(keyPrefix, 0, keyBuf, 0, keyPrefix.length);
System.arraycopy(valuePrefix, 0, valueBuf, 0, valuePrefix.length);
db.put(getColumnFamily(), keyBuf, valueBuf);
repay(keyBuffers, keyBuf);
repay(valueBuffers, valueBuf);
}
@Benchmark
public void putByteArrays(final Counter counter) throws RocksDBException {
byte[] keyBuf = borrow(keyBuffers);
byte[] valueBuf = borrow(valueBuffers);
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
System.arraycopy(keyPrefix, 0, keyBuf, 0, keyPrefix.length);
System.arraycopy(valuePrefix, 0, valueBuf, 0, valuePrefix.length);
db.put(getColumnFamily(), new WriteOptions(), keyBuf, valueBuf);
repay(keyBuffers, keyBuf);
repay(valueBuffers, valueBuf);
}
@Benchmark
public void putByteBuffers(final Counter counter) throws RocksDBException {
ByteBuffer keyBuf = borrow(keyBuffersBB);
keyBuf.clear();
ByteBuffer valueBuf = borrow(valueBuffersBB);
valueBuf.clear();
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
keyBuf.put(keyPrefix, 0, keyPrefix.length);
keyBuf.position(keySize);
keyBuf.flip();
valueBuf.put(valuePrefix, 0, valuePrefix.length);
valueBuf.position(valueSize);
valueBuf.flip();
db.put(getColumnFamily(), new WriteOptions(), keyBuf, valueBuf);
repay(keyBuffersBB, keyBuf);
repay(valueBuffersBB, valueBuf);
}
}
+1
View File
@@ -21,6 +21,7 @@
<exclude name="FieldNamingConventions"/>
<exclude name="FormalParameterNamingConventions"/>
<exclude name="LinguisticNaming"/>
<exclude name="ConfusingTernary"/>
<!-- These could be fixed if we take the time to do it -->
<exclude name="CommentDefaultAccessModifier"/>
<exclude name="FieldDeclarationsShouldBeAtStartOfClass"/>
+284
View File
@@ -0,0 +1,284 @@
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
// This file defines helper methods for Java API write methods
//
#pragma once
#include <jni.h>
#include <cstring>
#include <exception>
#include <functional>
#include <string>
#include "rocksdb/rocksdb_namespace.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
#include "rocksjni/portal.h"
namespace ROCKSDB_NAMESPACE {
/**
* @brief Exception class used to make the flow of key/value (Put(), Get(),
* Merge(), ...) calls clearer.
*
* This class is used by Java API JNI methods in try { save/fetch } catch { ...
* } style.
*
*/
class KVException : public std::exception {
public:
// These values are expected on Java API calls to represent the result of a
// Get() which has failed; a negative length is returned to indicate an error.
static const int kNotFound = -1; // the key was not found in RocksDB
static const int kStatusError =
-2; // there was some other error fetching the value for the key
/**
* @brief Throw a KVException (and potentially a Java exception) if the
* RocksDB status is "bad"
*
* @param env JNI environment needed to create a Java exception
* @param status RocksDB status to examine
*/
static void ThrowOnError(JNIEnv* env, const Status& status) {
if (status.ok()) {
return;
}
if (status.IsNotFound()) {
// IsNotFound does not generate a Java Exception, any other bad status
// does..
throw KVException(kNotFound);
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, status);
throw KVException(kStatusError);
}
/**
* @brief Throw a KVException and a Java exception
*
* @param env JNI environment needed to create a Java exception
* @param message content of the exception we will throw
*/
static void ThrowNew(JNIEnv* env, const std::string& message) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, message);
throw KVException(kStatusError);
}
/**
* @brief Throw a KVException if there is already a Java exception in the JNI
* enviroment
*
* @param env
*/
static void ThrowOnError(JNIEnv* env) {
if (env->ExceptionCheck()) {
throw KVException(kStatusError);
}
}
KVException(jint code) : kCode_(code){};
virtual const char* what() const throw() {
return "Exception raised by JNI. There may be a Java exception in the "
"JNIEnv. Please check!";
}
jint Code() const { return kCode_; }
private:
jint kCode_;
};
/**
* @brief Construct a slice with the contents of a Java byte array
*
* The slice refers to an array into which the Java byte array's whole region is
* copied
*/
class JByteArraySlice {
public:
JByteArraySlice(JNIEnv* env, const jbyteArray& jarr, const jint jarr_off,
const jint jarr_len)
: arr_(new jbyte[jarr_len]),
slice_(reinterpret_cast<char*>(arr_), jarr_len) {
env->GetByteArrayRegion(jarr, jarr_off, jarr_len, arr_);
KVException::ThrowOnError(env);
};
~JByteArraySlice() {
slice_.clear();
delete[] arr_;
};
Slice& slice() { return slice_; }
private:
jbyte* arr_;
Slice slice_;
};
/**
* @brief Construct a slice with the contents of a direct Java ByterBuffer
*
* The slice refers directly to the contents of the buffer, no copy is made.
*
*/
class JDirectBufferSlice {
public:
JDirectBufferSlice(JNIEnv* env, const jobject& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: slice_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
jbuffer_off,
jbuffer_len) {
KVException::ThrowOnError(env);
jlong capacity = env->GetDirectBufferCapacity(jbuffer);
if (capacity < jbuffer_off + jbuffer_len) {
auto message = "Direct buffer offset " + std::to_string(jbuffer_off) +
" + length " + std::to_string(jbuffer_len) +
" exceeds capacity " + std::to_string(capacity);
KVException::ThrowNew(env, message);
slice_.clear();
}
}
~JDirectBufferSlice() { slice_.clear(); };
Slice& slice() { return slice_; }
private:
Slice slice_;
};
/**
* @brief Wrap a pinnable slice with a method to retrieve the contents back into
* Java
*
* The Java Byte Array version sets the byte array's region from the slice
*/
class JByteArrayPinnableSlice {
public:
/**
* @brief Construct a new JByteArrayPinnableSlice object referring to an
* existing java byte buffer
*
* @param env
* @param jbuffer
* @param jbuffer_off
* @param jbuffer_len
*/
JByteArrayPinnableSlice(JNIEnv* env, const jbyteArray& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: env_(env),
jbuffer_(jbuffer),
jbuffer_off_(jbuffer_off),
jbuffer_len_(jbuffer_len){};
/**
* @brief Construct an empty new JByteArrayPinnableSlice object
*
*/
JByteArrayPinnableSlice(JNIEnv* env) : env_(env){};
PinnableSlice& pinnable_slice() { return pinnable_slice_; }
~JByteArrayPinnableSlice() { pinnable_slice_.Reset(); };
/**
* @brief copy back contents of the pinnable slice into the Java ByteBuffer
*
* @return jint min of size of buffer and number of bytes in value for
* requested key
*/
jint Fetch() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
const jint result_len = std::min(jbuffer_len_, pinnable_len);
env_->SetByteArrayRegion(
jbuffer_, jbuffer_off_, result_len,
reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
KVException::ThrowOnError(
env_); // exception thrown: ArrayIndexOutOfBoundsException
return pinnable_len;
};
/**
* @brief create a new Java buffer and copy the result into it
*
* @return jbyteArray the java buffer holding the result
*/
jbyteArray NewByteArray() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
jbyteArray jbuffer = env_->NewByteArray(static_cast<jsize>(pinnable_len));
KVException::ThrowOnError(env_); // OutOfMemoryError
env_->SetByteArrayRegion(
jbuffer, 0, pinnable_len,
reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
KVException::ThrowOnError(env_); // ArrayIndexOutOfBoundsException
return jbuffer;
}
private:
JNIEnv* env_;
jbyteArray jbuffer_;
jint jbuffer_off_;
jint jbuffer_len_;
PinnableSlice pinnable_slice_;
};
/**
* @brief Wrap a pinnable slice with a method to retrieve the contents back into
* Java
*
* The Java Direct Buffer version copies the memory of the buffer from the slice
*/
class JDirectBufferPinnableSlice {
public:
JDirectBufferPinnableSlice(JNIEnv* env, const jobject& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: buffer_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
jbuffer_off),
jbuffer_len_(jbuffer_len) {
jlong capacity = env->GetDirectBufferCapacity(jbuffer);
if (capacity < jbuffer_off + jbuffer_len) {
auto message =
"Invalid value argument. Capacity is less than requested region. "
"offset " +
std::to_string(jbuffer_off) + " + length " +
std::to_string(jbuffer_len) + " exceeds capacity " +
std::to_string(capacity);
KVException::ThrowNew(env, message);
}
}
PinnableSlice& pinnable_slice() { return pinnable_slice_; }
~JDirectBufferPinnableSlice() { pinnable_slice_.Reset(); };
/**
* @brief copy back contents of the pinnable slice into the Java DirectBuffer
*
* @return jint min of size of buffer and number of bytes in value for
* requested key
*/
jint Fetch() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
const jint result_len = std::min(jbuffer_len_, pinnable_len);
memcpy(buffer_, pinnable_slice_.data(), result_len);
return pinnable_len;
};
private:
char* buffer_;
jint jbuffer_len_;
PinnableSlice pinnable_slice_;
};
} // namespace ROCKSDB_NAMESPACE
+20
View File
@@ -5175,6 +5175,15 @@ class TickerTypeJni {
return -0x41;
case ROCKSDB_NAMESPACE::Tickers::PREFETCH_HITS:
return -0x42;
case ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_DUMMY_HITS:
return -0x43;
case ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_HITS:
return -0x44;
case ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_PROMOTIONS:
return -0x45;
case ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS:
return -0x46;
case ROCKSDB_NAMESPACE::Tickers::TICKER_ENUM_MAX:
// 0x5F was the max value in the initial copy of tickers to Java.
// Since these values are exposed directly to Java clients, we keep
@@ -5550,6 +5559,17 @@ class TickerTypeJni {
return ROCKSDB_NAMESPACE::Tickers::PREFETCH_BYTES_USEFUL;
case -0x42:
return ROCKSDB_NAMESPACE::Tickers::PREFETCH_HITS;
case -0x43:
return ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_DUMMY_HITS;
case -0x44:
return ROCKSDB_NAMESPACE::Tickers::COMPRESSED_SECONDARY_CACHE_HITS;
case -0x45:
return ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_PROMOTIONS;
case -0x46:
return ROCKSDB_NAMESPACE::Tickers::
COMPRESSED_SECONDARY_CACHE_PROMOTION_SKIPS;
case 0x5F:
// 0x5F was the max value in the initial copy of tickers to Java.
// Since these values are exposed directly to Java clients, we keep
+109 -168
View File
@@ -26,6 +26,7 @@
#include "rocksdb/types.h"
#include "rocksdb/version.h"
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/kv_helper.h"
#include "rocksjni/portal.h"
#ifdef min
@@ -600,55 +601,6 @@ void Java_org_rocksdb_RocksDB_dropColumnFamilies(
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DB::Put
/**
* @return true if the put succeeded, false if a Java Exception was thrown
*/
bool rocksdb_put_helper(JNIEnv* env, ROCKSDB_NAMESPACE::DB* db,
const ROCKSDB_NAMESPACE::WriteOptions& write_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle,
jbyteArray jkey, jint jkey_off, jint jkey_len,
jbyteArray jval, jint jval_off, jint jval_len) {
jbyte* key = new jbyte[jkey_len];
env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] key;
return false;
}
jbyte* value = new jbyte[jval_len];
env->GetByteArrayRegion(jval, jval_off, jval_len, value);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] value;
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s;
if (cf_handle != nullptr) {
s = db->Put(write_options, cf_handle, key_slice, value_slice);
} else {
// backwards compatibility
s = db->Put(write_options, key_slice, value_slice);
}
// cleanup
delete[] value;
delete[] key;
if (s.ok()) {
return true;
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return false;
}
}
/*
* Class: org_rocksdb_RocksDB
* Method: put
@@ -662,8 +614,14 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
static const ROCKSDB_NAMESPACE::WriteOptions default_write_options =
ROCKSDB_NAMESPACE::WriteOptions();
rocksdb_put_helper(env, db, default_write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(default_write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@@ -682,13 +640,21 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BIIJ(JNIEnv* env, jobject,
ROCKSDB_NAMESPACE::WriteOptions();
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_put_helper(env, db, default_write_options, cf_handle, jkey,
jkey_off, jkey_len, jval, jval_off, jval_len);
} else {
if (cf_handle == nullptr) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
"Invalid ColumnFamilyHandle."));
return;
}
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
db->Put(default_write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
@@ -706,8 +672,15 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
rocksdb_put_helper(env, db, *write_options, nullptr, jkey, jkey_off, jkey_len,
jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(*write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@@ -724,13 +697,19 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BIIJ(
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_put_helper(env, db, *write_options, cf_handle, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
} else {
if (cf_handle == nullptr) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
"Invalid ColumnFamilyHandle."));
return;
}
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(*write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
@@ -1309,53 +1288,6 @@ jint Java_org_rocksdb_RocksDB_getDirect(JNIEnv* env, jobject /*jdb*/,
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DB::Merge
/**
* @return true if the merge succeeded, false if a Java Exception was thrown
*/
bool rocksdb_merge_helper(JNIEnv* env, ROCKSDB_NAMESPACE::DB* db,
const ROCKSDB_NAMESPACE::WriteOptions& write_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle,
jbyteArray jkey, jint jkey_off, jint jkey_len,
jbyteArray jval, jint jval_off, jint jval_len) {
jbyte* key = new jbyte[jkey_len];
env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
jbyte* value = new jbyte[jval_len];
env->GetByteArrayRegion(jval, jval_off, jval_len, value);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] value;
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s;
if (cf_handle != nullptr) {
s = db->Merge(write_options, cf_handle, key_slice, value_slice);
} else {
s = db->Merge(write_options, key_slice, value_slice);
}
// cleanup
delete[] value;
delete[] key;
if (s.ok()) {
return true;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return false;
}
/*
* Class: org_rocksdb_RocksDB
* Method: merge
@@ -1369,8 +1301,14 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
static const ROCKSDB_NAMESPACE::WriteOptions default_write_options =
ROCKSDB_NAMESPACE::WriteOptions();
rocksdb_merge_helper(env, db, default_write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(default_write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@@ -1388,8 +1326,15 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BIIJ(
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_merge_helper(env, db, default_write_options, cf_handle, jkey,
jkey_off, jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(default_write_options, cf_handle, key.slice(),
value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
@@ -1409,8 +1354,14 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BII(
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
rocksdb_merge_helper(env, db, *write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(*write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@@ -1428,8 +1379,15 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ(
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_merge_helper(env, db, *write_options, cf_handle, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
db->Merge(*write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
@@ -1437,16 +1395,37 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ(
}
}
jlong rocksdb_iterator_helper(
ROCKSDB_NAMESPACE::DB* db, ROCKSDB_NAMESPACE::ReadOptions read_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle) {
ROCKSDB_NAMESPACE::Iterator* iterator = nullptr;
if (cf_handle != nullptr) {
iterator = db->NewIterator(read_options, cf_handle);
} else {
iterator = db->NewIterator(read_options);
}
return GET_CPLUSPLUS_POINTER(iterator);
/*
* Class: org_rocksdb_RocksDB
* Method: mergeDirect
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)V
*/
void Java_org_rocksdb_RocksDB_mergeDirect(
JNIEnv* env, jobject /*jdb*/, jlong jdb_handle, jlong jwrite_options_handle,
jobject jkey, jint jkey_off, jint jkey_len, jobject jval, jint jval_off,
jint jval_len, jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
auto merge = [&env, &db, &cf_handle, &write_options](
ROCKSDB_NAMESPACE::Slice& key,
ROCKSDB_NAMESPACE::Slice& value) {
ROCKSDB_NAMESPACE::Status s;
if (cf_handle == nullptr) {
s = db->Merge(*write_options, key, value);
} else {
s = db->Merge(*write_options, cf_handle, key, value);
}
if (s.ok()) {
return;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
};
ROCKSDB_NAMESPACE::JniUtil::kv_op_direct(merge, env, jkey, jkey_off, jkey_len,
jval, jval_off, jval_len);
}
/*
@@ -2644,55 +2623,17 @@ jobjectArray Java_org_rocksdb_RocksDB_keyMayExistFoundValue(
/*
* Class: org_rocksdb_RocksDB
* Method: iterator
* Signature: (J)J
*/
jlong Java_org_rocksdb_RocksDB_iterator__J(JNIEnv*, jobject, jlong db_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
return rocksdb_iterator_helper(db, ROCKSDB_NAMESPACE::ReadOptions(), nullptr);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iterator
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_RocksDB_iterator__JJ(JNIEnv*, jobject, jlong db_handle,
jlong jread_options_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto& read_options =
*reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return rocksdb_iterator_helper(db, read_options, nullptr);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iteratorCF
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_RocksDB_iteratorCF__JJ(JNIEnv*, jobject, jlong db_handle,
jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
return rocksdb_iterator_helper(db, ROCKSDB_NAMESPACE::ReadOptions(),
cf_handle);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iteratorCF
* Signature: (JJJ)J
*/
jlong Java_org_rocksdb_RocksDB_iteratorCF__JJJ(JNIEnv*, jobject,
jlong db_handle,
jlong jcf_handle,
jlong jread_options_handle) {
jlong Java_org_rocksdb_RocksDB_iterator(JNIEnv*, jobject, jlong db_handle,
jlong jcf_handle,
jlong jread_options_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
auto& read_options =
*reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return rocksdb_iterator_helper(db, read_options, cf_handle);
return GET_CPLUSPLUS_POINTER(db->NewIterator(read_options, cf_handle));
}
/*
+359 -235
View File
@@ -14,6 +14,7 @@
#include "include/org_rocksdb_Transaction.h"
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/kv_helper.h"
#include "rocksjni/portal.h"
#if defined(_MSC_VER)
@@ -158,90 +159,113 @@ typedef std::function<ROCKSDB_NAMESPACE::Status(
std::string*)>
FnGet;
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
jbyteArray txn_get_helper(JNIEnv* env, const FnGet& fn_get,
const jlong& jread_options_handle,
const jbyteArray& jkey, const jint& jkey_part_len) {
jbyte* key = env->GetByteArrayElements(jkey, nullptr);
if (key == nullptr) {
// exception thrown: OutOfMemoryError
return nullptr;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key),
jkey_part_len);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
std::string value;
ROCKSDB_NAMESPACE::Status s = fn_get(*read_options, key_slice, &value);
// trigger java unref on key.
// by passing JNI_ABORT, it will simply release the reference without
// copying the result back to the java byte array.
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
if (s.IsNotFound()) {
return nullptr;
}
if (s.ok()) {
jbyteArray jret_value = env->NewByteArray(static_cast<jsize>(value.size()));
if (jret_value == nullptr) {
// exception thrown: OutOfMemoryError
return nullptr;
}
env->SetByteArrayRegion(
jret_value, 0, static_cast<jsize>(value.size()),
const_cast<jbyte*>(reinterpret_cast<const jbyte*>(value.c_str())));
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
return nullptr;
}
return jret_value;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return nullptr;
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BIJ)[B
* Signature: (JJ[BIIJ)[B
*/
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BIJ(
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BIIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len, jlong jcolumn_family_handle) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnGet fn_get =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, std::string*)>(
&ROCKSDB_NAMESPACE::Transaction::Get, txn, std::placeholders::_1,
column_family_handle, std::placeholders::_2, std::placeholders::_3);
return txn_get_helper(env, fn_get, jread_options_handle, jkey, jkey_part_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BI)[B
* Signature: (JJ[BII)[B
*/
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BI(
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnGet fn_get =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
const ROCKSDB_NAMESPACE::Slice&, std::string*)>(
&ROCKSDB_NAMESPACE::Transaction::Get, txn, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
return txn_get_helper(env, fn_get, jread_options_handle, jkey, jkey_part_len);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, key.slice(), &value.pinnable_slice()));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BII[BIIJ)I
*/
jint Java_org_rocksdb_Transaction_get__JJ_3BII_3BIIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len, jbyteArray jval,
jint jval_off, jint jval_part_len, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env, jval, jval_off,
jval_part_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getDirect
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)I
*/
jint Java_org_rocksdb_Transaction_getDirect(JNIEnv* env, jobject, jlong jhandle,
jlong jread_options_handle,
jobject jkey_bb, jint jkey_off,
jint jkey_part_len, jobject jval_bb,
jint jval_off, jint jval_part_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
jval_part_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
@@ -428,47 +452,91 @@ jobjectArray Java_org_rocksdb_Transaction_multiGet__JJ_3_3B(
/*
* Class: org_rocksdb_Transaction
* Method: getForUpdate
* Signature: (JJ[BIJZZ)[B
* Signature: (JJ[BIIJZZ)[B
*/
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIJZZ(
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIIJZZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len,
jlong jcolumn_family_handle, jboolean jexclusive, jboolean jdo_validate) {
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnGet fn_get_for_update =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, std::string*, bool, bool)>(
&ROCKSDB_NAMESPACE::Transaction::GetForUpdate, txn,
std::placeholders::_1, column_family_handle, std::placeholders::_2,
std::placeholders::_3, jexclusive, jdo_validate);
return txn_get_helper(env, fn_get_for_update, jread_options_handle, jkey,
jkey_part_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getForUpdate
* Signature: (JJ[BIZZ)[B
* Signature: (JJ[BII[BIIJZZ)I
*/
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIZZ(
jint Java_org_rocksdb_Transaction_getForUpdate__JJ_3BII_3BIIJZZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len, jboolean jexclusive,
jboolean jdo_validate) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len, jbyteArray jval,
jint jval_off, jint jval_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnGet fn_get_for_update =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
const ROCKSDB_NAMESPACE::Slice&, std::string*, bool, bool)>(
&ROCKSDB_NAMESPACE::Transaction::GetForUpdate, txn,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
jexclusive, jdo_validate);
return txn_get_helper(env, fn_get_for_update, jread_options_handle, jkey,
jkey_part_len);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env, jval, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getDirectForUpdate
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZZ)I
*/
jint Java_org_rocksdb_Transaction_getDirectForUpdate(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jobject jkey_bb, jint jkey_off, jint jkey_part_len, jobject jval_bb,
jint jval_off, jint jval_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
@@ -519,29 +587,15 @@ jobjectArray Java_org_rocksdb_Transaction_multiGetForUpdate__JJ_3_3B(
jread_options_handle, jkey_parts);
}
/*
* Class: org_rocksdb_Transaction
* Method: getIterator
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_Transaction_getIterator__JJ(JNIEnv* /*env*/,
jobject /*jobj*/,
jlong jhandle,
jlong jread_options_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return GET_CPLUSPLUS_POINTER(txn->GetIterator(*read_options));
}
/*
* Class: org_rocksdb_Transaction
* Method: getIterator
* Signature: (JJJ)J
*/
jlong Java_org_rocksdb_Transaction_getIterator__JJJ(
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle,
jlong jread_options_handle, jlong jcolumn_family_handle) {
jlong Java_org_rocksdb_Transaction_getIterator(JNIEnv* /*env*/,
jobject /*jobj*/, jlong jhandle,
jlong jread_options_handle,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
@@ -552,84 +606,93 @@ jlong Java_org_rocksdb_Transaction_getIterator__JJJ(
txn->GetIterator(*read_options, column_family_handle));
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>
FnWriteKV;
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
void txn_write_kv_helper(JNIEnv* env, const FnWriteKV& fn_write_kv,
const jbyteArray& jkey, const jint& jkey_part_len,
const jbyteArray& jval, const jint& jval_len) {
jbyte* key = env->GetByteArrayElements(jkey, nullptr);
if (key == nullptr) {
// exception thrown: OutOfMemoryError
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BII[BIIJZ)V
*/
void Java_org_rocksdb_Transaction_put__J_3BII_3BIIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
jbyte* value = env->GetByteArrayElements(jval, nullptr);
if (value == nullptr) {
// exception thrown: OutOfMemoryError
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
return;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key),
jkey_part_len);
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s = fn_write_kv(key_slice, value_slice);
// trigger java unref on key.
// by passing JNI_ABORT, it will simply release the reference without
// copying the result back to the java byte array.
env->ReleaseByteArrayElements(jval, value, JNI_ABORT);
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
if (s.ok()) {
return;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
}
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BI[BIJZ)V
* Signature: (J[BII[BII)V
*/
void Java_org_rocksdb_Transaction_put__J_3BI_3BIJZ(
void Java_org_rocksdb_Transaction_put__J_3BII_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: putDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZ)V
*/
void Java_org_rocksdb_Transaction_putDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2IIJZ(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_put =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&,
bool)>(&ROCKSDB_NAMESPACE::Transaction::Put, txn,
column_family_handle, std::placeholders::_1,
std::placeholders::_2, jassume_tracked);
txn_write_kv_helper(env, fn_put, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BI[BI)V
* Method: putDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V
*/
void Java_org_rocksdb_Transaction_put__J_3BI_3BI(JNIEnv* env, jobject /*jobj*/,
jlong jhandle, jbyteArray jkey,
jint jkey_part_len,
jbyteArray jval,
jint jval_len) {
void Java_org_rocksdb_Transaction_putDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2II(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_put =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::Put, txn, std::placeholders::_1,
std::placeholders::_2);
txn_write_kv_helper(env, fn_put, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
@@ -780,41 +843,92 @@ void Java_org_rocksdb_Transaction_put__J_3_3BI_3_3BI(
/*
* Class: org_rocksdb_Transaction
* Method: merge
* Signature: (J[BI[BIJZ)V
* Signature: (J[BII[BIIJZ)V
*/
void Java_org_rocksdb_Transaction_merge__J_3BI_3BIJZ(
void Java_org_rocksdb_Transaction_merge__J_3BII_3BIIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_merge =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&,
bool)>(&ROCKSDB_NAMESPACE::Transaction::Merge, txn,
column_family_handle, std::placeholders::_1,
std::placeholders::_2, jassume_tracked);
txn_write_kv_helper(env, fn_merge, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: merge
* Signature: (J[BI[BI)V
* Signature: (J[BII[BII)V
*/
void Java_org_rocksdb_Transaction_merge__J_3BI_3BI(
void Java_org_rocksdb_Transaction_merge__J_3BII_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_merge =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::Merge, txn, std::placeholders::_1,
std::placeholders::_2);
txn_write_kv_helper(env, fn_merge, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZ)V
*/
JNIEXPORT void JNICALL
Java_org_rocksdb_Transaction_mergeDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2IIJZ(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V
*/
JNIEXPORT void JNICALL
Java_org_rocksdb_Transaction_mergeDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2II(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
@@ -1065,14 +1179,15 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3BI_3BIJ(
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_put_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::PutUntracked, txn,
column_family_handle, std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_put_untracked, jkey, jkey_part_len, jval,
jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, 0, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, 0, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->PutUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@@ -1084,13 +1199,14 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_put_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::PutUntracked, txn,
std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_put_untracked, jkey, jkey_part_len, jval,
jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, 0, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, 0, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->PutUntracked(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@@ -1137,42 +1253,50 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3_3BI_3_3BI(
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntracked
* Signature: (J[BI[BIJ)V
* Signature: (J[BII[BIIJ)V
*/
void Java_org_rocksdb_Transaction_mergeUntracked__J_3BI_3BIJ(
void Java_org_rocksdb_Transaction_mergeUntracked(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->MergeUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntrackedDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)V
*/
void Java_org_rocksdb_Transaction_mergeUntrackedDirect(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jobject jkey, jint jkey_off,
jint jkey_part_len, jobject jval, jint jval_off, jint jval_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_merge_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::MergeUntracked, txn,
column_family_handle, std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_merge_untracked, jkey, jkey_part_len, jval,
jval_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntracked
* Signature: (J[BI[BI)V
*/
void Java_org_rocksdb_Transaction_mergeUntracked__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_merge_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::MergeUntracked, txn,
std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_merge_untracked, jkey, jkey_part_len, jval,
jval_len);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->MergeUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
Loaded 100 of 232 files, more files were not shown because too many files have changed in this diff. Show more