Compare commits

..
64 Commits
Author SHA1 Message Date
anand76 a7e70f95aa Update HISTORY.md and version.h for 9.5.2 2024-08-13 12:59:14 -07:00
anand76 c062d4e662 Add ticker stats for read corruption retries (#12923)
Summary:
Add a couple of ticker stats for corruption retry count and successful retries. This PR also eliminates an extra read attempt when there's a checksum mismatch in a block read from the prefetch buffer.

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

Test Plan: Update existing tests

Reviewed By: jowlyzhang

Differential Revision: D61024687

Pulled By: anand1976

fbshipit-source-id: 3a08403580ab244000e0d480b7ee0f5a03d76b06
2024-08-13 12:40:01 -07:00
anand76 dec94ecdbc Update history 2024-08-09 09:18:48 -07:00
anand76 c20eefc0a3 Make transaction name conflict check more robust (#12895)
Summary:
The `PessimisticTransaction::SetName()` code checks for an existing txn of the given name before registering the new txn. However, this is not atomic, which could result in a race condition if two txns try to register with the same name. Both might succeed and lead to unpredictable behavior. This PR makes the test and set atomic.

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

Reviewed By: pdillinger

Differential Revision: D60460482

Pulled By: anand1976

fbshipit-source-id: e8afeb2356e1b8f4e8df785cb73532739f82579d
2024-08-09 09:17:22 -07:00
Yu Zhang 30708207c8 update HISTORY.md and version.h for 9.5.1 2024-08-05 15:07:27 -07:00
Yu Zhang a24f2ae791 Fix file deletions in DestroyDB not rate limited (#12891)
Summary:
Make `DestroyDB` slowly delete files if it's configured and enabled via `SstFileManager`.

It's currently not available mainly because of DeleteScheduler's logic related to tracked total_size_ and total_trash_size_. These accounting and logic should not be applied to `DestroyDB`. This PR adds a `DeleteUnaccountedDBFile` util for this purpose which deletes files without accounting it.  This util also supports assigning a file to a specified trash bucket so that user can later wait for a specific trash bucket to be empty. For `DestroyDB`, files with more than 1 hard links will be deleted immediately.

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

Test Plan: Added unit tests, existing tests.

Reviewed By: anand1976

Differential Revision: D60300220

Pulled By: jowlyzhang

fbshipit-source-id: 8b18109a177a3a9532f6dc2e40e08310c08ca3c7
2024-08-05 15:06:04 -07:00
Hui Xiao d1826fb6b9 history 2024-07-22 11:24:28 -07:00
Changyu Bi c064ac3bc5 Avoid opening table files and reading table properties under mutex (#12879)
Summary:
InitInputTableProperties() can open and do IOs and is called under mutex_. This PR removes it from FinalizeInputInfo(). It is now called in CompactionJob::Run() and BuildCompactionJobInfo() (called in NotifyOnCompactionBegin()) without holding mutex_.

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

Test Plan: existing unit tests. Added assert in GetInputTableProperties() to ensure that input_table_properties_ is initialized whenever it's called.

Reviewed By: hx235

Differential Revision: D59933195

Pulled By: cbi42

fbshipit-source-id: c8089e13af8567fa3ab4b94d9ec384ae98ab2ec8
2024-07-19 19:12:45 -07:00
Changyu Bi 4384dd5eee Support ingesting SST files generated by a live DB (#12750)
Summary:
... to enable use cases like using RocksDB to merge sort data for ingestion. A new file ingestion option `IngestExternalFileOptions::allow_db_generated_files` is introduced to allows users to ingest SST files generated by live DBs instead of SstFileWriter. For now this only works if the SST files being ingested have zero as their largest sequence number AND do not overlap with any data in the DB (so we can assign seqno 0 which matches the seqno of all ingested keys).

The feature is marked the option as experimental for now.

Main changes needed to enable this:
- ignore CF id mismatch during ingestion
- ignore the missing external file version table property

Rest of the change is mostly in new unit tests.

A previous attempt is in https://github.com/facebook/rocksdb/issues/5602.

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

Test Plan: - new unit tests

Reviewed By: ajkr, jowlyzhang

Differential Revision: D58396673

Pulled By: cbi42

fbshipit-source-id: aae513afad7b1ff5d4faa48104df5f384926bf03
2024-07-19 16:14:54 -07:00
anand76 0fca5e31b4 Fix race between manifest error recovery and file ingestion (#12871)
Summary:
This PR fixes an assertion failure in `DBImpl::ResumeImpl` - `assert(!versions_->descriptor_log_)`. In `VersionSet`, `descriptor_log_` has a pointer to the current MANIFEST writer. When there's an error updating the manifest, `descriptor_log_` is reset, and the error recovery thread checks `io_status()` in `VersionSet` and attempts to write a new MANIFEST. If another DB manipulation happens at the same time (like external file ingestion, column family manipulation etc), it calls `LogAndApply`, which also attempts to write a new MANIFEST. The assertion in `ResumeImpl` might fail in this case since the other MANIFEST writer may have updated `descriptor_log_`. To prevent the assertion, this fix updates both `io_status_` and `descriptor_log_` while holding the DB mutex.

The other option would have been to simply remove the assert. But I think its important to have it to ensure the invariant that `io_status_` is cleared if the MANIFEST is written successfully, and this fix makes things easier to reason about.

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

Test Plan: Existing tests and crash test

Reviewed By: hx235

Differential Revision: D59926947

Pulled By: anand1976

fbshipit-source-id: af9ad18da3e29fc62c7ec2e30e0738aa33d4e5f1
2024-07-19 10:37:51 -07:00
Peter Dillinger de6d0e5ec3 Reduce cases of impacted performance from bug fix (#12874)
Summary:
https://github.com/facebook/rocksdb/issues/12872 was a bit too gross of a fix, because we still don't need to track previous prefix in FullFilterBlockBuilder for many non-partitioned use cases. This basically narrows the fix (and potentail CPU regression) to partitioned+prefix filter cases, which are the cases that needed to be fixed.

A better efficiency fix would still be nice but not as high of a priority.

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

Test Plan: existing tests (just added in https://github.com/facebook/rocksdb/issues/12872)

Reviewed By: jowlyzhang

Differential Revision: D59885591

Pulled By: pdillinger

fbshipit-source-id: 8f273fc3e14c4b60c8a55501dc4bbcc325cd17a1
2024-07-17 16:42:27 -07:00
Peter Dillinger 93b163d1a2 Fix major bug with prefixes, SeekForPrev, and partitioned filters (#12872)
Summary:
Basically, the fix in https://github.com/facebook/rocksdb/issues/8137 was incomplete (and I missed it in the review), because if `whole_key_filtering` is false, then `last_prefix_str_` will never be set to non-empty and the fix doesn't work. Also related to https://github.com/facebook/rocksdb/issues/5835.

This is intended as a safe, simple fix that will regress CPU efficiency slightly (for `whole_key_filtering=false` cases, because of extra prefix string copies during flush & compaction). An efficient fix is not possible without some substantial refactoring.

Also in this PR: new test DBBloomFilterTest.FilterNumEntriesCoalesce tests an adjacent code path that was previously untested for its effect of ensuring the number of unique prefixes and keys is tracked properly when both prefixes and whole keys are going into a filter. (Test fails when either of the two code segments checking for duplicates is disabled.) In addition, the same test would fail before the main bug fix here because the code would inappropriately add the empty string to the filter (because of unmodified `last_prefix_str_`).

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

Test Plan: In addition to DBBloomFilterTest.FilterNumEntriesCoalesce, extended DBBloomFilterTest.SeekForPrevWithPartitionedFilters to cover the broken case. (Mostly whitespace change.)

Reviewed By: jowlyzhang

Differential Revision: D59873793

Pulled By: pdillinger

fbshipit-source-id: 2a7b7f09ca73dc188fb4dab833826ad6da7ebb11
2024-07-17 14:08:35 -07:00
Hui Xiao 21db55f816 Move WAL sync before memtable insertion (#12869)
Summary:
**Context/Summary:**
WAL sync currently happens after memtable write. This causes inconvenience in stress test as we can't simply rollback the ExpectedState when write fails due to injected WAL sync error so something complicated like https://github.com/facebook/rocksdb/pull/12838 might be needed. After moving WAL sync before memtable insertion, there should not be injected IO error after memtable insertion so we can keep the current simple way of handling failed write in stress test with ExpectedState rollback.

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

Test Plan:
1. Below command failed with `iterator has key 0000000000000207000000000000012B0000000000000013, but expected state does not.` before this PR and passes after
```
./db_stress  --WAL_size_limit_MB=0 --WAL_ttl_seconds=0 --acquire_snapshot_one_in=10000 --adaptive_readahead=1 --adm_policy=1 --advise_random_on_open=0 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_fallocate=0 --async_io=0 --auto_readahead_size=0 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=0 --avoid_unnecessary_blocking_io=0 --backup_max_size=104857600 --backup_one_in=0 --batch_protection_bytes_per_key=0 --bgerror_resume_retry_interval=1000000 --block_align=1 --block_protection_bytes_per_key=4 --block_size=16384 --bloom_before_level=4 --bloom_bits=56.810257702625165 --bottommost_compression_type=none --bottommost_file_compaction_delay=0 --bytes_per_sync=262144 --cache_index_and_filter_blocks=1 --cache_index_and_filter_blocks_with_high_priority=1 --cache_size=8388608 --cache_type=auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=1 --charge_filter_construction=1 --charge_table_reader=0 --check_multiget_consistency=0 --check_multiget_entity_consistency=1 --checkpoint_one_in=10000 --checksum_type=kxxHash --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=1000 --compact_range_one_in=1000 --compaction_pri=4 --compaction_readahead_size=1048576 --compaction_ttl=10 --compress_format_version=1 --compressed_secondary_cache_ratio=0.0 --compressed_secondary_cache_size=0 --compression_checksum=0 --compression_max_dict_buffer_bytes=0 --compression_max_dict_bytes=0 --compression_parallel_threads=1 --compression_type=none --compression_use_zstd_dict_trainer=0 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc=04:00-08:00 --data_block_index_type=1 --db=/dev/shm/rocksdb_test/rocksdb_crashtest_blackbox --db_write_buffer_size=0 --default_temperature=kWarm --default_write_temperature=kCold --delete_obsolete_files_period_micros=30000000 --delpercent=0 --delrangepercent=0 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_file_deletions_one_in=10000 --disable_manual_compaction_one_in=1000000 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=0 --enable_index_compression=1 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=0 --enable_sst_partitioner_factory=0 --enable_thread_tracking=0 --enable_write_thread_adaptive_yield=0 --error_recovery_with_no_fault_injection=1 --exclude_wal_from_write_fault_injection=1 --expected_values_dir=/dev/shm/rocksdb_test/rocksdb_crashtest_expected --fail_if_options_file_error=1 --fifo_allow_compaction=0 --file_checksum_impl=crc32c --fill_cache=1 --flush_one_in=1000000 --format_version=3 --get_all_column_family_metadata_one_in=1000000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=1000000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0.5 --index_block_restart_interval=4 --index_shortening=2 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=16384 --inplace_update_support=0 --iterpercent=50 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100 --last_level_temperature=kWarm --level_compaction_dynamic_level_bytes=1 --lock_wal_one_in=10000 --log_file_time_to_roll=60 --log_readahead_size=16777216 --long_running_snapshots=1 --low_pri_pool_ratio=0 --lowest_used_cache_tier=0 --manifest_preallocation_size=0 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=10 --max_auto_readahead_size=16384 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=100000 --max_key_len=3 --max_log_file_size=1048576 --max_manifest_file_size=32768 --max_sequential_skip_in_iterations=1 --max_total_wal_size=0 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=8388608 --memtable_insert_hint_per_batch=1 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.01 --memtable_protection_bytes_per_key=1 --memtable_whole_key_filtering=1 --memtablerep=skip_list --metadata_charge_policy=1 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=0 --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_read_fault_one_in=0 --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_hits=1 --optimize_filters_for_memory=1 --optimize_multiget_for_io=1 --paranoid_file_checks=0 --partition_filters=0 --partition_pinning=3 --pause_background_one_in=1000000 --periodic_compaction_seconds=2 --prefix_size=7 --prefixpercent=0 --prepopulate_block_cache=0 --preserve_internal_time_seconds=0 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=1000 --readahead_size=524288 --readpercent=0 --recycle_log_file_num=1 --reopen=0 --report_bg_io_stats=0 --reset_stats_one_in=1000000 --sample_for_compression=0 --secondary_cache_fault_one_in=0 --set_options_one_in=0 --skip_stats_update_on_db_open=1 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=68719476736 --sqfc_name=foo --sqfc_version=0 --sst_file_manager_bytes_per_sec=104857600 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=10 --stats_history_buffer_size=0 --strict_bytes_per_sync=1 --subcompactions=4 --sync=1 --sync_fault_injection=0 --table_cache_numshardbits=6 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=2 --uncache_aggressiveness=239 --universal_max_read_amp=-1 --unpartitioned_pinning=1 --use_adaptive_mutex=1 --use_adaptive_mutex_lru=1 --use_attribute_group=0 --use_delta_encoding=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_cf_iterator=0 --use_multi_get_entity=0 --use_multiget=0 --use_put_entity_one_in=0 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=0 --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_compression=0 --verify_db_one_in=100000 --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=none --write_buffer_size=33554432 --write_dbid_to_manifest=0 --write_fault_one_in=128 --writepercent=50

Reviewed By: jowlyzhang

Differential Revision: D59825730

Pulled By: hx235

fbshipit-source-id: 7d77aaf177ded2f99bf1ce19f5a4bd0783b9ca92
2024-07-17 13:39:14 -07:00
Hui Xiao 6870cc1187 Temporally disable log recycle with testing GetLiveFilesStorageInfo() (#12868)
Summary:
**Context/Summary:**
We recently discovered a case where `GetLiveFilesStorageInfo()` failed when `Options::recycle_log_file_num` > 0. Before fixing the incompatibility, we disable these combination in stress test.

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

Test Plan: monitor CI

Reviewed By: jowlyzhang

Differential Revision: D59820802

Pulled By: hx235

fbshipit-source-id: 7b09063af6d72ae0ba187b4cf8887abd8a78e5e8
2024-07-16 12:37:50 -07:00
Hui Xiao 9e4ee7f0c6 Fix non-okay status being ignored in write path under two_write_queues_ (#12866)
Summary:
Context/Summary: see above, though the impact is small.

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

Test Plan: exiting UT

Reviewed By: anand1976

Differential Revision: D59782913

Pulled By: hx235

fbshipit-source-id: ec02843645cce49466bde602035d2e61c31965b8
2024-07-16 10:55:08 -07:00
anand76 5aa675457e Fix unhandled MANIFEST write errors (#12865)
Summary:
The failure of `WriteCurrentStateToManifest()` in `VersionSet::ProcessManifestWrites()` was not handled properly. If it failed, `manifest_io_status` was not updated, leading to `manifest_file_number_` being updated to the newly created manifest even though its bad. This would lead to the bad manifest immediately getting deleted, and also the good manifest (referenced by `CURRENT`) getting deleted by obsolete file deletion because of `manifest_file_number_` not referencing its number.

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

Reviewed By: hx235

Differential Revision: D59782940

Pulled By: anand1976

fbshipit-source-id: f752fb9a1c23fd3d734616e273613cbac204301b
2024-07-15 19:13:29 -07:00
Hui Xiao 4ff35afb42 Fix a bug where OnErrorRecoveryBegin() is not called before auto-recovery (#12860)
Summary:
**Context/Summary:**
`*auto_recovery` needs to be set true in order for `OnErrorRecoveryBegin()` to be called before auto-recovery
https://github.com/facebook/rocksdb/blob/3db030d7ee1b887ce818ec6f6a8d10949f9e9a22/db/event_helpers.cc#L64-L66
Currently it's set false for auto-recovery. This PR fixes it.

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

Test Plan:
- Manual observation that it is called
- Existing UT

Reviewed By: jowlyzhang

Differential Revision: D59693315

Pulled By: hx235

fbshipit-source-id: 3f428c5b1e9818bb7697fdcd7f245d11378eb14a
2024-07-15 17:00:14 -07:00
WangQian 755010f8d3 Fix the bug with using the user comparator to compare prefix. (#12862)
Summary:
Fixes https://github.com/facebook/rocksdb/issues/12855

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

Reviewed By: cbi42

Differential Revision: D59771651

Pulled By: jowlyzhang

fbshipit-source-id: ffe0025143f51f9ce1b46900c3fef6a20eb34f4a
2024-07-15 15:13:29 -07:00
Peter Dillinger 0e3e43f4d1 FaultInjectionTestFS follow-up and clean-up (#12861)
Summary:
In follow-up to https://github.com/facebook/rocksdb/issues/12852:
* Use std::copy in place of copy_n for potentially overlapping buffer
* Get rid of troublesome -1 idiom from `pos_at_last_append_` and `pos_at_last_sync_`
* Small improvements to test FaultInjectionFSTest.ReadUnsyncedData

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

Test Plan: CI, crash test, etc.

Reviewed By: cbi42

Differential Revision: D59757484

Pulled By: pdillinger

fbshipit-source-id: c6fbdc2e97c959983184925a855cc8b0285fa23f
2024-07-15 10:28:34 -07:00
Changyu Bi b800b5eb6a Deflake ThreadStatus related unit tests (#12858)
Summary:
Unit tests `DBTest.ThreadStatusFlush` and `DBTestWithParam.ThreadStatusSingleCompaction` have been flaky and fail with error message
```
[ RUN      ] DBTest.ThreadStatusFlush
op_count: 0, expected_count 1
thread id: 718113, thread status: , cf_name
thread id: 718114, thread status: , cf_name pikachu
/__w/rocksdb/rocksdb/db/db_test.cc:4817: Failure
Value of: VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 1)
  Actual: false
Expected: true
[  FAILED  ] DBTest.ThreadStatusFlush (106 ms)

[ RUN      ] DBTestWithParam/DBTestWithParam.ThreadStatusSingleCompaction/0
db/db_test.cc:4673: Failure
Expected equality of these values:
  op_count
    Which is: 0
  expected_count
    Which is: 1
[  FAILED  ] DBTestWithParam/DBTestWithParam.ThreadStatusSingleCompaction/0, where GetParam() = (1, false)
```

One cause for this is that before flush/compaction finishes, we will go through `~WritableFileWriter()`, either for WAL or SST file, and temporarily set thread_operation to UNKNOWN. This UNKNOWN thread operation seem to be there for some stress test verification. This PR fixes these tests by setting the IOActivity in ~WritableFileWriter() for debug build.

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

Test Plan: monitor future test failure.

Reviewed By: hx235

Differential Revision: D59691564

Pulled By: cbi42

fbshipit-source-id: 3f96998bba9d42aba50d1830c2b51bef2dd6705f
2024-07-15 09:56:09 -07:00
Peter Dillinger 72438a6788 Support read & write with unsynced data in FaultInjectionTestFS (#12852)
Summary:
Follow-up to https://github.com/facebook/rocksdb/issues/12729 and others to fix FaultInjectionTestFS handling the case where a live WAL is being appended to and synced while also being copied for checkpoint or backup, up to a known flushed (but not necessarily synced) prefix of the file. It was tricky to structure the code in a way that could handle a tricky race with Sync in another thread (see code comments, thanks Changyu) while maintaining good performance and test-ability.

For more context, see the call to FlushWAL() in DBImpl::GetLiveFilesStorageInfo().

Also, the unit test for https://github.com/facebook/rocksdb/issues/12729 was neutered by https://github.com/facebook/rocksdb/issues/12797, and this re-enables the functionality it is testing.

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

Test Plan:
unit test expanded/updated. Local runs of blackbox_crash_test.

The implementation is structured so that a multi-threaded unit test is not needed to cover at least the code lines, as the race handling is folded into "catch up after returning unsynced and then a sync."

Reviewed By: cbi42

Differential Revision: D59594045

Pulled By: pdillinger

fbshipit-source-id: 94667bb72255e2952586c53bae2c2dd384e85a50
2024-07-12 16:01:57 -07:00
Yu Zhang 3db030d7ee Fix bug for recovering a prepared but not committed txn (#12856)
Summary:
This PR fix a bug for recovering a prepared Transaction that can contain user-defined timestamps.

The `Transaction::Put` type of APIs expect the key provided to be user key without timestamps. When the original transaction added a key for a column family that enables user-defined timestamps, say of size 8. Internally `WriteBatch::Put` will leave a placeholder 8 bytes for the final commit timestamp. For example:
https://github.com/facebook/rocksdb/blob/cec28aa90f0e38666c0b3485d197ecbe0c2a025f/db/write_batch.cc#L937

When rebuilding this transaction from a `WriteBatch` from WAL log, we should consider this and remove the tailing 8 bytes of a key before adding it via the public Transaction write APIs.

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

Test Plan: Added unit test that would fail without this fix

Reviewed By: cbi42

Differential Revision: D59656399

Pulled By: jowlyzhang

fbshipit-source-id: c716aefa4d548770b691efe96ac8e6d7dab458b9
2024-07-11 16:25:35 -07:00
Changyu Bi cec28aa90f Fix SetOptions() failure in stress test (#12854)
Summary:
fix SetOptions() so that max_read_amp is at least level0_file_num_compaction_trigger.

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

Test Plan: monitor stress test new failure

Reviewed By: hx235

Differential Revision: D59618547

Pulled By: cbi42

fbshipit-source-id: b83371f293b87097ee9cdd32d662e9965cde57e6
2024-07-10 21:36:44 -07:00
anand76 37b81bd28f Avoid SyncWAL if flushing during shutdown (#12853)
Summary:
https://github.com/facebook/rocksdb/issues/12746 added calls to FlushWAL/SyncWAL in db_stress during reopen, in order to ensure persistence of unpersisted data and avoid false alarms due to lack of prefix recovery support in db_stress reopen. However, there's no need to flush/sync the WAL if avoid_flush_during_shutdown is false, as the WAL will not be needed during recovery. This allows file systems that don't support SyncWAL (not thread safe) to avoid the need by requesting flush during shutdown.

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

Reviewed By: hx235

Differential Revision: D59604138

Pulled By: anand1976

fbshipit-source-id: 4c4470b3c956d6bf64f5b8a1a5727a8b888f1a5f
2024-07-10 15:59:35 -07:00
Jay Huh 6997dd909c Disable attribute group txn tests (#12851)
Summary:
Transactions are not yet supported in AttributeGroup APIs. Disabling `use_attribute_group` for txn tests

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

Test Plan:
Verified output that `--use_attribute_group=0`

```
python3 tools/db_crashtest.py whitebox --txn
```
```
python3 tools/db_crashtest.py whitebox --optimistic_txn
```

Reviewed By: hx235

Differential Revision: D59565635

Pulled By: jaykorean

fbshipit-source-id: 7d618f475b6d2e5a53c3c59cdf1e694f3893ae58
2024-07-10 10:53:30 -07:00
Changyu Bi d6f265f9d6 Fix race in multiops txn stress test (#12847)
Summary:
`MultiOpsTxnsStressListener::OnCompactionCompleted()` access `db_` and can be called while db_ is being destroyed in ~StressTest(). This causes TSAN to complain about data race. This PR fixes this issue by calling db_->Close() first to stop all background work. Also moved the cleanup out of StressTest destructor to avoid race between the listener and  ~StressTest().

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

Test Plan: monitor crash test failure.

Reviewed By: hx235

Differential Revision: D59492691

Pulled By: cbi42

fbshipit-source-id: afcbab084cc9ac0904d6b04809b0888498ca8e66
2024-07-09 16:51:38 -07:00
Hui Xiao ebe2116240 Remove false-postive assertion in FaultInjectionTestFS::RenameFile (#12828)
Summary:
**Context/Summary:**
The assertion `tlist.find(tdn.second) == tlist.end()` https://github.com/facebook/rocksdb/blame/9eebaf11cbd875435b572f05f0378ecdb761cc74/utilities/fault_injection_fs.cc#L1003 can catch us false positive.

Some context
(1) When fault injection is enabled and db open fails because of that, crash test will retry open without injected error in order to proceed with a clean open:
https://github.com/facebook/rocksdb/blob/9eebaf11cbd875435b572f05f0378ecdb761cc74/db_stress_tool/db_stress_test_base.cc#L3559
https://github.com/facebook/rocksdb/blob/9eebaf11cbd875435b572f05f0378ecdb761cc74/db_stress_tool/db_stress_test_base.cc#L3586-L3639
(2)
a. `FaultInjectionTestFS::dir_to_new_files_since_last_sync` records files that are created but not yet synced.
b. When we create CURRENT, we will first create a temp file and rename it as "CURRENT". As part of the renaming, we will [assert](https://github.com/facebook/rocksdb/blame/9eebaf11cbd875435b572f05f0378ecdb761cc74/utilities/fault_injection_fs.cc#L1003) `FaultInjectionTestFS::dir_to_new_files_since_last_sync ` doesn't already have a file named `CURRENT`.

Suppose the following sequence of events happened:

(1) 1st open, with metadata write error
1. As part of creating CURRENT file, added "CURRENT" to `FaultInjectionTestFS::dir_to_new_files_since_last_sync_`
https://github.com/facebook/rocksdb/blob/9eebaf11cbd875435b572f05f0378ecdb761cc74/utilities/fault_injection_fs.cc#L735
2.  `SyncDir()` here https://github.com/facebook/rocksdb/blob/9eebaf11cbd875435b572f05f0378ecdb761cc74/file/filename.cc#L412 failed with injected metadata write error. Therefore, "CURRENT" file didn't get removed from `FaultInjectionTestFS::dir_to_new_files_since_last_sync_` as it would if `SyncDir()` succeeded https://github.com/facebook/rocksdb/blob/9eebaf11cbd875435b572f05f0378ecdb761cc74/utilities/fault_injection_fs.h#L344

(2) 2st open
1. Attempted to create a CURRENT file and failed during renaming since `FaultInjectionTestFS::dir_to_new_files_since_last_sync_` already had a file called CURRENT. So  will fail
```
assertion failed - tlist.find(tdn.second) == tlist.end()
```

This PR fixed this by removing the assertion. It used to catch us some missing sync of some directory (e.,g https://github.com/facebook/rocksdb/pull/10573) so we will keep thinking about a better way to catch that.

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

Test Plan:
Command constantly failed before the fix but passed after the PR running for 10 minutes
```
python3 tools/db_crashtest.py --simple blackbox --interval=10 --WAL_size_limit_MB=1 --WAL_ttl_seconds=60 --acquire_snapshot_one_in=100 --adaptive_readahead=1 --adm_policy=2 --advise_random_on_open=1 --allow_concurrent_memtable_write=1 --allow_data_in_errors=True --allow_fallocate=1 --async_io=0 --auto_readahead_size=1 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=0 --avoid_unnecessary_blocking_io=0 --backup_max_size=104857600 --backup_one_in=100000 --batch_protection_bytes_per_key=0 --bgerror_resume_retry_interval=100 --block_align=0 --block_protection_bytes_per_key=8 --block_size=16384 --bloom_before_level=1 --bloom_bits=10 --bottommost_compression_type=lz4hc --bottommost_file_compaction_delay=86400 --bytes_per_sync=0 --cache_index_and_filter_blocks=1 --cache_index_and_filter_blocks_with_high_priority=0 --cache_size=8388608 --cache_type=tiered_auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=0 --charge_filter_construction=0 --charge_table_reader=0 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=10000 --checksum_type=kCRC32c --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=1000 --compact_range_one_in=1000000 --compaction_pri=3 --compaction_readahead_size=0 --compaction_ttl=1 --compress_format_version=1 --compressed_secondary_cache_ratio=0.5 --compressed_secondary_cache_size=0 --compression_checksum=0 --compression_max_dict_buffer_bytes=15 --compression_max_dict_bytes=16384 --compression_parallel_threads=1 --compression_type=zstd --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=65536 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=1 --db_write_buffer_size=0 --default_temperature=kHot --default_write_temperature=kUnknown --delete_obsolete_files_period_micros=30000000 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=1 --disable_file_deletions_one_in=10000 --disable_manual_compaction_one_in=10000 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=0 --enable_index_compression=1 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=0 --enable_sst_partitioner_factory=1 --enable_thread_tracking=1 --enable_write_thread_adaptive_yield=0 --error_recovery_with_no_fault_injection=1 --exclude_wal_from_write_fault_injection=1 --fail_if_options_file_error=1 --fifo_allow_compaction=0 --file_checksum_impl=crc32c --fill_cache=1 --flush_one_in=1000000 --format_version=3 --get_all_column_family_metadata_one_in=1000000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=100000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=2097152 --high_pri_pool_ratio=0 --index_block_restart_interval=2 --index_shortening=0 --index_type=2 --ingest_external_file_one_in=0 --initial_auto_readahead_size=16384 --inplace_update_support=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100000 --last_level_temperature=kWarm --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=10000 --log_file_time_to_roll=60 --log_readahead_size=16777216 --long_running_snapshots=1 --low_pri_pool_ratio=0.5 --lowest_used_cache_tier=1 --manifest_preallocation_size=0 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=10 --max_auto_readahead_size=16384 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=1000000 --max_key_len=3 --max_log_file_size=0 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=1 --max_total_wal_size=0 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=3 --max_write_buffer_size_to_maintain=2097152 --memtable_insert_hint_per_batch=0 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.1 --memtable_protection_bytes_per_key=8 --memtable_whole_key_filtering=0 --memtablerep=skip_list --metadata_charge_policy=1 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=0 --min_write_buffer_number_to_merge=2 --mmap_read=1 --mock_direct_io=False --nooverwritepercent=1 --num_file_reads_for_auto_readahead=1 --open_files=-1 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=8 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --ops_per_thread=100000000 --optimize_filters_for_hits=0 --optimize_filters_for_memory=0 --optimize_multiget_for_io=1 --paranoid_file_checks=1 --partition_filters=1 --partition_pinning=3 --pause_background_one_in=1000000 --periodic_compaction_seconds=1000 --prefix_size=5 --prefixpercent=5 --prepopulate_block_cache=1 --preserve_internal_time_seconds=0 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=32 --read_fault_one_in=0 --readahead_size=524288 --readpercent=45 --recycle_log_file_num=0 --reopen=0 --report_bg_io_stats=0 --reset_stats_one_in=1000000 --sample_for_compression=0 --secondary_cache_fault_one_in=32 --secondary_cache_uri= --set_options_one_in=0 --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=68719476736 --sqfc_name=foo --sqfc_version=1 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=10 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=1 --subcompactions=2 --sync=0 --sync_fault_injection=1 --table_cache_numshardbits=6 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=2 --uncache_aggressiveness=1582 --universal_max_read_amp=4 --unpartitioned_pinning=0 --use_adaptive_mutex=0 --use_adaptive_mutex_lru=1 --use_attribute_group=1 --use_delta_encoding=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_cf_iterator=1 --use_multi_get_entity=1 --use_multiget=0 --use_put_entity_one_in=1 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=0 --use_write_buffer_manager=0 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000 --verify_compression=1 --verify_db_one_in=10000 --verify_file_checksums_one_in=1000 --verify_iterator_with_expected_state_one_in=5 --verify_sst_unique_id_in_manifest=1 --wal_bytes_per_sync=0 --wal_compression=none --write_buffer_size=33554432 --write_dbid_to_manifest=1 --write_fault_one_in=8 --writepercent=35
```

Reviewed By: cbi42

Differential Revision: D59241548

Pulled By: hx235

fbshipit-source-id: 5bb49e6a94943273f47578a2caf3d08ca5b67e5f
2024-07-09 15:35:54 -07:00
Konstantin Ilin 5ecb92760a Create C API function to iterate over WriteBatch for custom Column Families (#12718)
Summary:
Create C API function for iterating over WriteBatch for custom Column Families
Adding function to C API that exposes column family specific methods to iterate over WriteBatch: put_cf, delete_cf and merge_cf. This is required when the one needs to read changes for any non-default column family. Without that functionality it is impossible to iterate over changes in WAL that are relevant to custom column families.

Fixes https://github.com/facebook/rocksdb/issues/12790

Testing:
Added WriteBatch iteration test to "columnfamilies" section of C API unit tests

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

Reviewed By: cbi42

Differential Revision: D59483601

Pulled By: ajkr

fbshipit-source-id: b68b900636304528a38620a8c3ad82fdce4b60cb
2024-07-09 12:05:08 -07:00
w41ter b837d41ab1 Expose SizeApproximationFlags to C API (#12836)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12836

Reviewed By: cbi42

Differential Revision: D59502673

Pulled By: ajkr

fbshipit-source-id: fc9f77d6740d8efa45d9357662f0f827dbd0511f
2024-07-09 12:00:50 -07:00
Yu Zhang 2e1b3f921f Remove unreachable code (#12846)
Summary:
Removing some unreachable code.

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

Reviewed By: cbi42

Differential Revision: D59498423

Pulled By: jowlyzhang

fbshipit-source-id: 6b2c51732d94b1f69a8ba7474b16a171d4e6d640
2024-07-09 09:24:43 -07:00
Jeffery 62b62cf135 Fix CondVar::TimedWait for Windows (#12815)
Summary:
Based on https://github.com/microsoft/STL/issues/369
They fixed the issue in `std::condition_variable_any` but not in `std::condition_variable`, which is currently used in rocksdb repo. So we need to implement the work around regardless of `_MSVC_STL_UPDATE`.

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

Reviewed By: cbi42

Differential Revision: D59493690

Pulled By: ajkr

fbshipit-source-id: ad0fc9ef9f2357347d21e271c2f1d0a3a97d89be
2024-07-08 21:38:21 -07:00
Zixuan Tan a97a1f3247 Fix incorrect refillPeriodMicros unit in the document (#12832)
Summary:
The default value for `refillPeriodMicros` is `100 * 1000`, which means 100ms (or 100,000us).

The document comments say 100,000ms (equivalent to 100 seconds), which is incorrect and misleading. This PR fixes this typo.

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

Reviewed By: cbi42

Differential Revision: D59492336

Pulled By: ajkr

fbshipit-source-id: c2f55a8b996fe078a1510fcbebaea92ec0075929
2024-07-08 18:08:53 -07:00
WangQian f471e56190 fix the non initialized bug in StderrLogger. (#12839)
Summary:
This PR is intended to fix a potential uninitialized variable bug.

Fixes https://github.com/facebook/rocksdb/issues/12837

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

Reviewed By: ajkr

Differential Revision: D59398888

Pulled By: cbi42

fbshipit-source-id: 337391d7c1e73c0ff61797f88fbb4a8379500211
2024-07-08 15:59:02 -07:00
Chdy 110ce5f4a3 fix: Round-Robin pri under leveled compaction allows subcompactions b… (#12843)
Summary:
### Summary: Round-Robin pri under leveled compaction allows subcompactions by default is not compatible with PlainTable

```c++
bool Compaction::ShouldFormSubcompactions() const {
  if (cfd_ == nullptr) {
    return false;
  }

  // Round-Robin pri under leveled compaction allows subcompactions by default
  // and the number of subcompactions can be larger than max_subcompactions_
  if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
      cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
    return output_level_ > 0;
  }

  if (max_subcompactions_ <= 1) {
    return false;
  }
```

PlainTable does not support Subcompaction, including when AdaptiveTable is applied to PlainTable.  subcompaction by default will result in the following error in some scenarios.

```c++
void PlainTableIterator::Seek(const Slice& target) {
  if (use_prefix_seek_ != !table_->IsTotalOrderMode()) {
    // This check is done here instead of NewIterator() to permit creating an
    // iterator with total_order_seek = true even if we won't be able to Seek()
    // it. This is needed for compaction: it creates iterator with
    // total_order_seek = true but usually never does Seek() on it,
    // only SeekToFirst().
    status_ = Status::InvalidArgument(
        "total_order_seek not implemented for PlainTable.");
    offset_ = next_offset_ = table_->file_info_.data_end_offset;
    return;
  }
```

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

Reviewed By: ajkr

Differential Revision: D59433477

Pulled By: cbi42

fbshipit-source-id: fb780ba7f7e8efdfedb7480abf14dd38e0b63677
2024-07-08 12:25:11 -07:00
Radek Hubner b6c3495a71 Update snappy dependency for Java releases. (#12207)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12207

Reviewed By: hx235

Differential Revision: D59299915

Pulled By: cbi42

fbshipit-source-id: 3f5fa88b0c5e8366a08734f99db1d3de942cd60b
2024-07-05 09:30:28 -07:00
Hui Xiao 1f589a3f73 Clarify GetProperty API doc (#12829)
Summary:
**Context/Summary:** as titled since https://github.com/facebook/rocksdb/blob/9eebaf11cbd875435b572f05f0378ecdb761cc74/db/internal_stats.cc#L1162.

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

Test Plan: no code change

Reviewed By: pdillinger

Differential Revision: D59243565

Pulled By: hx235

fbshipit-source-id: 074137b29bb12d9d965d154626a3289f85a39c52
2024-07-02 13:15:00 -07:00
Changyu Bi de16611a50 Fix WAL corruption in stress test (#12834)
Summary:
We are seeing WAL corruption in crash tests where wal_compression and recycled_wal are enabled. With wal_compression, we write a SetCompression record when creating a WAL, which can happen during DB open time. Our current stress test set up may write directly to the underlying WAL file during DB open, while writing to a buffer under TestFSWritableFile later for sync fault injection. This causes the last synced position to be inaccurately tracked in TestFSWritableFile and causes reads to return incorrect data.

This PR removes the line that causes this mixture of WAL writes. Also updated TestFSWritableFile to avoid such a mixture of buffered and direct writes.

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

Test Plan:
the following command repros WAL corruption before this PR
```
 ./db_stress --WAL_size_limit_MB=0 --WAL_ttl_seconds=60 --acquire_snapshot_one_in=10000 --adaptive_readahead=0 --adm_policy=1 --advise_random_on_open=1 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_fallocate=0 --async_io=1 --auto_readahead_size=0 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=0 --avoid_unnecessary_blocking_io=1 --backup_max_size=104857600 --backup_one_in=1000 --batch_protection_bytes_per_key=8 --bgerror_resume_retry_interval=1000000 --block_align=0 --block_protection_bytes_per_key=1 --block_size=16384 --bloom_before_level=0 --bloom_bits=8 --bottommost_compression_type=snappy --bottommost_file_compaction_delay=600 --bytes_per_sync=0 --cache_index_and_filter_blocks=0 --cache_index_and_filter_blocks_with_high_priority=1 --cache_size=33554432 --cache_type=auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=0 --charge_filter_construction=1 --charge_table_reader=1 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=10000 --checksum_type=kxxHash64 --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=1000 --compact_range_one_in=1000000 --compaction_pri=4 --compaction_readahead_size=1048576 --compaction_ttl=0 --compress_format_version=1 --compressed_secondary_cache_size=16777216 --compression_checksum=1 --compression_max_dict_buffer_bytes=1099511627775 --compression_max_dict_bytes=16384 --compression_parallel_threads=1 --compression_type=zstd --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=1 --db_write_buffer_size=134217728 --default_temperature=kHot --default_write_temperature=kCold --delete_obsolete_files_period_micros=21600000000 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=1 --disable_file_deletions_one_in=1000000 --disable_manual_compaction_one_in=10000 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=1 --enable_index_compression=1 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=1 --enable_sst_partitioner_factory=1 --enable_thread_tracking=1 --enable_write_thread_adaptive_yield=1 --error_recovery_with_no_fault_injection=0 --exclude_wal_from_write_fault_injection=1 --fail_if_options_file_error=0 --fifo_allow_compaction=1 --file_checksum_impl=crc32c --fill_cache=0 --flush_one_in=1000000 --format_version=2 --get_all_column_family_metadata_one_in=1000000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=1000000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0.5 --index_block_restart_interval=13 --index_shortening=0 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=0 --inplace_update_support=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100 --last_level_temperature=kUnknown --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=1000000 --log_file_time_to_roll=0 --log_readahead_size=0 --long_running_snapshots=1 --low_pri_pool_ratio=0 --lowest_used_cache_tier=0 --manifest_preallocation_size=5120 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=0 --max_auto_readahead_size=524288 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=100000 --max_key_len=3 --max_log_file_size=0 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=8 --max_total_wal_size=0 --max_write_batch_group_size_bytes=1048576 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=4194304 --memtable_insert_hint_per_batch=1 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.5 --memtable_protection_bytes_per_key=0 --memtable_whole_key_filtering=0 --memtablerep=skip_list --metadata_charge_policy=1 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=0 --min_write_buffer_number_to_merge=1 --mmap_read=0 --mock_direct_io=True --nooverwritepercent=1 --num_file_reads_for_auto_readahead=1 --open_files=-1 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=8 --open_read_fault_one_in=0 --open_write_fault_one_in=16 --ops_per_thread=100000000 --optimize_filters_for_hits=1 --optimize_filters_for_memory=1 --optimize_multiget_for_io=1 --paranoid_file_checks=0 --partition_filters=0 --partition_pinning=3 --pause_background_one_in=10000 --periodic_compaction_seconds=0 --prefix_size=8 --prefixpercent=5 --prepopulate_block_cache=1 --preserve_internal_time_seconds=0 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=1000 --readahead_size=16384 --readpercent=45 --recycle_log_file_num=1 --reopen=0 --report_bg_io_stats=1 --reset_stats_one_in=10000 --sample_for_compression=0 --secondary_cache_fault_one_in=0 --secondary_cache_uri= --set_options_one_in=0 --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=1048576 --sqfc_name=bar --sqfc_version=2 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=10 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=1 --subcompactions=4 --sync=0 --sync_fault_injection=1 --table_cache_numshardbits=-1 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=3 --uncache_aggressiveness=113 --universal_max_read_amp=4 --unpartitioned_pinning=3 --use_adaptive_mutex=1 --use_adaptive_mutex_lru=1 --use_attribute_group=0 --use_delta_encoding=1 --use_direct_io_for_flush_and_compaction=1 --use_direct_reads=0 --use_full_merge_v1=0 --use_get_entity=0 --use_merge=0 --use_multi_cf_iterator=0 --use_multi_get_entity=1 --use_multiget=0 --use_put_entity_one_in=0 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=5 --use_write_buffer_manager=1 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000000 --verify_compression=0 --verify_db_one_in=10000 --verify_file_checksums_one_in=1000 --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=33554432 --write_dbid_to_manifest=0 --write_fault_one_in=128 --writepercent=35 --preserve_unverified_changes=1 --db=/dev/shm/rocksdb_test/blackbox --expected_values_dir=/dev/shm/rocksdb_test/expected
Choosing random keys with no overwrite
...
(Re-)verified 0 unique IDs
2024/07/01-16:42:46  Initializing worker threads
Crash-recovery verification passed :)
2024/07/01-16:42:46  Starting database operations
^C

./db_stress --WAL_size_limit_MB=0 --WAL_ttl_seconds=60 --acquire_snapshot_one_in=10000 --adaptive_readahead=0 --adm_policy=1 --advise_random_on_open=1 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_fallocate=0 --async_io=1 --auto_readahead_size=0 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=0 --avoid_unnecessary_blocking_io=1 --backup_max_size=104857600 --backup_one_in=1000 --batch_protection_bytes_per_key=8 --bgerror_resume_retry_interval=1000000 --block_align=0 --block_protection_bytes_per_key=1 --block_size=16384 --bloom_before_level=0 --bloom_bits=8 --bottommost_compression_type=snappy --bottommost_file_compaction_delay=600 --bytes_per_sync=0 --cache_index_and_filter_blocks=0 --cache_index_and_filter_blocks_with_high_priority=1 --cache_size=33554432 --cache_type=auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=0 --charge_filter_construction=1 --charge_table_reader=1 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=10000 --checksum_type=kxxHash64 --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=1000 --compact_range_one_in=1000000 --compaction_pri=4 --compaction_readahead_size=1048576 --compaction_ttl=0 --compress_format_version=1 --compressed_secondary_cache_size=16777216 --compression_checksum=1 --compression_max_dict_buffer_bytes=1099511627775 --compression_max_dict_bytes=16384 --compression_parallel_threads=1 --compression_type=zstd --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=1 --db_write_buffer_size=134217728 --default_temperature=kHot --default_write_temperature=kCold --delete_obsolete_files_period_micros=21600000000 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=1 --disable_file_deletions_one_in=1000000 --disable_manual_compaction_one_in=10000 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=1 --enable_index_compression=1 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=1 --enable_sst_partitioner_factory=1 --enable_thread_tracking=1 --enable_write_thread_adaptive_yield=1 --error_recovery_with_no_fault_injection=0 --exclude_wal_from_write_fault_injection=1 --fail_if_options_file_error=0 --fifo_allow_compaction=1 --file_checksum_impl=crc32c --fill_cache=0 --flush_one_in=1000000 --format_version=2 --get_all_column_family_metadata_one_in=1000000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=1000000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0.5 --index_block_restart_interval=13 --index_shortening=0 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=0 --inplace_update_support=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100 --last_level_temperature=kUnknown --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=1000000 --log_file_time_to_roll=0 --log_readahead_size=0 --long_running_snapshots=1 --low_pri_pool_ratio=0 --lowest_used_cache_tier=0 --manifest_preallocation_size=5120 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=0 --max_auto_readahead_size=524288 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=100000 --max_key_len=3 --max_log_file_size=0 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=8 --max_total_wal_size=0 --max_write_batch_group_size_bytes=1048576 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=4194304 --memtable_insert_hint_per_batch=1 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.5 --memtable_protection_bytes_per_key=0 --memtable_whole_key_filtering=0 --memtablerep=skip_list --metadata_charge_policy=1 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=0 --min_write_buffer_number_to_merge=1 --mmap_read=0 --mock_direct_io=True --nooverwritepercent=1 --num_file_reads_for_auto_readahead=1 --open_files=-1 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=8 --open_read_fault_one_in=0 --open_write_fault_one_in=16 --ops_per_thread=100000000 --optimize_filters_for_hits=1 --optimize_filters_for_memory=1 --optimize_multiget_for_io=1 --paranoid_file_checks=0 --partition_filters=0 --partition_pinning=3 --pause_background_one_in=10000 --periodic_compaction_seconds=0 --prefix_size=8 --prefixpercent=5 --prepopulate_block_cache=1 --preserve_internal_time_seconds=0 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=1000 --readahead_size=16384 --readpercent=45 --recycle_log_file_num=1 --reopen=0 --report_bg_io_stats=1 --reset_stats_one_in=10000 --sample_for_compression=0 --secondary_cache_fault_one_in=0 --secondary_cache_uri= --set_options_one_in=0 --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=1048576 --sqfc_name=bar --sqfc_version=2 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=10 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=1 --subcompactions=4 --sync=0 --sync_fault_injection=1 --table_cache_numshardbits=-1 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=3 --uncache_aggressiveness=113 --universal_max_read_amp=4 --unpartitioned_pinning=3 --use_adaptive_mutex=1 --use_adaptive_mutex_lru=1 --use_attribute_group=0 --use_delta_encoding=1 --use_direct_io_for_flush_and_compaction=1 --use_direct_reads=0 --use_full_merge_v1=0 --use_get_entity=0 --use_merge=0 --use_multi_cf_iterator=0 --use_multi_get_entity=1 --use_multiget=0 --use_put_entity_one_in=0 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=5 --use_write_buffer_manager=1 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000000 --verify_compression=0 --verify_db_one_in=10000 --verify_file_checksums_one_in=1000 --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=33554432 --write_dbid_to_manifest=0 --write_fault_one_in=128 --writepercent=35 --preserve_unverified_changes=1 --db=/dev/shm/rocksdb_test/blackbox --expected_values_dir=/dev/shm/rocksdb_test/expected
Choosing random keys with no overwrite
...
Crash-recovery verification passed :)
2024/07/01-16:43:02  Starting database operations
Failure in BackupEngine::CreateNewBackup with: Corruption: bad record length under specified BackupEngineOptions: share_table_files: 1, share_files_with_checksum: 1, share_files_with_checksum_naming: 2147483650, schema_version: 1, max_background_operations: 1, backup_rate_limiter: 0x7f2373676280, restore_rate_limiter: 0, current_temperatures_override_manifest: 1, CreateBackupOptions: flush_before_backup: 0, decrease_background_thread_cpu_priority: 0, background_thread_cpu_priority: 2, RestoreOptions: keep_log_files: 1 (Empty string or missing field indicates default option or value is used)
Verification failed: Backup/restore failed: Corruption: bad record length
db_stress: db_stress_tool/db_stress_test_base.cc:528: void rocksdb::StressTest::ProcessStatus(rocksdb::SharedState*, std::string, const rocksdb::Status&, bool) const: Assertion `false' failed.
Received signal 6 (Aborted)
Invoking GDB for stack trace...
^CCouldn't get CS register: No such process.
Couldn't get registers: No such process.
[Inferior 1 (process 2097222) detached]
```

Reviewed By: pdillinger

Differential Revision: D59260401

Pulled By: cbi42

fbshipit-source-id: fdcdaaab2e14b527b26fbdfa819b4fe3f745a4de
2024-07-02 13:02:39 -07:00
Peter Dillinger 0bb939611d Avoid unnecessary work in internal calls to GetSortedWalFiles (#12831)
Summary:
We are seeing a number of crash test failures coming from checkpoint and backup code, likely from WalManager::GetSortedWalFiles -> ... -> WalManager::ReadFirstLine and this code path is not needed, because we don't need to know the sequence numbers of WAL files going into a checkpoint or backup. We can minimize the impact of whatever inconsistency is causing that problem by not relying on it where it's not needed.

Similarly, when we only need a roughly accurate set of current WAL files, we don't need to query all the archived WAL files (and redundantly the live ones again).

So this reduces filesystem queries and DB mutex acquires in creating backups and checkpoints.

Needed follow-up:
Figure out what is causing various failures with an apparent inconsistency where GetSortedWalFiles fails on reading a WAL file. If it's an injected failure, perhaps it's not propagating that injected failure appropriately. It might also be an inconsistency between what the DB knows is flushed and what WalManager reads from the filesystem (which we know is dubious and should be phased out, which this is arguably another step toward). Or completing that phase-out might solve the problem without a full diagnosis.

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

Test Plan:
existing tests (easily caught when I went too far in initally developing this change)

Update to BackupUsingDirectIO test so that there's a WAL file in what is backed up. (Was relying on some oddity.)

Reviewed By: cbi42

Differential Revision: D59252649

Pulled By: pdillinger

fbshipit-source-id: 7ad4187a1c70caa59a6d6c1c643ef95232b929f5
2024-07-01 23:29:02 -07:00
Hui Xiao 84296bc248 Reset seen_injected_error_ with seen_error_ (#12830)
Summary:
**Context/Summary** : as titled as seen_injected_error_ is a subcategory of seen_error_

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

Test Plan: existing CI as it only affects crash test code

Reviewed By: jaykorean

Differential Revision: D59249018

Pulled By: hx235

fbshipit-source-id: 20e4c22cade57e12a104a03999e4c841a3648b11
2024-07-01 16:40:57 -07:00
Jeffery 093f4ef82c Fix db_rate_limiter_test for win (#12816)
Summary:
We didn't implement file system prefetch for OS Win. During table open, it uses `FilePrefetchBuffer` instead and only do 1 read instead of 4 in BufferedIO.

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

Reviewed By: jaykorean

Differential Revision: D59181835

Pulled By: ajkr

fbshipit-source-id: 18b8f0247408cd1a80f289357ede5232ae5a3c66
2024-07-01 16:14:19 -07:00
Changyu Bi 9eebaf11cb Fix stress test SetOptions() setting incompatible options (#12827)
Summary:
To fix errors like "Verification failed: SetOptions failed: Invalid argument: max_successive_merges > 0 is incompatible with unordered_write".

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

Test Plan: no new crash test failure due to this option combination.

Reviewed By: hx235

Differential Revision: D59233002

Pulled By: cbi42

fbshipit-source-id: 2a3e4d57a56f07bdda49ea36f0f9f6a30f17bbc3
2024-07-01 12:17:22 -07:00
Hui Xiao 69ad597b46 Disable fault injection for TestGetProperty (#12825)
Summary:
**Context/Summary:**
See titled; along with one more minor fix to other disabling

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

Test Plan: CI won't show `Failed to get DB property: rocksdb.aggregated-table-properties`

Reviewed By: jaykorean

Differential Revision: D59231819

Pulled By: hx235

fbshipit-source-id: a8e73c9e06eeceb4c6025a4885823a3eba25c359
2024-07-01 10:53:51 -07:00
Hui Xiao c7e94bc878 Disable WAL write error injection when reopen with WAL (#12820)
Summary:
**Context/Summary:**

Right now we need to persist WAL data before closing for reopen when `reopen > 0` and `disable_wal = false` https://github.com/facebook/rocksdb/blame/71f9e6b5b36e3223e8dba29df75e4e5008818d16/db_stress_tool/db_stress_test_base.cc#L3479.  Previous injected WAL write errors may not be cleared by the time of closing and ready or persisting WAL. To simplify, we disable any WAL write error injection when `reopen > 0` and  `disable_wal = false`

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

Test Plan:
Below command failed `Error persisting WAL data which is needed before reopening the DB: IO error: Writer has previous injected error.` with but passes after we disable WAL write error injection (exclude_wal_from_write_fault_injection=1, metadata_write_fault_one_in=0)
```
./db_stress --WAL_size_limit_MB=0 --WAL_ttl_seconds=0 --acquire_snapshot_one_in=0 --adaptive_readahead=0 --adm_policy=1 --advise_random_on_open=0 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_fallocate=1 --async_io=0 --auto_readahead_size=0 --avoid_flush_during_recovery=1 --avoid_flush_during_shutdown=1 --avoid_unnecessary_blocking_io=0 --backup_max_size=104857600 --backup_one_in=1000 --batch_protection_bytes_per_key=0 --bgerror_resume_retry_interval=100 --block_align=0 --block_protection_bytes_per_key=8 --block_size=16384 --bloom_before_level=2147483646 --bloom_bits=8.890585558621982 --bottommost_compression_type=zstd --bottommost_file_compaction_delay=0 --bytes_per_sync=0 --cache_index_and_filter_blocks=1 --cache_index_and_filter_blocks_with_high_priority=0 --cache_size=8388608 --cache_type=auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=0 --charge_filter_construction=1 --charge_table_reader=1 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=10000 --checksum_type=kxxHash --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=1000 --compact_range_one_in=0 --compaction_pri=0 --compaction_readahead_size=0 --compaction_ttl=0 --compress_format_version=2 --compressed_secondary_cache_size=16777216 --compression_checksum=1 --compression_max_dict_buffer_bytes=1 --compression_max_dict_bytes=16384 --compression_parallel_threads=1 --compression_type=snappy --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=1 --db_write_buffer_size=1048576 --default_temperature=kCold --default_write_temperature=kCold --delete_obsolete_files_period_micros=21600000000 --delpercent=0 --delrangepercent=0 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_file_deletions_one_in=1000000 --disable_manual_compaction_one_in=1000000 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=1 --enable_do_not_compress_roles=1 --enable_index_compression=1 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=0 --enable_sst_partitioner_factory=0 --enable_thread_tracking=0 --enable_write_thread_adaptive_yield=1 --error_recovery_with_no_fault_injection=0 --exclude_wal_from_write_fault_injection=0 --fail_if_options_file_error=1 --fifo_allow_compaction=1 --file_checksum_impl=xxh64 --fill_cache=0 --flush_one_in=1000000 --format_version=5 --get_all_column_family_metadata_one_in=10000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=1000000 --get_property_one_in=1000000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0 --index_block_restart_interval=1 --index_shortening=0 --index_type=0 --ingest_external_file_one_in=1000 --initial_auto_readahead_size=524288 --inplace_update_support=1 --iterpercent=0 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100  --last_level_temperature=kUnknown --level_compaction_dynamic_level_bytes=1 --lock_wal_one_in=10000 --log2_keys_per_lock=10 --log_file_time_to_roll=60 --log_readahead_size=0 --long_running_snapshots=0 --low_pri_pool_ratio=0 --lowest_used_cache_tier=1 --manifest_preallocation_size=5120 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=10 --max_auto_readahead_size=524288 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=100000 --max_key_len=3 --max_log_file_size=0 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=1 --max_total_wal_size=0 --max_write_batch_group_size_bytes=64 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=8388608 --memtable_insert_hint_per_batch=0 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.01 --memtable_protection_bytes_per_key=2 --memtable_whole_key_filtering=1 --memtablerep=skip_list --metadata_charge_policy=0 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=0 --min_write_buffer_number_to_merge=2 --mmap_read=0 --mock_direct_io=False --nooverwritepercent=1 --num_file_reads_for_auto_readahead=0 --open_files=500000 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=0 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --ops_per_thread=2000 --optimize_filters_for_hits=1 --optimize_filters_for_memory=0 --optimize_multiget_for_io=1 --paranoid_file_checks=1 --partition_filters=0 --partition_pinning=3 --pause_background_one_in=1000000 --periodic_compaction_seconds=0 --prefix_size=5 --prefixpercent=0 --prepopulate_block_cache=0 --preserve_internal_time_seconds=3600 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=1000 --readahead_size=0 --readpercent=0 --recycle_log_file_num=1 --reopen=20 --report_bg_io_stats=0 --reset_stats_one_in=10000 --sample_for_compression=0 --secondary_cache_fault_one_in=0 --secondary_cache_uri= --skip_stats_update_on_db_open=1 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=68719476736 --sqfc_name=bar --sqfc_version=1 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=10 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=1 --subcompactions=1 --sync=0 --sync_fault_injection=0 --table_cache_numshardbits=-1 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=1 --uncache_aggressiveness=3 --universal_max_read_amp=10 --unpartitioned_pinning=1 --use_adaptive_mutex=0 --use_adaptive_mutex_lru=1 --use_attribute_group=1 --use_delta_encoding=1 --use_direct_io_for_flush_and_compaction=0 --use_direct_reads=0 --use_full_merge_v1=1 --use_get_entity=0 --use_merge=0 --use_multi_cf_iterator=1 --use_multi_get_entity=0 --use_multiget=1 --use_put_entity_one_in=0 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=0 --use_write_buffer_manager=1 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000 --verify_compression=1 --verify_db_one_in=100000 --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=none --write_buffer_size=33554432 --write_dbid_to_manifest=1 --write_fault_one_in=10 --writepercent=100
```

Reviewed By: jaykorean

Differential Revision: D59119811

Pulled By: hx235

fbshipit-source-id: bcc3839567b38f939a66aa55d539f2e6a6e94cba
2024-07-01 09:41:09 -07:00
Jay Huh 22fe23edc8 Fix unknown flag "manual_wal_flush" (#12823)
Summary:
- Fix `manual_wal_flush` -> `manual_wal_flush_one_in`
- auto-formatter fixed format in other settings

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

Test Plan: CI

Reviewed By: hx235

Differential Revision: D59177107

Pulled By: jaykorean

fbshipit-source-id: 2400b2822f42299d03e150e3a098c62e7fdaf1f8
2024-06-28 19:51:17 -07:00
Yu Zhang 8c1558a3e0 Add some documentation for Env related interfaces (#12813)
Summary:
As titled. Added some documentations for some `Env` interfaces and removed some obsolete doc for `Options.env`.

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

Reviewed By: ajkr

Differential Revision: D59119632

Pulled By: jowlyzhang

fbshipit-source-id: 114b13f0f843cde6ebc0746156b80c94ea2ce143
2024-06-28 18:56:40 -07:00
Hui Xiao 0b10e7dbba Ignore more non-critical IO error in BlockCacheLookupForReadAheadSize() in crash test (#12822)
Summary:
**Context/Summary:**
Similar to https://github.com/facebook/rocksdb/pull/12814#issue-2376803461

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

Test Plan: CI

Reviewed By: jaykorean

Differential Revision: D59166188

Pulled By: hx235

fbshipit-source-id: 68c2eb7b319103ede0ba34944a0737440aecb17f
2024-06-28 12:01:13 -07:00
HypenZou 5bb7f95ed6 Don't take archived log size into account when calculating log size for flush (#12680)
Summary:
**Context/Summary:**
It seems unreasonable to take the archived log size into account when calculating log size **for flush** in method CreateCheckpoint. If the user sets WAL_ttl_seconds or WAL_size_limit_MB, the argument _log_size_for_flush_ can easily be reached due to the size of the archived dir. As a result, the flush may always be triggered.
**Test**
corverd by ./checkpoint_test

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

Reviewed By: jaykorean

Differential Revision: D59097904

Pulled By: ajkr

fbshipit-source-id: 0ed29c1b078d8f40b85288541b008e00dbc517d3
2024-06-28 11:56:26 -07:00
Hui Xiao aec15eebec Ignore non-critical IO error in BlockCacheLookupForReadAheadSize() in crash test (#12814)
Summary:
**Context/Summary:**

Error in `BlockCacheLookupForReadAheadSize()` is not critical enough to return such error in read path. That's because the worst case is to not have any read ahead. See below comment. https://github.com/facebook/rocksdb/blob/a31fe521732c6150003ea43f1e30f27f13be597c/table/block_based/block_based_table_iterator.cc#L867-L871

Therefore we should allow the read to return ok() even when we inject read error there.

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

Test Plan:
Below command failed with ` Didn't get expected error from PrefixScan` before the fix but passes after

```
./db_stress --WAL_size_limit_MB=0 --WAL_ttl_seconds=60 --acquire_snapshot_one_in=100 --adaptive_readahead=0 --adm_policy=3 --advise_random_on_open=0 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_fallocate=1 --async_io=0 --auto_readahead_size=1 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=1 --avoid_unnecessary_blocking_io=1 --backup_max_size=104857600 --backup_one_in=1000 --batch_protection_bytes_per_key=8 --bgerror_resume_retry_interval=1000000 --block_align=0 --block_protection_bytes_per_key=1 --block_size=16384 --bloom_before_level=5 --bloom_bits=29.31310447925055 --bottommost_compression_type=lz4hc --bottommost_file_compaction_delay=0 --bytes_per_sync=262144 --cache_index_and_filter_blocks=1 --cache_index_and_filter_blocks_with_high_priority=0 --cache_size=8388608 --cache_type=tiered_auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=0 --charge_file_metadata=1 --charge_filter_construction=0 --charge_table_reader=1 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --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=0 --compaction_readahead_size=0 --compaction_ttl=0 --compress_format_version=2 --compressed_secondary_cache_ratio=0.6666666666666666 --compressed_secondary_cache_size=0 --compression_checksum=1 --compression_max_dict_buffer_bytes=0 --compression_max_dict_bytes=0 --compression_parallel_threads=1 --compression_type=lz4 --compression_use_zstd_dict_trainer=0 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=0 --db=/dev/shm/rocksdb_test/rocksdb_crashtest_whitebox --db_write_buffer_size=8388608 --default_temperature=kHot --default_write_temperature=kHot --delete_obsolete_files_period_micros=30000000 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=1 --disable_file_deletions_one_in=10000 --disable_manual_compaction_one_in=10000 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=1 --enable_index_compression=0 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=1 --enable_sst_partitioner_factory=0 --enable_thread_tracking=1 --enable_write_thread_adaptive_yield=1 --error_recovery_with_no_fault_injection=0 --exclude_wal_from_write_fault_injection=0 --expected_values_dir=/dev/shm/rocksdb_test/rocksdb_crashtest_expected --fail_if_options_file_error=0 --fifo_allow_compaction=0 --file_checksum_impl=big --fill_cache=1 --flush_one_in=1000 --format_version=3 --get_all_column_family_metadata_one_in=1000000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=10000 --get_properties_of_all_tables_one_in=100000 --get_property_one_in=1000000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0 --index_block_restart_interval=2 --index_shortening=0 --index_type=2 --ingest_external_file_one_in=1000 --initial_auto_readahead_size=0 --inplace_update_support=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100 --last_level_temperature=kHot --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=1000000 --log2_keys_per_lock=10 --log_file_time_to_roll=60 --log_readahead_size=0 --long_running_snapshots=0 --low_pri_pool_ratio=0 --lowest_used_cache_tier=2 --manifest_preallocation_size=0 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=0 --max_auto_readahead_size=16384 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=100000 --max_key_len=3 --max_log_file_size=0 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=1 --max_total_wal_size=0 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=2097152 --memtable_insert_hint_per_batch=0 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.1 --memtable_protection_bytes_per_key=8 --memtable_whole_key_filtering=1 --memtablerep=skip_list --metadata_charge_policy=0 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=128 --min_write_buffer_number_to_merge=2 --mmap_read=0 --mock_direct_io=True --nooverwritepercent=1 --num_file_reads_for_auto_readahead=1 --open_files=100 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=8 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --ops_per_thread=20000000 --optimize_filters_for_hits=0 --optimize_filters_for_memory=1 --optimize_multiget_for_io=0 --paranoid_file_checks=1 --partition_filters=1 --partition_pinning=2 --pause_background_one_in=10000 --periodic_compaction_seconds=0 --prefix_size=5 --prefixpercent=5 --prepopulate_block_cache=1 --preserve_internal_time_seconds=0 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=32 --read_fault_one_in=1000 --readahead_size=524288 --readpercent=45 --recycle_log_file_num=0 --reopen=20 --report_bg_io_stats=1 --reset_stats_one_in=1000000 --sample_for_compression=5 --secondary_cache_fault_one_in=32 --secondary_cache_uri= --skip_stats_update_on_db_open=1 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=1048576 --sqfc_name=foo --sqfc_version=1 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=600 --stats_history_buffer_size=0 --strict_bytes_per_sync=1 --subcompactions=2 --sync=0 --sync_fault_injection=0 --table_cache_numshardbits=6 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=1 --uncache_aggressiveness=203 --universal_max_read_amp=10 --unpartitioned_pinning=0 --use_adaptive_mutex=1 --use_adaptive_mutex_lru=1 --use_attribute_group=0 --use_delta_encoding=0 --use_direct_io_for_flush_and_compaction=1 --use_direct_reads=0 --use_full_merge_v1=1 --use_get_entity=0 --use_merge=0 --use_multi_cf_iterator=0 --use_multi_get_entity=0 --use_multiget=1 --use_put_entity_one_in=0 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=0 --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_compression=1 --verify_db_one_in=10000 --verify_file_checksums_one_in=1000 --verify_iterator_with_expected_state_one_in=5 --verify_sst_unique_id_in_manifest=1 --wal_bytes_per_sync=0 --wal_compression=none --write_buffer_size=33554432 --write_dbid_to_manifest=0 --write_fault_one_in=1000 --writepercent=35
```

Reviewed By: jaykorean

Differential Revision: D59092430

Pulled By: hx235

fbshipit-source-id: 39558c34461ce92275cae706c33dfd00e6f0ecce
2024-06-26 23:02:28 -07:00
Changyu Bi a31fe52173 Remove the return value of SetBGError() (#12792)
Summary:
the return value for `ErrorHandler::SetBGError(error)` seems to be not well-defined, it can be `bg_error_` (no matter if the `bg_error_` is set to the input error), ok status or [`recovery_error_`](https://github.com/facebook/rocksdb/blob/3ee4d5a11a882056b341a9a1694a71371a39f664/db/error_handler.cc#L669) from `StartRecoverFromRetryableBGIOError()`.  The `recovery_error_` returned may be an OK status.

We have only a few places that use the return value of  `SetBGError()` and they don't need to do so. Using the return value may even be wrong for example in https://github.com/facebook/rocksdb/blob/3ee4d5a11a882056b341a9a1694a71371a39f664/db/db_impl/db_impl_write.cc#L2365 where a non-ok `s` could be overwritten to OK. This PR changes SetBGError() to return void and clean up relevant code.

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

Test Plan: existing unit tests and go over all places where return value of `SetBGError()` is used.

Reviewed By: hx235

Differential Revision: D58904898

Pulled By: cbi42

fbshipit-source-id: d58a20ba5a40e3f35367c6034a32c755088c3653
2024-06-26 18:17:05 -07:00
Hui Xiao 0d93c8a6ca Decouple sync fault and write injection in FaultInjectionTestFS & fix tracing issue under WAL write error injection (#12797)
Summary:
**Context/Summary:**

After injecting write error to WAL, we started to see crash recovery verification failure in prefix recovery. That's because the current tracing implementation traces every write before it writes to WAL even when the WAL write can fail with write error injection. One consequence of that is the traced writes in trace files does not corresponding to write sequence sequence anymore e.g, it has more traced writes that the actual assigned sequence number to successful writes. Therefore https://github.com/facebook/rocksdb/blob/b4a84efb4e842b782e976de5b22a4554c2f76edd/db_stress_tool/expected_state.cc#L674 won't restore the ExpectedState to the correct sequence number we want.

Ideally, we should have a prepare-commit mechanism for tracing just like our ExpectedState so we can ignore the traced write if the write fails later. But for now, to simplify, we simply don't inject WAL error (and metadata write error cuz it could fail write when sync WAL dir fails)

To do so, we need to be able to exclude WAL from write injection but still allow sync fault injection in it to maintain its original sync fault testing coverage. This prompts us to decouple sync fault and write injection in FaultInjectionTestFS. And this is what this PR mainly about.

So now `FaultInjectionTestFS` works as the following:
- If direct_writable is true, then `FaultInjectionTestFS` is bypassed for writable file
- Otherwise, FaultInjectionTestFS` can buffer data for sync fault injection (if inject_unsynced_data_loss_ == true, global settings) and/or inject write error (if MaybeInjectThreadLocalError(), thread-local settings). WAL file can be optionally excluded from write injection

Bonus: better naming of relevant variables

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

Test Plan:
- The follow commands failed before this fix but passes after
```
python3 tools/db_crashtest.py --simple blackbox \
    --interval=5 \
    --preserve_unverified_changes=1 \
    --threads=32 \
    --disable_auto_compactions=1 \
    --WAL_size_limit_MB=0 --WAL_ttl_seconds=0 --acquire_snapshot_one_in=0 --adaptive_readahead=0 --adm_policy=0 --advise_random_on_open=1 --allow_concurrent_memtable_write=0 --allow_data_in_errors=True --allow_fallocate=1 --async_io=0 --auto_readahead_size=0 --avoid_flush_during_recovery=1 --avoid_flush_during_shutdown=0 --avoid_unnecessary_blocking_io=0 --backup_max_size=104857600 --backup_one_in=0 --batch_protection_bytes_per_key=0 --bgerror_resume_retry_interval=1000000 --block_align=0 --block_protection_bytes_per_key=4 --block_size=16384 --bloom_before_level=2147483646 --bloom_bits=3.2003682301518492 --bottommost_compression_type=zlib --bottommost_file_compaction_delay=600 --bytes_per_sync=0 --cache_index_and_filter_blocks=1 --cache_index_and_filter_blocks_with_high_priority=1 --cache_size=33554432 --cache_type=fixed_hyper_clock_cache --charge_compression_dictionary_building_buffer=0 --charge_file_metadata=0 --charge_filter_construction=0 --charge_table_reader=1 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=0 --checksum_type=kxxHash64 --clear_column_family_one_in=0 --column_families=1 --compact_files_one_in=0 --compact_range_one_in=0 --compaction_pri=2 --compaction_readahead_size=0 --compaction_ttl=0 --compress_format_version=1 --compressed_secondary_cache_size=16777216 --compression_checksum=1 --compression_max_dict_buffer_bytes=549755813887 --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 --daily_offpeak_time_utc=00:00-23:59 --data_block_index_type=0 \
    --db_write_buffer_size=0 --delete_obsolete_files_period_micros=0 --delpercent=0 --delrangepercent=0 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_file_deletions_one_in=0 --disable_manual_compaction_one_in=0 --disable_wal=0 --dump_malloc_stats=0 --enable_checksum_handoff=0 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=1 --enable_index_compression=0 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=0 --enable_sst_partitioner_factory=0 --enable_thread_tracking=0 --enable_write_thread_adaptive_yield=0 --error_recovery_with_no_fault_injection=0 --fail_if_options_file_error=0 --fifo_allow_compaction=1 --file_checksum_impl=xxh64 --fill_cache=0 --flush_one_in=100 --format_version=4 --get_all_column_family_metadata_one_in=0 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=0 --get_properties_of_all_tables_one_in=0 --get_property_one_in=0 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0.5 --index_block_restart_interval=9 --index_shortening=1 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=0 --inplace_update_support=0 --iterpercent=0 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=0 --last_level_temperature=kUnknown --level_compaction_dynamic_level_bytes=1 --lock_wal_one_in=0 --log2_keys_per_lock=10 --log_file_time_to_roll=0 --log_readahead_size=16777216 --long_running_snapshots=0 --low_pri_pool_ratio=0 --lowest_used_cache_tier=2 --manifest_preallocation_size=0 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=0 --max_auto_readahead_size=524288 --max_background_compactions=1 --max_bytes_for_level_base=67108864 --max_key=1000 --max_key_len=3 --memtable_insert_hint_per_batch=0 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.5 --memtable_protection_bytes_per_key=8 --memtable_whole_key_filtering=0 --memtablerep=skip_list --metadata_charge_policy=0 --metadata_read_fault_one_in=0 --metadata_write_fault_one_in=0 --min_write_buffer_number_to_merge=1 --mmap_read=0 --mock_direct_io=False --nooverwritepercent=1 --num_file_reads_for_auto_readahead=0 --open_files=-1 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=0 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --ops_per_thread=20000000 \
    --optimize_filters_for_hits=1 --optimize_filters_for_memory=1 --optimize_multiget_for_io=0 --paranoid_file_checks=1 --partition_filters=0 --partition_pinning=3 --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 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=0 --readahead_size=0 --readpercent=0 --recycle_log_file_num=0 --reopen=0 --report_bg_io_stats=0 --reset_stats_one_in=1000000 --sample_for_compression=5 --secondary_cache_fault_one_in=0 --secondary_cache_uri= --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=68719476736 --sqfc_name=bar --sqfc_version=1 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=10 --stats_history_buffer_size=0 --strict_bytes_per_sync=0 --subcompactions=1 --sync=0 --sync_fault_injection=1 --table_cache_numshardbits=0 --target_file_size_base=16777216 --target_file_size_multiplier=1 --test_batches_snapshots=0 --top_level_index_pinning=3 --uncache_aggressiveness=9890 --universal_max_read_amp=-1 --unpartitioned_pinning=3 --use_adaptive_mutex=0 --use_adaptive_mutex_lru=1 --use_attribute_group=0 --use_delta_encoding=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_cf_iterator=0 --use_multi_get_entity=0 --use_multiget=0 --use_put_entity_one_in=0 --use_sqfc_for_range_queries=0 --use_timed_put_one_in=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_compression=1 --verify_db_one_in=0 --verify_file_checksums_one_in=0 --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=335544320 --write_dbid_to_manifest=1 --write_fault_one_in=100 --writepercent=100

```
- CI

Reviewed By: cbi42

Differential Revision: D58917145

Pulled By: hx235

fbshipit-source-id: b6397036bea035a92341c2b05fb01872db2153d7
2024-06-26 14:56:35 -07:00
Hui Xiao 41c6b4549a Revert back to previous ReadAsync error injection (#12811)
Summary:
**Context/Summary:**
https://github.com/facebook/rocksdb/pull/12713 adjusted the error injection in ReadAsync. See original behavior here https://github.com/facebook/rocksdb/blob/71f9e6b5b36e3223e8dba29df75e4e5008818d16/utilities/fault_injection_fs.cc#L456-L484

The PR returns the injected error instead of the ReadAsync() status. It also allows cb to be call in `TestFSRandomAccessFile` layer when ReadAsync() and the cb can called within `FSRandomAccessFile` layer so cb can be double called. It appears to be the root-cause of the following frequent error`AddressSanitizer: heap-use-after-free on rocksdb::RandomAccessFileReader::ReadAsync` though I don't have a confirmed repro yet. Considering this change to mostly revert to previous behavior, it should be safe to proceed anyway.

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

Test Plan: Monitor CI

Reviewed By: jaykorean

Differential Revision: D59067927

Pulled By: hx235

fbshipit-source-id: 8645e5a52d44b7ed2186438f885b4ea13f10b59d
2024-06-26 13:29:10 -07:00
Hui Xiao 58bc4db456 Print more debugging info & further disable backup/restore error inejction (#12812)
Summary:
**Context/Summary:**
Print more info for debugging a TestCheckpoint error; further disable backup/restore error injection as it has not been stabilized with our new thread-local error injection. Will need to enable it separately later.

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

Test Plan: CI

Reviewed By: jaykorean

Differential Revision: D59072678

Pulled By: hx235

fbshipit-source-id: 9481ccf62db952288e7f47ee4b68a34ad0651d5c
2024-06-26 13:09:23 -07:00
Jason Volk 8bf1f6f87f Add info logging via callback to C api. (#12537)
Summary:
I'd like to get this in so the Rust folks can integrate with their splendid logging/tracing frameworks; will be hugely appreciated. 🙏🏻
The infolog capabilities for C embeddings are quite spartan. LOG files were generated involuntarily until redirection to stderr was added by https://github.com/facebook/rocksdb/issues/12262; still insufficient for apps which cannot tolerate pollution of their stdio and tend to have existing logging frameworks to tie into for that.

Adds a very minimal derive of Logger around a C callback, written in the spirit, useful for FFI interfaces from other languages to integrate infolog.

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

Reviewed By: ajkr

Differential Revision: D57597766

Pulled By: cbi42

fbshipit-source-id: ec684ce4ddf77a0a6ebbf013a1bacb4ff2e49eb0
2024-06-26 10:29:05 -07:00
Richard Barnes a06a7fdc88 Remove extra semi colon from internal_repo_rocksdb/repo/util/xxhash.h
Summary:
`-Wextra-semi` or `-Wextra-semi-stmt`

If the code compiles, this is safe to land.

Reviewed By: dmm-fb

Differential Revision: D59007259

fbshipit-source-id: ee0e01e1cc14ebe183d3b74153ef77f11625d983
2024-06-26 07:26:20 -07:00
Hui Xiao 6f79496475 Tag FaultInjectionIOType::kRead for FaultInjectionTestFS new read file & fix unrelease snapshot (#12810)
Summary:
**Context/Summary:** It makes more sense to mark error injection during creation as read file as "kread" so we don't get confusing msg like below
```
stderr:
 error : Get() returns IO error: injected metadata write error for key: 000000000000004F000000000000012B00000000000000EF.
Verification failed :(
```

Also an early return here https://github.com/facebook/rocksdb/blob/e0ddbee76fdc55b1e9f449b6e430b76291268786/db_stress_tool/db_stress_test_base.cc#L2871 can lead to unreleased snapshot upon DB restart `Non-ok close status: Operation aborted: Cannot close DB with unreleased snapshot`. This PR fixed it too.

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

Test Plan: CI

Reviewed By: jowlyzhang

Differential Revision: D59022154

Pulled By: hx235

fbshipit-source-id: 18c489d4692e2eb4fb32937967f57c8a81010cc3
2024-06-25 15:44:07 -07:00
Hui Xiao e0ddbee76f Remove unnecessary injected error logging in crash test (#12807)
Summary:
Context/Summary: as titled, since injected error log isn't that useful for debugging and takes up a lot of console printing space

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

Test Plan: CI

Reviewed By: pdillinger, jowlyzhang

Differential Revision: D58969796

Pulled By: hx235

fbshipit-source-id: 1663fb0779d7a049fc3b101ddefd263be7bdd4b5
2024-06-24 20:51:39 -07:00
Yu Zhang ff204d8ecd Add entry for #12803: fix race between event listener and error handler (#12809)
Summary:
As titled.

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

Reviewed By: hx235

Differential Revision: D58974154

Pulled By: jowlyzhang

fbshipit-source-id: 7e44b54d9fa3bfbd58a4154a2c7e91aec905c34b
2024-06-24 17:01:41 -07:00
Hui Xiao 56f7ef50d7 Fix nullptr access and race to fault_fs_guard (#12799)
Summary:
**Context/Summary:**

There are a couple places where we forgot to check fault_fs_guard before accessing it. So we can see something like this occasionally

```
=138831==Hint: address points to the zero page.
SCARINESS: 10 (null-deref)
AddressSanitizer:DEADLYSIGNAL
    #0 0x18b9e0b in rocksdb::ThreadLocalPtr::Get() const fbcode/internal_repo_rocksdb/repo/util/thread_local.cc:503
    https://github.com/facebook/rocksdb/issues/1 0x83d8b7 in rocksdb::StressTest::TestCompactRange(rocksdb::ThreadState*, long, rocksdb::Slice const&, rocksdb::ColumnFamilyHandle*) fbcode/internal_repo_rocksdb/repo/utilities/fault_injection_fs.h
```
Also accessing of `io_activties_exempted_from_fault_injection.find` not fully synced so we see the following
```
WARNING: ThreadSanitizer: data race (pid=90939)
  Write of size 8 at 0x7b4c000004d0 by thread T762 (mutexes: write M0):
    #0 std::_Rb_tree<rocksdb::Env::IOActivity, rocksdb::Env::IOActivity, std::_Identity<rocksdb::Env::IOActivity>, std::less<rocksdb::Env::IOActivity>, std::allocator<rocksdb::Env::IOActivity>>::operator=(std::_Rb_tree<rocksdb::Env::IOActivity, rocksdb::Env::IOActivity, std::_Identity<rocksdb::Env::IOActivity>, std::less<rocksdb::Env::IOActivity>, std::allocator<rocksdb::Env::IOActivity>> const&) fbcode/third-party-buck/platform010/build/libgcc/include/c++/trunk/bits/stl_tree.h:208 (db_stress+0x411c32) (BuildId: b803e5aca22c6b080defed8e85b7bfec)
    https://github.com/facebook/rocksdb/issues/1 rocksdb::DbStressListener::OnErrorRecoveryCompleted(rocksdb::Status) fbcode/third-party-buck/platform010/build/libgcc/include/c++/trunk/bits/stl_set.h:298 (db_stress+0x4112e5) (BuildId: b803e5aca22c6b080defed8e85b7bfec)
    https://github.com/facebook/rocksdb/issues/2 rocksdb::EventHelpers::NotifyOnErrorRecoveryEnd(std::vector<std::shared_ptr<rocksdb::EventListener>, std::allocator<std::shared_ptr<rocksdb::EventListener>>> const&, rocksdb::Status const&, rocksdb::Status const&, rocksdb::InstrumentedMutex*) fbcode/internal_repo_rocksdb/repo/db/event_helpers.cc:239 (db_stress+0xa09d60) (BuildId: b803e5aca22c6b080defed8e85b7bfec)

  Previous read of size 8 at 0x7b4c000004d0 by thread T131 (mutexes: write M1):
    #0 rocksdb::FaultInjectionTestFS::MaybeInjectThreadLocalError(rocksdb::FaultInjectionIOType, rocksdb::IOOptions const&, rocksdb::FaultInjectionTestFS::ErrorOperation, rocksdb::Slice*, bool, char*, bool, bool*) fbcode/third-party-buck/platform010/build/libgcc/include/c++/trunk/bits/stl_tree.h:798 (db_stress+0xf7d0f3) (BuildId: b803e5aca22c6b080defed8e85b7bfec)
```

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

Test Plan: CI

Reviewed By: jowlyzhang

Differential Revision: D58917449

Pulled By: hx235

fbshipit-source-id: f24fc1acc2a7d91f9f285447a97ba41397f48dbd
2024-06-24 16:10:36 -07:00
Changyu Bi e3f5125ff1 Fix "no new line at end of file" (#12806)
Summary:
internal CI with -Wnewline-eof complains about this.

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

Reviewed By: jowlyzhang

Differential Revision: D58969551

Pulled By: cbi42

fbshipit-source-id: f97bd23f82129bac8dfd97b0ff4dbf7d2ded95d4
2024-06-24 14:50:08 -07:00
Changyu Bi 748f74aca3 Update main branch for 9.4 release (#12802)
Summary:
Main branch cut at e90e9153d5.
Updated HISTORY.md, version and format compatibility test.

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

Reviewed By: ajkr

Differential Revision: D58956464

Pulled By: cbi42

fbshipit-source-id: 50d786c145cebf93d1dd554b1b0e26baac3cc88c
2024-06-24 11:53:05 -07:00
Peter Dillinger 39455974cb Fix possible double-free on TruncatedRangeDelIterator (#12805)
Summary:
Not sure where or how it happens, but using a recent CircleCI failure I got a reliable db_stress reproducer.

Using std::unique_ptr appropriately for managing them has apparently (and unsurprisingly) fixed the problem without needing to know exactly where the problem was.

Suggested follow-up:
* Three or even four levels of pointers is very confusing to work with. Surely this part can be cleaned up to be simpler.

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

Test Plan:
Reproducer passes, plus ASAN test and crash test runs. I don't think it's worth the extra work to track down the details and create a careful unit test.

```
./db_stress --WAL_size_limit_MB=1 --WAL_ttl_seconds=60 --acquire_snapshot_one_in=10000 --adaptive_readahead=1 --adm_policy=2 --advise_random_on_open=1 --allow_data_in_errors=True --allow_fallocate=1 --async_io=0 --auto_readahead_size=1 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=1 --avoid_unnecessary_blocking_io=1 --backup_max_size=104857600 --backup_one_in=100000 --batch_protection_bytes_per_key=0 --bgerror_resume_retry_interval=1000000 --block_align=1 --block_protection_bytes_per_key=4 --block_size=16384 --bloom_before_level=2147483646 --bloom_bits=15 --bottommost_compression_type=none --bottommost_file_compaction_delay=3600 --bytes_per_sync=262144 --cache_index_and_filter_blocks=0 --cache_index_and_filter_blocks_with_high_priority=0 --cache_size=33554432 --cache_type=tiered_lru_cache --charge_compression_dictionary_building_buffer=0 --charge_file_metadata=1 --charge_filter_construction=0 --charge_table_reader=0 --check_multiget_consistency=1 --check_multiget_entity_consistency=1 --checkpoint_one_in=10000 --checksum_type=kxxHash --clear_column_family_one_in=0 --compact_files_one_in=1000000 --compact_range_one_in=1000 --compaction_pri=0 --compaction_readahead_size=0 --compaction_ttl=0 --compress_format_version=2 --compressed_secondary_cache_ratio=0.2 --compressed_secondary_cache_size=0 --compression_checksum=0 --compression_max_dict_buffer_bytes=0 --compression_max_dict_bytes=0 --compression_parallel_threads=1 --compression_type=none --compression_use_zstd_dict_trainer=0 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=0 --db=/dev/shm/rocksdb.gpxs/rocksdb_crashtest_blackbox --db_write_buffer_size=0 --default_temperature=kWarm --default_write_temperature=kCold --delete_obsolete_files_period_micros=21600000000 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_file_deletions_one_in=10000 --disable_manual_compaction_one_in=1000000 --disable_wal=0 --dump_malloc_stats=1 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=0 --enable_index_compression=0 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=1 --enable_sst_partitioner_factory=0 --enable_thread_tracking=1 --enable_write_thread_adaptive_yield=0 --error_recovery_with_no_fault_injection=0 --expected_values_dir=/dev/shm/rocksdb.gpxs/rocksdb_crashtest_expected --fail_if_options_file_error=0 --fifo_allow_compaction=0 --file_checksum_impl=none --fill_cache=1 --flush_one_in=1000000 --format_version=3 --get_all_column_family_metadata_one_in=1000000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=10000 --get_properties_of_all_tables_one_in=100000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0 --index_block_restart_interval=4 --index_shortening=0 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=16384 --inplace_update_support=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100 --last_level_temperature=kHot --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=1000000 --log_file_time_to_roll=0 --log_readahead_size=0 --long_running_snapshots=1 --low_pri_pool_ratio=0 --lowest_used_cache_tier=2 --manifest_preallocation_size=5120 --manual_wal_flush_one_in=1000 --mark_for_compaction_one_file_in=10 --max_auto_readahead_size=16384 --max_background_compactions=20 --max_bytes_for_level_base=10485760 --max_key=2500000 --max_key_len=3 --max_log_file_size=0 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=1 --max_total_wal_size=0 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=3 --max_write_buffer_size_to_maintain=0 --memtable_insert_hint_per_batch=1 --memtable_max_range_deletions=100 --memtable_prefix_bloom_size_ratio=0 --memtable_protection_bytes_per_key=4 --memtable_whole_key_filtering=0 --memtablerep=skip_list --metadata_charge_policy=0 --metadata_read_fault_one_in=32 --metadata_write_fault_one_in=0 --min_write_buffer_number_to_merge=2 --mmap_read=1 --mock_direct_io=False --nooverwritepercent=1 --num_file_reads_for_auto_readahead=0 --open_files=100 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=8 --open_read_fault_one_in=0 --open_write_fault_one_in=16 --ops_per_thread=100000000 --optimize_filters_for_hits=1 --optimize_filters_for_memory=0 --optimize_multiget_for_io=1 --paranoid_file_checks=1 --partition_filters=0 --partition_pinning=1 --pause_background_one_in=1000000 --periodic_compaction_seconds=0 --prefix_size=-1 --prefixpercent=0 --prepopulate_block_cache=1 --preserve_internal_time_seconds=60 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=32 --readahead_size=524288 --readpercent=50 --recycle_log_file_num=1 --reopen=0 --report_bg_io_stats=1 --reset_stats_one_in=10000 --sample_for_compression=5 --secondary_cache_fault_one_in=32 --secondary_cache_uri= --set_options_one_in=10000 --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=68719476736 --sqfc_name=bar --sqfc_version=1 --sst_file_manager_bytes_per_sec=104857600 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=0 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=1 --subcompactions=3 --sync=0 --sync_fault_injection=1 --table_cache_numshardbits=0 --target_file_size_base=524288 --target_file_size_multiplier=2 --test_batches_snapshots=0 --test_cf_consistency=1 --top_level_index_pinning=1 --uncache_aggressiveness=5 --universal_max_read_amp=-1 --unpartitioned_pinning=2 --use_adaptive_mutex=0 --use_adaptive_mutex_lru=0 --use_attribute_group=1 --use_delta_encoding=1 --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_cf_iterator=0 --use_multi_get_entity=0 --use_multiget=1 --use_put_entity_one_in=1 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=0 --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_compression=1 --verify_db_one_in=100000 --verify_file_checksums_one_in=0 --verify_iterator_with_expected_state_one_in=0 --verify_sst_unique_id_in_manifest=1 --wal_bytes_per_sync=0 --wal_compression=none --write_buffer_size=1048576 --write_dbid_to_manifest=1 --write_fault_one_in=0 --writepercent=35
```

Reviewed By: cbi42

Differential Revision: D58958390

Pulled By: pdillinger

fbshipit-source-id: 1271cfdcc3c574f78cd59f3c68148f7ed4a19c47
2024-06-24 11:51:16 -07:00
Yu Zhang fa4ffc816c Fix race condition between event listener and error handler (#12803)
Summary:
Fix a race for accessing `bg_error_` after mutex is released. We make some copies before releasing to avoid this.

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

Reviewed By: cbi42

Differential Revision: D58957557

Pulled By: jowlyzhang

fbshipit-source-id: 3c7369a3b8c8707aebc0044ff98288c898c05cb8
2024-06-24 11:45:28 -07:00
Andrew Kryczka 13549817af Update pinned folly version (#12801)
Summary:
https://github.com/facebook/folly/commit/843fd576793d91c4c55fa3495b1694e5d708c54b fixed the URL for libsodium. Updated folly version to latest, which includes that commit. I am not sure the URL will be stable, but it still seems better than substituting the URL.

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

Reviewed By: cbi42

Differential Revision: D58921033

Pulled By: ajkr

fbshipit-source-id: 442ea3ff83ced2679ea9bfd04945e9449ce2ff96
2024-06-24 10:46:29 -07:00
Richard Barnes 5c391c7713 Fix deprecated dynamic exception in infra_asic_fpga/validation/freya/bmtc/ssp/src/gcc-arm-none-eabi-9-2019-q4-major/arm-none-eabi/include/c++/9.2.1/tr1/special_function_util.h +5
Summary:
LLVM has detected a violation of `-Wdeprecated-dynamic-exception-spec`. Dynamic exceptions were removed in C++17. This diff fixes the deprecated instance(s).

See [Dynamic exception specification](https://en.cppreference.com/w/cpp/language/except_spec) and [noexcept specifier](https://en.cppreference.com/w/cpp/language/noexcept_spec).

Reviewed By: dmm-fb

Differential Revision: D58953050

fbshipit-source-id: 8559dc925ea5ed0a6dbf938aa02ee810f01047ae
2024-06-24 09:30:20 -07:00
89 changed files with 2145 additions and 864 deletions

No files matched your search

+27 -1
View File
@@ -1,10 +1,36 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 9.4.1 (08/02/2024)
## 9.5.2 (08/13/2024)
### Bug Fixes
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
### Public API Changes
* Add ticker stats to count file read retries due to checksum mismatch
## 9.5.1 (08/02/2024)
### Bug Fixes
* *Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
## 9.5.0 (07/19/2024)
### Public API Changes
* Introduced new C API function rocksdb_writebatch_iterate_cf for column family-aware iteration over the contents of a WriteBatch
* Add support to ingest SST files generated by a DB instead of SstFileWriter. This can be enabled with experimental option `IngestExternalFileOptions::allow_db_generated_files`.
### Behavior Changes
* When calculating total log size for the `log_size_for_flush` argument in `CreateCheckpoint` API, the size of the archived log will not be included to avoid unnecessary flush
### Bug Fixes
* Fix a major bug in which an iterator using prefix filtering and SeekForPrev might miss data when the DB is using `whole_key_filtering=false` and `partition_filters=true`.
* Fixed a bug where `OnErrorRecoveryBegin()` is not called before auto recovery starts.
* Fixed a bug where event listener reads ErrorHandler's `bg_error_` member without holding db mutex(#12803).
* Fixed a bug in handling MANIFEST write error that caused the latest valid MANIFEST file to get deleted, resulting in the DB being unopenable.
* Fixed a race between error recovery due to manifest sync or write failure and external SST file ingestion. Both attempt to write a new manifest file, which causes an assertion failure.
### Performance Improvements
* Fix an issue where compactions were opening table files and reading table properties while holding db mutex_.
* Reduce unnecessary filesystem queries and DB mutex acquires in creating backups and checkpoints.
## 9.4.0 (06/23/2024)
### New Features
* Added a `CompactForTieringCollectorFactory` to auto trigger compaction for tiering use case.
+4 -6
View File
@@ -2139,8 +2139,8 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
SNAPPY_VER ?= 1.1.8
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
SNAPPY_VER ?= 1.2.1
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
LZ4_VER ?= 1.9.4
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
@@ -2237,7 +2237,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
-rm -rf snappy-$(SNAPPY_VER)
tar xvzf snappy-$(SNAPPY_VER).tar.gz
mkdir snappy-$(SNAPPY_VER)/build
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSNAPPY_BUILD_BENCHMARKS=OFF -DSNAPPY_BUILD_TESTS=OFF --compile-no-warning-as-error ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
lz4-$(LZ4_VER).tar.gz:
@@ -2484,13 +2484,11 @@ checkout_folly:
fi
@# Pin to a particular version for public CI, so that PR authors don't
@# need to worry about folly breaking our integration. Update periodically
cd third-party/folly && git reset --hard 17054c85c2dee0e84cbee7ef28d7bae7d0b1630e
cd third-party/folly && git reset --hard c48fdd205c1c291651749d532b8055fe822bba25
@# NOTE: this hack is required for clang in some cases
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
@# NOTE: this hack is required for gcc in some cases
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
@# NOTE: this hack is only needed if building target build_folly in the future
perl -pi -e 's,https://github.com/jedisct1/libsodium/releases/download/1.0.17/libsodium-1.0.17.tar.gz,https://download.libsodium.org/libsodium/releases/old/unsupported/libsodium-1.0.17.tar.gz,' third-party/folly/build/fbcode_builder/manifests/libsodium
@# NOTE: boost source will be needed for any build including `USE_FOLLY_LITE` builds as those depend on boost headers
cd third-party/folly && $(PYTHON) build/fbcode_builder/getdeps.py fetch boost
+4 -4
View File
@@ -138,10 +138,10 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
reinit_internal_iter();
break;
} else {
delete *memtable_range_tombstone_iter_;
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
&cfd->internal_comparator(), nullptr, nullptr);
*memtable_range_tombstone_iter_ =
std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
&cfd->internal_comparator(), nullptr, nullptr);
}
}
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
+4 -2
View File
@@ -55,7 +55,8 @@ class ArenaWrappedDBIter : public Iterator {
db_iter_->SetIter(iter);
}
void SetMemtableRangetombstoneIter(TruncatedRangeDelIterator** iter) {
void SetMemtableRangetombstoneIter(
std::unique_ptr<TruncatedRangeDelIterator>* iter) {
memtable_range_tombstone_iter_ = iter;
}
@@ -110,7 +111,8 @@ class ArenaWrappedDBIter : public Iterator {
bool allow_refresh_ = true;
// If this is nullptr, it means the mutable memtable does not contain range
// tombstone when added under this DBIter.
TruncatedRangeDelIterator** memtable_range_tombstone_iter_ = nullptr;
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
nullptr;
};
// Generate the arena wrapped iterator class.
+106
View File
@@ -532,6 +532,42 @@ struct rocksdb_universal_compaction_options_t {
ROCKSDB_NAMESPACE::CompactionOptionsUniversal* rep;
};
struct rocksdb_callback_logger_t : public Logger {
static const ssize_t STACK_BUFSZ = 512;
rocksdb_callback_logger_t(InfoLogLevel log_level,
void (*logv_cb)(void*, unsigned, char*, size_t),
void* priv)
: Logger(log_level), logv_cb_(logv_cb), priv_(priv) {}
using Logger::Logv;
void Logv(const InfoLogLevel level, const char* fmt, va_list ap0) override {
char stack_buf[STACK_BUFSZ];
char* alloc_buf = nullptr;
char* buf = stack_buf;
int len = 0;
va_list ap1;
if (!logv_cb_) return;
va_copy(ap1, ap0);
len = vsnprintf(buf, STACK_BUFSZ, fmt, ap0);
if (len <= 0)
goto cleanup;
else if (len >= STACK_BUFSZ) {
buf = alloc_buf = reinterpret_cast<char*>(malloc(len + 1));
if (!buf) goto cleanup;
len = vsnprintf(buf, len + 1, fmt, ap1);
if (len <= 0) goto cleanup;
}
logv_cb_(priv_, unsigned(level), buf, size_t(len));
cleanup:
va_end(ap1);
free(alloc_buf);
}
private:
void (*logv_cb_)(void*, unsigned, char*, size_t) = nullptr;
void* priv_ = nullptr;
};
static bool SaveError(char** errptr, const Status& s) {
assert(errptr != nullptr);
if (s.ok()) {
@@ -1802,6 +1838,26 @@ void rocksdb_approximate_sizes_cf(
delete[] ranges;
}
extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
int num_ranges, const char* const* range_start_key,
const size_t* range_start_key_len, const char* const* range_limit_key,
const size_t* range_limit_key_len, uint8_t include_flags, uint64_t* sizes,
char** errptr) {
Range* ranges = new Range[num_ranges];
for (int i = 0; i < num_ranges; i++) {
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
}
Status s = db->rep->GetApproximateSizes(
column_family->rep, ranges, num_ranges, sizes,
static_cast<DB::SizeApproximationFlags>(include_flags));
if (!s.ok()) {
SaveError(errptr, s);
}
delete[] ranges;
}
void rocksdb_delete_file(rocksdb_t* db, const char* name) {
db->rep->DeleteFile(name);
}
@@ -2230,6 +2286,32 @@ class H : public WriteBatch::Handler {
}
};
class HCF : public WriteBatch::Handler {
public:
void* state_;
void (*put_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen);
void (*deleted_cf_)(void*, uint32_t cfid, const char* k, size_t klen);
void (*merge_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen);
Status PutCF(uint32_t column_family_id, const Slice& key,
const Slice& value) override {
(*put_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
value.size());
return Status::OK();
}
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
(*deleted_cf_)(state_, column_family_id, key.data(), key.size());
return Status::OK();
}
Status MergeCF(uint32_t column_family_id, const Slice& key,
const Slice& value) override {
(*merge_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
value.size());
return Status::OK();
}
};
void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
void (*put)(void*, const char* k, size_t klen,
const char* v, size_t vlen),
@@ -2242,6 +2324,21 @@ void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
b->rep.Iterate(&handler);
}
void rocksdb_writebatch_iterate_cf(
rocksdb_writebatch_t* b, void* state,
void (*put_cf)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen),
void (*deleted_cf)(void*, uint32_t cfid, const char* k, size_t klen),
void (*merge_cf)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen)) {
HCF handler;
handler.state_ = state;
handler.put_cf_ = put_cf;
handler.deleted_cf_ = deleted_cf;
handler.merge_cf_ = merge_cf;
b->rep.Iterate(&handler);
}
const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
*size = b->rep.GetDataSize();
return b->rep.Data().c_str();
@@ -3037,6 +3134,15 @@ rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
return logger;
}
rocksdb_logger_t* rocksdb_logger_create_callback_logger(
int log_level, void (*callback)(void*, unsigned, char*, size_t),
void* priv) {
rocksdb_logger_t* logger = new rocksdb_logger_t;
logger->rep = std::make_shared<rocksdb_callback_logger_t>(
static_cast<InfoLogLevel>(log_level), callback, priv);
return logger;
}
void rocksdb_logger_destroy(rocksdb_logger_t* logger) { delete logger; }
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
+88
View File
@@ -199,6 +199,79 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
(*state)++;
}
// Callback from rocksdb_writebatch_iterate_cf()
static void CheckPutCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen) {
int* state = (int*)ptr;
switch (*state) {
case 0:
CheckEqual("bar", k, klen);
CheckEqual("b", v, vlen);
CheckCondition(cfid == 1);
break;
case 1:
CheckEqual("box", k, klen);
CheckEqual("c", v, vlen);
CheckCondition(cfid == 1);
break;
case 4:
CheckEqual("foo", k, klen);
CheckEqual("f", v, vlen);
CheckCondition(cfid == 0);
break;
case 6:
CheckEqual("baz", k, klen);
CheckEqual("a", v, vlen);
CheckCondition(cfid == 0);
break;
default:
CheckCondition(false);
break;
}
(*state)++;
}
// Callback from rocksdb_writebatch_iterate_cf()
static void CheckDelCF(void* ptr, uint32_t cfid, const char* k, size_t klen) {
int* state = (int*)ptr;
switch (*state) {
case 2:
CheckEqual("bar", k, klen);
CheckCondition(cfid == 1);
break;
case 5:
CheckEqual("foo", k, klen);
CheckCondition(cfid == 0);
break;
default:
CheckCondition(false);
break;
}
(*state)++;
}
// Callback from rocksdb_writebatch_iterate_cf()
static void CheckMergeCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen) {
int* state = (int*)ptr;
switch (*state) {
case 3:
CheckEqual("box", k, klen);
CheckEqual("cc", v, vlen);
CheckCondition(cfid == 1);
break;
case 7:
CheckEqual("baz", k, klen);
CheckEqual("aa", v, vlen);
CheckCondition(cfid == 0);
break;
default:
CheckCondition(false);
break;
}
(*state)++;
}
static void CmpDestroy(void* arg) { (void)arg; }
static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
@@ -1688,6 +1761,21 @@ int main(int argc, char** argv) {
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
CheckPinGetCF(db, roptions, handles[1], "box", "c");
CheckPinGetCF(db, roptions, handles[1], "buff", "rocksdb");
rocksdb_writebatch_clear(wb);
// Test WriteBatch iteration with Column Family
int pos = 0;
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
rocksdb_writebatch_merge_cf(wb, handles[1], "box", 3, "cc", 2);
rocksdb_writebatch_put(wb, "foo", 3, "f", 1);
rocksdb_writebatch_delete(wb, "foo", 3);
rocksdb_writebatch_put(wb, "baz", 3, "a", 1);
rocksdb_writebatch_merge(wb, "baz", 3, "aa", 2);
rocksdb_writebatch_iterate_cf(wb, &pos, CheckPutCF, CheckDelCF,
CheckMergeCF);
CheckCondition(pos == 8);
rocksdb_writebatch_clear(wb);
rocksdb_writebatch_destroy(wb);
rocksdb_flush_wal(db, 1, &err);
+8 -1
View File
@@ -63,7 +63,9 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
return sum;
}
void Compaction::SetInputVersion(Version* _input_version) {
// TODO(hx235): consider making this function part of the construction so we
// don't forget to call it
void Compaction::FinalizeInputInfo(Version* _input_version) {
input_version_ = _input_version;
cfd_ = input_version_->cfd();
@@ -864,6 +866,11 @@ bool Compaction::ShouldFormSubcompactions() const {
return false;
}
if (cfd_->ioptions()->table_factory->Name() ==
TableFactory::kPlainTableName()) {
return false;
}
// Round-Robin pri under leveled compaction allows subcompactions by default
// and the number of subcompactions can be larger than max_subcompactions_
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
+11 -9
View File
@@ -289,14 +289,7 @@ class Compaction {
// is the sum of all input file sizes.
uint64_t OutputFilePreallocationSize() const;
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
// status and fail the compaction if needed
// TODO(hx235): consider making this function part of the construction so we
// don't forget to call it
void FinalizeInputInfo(Version* input_version) {
SetInputVersion(input_version);
InitInputTableProperties().PermitUncheckedError();
}
void FinalizeInputInfo(Version* input_version);
struct InputLevelSummaryBuffer {
char buffer[128];
@@ -333,6 +326,16 @@ class Compaction {
int output_level, VersionStorageInfo* vstorage,
const std::vector<CompactionInputFiles>& inputs);
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
// status and fail the compaction if needed
//
// May open and read table files for table property.
// Should not be called while holding mutex_.
const TablePropertiesCollection& GetOrInitInputTableProperties() {
InitInputTableProperties().PermitUncheckedError();
return input_table_properties_;
}
const TablePropertiesCollection& GetInputTableProperties() const {
return input_table_properties_;
}
@@ -433,7 +436,6 @@ class Compaction {
const int output_level);
private:
void SetInputVersion(Version* input_version);
Status InitInputTableProperties();
+6
View File
@@ -468,6 +468,11 @@ void CompactionJob::GenSubcompactionBoundaries() {
ReadOptions read_options(Env::IOActivity::kCompaction);
read_options.rate_limiter_priority = GetRateLimiterPriority();
auto* c = compact_->compaction;
if (c->immutable_options()->table_factory->Name() ==
TableFactory::kPlainTableName()) {
return;
}
if (c->max_subcompactions() <= 1 &&
!(c->immutable_options()->compaction_pri == kRoundRobin &&
c->immutable_options()->compaction_style == kCompactionStyleLevel)) {
@@ -621,6 +626,7 @@ Status CompactionJob::Run() {
const size_t num_threads = compact_->sub_compact_states.size();
assert(num_threads > 0);
const uint64_t start_micros = db_options_.clock->NowMicros();
compact_->compaction->GetOrInitInputTableProperties();
// Launch a thread for each of subcompactions 1...num_threads-1
std::vector<port::Thread> thread_pool;
+2
View File
@@ -275,6 +275,8 @@ Status CompactionServiceCompactionJob::Run() {
log_buffer_->FlushBufferToLog();
LogCompaction();
const uint64_t start_micros = db_options_.clock->NowMicros();
c->GetOrInitInputTableProperties();
// Pick the only sub-compaction we should have
assert(compact_->sub_compact_states.size() == 1);
SubcompactionState* sub_compact = compact_->sub_compact_states.data();
+107 -43
View File
@@ -322,6 +322,67 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
}
}
TEST_F(DBBloomFilterTest, FilterNumEntriesCoalesce) {
for (bool partition_filters : {true, false}) {
SCOPED_TRACE("partition_filters=" + std::to_string(partition_filters));
for (bool prefix : {true, false}) {
SCOPED_TRACE("prefix=" + std::to_string(prefix));
for (bool whole : {true, false}) {
SCOPED_TRACE("whole=" + std::to_string(whole));
Options options = last_options_;
options.prefix_extractor.reset();
if (prefix) {
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
}
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
bbto.whole_key_filtering = whole;
if (partition_filters) {
bbto.partition_filters = true;
bbto.index_type =
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
}
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
// Need a snapshot to allow keeping multiple entries for the same key
std::vector<const Snapshot*> snapshots;
for (int i = 1; i <= 3; ++i) {
std::string val = "val" + std::to_string(i);
ASSERT_OK(Put("foo1", val));
ASSERT_OK(Put("foo2", val));
ASSERT_OK(Put("bar1", val));
ASSERT_OK(Put("bar2", val));
ASSERT_OK(Put("bar3", val));
snapshots.push_back(db_->GetSnapshot());
}
ASSERT_OK(Flush());
TablePropertiesCollection tpc;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tpc));
// sanity checks
ASSERT_EQ(tpc.size(), 1U);
auto& tp = *tpc.begin()->second;
EXPECT_EQ(tp.num_entries, 3U * 5U);
// test checks
unsigned ex_filter_entries = 0;
if (whole) {
ex_filter_entries += 5; // unique keys
}
if (prefix) {
ex_filter_entries += 2; // unique prefixes
}
EXPECT_EQ(tp.num_filter_entries, ex_filter_entries);
for (auto* sn : snapshots) {
db_->ReleaseSnapshot(sn);
}
}
}
}
}
TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
for (bool partition_filters : {true, false}) {
Options options = last_options_;
@@ -3143,52 +3204,55 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
}
TEST_F(DBBloomFilterTest, SeekForPrevWithPartitionedFilters) {
Options options = CurrentOptions();
constexpr size_t kNumKeys = 10000;
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeys + 10));
options.create_if_missing = true;
constexpr size_t kPrefixLength = 4;
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixLength));
options.compression = kNoCompression;
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(50));
bbto.index_shortening =
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
bbto.block_size = 128;
bbto.metadata_block_size = 128;
bbto.partition_filters = true;
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
for (bool wkf : {true, false}) {
SCOPED_TRACE("whole_key_filtering=" + std::to_string(wkf));
Options options = CurrentOptions();
constexpr size_t kNumKeys = 10000;
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeys + 10));
options.create_if_missing = true;
constexpr size_t kPrefixLength = 4;
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixLength));
options.compression = kNoCompression;
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(50));
bbto.index_shortening =
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
bbto.block_size = 128;
bbto.metadata_block_size = 128;
bbto.partition_filters = true;
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
bbto.whole_key_filtering = wkf;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
const std::string value(64, '\0');
const std::string value(64, '\0');
WriteOptions write_opts;
write_opts.disableWAL = true;
for (size_t i = 0; i < kNumKeys; ++i) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i;
ASSERT_OK(db_->Put(write_opts, oss.str(), value));
WriteOptions write_opts;
write_opts.disableWAL = true;
for (size_t i = 0; i < kNumKeys; ++i) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i;
ASSERT_OK(db_->Put(write_opts, oss.str(), value));
}
ASSERT_OK(Flush());
ReadOptions read_opts;
// Use legacy, implicit prefix seek
read_opts.total_order_seek = false;
read_opts.auto_prefix_mode = false;
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
for (size_t i = 0; i < kNumKeys; ++i) {
// Seek with a key after each one added but with same prefix. One will
// surely cross a partition boundary.
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i << "a";
it->SeekForPrev(oss.str());
ASSERT_OK(it->status());
ASSERT_TRUE(it->Valid());
}
}
ASSERT_OK(Flush());
ReadOptions read_opts;
// Use legacy, implicit prefix seek
read_opts.total_order_seek = false;
read_opts.auto_prefix_mode = false;
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
for (size_t i = 0; i < kNumKeys; ++i) {
// Seek with a key after each one added but with same prefix. One will
// surely cross a partition boundary.
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i << "a";
it->SeekForPrev(oss.str());
ASSERT_OK(it->status());
ASSERT_TRUE(it->Valid());
}
it.reset();
}
namespace {
+19 -3
View File
@@ -21,6 +21,7 @@
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/metadata.h"
#include "rocksdb/transaction_log.h"
#include "rocksdb/types.h"
#include "test_util/sync_point.h"
#include "util/file_checksum_helper.h"
@@ -92,6 +93,11 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
}
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
return GetSortedWalFilesImpl(files,
/*need_seqnos*/ true);
}
Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
// Record tracked WALs as a (minimum) cross-check for directory scan
std::vector<uint64_t> required_by_manifest;
@@ -117,7 +123,10 @@ Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
}
}
Status s = wal_manager_.GetSortedWalFiles(files);
// NOTE: need to include archived WALs because needed WALs might have been
// archived since getting required_by_manifest set
Status s = wal_manager_.GetSortedWalFiles(files, need_seqnos,
/*include_archived*/ true);
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
if (deletions_disabled.ok()) {
@@ -206,7 +215,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
} else if (opts.wal_size_for_flush > 0) {
// FIXME: avoid querying the filesystem for current WAL state
// If the outstanding WAL files are small, we skip the flush.
s = GetSortedWalFiles(live_wal_files);
// Don't take archived log size into account when calculating wal
// size for flush, and don't need to verify consistency with manifest
// here & now.
s = wal_manager_.GetSortedWalFiles(live_wal_files,
/* need_seqnos */ false,
/*include_archived*/ false);
if (!s.ok()) {
return s;
@@ -217,6 +231,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
// We may be able to cover 2PC case too.
uint64_t total_wal_size = 0;
for (auto& wal : live_wal_files) {
assert(wal->Type() == kAliveLogFile);
total_wal_size += wal->SizeFileBytes();
}
if (total_wal_size < opts.wal_size_for_flush) {
@@ -428,7 +443,8 @@ Status DBImpl::GetLiveFilesStorageInfo(
// WAL files.
if (s.ok()) {
// FIXME: avoid querying the filesystem for current WAL state
s = GetSortedWalFiles(live_wal_files);
s = GetSortedWalFilesImpl(live_wal_files,
/* need_seqnos */ false);
}
if (!s.ok()) {
return s;
+22 -10
View File
@@ -392,8 +392,8 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
if (!s.ok()) {
io_s = versions_->io_status();
if (!io_s.ok()) {
s = error_handler_.SetBGError(io_s,
BackgroundErrorReason::kManifestWrite);
error_handler_.SetBGError(io_s,
BackgroundErrorReason::kManifestWrite);
}
}
}
@@ -917,8 +917,8 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
if (!s.ok() && versions_->io_status().IsIOError()) {
s = error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
}
@@ -1725,8 +1725,8 @@ Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
read_options, write_options, synced_wals, &mutex_,
directories_.GetDbDir());
if (!status.ok() && versions_->io_status().IsIOError()) {
status = error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
return status;
}
@@ -2065,19 +2065,19 @@ InternalIterator* DBImpl::NewInternalIterator(
read_options, super_version->GetSeqnoToTimeMapping(), arena);
Status s;
if (!read_options.ignore_range_deletions) {
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
auto range_del_iter = super_version->mem->NewRangeTombstoneIterator(
read_options, sequence, false /* immutable_memtable */);
if (range_del_iter == nullptr || range_del_iter->empty()) {
delete range_del_iter;
} else {
mem_tombstone_iter = new TruncatedRangeDelIterator(
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
&cfd->ioptions()->internal_comparator, nullptr /* smallest */,
nullptr /* largest */);
}
merge_iter_builder.AddPointAndTombstoneIterator(mem_iter,
mem_tombstone_iter);
merge_iter_builder.AddPointAndTombstoneIterator(
mem_iter, std::move(mem_tombstone_iter));
} else {
merge_iter_builder.AddIterator(mem_iter);
}
@@ -5828,6 +5828,18 @@ Status DBImpl::IngestExternalFiles(
"timestamps enabled doesn't support ingest behind.");
}
}
if (ingest_opts.allow_db_generated_files) {
if (ingest_opts.write_global_seqno) {
return Status::NotSupported(
"write_global_seqno is deprecated and does not work with "
"allow_db_generated_files.");
}
if (ingest_opts.move_files) {
return Status::NotSupported(
"Options move_files and allow_db_generated_files are not "
"compatible.");
}
}
}
// TODO (yanqin) maybe handle the case in which column_families have
+5
View File
@@ -508,6 +508,8 @@ class DBImpl : public DB {
Status GetLiveFiles(std::vector<std::string>&, uint64_t* manifest_file_size,
bool flush_memtable = true) override;
Status GetSortedWalFiles(VectorWalPtr& files) override;
Status GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos);
// Get the known flushed sizes of WALs that might still be written to
// or have pending sync.
// NOTE: unlike alive_log_files_, this function includes WALs that might
@@ -2348,6 +2350,9 @@ class DBImpl : public DB {
bool HaveManualCompaction(ColumnFamilyData* cfd);
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
void UpdateDeletionCompactionStats(const std::unique_ptr<Compaction>& c);
// May open and read table files for table property.
// Should not be called while holding mutex_.
void BuildCompactionJobInfo(const ColumnFamilyData* cfd, Compaction* c,
const Status& st,
const CompactionJobStats& compaction_job_stats,
+3 -1
View File
@@ -1595,10 +1595,12 @@ Status DBImpl::CompactFilesImpl(
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
mutex_.Unlock();
if (compaction_job_info != nullptr) {
BuildCompactionJobInfo(cfd, c.get(), s, compaction_job_stats,
job_context->job_id, compaction_job_info);
}
mutex_.Lock();
if (status.ok()) {
// Done
@@ -4161,7 +4163,7 @@ void DBImpl::BuildCompactionJobInfo(
compaction_job_info->base_input_level = c->start_level();
compaction_job_info->output_level = c->output_level();
compaction_job_info->stats = compaction_job_stats;
const auto& input_table_properties = c->GetInputTableProperties();
const auto& input_table_properties = c->GetOrInitInputTableProperties();
const auto& output_table_properties = c->GetOutputTableProperties();
compaction_job_info->table_properties.insert(input_table_properties.begin(),
input_table_properties.end());
+7 -1
View File
@@ -530,6 +530,12 @@ Status DBImpl::Recover(
/*no_error_if_files_missing=*/false, is_retry,
&desc_status);
desc_status.PermitUncheckedError();
if (is_retry) {
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_COUNT);
if (desc_status.ok()) {
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
}
}
if (can_retry) {
// If we're opening for the first time and the failure is likely due to
// a corrupt MANIFEST file (could result in either the log::Reader
@@ -640,7 +646,7 @@ Status DBImpl::Recover(
f->fd.smallest_seqno, f->fd.largest_seqno,
f->marked_for_compaction,
f->temperature, // this can be different from
// `last_level_temperature`
// `last_level_temperature`
f->oldest_blob_file_number, f->oldest_ancester_time,
f->file_creation_time, f->epoch_number,
f->file_checksum, f->file_checksum_func_name,
+32 -32
View File
@@ -556,6 +556,35 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
const SequenceNumber current_sequence = last_sequence + 1;
last_sequence += seq_inc;
if (log_context.need_log_sync) {
VersionEdit synced_wals;
log_write_mutex_.Lock();
if (status.ok()) {
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
&synced_wals);
} else {
MarkLogsNotSynced(logfile_number_);
}
log_write_mutex_.Unlock();
if (status.ok() && synced_wals.IsWalAddition()) {
InstrumentedMutexLock l(&mutex_);
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
}
// Requesting sync with two_write_queues_ is expected to be very rare. We
// hence provide a simple implementation that is not necessarily
// efficient.
if (status.ok() && two_write_queues_) {
if (manual_wal_flush_) {
status = FlushWAL(true);
} else {
status = SyncWAL();
}
}
}
// PreReleaseCallback is called after WAL write and before memtable write
if (status.ok()) {
SequenceNumber next_sequence = current_sequence;
@@ -639,34 +668,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
assert(pre_release_cb_status.ok());
}
if (log_context.need_log_sync) {
VersionEdit synced_wals;
log_write_mutex_.Lock();
if (status.ok()) {
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
&synced_wals);
} else {
MarkLogsNotSynced(logfile_number_);
}
log_write_mutex_.Unlock();
if (status.ok() && synced_wals.IsWalAddition()) {
InstrumentedMutexLock l(&mutex_);
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
}
// Requesting sync with two_write_queues_ is expected to be very rare. We
// hence provide a simple implementation that is not necessarily efficient.
if (two_write_queues_) {
if (manual_wal_flush_) {
status = FlushWAL(true);
} else {
status = SyncWAL();
}
}
}
bool should_exit_batch_group = true;
if (in_parallel_group) {
// CompleteParallelWorker returns true if this thread should
@@ -1204,8 +1205,7 @@ void DBImpl::MemTableInsertStatusCheck(const Status& status) {
mutex_.Lock();
assert(!error_handler_.IsBGWorkStopped());
// Maybe change the return status to void?
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable)
.PermitUncheckedError();
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
mutex_.Unlock();
}
}
@@ -2363,8 +2363,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
read_options, write_options, &wal_deletion, &mutex_,
directories_.GetDbDir());
if (!s.ok() && versions_->io_status().IsIOError()) {
s = error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
if (!s.ok()) {
return s;
+27
View File
@@ -705,6 +705,7 @@ class DBIOCorruptionTest
DBIOCorruptionTest() : DBIOFailureTest() {
BlockBasedTableOptions bbto;
options_ = CurrentOptions();
options_.statistics = CreateDBStatistics();
base_env_ = env_;
EXPECT_NE(base_env_, nullptr);
@@ -727,6 +728,8 @@ class DBIOCorruptionTest
Status ReopenDB() { return TryReopen(options_); }
Statistics* stats() { return options_.statistics.get(); }
protected:
std::unique_ptr<Env> env_guard_;
std::shared_ptr<CorruptionFS> fs_;
@@ -749,8 +752,12 @@ TEST_P(DBIOCorruptionTest, GetReadCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_EQ(val, "val1");
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_TRUE(s.IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -773,8 +780,12 @@ TEST_P(DBIOCorruptionTest, IterReadCorruptionRetry) {
}
if (std::get<2>(GetParam())) {
ASSERT_OK(iter->status());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_TRUE(iter->status().IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
delete iter;
}
@@ -799,9 +810,13 @@ TEST_P(DBIOCorruptionTest, MultiGetReadCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_EQ(values[0].ToString(), "val1");
ASSERT_EQ(values[1].ToString(), "val2");
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_TRUE(statuses[0].IsCorruption());
ASSERT_TRUE(statuses[1].IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -818,6 +833,9 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
std::string val;
ReadOptions ro;
@@ -826,6 +844,7 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
ASSERT_EQ(val, "val1");
} else {
ASSERT_TRUE(s.IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -838,6 +857,9 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
Status s = Flush();
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
std::string val;
ReadOptions ro;
@@ -846,6 +868,7 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
ASSERT_EQ(val, "val1");
} else {
ASSERT_NOK(s);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -862,8 +885,12 @@ TEST_P(DBIOCorruptionTest, ManifestCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_OK(ReopenDB());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_EQ(ReopenDB(), Status::Corruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
SyncPoint::GetInstance()->DisableProcessing();
}
+6
View File
@@ -245,7 +245,13 @@ TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
// In DirectIO, where we support tail prefetching, during table open, we only
// do 1 read instead of 4 as described above. Actual checksum verification
// reads stay the same.
#ifdef OS_WIN
// No file system prefetch implemented for OS Win. During table open,
// we only do 1 read for BufferedIO.
int num_read_per_file = 4;
#else
int num_read_per_file = (!use_direct_io_) ? 7 : 4;
#endif
int expected = kNumFiles * num_read_per_file;
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
+1 -1
View File
@@ -921,7 +921,7 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
// before restarting the DB.
// We have to set this on the 2nd to last file for it to delay deletion
// on the last file. (Quirk of DeleteScheduler::BackgroundEmptyTrash())
options.sst_file_manager->SetDeleteRateBytesPerSecond(1024 * 1024);
options.sst_file_manager->SetDeleteRateBytesPerSecond(1);
}
ASSERT_OK(Put("Key2", DummyString(1024, v)));
ASSERT_OK(Put("Key3", DummyString(1024, v)));
+5
View File
@@ -565,6 +565,11 @@ Options DBTestBase::GetOptions(
options.unordered_write = false;
break;
}
case kBlockBasedTableWithBinarySearchWithFirstKeyIndex: {
table_options.index_type =
BlockBasedTableOptions::kBinarySearchWithFirstKey;
break;
}
default:
break;
+1
View File
@@ -1041,6 +1041,7 @@ class DBTestBase : public testing::Test {
kPartitionedFilterWithNewTableReaderForCompactions,
kUniversalSubcompactions,
kUnorderedWrite,
kBlockBasedTableWithBinarySearchWithFirstKeyIndex,
// This must be the last line
kEnd,
};
+34 -39
View File
@@ -250,8 +250,6 @@ void ErrorHandler::CancelErrorRecovery() {
EndAutoRecovery();
}
STATIC_AVOID_DESTRUCTION(const Status, kOkStatus){Status::OK()};
// This is the main function for looking at an error during a background
// operation and deciding the severity, and error recovery strategy. The high
// level algorithm is as follows -
@@ -270,11 +268,11 @@ STATIC_AVOID_DESTRUCTION(const Status, kOkStatus){Status::OK()};
// This can also get called as part of a recovery operation. In that case, we
// also track the error separately in recovery_error_ so we can tell in the
// end whether recovery succeeded or not
const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason) {
void ErrorHandler::HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason) {
db_mutex_->AssertHeld();
if (bg_err.ok()) {
return kOkStatus;
return;
}
ROCKS_LOG_INFO(db_options_.info_log,
@@ -339,7 +337,7 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
} else {
// This error is less severe than previously encountered error. Don't
// take any further action
return bg_error_;
return;
}
}
@@ -356,7 +354,6 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
if (bg_error_.severity() >= Status::Severity::kHardError) {
is_db_stopped_.store(true, std::memory_order_release);
}
return bg_error_;
}
// This is the main function for looking at IO related error during the
@@ -383,14 +380,14 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
// 3) for other cases, HandleKnownErrors(const Status& bg_err,
// BackgroundErrorReason reason) will be called to handle other error cases
// such as delegating to SstFileManager to handle no space error.
const Status& ErrorHandler::SetBGError(const Status& bg_status,
BackgroundErrorReason reason) {
void ErrorHandler::SetBGError(const Status& bg_status,
BackgroundErrorReason reason) {
db_mutex_->AssertHeld();
Status tmp_status = bg_status;
IOStatus bg_io_err = status_to_io_status(std::move(tmp_status));
if (bg_io_err.ok()) {
return kOkStatus;
return;
}
ROCKS_LOG_WARN(db_options_.info_log, "Background IO error %s",
bg_io_err.ToString().c_str());
@@ -413,11 +410,11 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&bg_err, db_mutex_, &auto_recovery);
recover_context_ = context;
return bg_error_;
} else if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
(bg_io_err.GetScope() ==
IOStatus::IOErrorScope::kIOErrorScopeFile ||
bg_io_err.GetRetryable())) {
return;
}
if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
(bg_io_err.GetScope() == IOStatus::IOErrorScope::kIOErrorScopeFile ||
bg_io_err.GetRetryable())) {
// Second, check if the error is a retryable IO error (file scope IO error
// is also treated as retryable IO error in RocksDB write path). if it is
// retryable error and its severity is higher than bg_error_, overwrite the
@@ -426,10 +423,6 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
// IO error as hard error. Note that, all the NoSpace error should be
// handled by the SstFileManager::StartErrorRecovery(). Therefore, no matter
// it is retryable or file scope, this logic will be bypassed.
bool auto_recovery = false;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&new_bg_io_err, db_mutex_,
&auto_recovery);
RecordStats({ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT},
{} /* int_histograms */);
@@ -445,9 +438,13 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
ROCKS_LOG_INFO(
db_options_.info_log,
"ErrorHandler: Compaction will schedule by itself to resume\n");
bool auto_recovery = false;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&new_bg_io_err, db_mutex_,
&auto_recovery);
// Not used in this code path.
new_bg_io_err.PermitUncheckedError();
return bg_error_;
return;
}
Status::Severity severity;
@@ -469,10 +466,14 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
Status bg_err(new_bg_io_err, severity);
CheckAndSetRecoveryAndBGError(bg_err);
recover_context_ = context;
return StartRecoverFromRetryableBGIOError(bg_io_err);
} else {
return HandleKnownErrors(new_bg_io_err, reason);
bool auto_recovery = db_options_.max_bgerror_resume_count > 0;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&new_bg_io_err, db_mutex_,
&auto_recovery);
StartRecoverFromRetryableBGIOError(bg_io_err);
return;
}
HandleKnownErrors(new_bg_io_err, reason);
}
void ErrorHandler::AddFilesToQuarantine(
@@ -620,23 +621,23 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
return s;
}
const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
void ErrorHandler::StartRecoverFromRetryableBGIOError(
const IOStatus& io_error) {
db_mutex_->AssertHeld();
if (bg_error_.ok()) {
return bg_error_;
} else if (io_error.ok()) {
return kOkStatus;
} else if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
// Auto resume BG error is not enabled, directly return bg_error_.
return bg_error_;
} else if (end_recovery_) {
if (bg_error_.ok() || io_error.ok()) {
return;
}
if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
// Auto resume BG error is not enabled
return;
}
if (end_recovery_) {
// Can temporarily release db mutex
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, bg_error_,
Status::ShutdownInProgress(),
db_mutex_);
db_mutex_->AssertHeld();
return bg_error_;
return;
}
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
ROCKS_LOG_INFO(
@@ -664,12 +665,6 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
recovery_thread_.reset(
new port::Thread(&ErrorHandler::RecoverFromRetryableBGIOError, this));
if (recovery_error_.ok()) {
return recovery_error_;
} else {
return bg_error_;
}
}
// Automatic recover from Retryable BG IO error. Must be called after db
+3 -4
View File
@@ -56,7 +56,7 @@ class ErrorHandler {
Status::Severity GetErrorSeverity(BackgroundErrorReason reason,
Status::Code code, Status::SubCode subcode);
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
void SetBGError(const Status& bg_err, BackgroundErrorReason reason);
Status GetBGError() const { return bg_error_; }
@@ -135,11 +135,10 @@ class ErrorHandler {
// unsorted.
autovector<uint64_t> files_to_quarantine_;
const Status& HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason);
void HandleKnownErrors(const Status& bg_err, BackgroundErrorReason reason);
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
void RecoverFromNoSpace();
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
void StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
void RecoverFromRetryableBGIOError();
// First, if it is in recovery and the recovery_error is ok. Set the
// recovery_error_ to bg_err. Second, if the severity is higher than the
+6 -2
View File
@@ -851,13 +851,17 @@ TEST_F(DBErrorHandlingFSTest, DoubleManifestWriteError) {
});
SyncPoint::GetInstance()->EnableProcessing();
s = Flush();
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_TRUE(s.IsNoSpace());
ASSERT_EQ(dbfull()->TEST_GetBGError().severity(),
ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_FALSE(dbfull()->TEST_GetFilesToQuarantine().empty());
fault_fs_->SetFilesystemActive(true);
// This Resume() will attempt to create a new manifest file and fail again
s = dbfull()->Resume();
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_TRUE(s.IsNoSpace());
ASSERT_EQ(dbfull()->TEST_GetBGError().severity(),
ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_FALSE(dbfull()->TEST_GetFilesToQuarantine().empty());
fault_fs_->SetFilesystemActive(true);
SyncPoint::GetInstance()->ClearAllCallBacks();
+6 -3
View File
@@ -228,15 +228,18 @@ void EventHelpers::NotifyOnErrorRecoveryEnd(
InstrumentedMutex* db_mutex) {
if (!listeners.empty()) {
db_mutex->AssertHeld();
// Make copies before releasing mutex to avoid race.
Status old_bg_error_cp = old_bg_error;
Status new_bg_error_cp = new_bg_error;
// release lock while notifying events
db_mutex->Unlock();
TEST_SYNC_POINT("NotifyOnErrorRecoveryEnd:MutexUnlocked:1");
TEST_SYNC_POINT("NotifyOnErrorRecoveryEnd:MutexUnlocked:2");
for (auto& listener : listeners) {
BackgroundErrorRecoveryInfo info;
info.old_bg_error = old_bg_error;
info.new_bg_error = new_bg_error;
listener->OnErrorRecoveryCompleted(old_bg_error);
info.old_bg_error = old_bg_error_cp;
info.new_bg_error = new_bg_error_cp;
listener->OnErrorRecoveryCompleted(old_bg_error_cp);
listener->OnErrorRecoveryEnd(info);
info.old_bg_error.PermitUncheckedError();
info.new_bg_error.PermitUncheckedError();
+53 -4
View File
@@ -44,9 +44,12 @@ Status ExternalSstFileIngestionJob::Prepare(
return status;
}
// Files generated in another DB or CF may have a different column family
// ID, so we let it pass here.
if (file_to_ingest.cf_id !=
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily &&
file_to_ingest.cf_id != cfd_->GetID()) {
file_to_ingest.cf_id != cfd_->GetID() &&
!ingestion_options_.allow_db_generated_files) {
return Status::InvalidArgument(
"External file column family id don't match");
}
@@ -111,6 +114,7 @@ Status ExternalSstFileIngestionJob::Prepare(
const std::string path_inside_db = TableFileName(
cfd_->ioptions()->cf_paths, f.fd.GetNumber(), f.fd.GetPathId());
if (ingestion_options_.move_files) {
assert(!ingestion_options_.allow_db_generated_files);
status =
fs_->LinkFile(path_outside_db, path_inside_db, IOOptions(), nullptr);
if (status.ok()) {
@@ -704,9 +708,16 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
// Get table version
auto version_iter = uprops.find(ExternalSstFilePropertyNames::kVersion);
if (version_iter == uprops.end()) {
return Status::Corruption("External file version not found");
if (!ingestion_options_.allow_db_generated_files) {
return Status::Corruption("External file version not found");
} else {
// 0 is special version for when a file from live DB does not have the
// version table property
file_to_ingest->version = 0;
}
} else {
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
}
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
auto seqno_iter = uprops.find(ExternalSstFilePropertyNames::kGlobalSeqno);
if (file_to_ingest->version == 2) {
@@ -733,8 +744,15 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
return Status::InvalidArgument(
"External SST file V1 does not support global seqno");
}
} else if (file_to_ingest->version == 0) {
// allow_db_generated_files is true
assert(seqno_iter == uprops.end());
file_to_ingest->original_seqno = 0;
file_to_ingest->global_seqno_offset = 0;
} else {
return Status::InvalidArgument("External file version is not supported");
return Status::InvalidArgument("External file version " +
std::to_string(file_to_ingest->version) +
" is not supported");
}
file_to_ingest->cf_id = static_cast<uint32_t>(props->column_family_id);
@@ -896,6 +914,25 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
} else if (!iter->status().ok()) {
return iter->status();
}
if (ingestion_options_.allow_db_generated_files) {
// Verify that all keys have seqno zero.
// TODO: store largest seqno in table property and validate it instead.
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
Status pik_status =
ParseInternalKey(iter->key(), &key, allow_data_in_errors);
if (!pik_status.ok()) {
return Status::Corruption("Corrupted key in external file. ",
pik_status.getState());
}
if (key.sequence != 0) {
return Status::NotSupported(
"External file has a key with non zero sequence number.");
}
}
if (!iter->status().ok()) {
return iter->status();
}
}
std::unique_ptr<InternalIterator> range_del_iter(
table_reader->NewRangeTombstoneIterator(ro));
@@ -911,6 +948,11 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
return Status::Corruption("Corrupted key in external file. ",
pik_status.getState());
}
if (key.sequence != 0) {
return Status::Corruption(
"External file has a range deletion with non zero sequence "
"number.");
}
RangeTombstone tombstone(key, range_del_iter->value());
InternalKey start_key = tombstone.SerializeKey();
@@ -1045,11 +1087,18 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
"Column family enables user-defined timestamps, please make sure the "
"key range (without timestamp) of external file does not overlap "
"with key range (without timestamp) in the db");
return status;
}
if (*assigned_seqno == 0) {
*assigned_seqno = last_seqno + 1;
}
}
if (ingestion_options_.allow_db_generated_files && *assigned_seqno != 0) {
return Status::InvalidArgument(
"An ingested file is assigned to a non-zero sequence number, which is "
"incompatible with ingestion option allow_db_generated_files.");
}
return status;
}
+334 -4
View File
@@ -3719,10 +3719,7 @@ TEST_F(ExternalSSTFileWithTimestampTest, TimestampsNotPersistedBasic) {
}
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
testing::Values(std::make_tuple(false, false),
std::make_tuple(false, true),
std::make_tuple(true, false),
std::make_tuple(true, true)));
testing::Combine(testing::Bool(), testing::Bool()));
INSTANTIATE_TEST_CASE_P(ExternSSTFileLinkFailFallbackTest,
ExternSSTFileLinkFailFallbackTest,
@@ -3730,6 +3727,339 @@ INSTANTIATE_TEST_CASE_P(ExternSSTFileLinkFailFallbackTest,
std::make_tuple(true, true),
std::make_tuple(false, false)));
class IngestDBGeneratedFileTest : public ExternalSSTFileTestBase,
public ::testing::WithParamInterface<bool> {
public:
IngestDBGeneratedFileTest() {
ingest_opts.allow_db_generated_files = true;
ingest_opts.move_files = false;
ingest_opts.verify_checksums_before_ingest = GetParam();
ingest_opts.snapshot_consistency = false;
}
protected:
IngestExternalFileOptions ingest_opts;
};
INSTANTIATE_TEST_CASE_P(BasicMultiConfig, IngestDBGeneratedFileTest,
testing::Bool());
TEST_P(IngestDBGeneratedFileTest, FailureCase) {
// Ingesting overlapping data should always fail.
do {
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
Options options = CurrentOptions();
CreateAndReopenWithCF({"toto"}, options);
// Fill CFs with overlapping keys. Will try to ingest CF1 into default CF.
for (int k = 0; k < 50; ++k) {
ASSERT_OK(Put(Key(k), "default_cf_" + Key(k)));
}
for (int k = 49; k < 100; ++k) {
ASSERT_OK(Put(1, Key(k), "cf1_" + Key(k)));
}
ASSERT_OK(Flush(/*cf=*/1));
{
// Verify that largest key of the file has non-zero seqno.
std::vector<std::vector<FileMetaData>> metadata;
dbfull()->TEST_GetFilesMetaData(handles_[1], &metadata, nullptr);
const FileMetaData& file = metadata[0][0];
ValueType vtype;
SequenceNumber seq;
UnPackSequenceAndType(ExtractInternalKeyFooter(file.largest.Encode()),
&seq, &vtype);
ASSERT_GE(seq, 0);
}
std::vector<LiveFileMetaData> live_meta;
db_->GetLiveFilesMetaData(&live_meta);
ASSERT_EQ(live_meta.size(), 1);
std::vector<std::string> to_ingest_files;
to_ingest_files.emplace_back(live_meta[0].directory + "/" +
live_meta[0].relative_filename);
// Ingesting a file whose boundary key has non-zero seqno.
Status s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(
s.ToString().find("External file has non zero sequence number") !=
std::string::npos);
ASSERT_NOK(s);
{
// Only non-boundary key with non-zero seqno.
const Snapshot* snapshot = db_->GetSnapshot();
ASSERT_OK(Put(1, Key(70), "cf1_" + Key(70)));
ASSERT_OK(Flush(1));
CompactRangeOptions cro;
cro.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
// Verify that only the non-boundary key of the file has non-zero seqno.
std::vector<std::vector<FileMetaData>> metadata;
// File may be at different level for different options.
dbfull()->TEST_GetFilesMetaData(handles_[1], &metadata, nullptr);
bool found_file = false;
for (const auto& level : metadata) {
if (level.empty()) {
continue;
}
ASSERT_FALSE(found_file);
found_file = true;
ASSERT_EQ(1, level.size());
const FileMetaData& file = level[0];
ValueType vtype;
SequenceNumber seq;
UnPackSequenceAndType(ExtractInternalKeyFooter(file.largest.Encode()),
&seq, &vtype);
ASSERT_EQ(seq, 0);
UnPackSequenceAndType(ExtractInternalKeyFooter(file.smallest.Encode()),
&seq, &vtype);
ASSERT_EQ(seq, 0);
ASSERT_GT(file.fd.largest_seqno, 0);
}
ASSERT_TRUE(found_file);
live_meta.clear();
db_->GetLiveFilesMetaData(&live_meta);
ASSERT_EQ(live_meta.size(), 1);
to_ingest_files[0] =
live_meta[0].directory + "/" + live_meta[0].relative_filename;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_NOK(s);
ASSERT_TRUE(
s.ToString().find(
"External file has a key with non zero sequence number") !=
std::string::npos);
db_->ReleaseSnapshot(snapshot);
}
CompactRangeOptions cro;
cro.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
live_meta.clear();
db_->GetLiveFilesMetaData(&live_meta);
ASSERT_EQ(live_meta.size(), 1);
ASSERT_EQ(0, live_meta[0].largest_seqno);
to_ingest_files[0] =
live_meta[0].directory + "/" + live_meta[0].relative_filename;
ingest_opts.allow_db_generated_files = false;
// Ingesting a DB genrate file with allow_db_generated_files = false;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(s.ToString().find("External file version not found") !=
std::string::npos);
ASSERT_NOK(s);
const std::string err =
"An ingested file is assigned to a non-zero sequence number, which is "
"incompatible with ingestion option allow_db_generated_files";
ingest_opts.allow_db_generated_files = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(s.ToString().find(err) != std::string::npos);
ASSERT_NOK(s);
if (options.compaction_style != kCompactionStyleUniversal) {
// FIXME: after fixing ingestion with universal compaction, currently
// will always ingest into L0.
ingest_opts.fail_if_not_bottommost_level = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_NOK(s);
ASSERT_TRUE(s.ToString().find("Files cannot be ingested to Lmax") !=
std::string::npos);
ingest_opts.fail_if_not_bottommost_level = false;
}
ingest_opts.write_global_seqno = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(s.ToString().find("write_global_seqno is deprecated and does "
"not work with allow_db_generated_files") !=
std::string::npos);
ASSERT_NOK(s);
ingest_opts.write_global_seqno = false;
// Delete the overlapping key.
ASSERT_OK(db_->Delete(WriteOptions(), handles_[1], Key(49)));
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
live_meta.clear();
db_->GetLiveFilesMetaData(&live_meta);
bool cf1_file_found = false;
for (const auto& f : live_meta) {
if (f.column_family_name == "toto") {
ASSERT_FALSE(cf1_file_found);
cf1_file_found = true;
ASSERT_EQ(0, f.largest_seqno);
to_ingest_files[0] = f.directory + "/" + f.relative_filename;
}
}
ASSERT_TRUE(cf1_file_found);
const Snapshot* snapshot = db_->GetSnapshot();
ingest_opts.snapshot_consistency = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
// snapshot_consistency with snapshot will assign a newest sequence number.
ASSERT_TRUE(s.ToString().find(err) != std::string::npos);
ASSERT_NOK(s);
ingest_opts.move_files = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ingest_opts.move_files = false;
ASSERT_TRUE(
s.ToString().find("Options move_files and allow_db_generated_files are "
"not compatible") != std::string::npos);
ASSERT_NOK(s);
ingest_opts.snapshot_consistency = false;
ASSERT_OK(db_->IngestExternalFile(to_ingest_files, ingest_opts));
db_->ReleaseSnapshot(snapshot);
// Verify default CF content.
std::string val;
for (int k = 0; k < 100; ++k) {
ASSERT_OK(db_->Get(ReadOptions(), Key(k), &val));
if (k < 50) {
ASSERT_EQ(val, "default_cf_" + Key(k));
} else {
ASSERT_EQ(val, "cf1_" + Key(k));
}
}
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
}
class IngestDBGeneratedFileTest2
: public ExternalSSTFileTestBase,
public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
public:
IngestDBGeneratedFileTest2() = default;
};
INSTANTIATE_TEST_CASE_P(VaryingOptions, IngestDBGeneratedFileTest2,
testing::Combine(testing::Bool(), testing::Bool(),
testing::Bool(), testing::Bool()));
TEST_P(IngestDBGeneratedFileTest2, NotOverlapWithDB) {
// Use a separate column family to sort some data, generate multiple SST
// files. Then ingest these files into another column family or DB. The data
// to be ingested does not overlap with existing data.
IngestExternalFileOptions ingest_opts;
ingest_opts.allow_db_generated_files = true;
ingest_opts.move_files = false;
ingest_opts.snapshot_consistency = std::get<0>(GetParam());
ingest_opts.allow_global_seqno = std::get<1>(GetParam());
ingest_opts.allow_blocking_flush = std::get<2>(GetParam());
ingest_opts.fail_if_not_bottommost_level = std::get<3>(GetParam());
do {
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
Options options = CurrentOptions();
// vector memtable for temp CF does not support concurrent write
options.allow_concurrent_memtable_write = false;
CreateAndReopenWithCF({"toto"}, options);
// non-empty bottommost level
WriteOptions wo;
for (int k = 0; k < 50; ++k) {
ASSERT_OK(db_->Put(wo, handles_[1], Key(k), "base_val_" + Key(k)));
}
ASSERT_OK(Flush());
CompactRangeOptions cro;
cro.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
// non-empty memtable
for (int k = 50; k < 100; ++k) {
ASSERT_OK(db_->Put(wo, handles_[1], Key(k), "base_val_" + Key(k)));
}
// load external data to sort, generate multiple files
Options temp_cf_opts;
ColumnFamilyHandle* temp_cfh;
temp_cf_opts.target_file_size_base = 4 << 10;
temp_cf_opts.memtable_factory.reset(new VectorRepFactory());
temp_cf_opts.allow_concurrent_memtable_write = false;
temp_cf_opts.compaction_style = kCompactionStyleUniversal;
ASSERT_OK(db_->CreateColumnFamily(temp_cf_opts, "temp_cf", &temp_cfh));
Random rnd(301);
std::vector<std::string> expected_value;
expected_value.resize(100);
// Out of order insertion of keys from 100 to 199.
for (int k = 99; k >= 0; --k) {
expected_value[k] = rnd.RandomString(200);
ASSERT_OK(db_->Put(wo, temp_cfh, Key(k + 100), expected_value[k]));
}
ASSERT_OK(db_->CompactRange(cro, temp_cfh, nullptr, nullptr));
std::vector<std::string> sst_file_paths;
ColumnFamilyMetaData cf_meta;
db_->GetColumnFamilyMetaData(temp_cfh, &cf_meta);
ASSERT_GT(cf_meta.file_count, 1);
for (const auto& level_meta : cf_meta.levels) {
if (level_meta.level + 1 < temp_cf_opts.num_levels) {
ASSERT_EQ(0, level_meta.files.size());
} else {
ASSERT_GT(level_meta.files.size(), 1);
for (const auto& meta : level_meta.files) {
ASSERT_EQ(0, meta.largest_seqno);
sst_file_paths.emplace_back(meta.directory + "/" +
meta.relative_filename);
}
}
}
ASSERT_OK(
db_->IngestExternalFile(handles_[1], sst_file_paths, ingest_opts));
// Verify state of the CF1
ReadOptions ro;
std::string val;
for (int k = 0; k < 100; ++k) {
ASSERT_OK(db_->Get(ro, handles_[1], Key(k), &val));
ASSERT_EQ(val, "base_val_" + Key(k));
ASSERT_OK(db_->Get(ro, handles_[1], Key(100 + k), &val));
ASSERT_EQ(val, expected_value[k]);
}
// Ingest into another DB.
if (!encrypted_env_) {
// Ingestion between encrypted env and non-encrypted env won't work.
std::string db2_path = test::PerThreadDBPath("DB2");
Options db2_options;
db2_options.create_if_missing = true;
DB* db2 = nullptr;
ASSERT_OK(DB::Open(db2_options, db2_path, &db2));
// Write some base data.
expected_value.emplace_back(rnd.RandomString(100));
ASSERT_OK(db2->Put(WriteOptions(), Key(200), expected_value.back()));
ASSERT_OK(db2->CompactRange(cro, nullptr, nullptr));
expected_value.emplace_back(rnd.RandomString(100));
ASSERT_OK(db2->Put(WriteOptions(), Key(201), expected_value.back()));
ASSERT_OK(db2->IngestExternalFile({sst_file_paths}, ingest_opts));
{
std::unique_ptr<Iterator> iter{db2->NewIterator(ReadOptions())};
iter->SeekToFirst();
// The DB should have keys 100-199 from ingested files, and keys 200 and
// 201 from itself.
for (int k = 100; k <= 201; ++k, iter->Next()) {
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key(), Key(k));
ASSERT_EQ(iter->value(), expected_value[k - 100]);
}
ASSERT_FALSE(iter->Valid());
ASSERT_OK(iter->status());
}
// Dropping the original CF should not affect db2, reopening it should not
// miss SST files.
ASSERT_OK(db_->DropColumnFamily(temp_cfh));
ASSERT_OK(db_->DestroyColumnFamilyHandle(temp_cfh));
ASSERT_OK(db2->Close());
delete db2;
ASSERT_OK(DB::Open(db2_options, db2_path, &db2));
ASSERT_OK(db2->Close());
delete db2;
ASSERT_OK(DestroyDB(db2_path, db2_options));
} else {
ASSERT_OK(db_->DropColumnFamily(temp_cfh));
ASSERT_OK(db_->DestroyColumnFamilyHandle(temp_cfh));
}
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+57 -23
View File
@@ -567,34 +567,44 @@ class FaultInjectionDBTest : public DBTestBase {
TEST(FaultInjectionFSTest, ReadUnsyncedData) {
std::shared_ptr<FaultInjectionTestFS> fault_fs =
std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
fault_fs->SetInjectUnsyncedDataLoss(true);
ASSERT_TRUE(fault_fs->ReadUnsyncedData());
ASSERT_TRUE(fault_fs->InjectUnsyncedDataLoss());
uint32_t len = Random::GetTLSInstance()->Uniform(10000) + 1;
Random rnd(len);
// This is a randomized mini-stress test, to reduce the chances of bugs in
// FaultInjectionTestFS being caught only in db_stress, where they are
// difficult to debug. ~1000 iterations might be needed to debug relevant
// code changes. Limiting to 10 for each regular unit test run.
auto seed = Random::GetTLSInstance()->Next();
for (int i = 0; i < 10; i++, seed++) {
Random rnd(seed);
uint32_t len = rnd.Uniform(10000) + 1;
// Create partially synced file
std::string f = test::PerThreadDBPath("read_unsynced.data");
std::string data = rnd.RandomString(len);
{
std::string f =
test::PerThreadDBPath("read_unsynced." + std::to_string(seed));
std::string data = rnd.RandomString(len);
// Create partially synced file
std::unique_ptr<FSWritableFile> w;
ASSERT_OK(fault_fs->NewWritableFile(f, {}, &w, nullptr));
uint32_t synced_len = rnd.Uniform(len + 1);
ASSERT_OK(w->Append(Slice(data.data(), synced_len), {}, nullptr));
ASSERT_OK(w->Sync({}, nullptr));
if (synced_len > 0) {
ASSERT_OK(w->Sync({}, nullptr));
}
ASSERT_OK(w->Append(Slice(data.data() + synced_len, len - synced_len), {},
nullptr));
// no sync here
ASSERT_OK(w->Close({}, nullptr));
}
// Test file size includes unsynced data
{
uint64_t file_size;
ASSERT_OK(fault_fs->GetFileSize(f, {}, &file_size, nullptr));
ASSERT_EQ(len, file_size);
}
// Test read file contents, with two reads that probably don't
// align with the unsynced split.
{
// Test file size includes unsynced data
{
uint64_t file_size;
ASSERT_OK(fault_fs->GetFileSize(f, {}, &file_size, nullptr));
ASSERT_EQ(len, file_size);
}
// Test read file contents, with two reads that probably don't
// align with the unsynced split. And maybe a sync or write between
// the two reads.
std::unique_ptr<FSSequentialFile> r;
ASSERT_OK(fault_fs->NewSequentialFile(f, {}, &r, nullptr));
uint32_t first_read_len = rnd.Uniform(len + 1);
@@ -603,12 +613,36 @@ TEST(FaultInjectionFSTest, ReadUnsyncedData) {
ASSERT_OK(r->Read(first_read_len, {}, &sl, scratch.get(), nullptr));
ASSERT_EQ(first_read_len, sl.size());
ASSERT_EQ(0, sl.compare(Slice(data.data(), first_read_len)));
uint32_t second_read_len = len - first_read_len;
// Maybe a sync and/or write and/or close between the two reads.
if (rnd.OneIn(2)) {
ASSERT_OK(w->Sync({}, nullptr));
}
if (rnd.OneIn(2)) {
uint32_t more_len = rnd.Uniform(1000) + 1;
std::string more_data = rnd.RandomString(more_len);
ASSERT_OK(w->Append(more_data, {}, nullptr));
data += more_data;
len += more_len;
}
if (rnd.OneIn(2)) {
ASSERT_OK(w->Sync({}, nullptr));
}
if (rnd.OneIn(2)) {
ASSERT_OK(w->Close({}, nullptr));
w.reset();
}
// Second read some of, all of, or more than rest of file
uint32_t second_read_len = rnd.Uniform(len + 1);
scratch.reset(new char[second_read_len]);
ASSERT_OK(r->Read(second_read_len, {}, &sl, scratch.get(), nullptr));
ASSERT_EQ(second_read_len, sl.size());
ASSERT_EQ(0,
sl.compare(Slice(data.data() + first_read_len, second_read_len)));
if (len - first_read_len < second_read_len) {
ASSERT_EQ(len - first_read_len, sl.size());
} else {
ASSERT_EQ(second_read_len, sl.size());
}
ASSERT_EQ(0, sl.compare(Slice(data.data() + first_read_len, sl.size())));
}
}
} // namespace ROCKSDB_NAMESPACE
+4 -4
View File
@@ -235,19 +235,19 @@ void MemTableListVersion::AddIterators(
SequenceNumber read_seq = options.snapshot != nullptr
? options.snapshot->GetSequenceNumber()
: kMaxSequenceNumber;
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
auto range_del_iter = m->NewRangeTombstoneIterator(
options, read_seq, true /* immutale_memtable */);
if (range_del_iter == nullptr || range_del_iter->empty()) {
delete range_del_iter;
} else {
mem_tombstone_iter = new TruncatedRangeDelIterator(
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
&m->GetInternalKeyComparator(), nullptr /* smallest */,
nullptr /* largest */);
}
merge_iter_builder->AddPointAndTombstoneIterator(mem_iter,
mem_tombstone_iter);
merge_iter_builder->AddPointAndTombstoneIterator(
mem_iter, std::move(mem_tombstone_iter));
}
}
}
+2 -2
View File
@@ -230,7 +230,7 @@ InternalIterator* TableCache::NewIterator(
const InternalKey* smallest_compaction_key,
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
uint8_t block_protection_bytes_per_key, const SequenceNumber* read_seqno,
TruncatedRangeDelIterator** range_del_iter) {
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter) {
PERF_TIMER_GUARD(new_table_iterator_nanos);
Status s;
@@ -285,7 +285,7 @@ InternalIterator* TableCache::NewIterator(
delete new_range_del_iter;
*range_del_iter = nullptr;
} else {
*range_del_iter = new TruncatedRangeDelIterator(
*range_del_iter = std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(
new_range_del_iter),
&icomparator, &file_meta.smallest, &file_meta.largest);
+1 -1
View File
@@ -100,7 +100,7 @@ class TableCache {
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
uint8_t protection_bytes_per_key,
const SequenceNumber* range_del_read_seqno = nullptr,
TruncatedRangeDelIterator** range_del_iter = nullptr);
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter = nullptr);
// If a seek to internal key "k" in specified file finds an entry,
// call get_context->SaveValue() repeatedly until
-1
View File
@@ -255,7 +255,6 @@ bool VersionEdit::EncodeTo(std::string* dst,
char p = static_cast<char>(0);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
dst);
+34 -27
View File
@@ -976,7 +976,8 @@ class LevelIterator final : public InternalIterator {
const std::vector<AtomicCompactionUnitBoundary>* compaction_boundaries =
nullptr,
bool allow_unprepared_value = false,
TruncatedRangeDelIterator**** range_tombstone_iter_ptr_ = nullptr)
std::unique_ptr<TruncatedRangeDelIterator>*** range_tombstone_iter_ptr_ =
nullptr)
: table_cache_(table_cache),
read_options_(read_options),
file_options_(file_options),
@@ -1116,9 +1117,8 @@ class LevelIterator final : public InternalIterator {
}
void ClearRangeTombstoneIter() {
if (range_tombstone_iter_ && *range_tombstone_iter_) {
delete *range_tombstone_iter_;
*range_tombstone_iter_ = nullptr;
if (range_tombstone_iter_) {
range_tombstone_iter_->reset();
}
}
@@ -1201,7 +1201,7 @@ class LevelIterator final : public InternalIterator {
// iterator end).
//
// *range_tombstone_iter_ points to range tombstones of the current SST file
TruncatedRangeDelIterator** range_tombstone_iter_;
std::unique_ptr<TruncatedRangeDelIterator>* range_tombstone_iter_;
// The sentinel key to be returned
Slice sentinel_;
@@ -1280,11 +1280,9 @@ void LevelIterator::Seek(const Slice& target) {
ts_sz);
if (prefix_extractor_->InDomain(target_user_key_without_ts) &&
(!prefix_extractor_->InDomain(next_file_first_user_key_without_ts) ||
user_comparator_.CompareWithoutTimestamp(
prefix_extractor_->Transform(target_user_key_without_ts), false,
prefix_extractor_->Transform(
next_file_first_user_key_without_ts),
false) != 0)) {
prefix_extractor_->Transform(target_user_key_without_ts)
.compare(prefix_extractor_->Transform(
next_file_first_user_key_without_ts)) != 0)) {
// SkipEmptyFileForward() will not advance to next file when this flag
// is set for reason detailed below.
//
@@ -1929,7 +1927,7 @@ InternalIterator* Version::TEST_GetLevelIterator(
int level, bool allow_unprepared_value) {
auto* arena = merge_iter_builder->GetArena();
auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr;
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
auto level_iter = new (mem) LevelIterator(
cfd_->table_cache(), read_options, file_options_,
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
@@ -2029,7 +2027,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
auto* arena = merge_iter_builder->GetArena();
if (level == 0) {
// Merge all level zero files together since they may overlap
TruncatedRangeDelIterator* tombstone_iter = nullptr;
std::unique_ptr<TruncatedRangeDelIterator> tombstone_iter = nullptr;
for (size_t i = 0; i < storage_info_.LevelFilesBrief(0).num_files; i++) {
const auto& file = storage_info_.LevelFilesBrief(0).files[i];
auto table_iter = cfd_->table_cache()->NewIterator(
@@ -2046,8 +2044,8 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
if (read_options.ignore_range_deletions) {
merge_iter_builder->AddIterator(table_iter);
} else {
merge_iter_builder->AddPointAndTombstoneIterator(table_iter,
tombstone_iter);
merge_iter_builder->AddPointAndTombstoneIterator(
table_iter, std::move(tombstone_iter));
}
}
if (should_sample) {
@@ -2064,7 +2062,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
// walks through the non-overlapping files in the level, opening them
// lazily.
auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr;
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
auto level_iter = new (mem) LevelIterator(
cfd_->table_cache(), read_options, soptions,
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
@@ -5510,6 +5508,7 @@ Status VersionSet::ProcessManifestWrites(
Status s;
IOStatus io_s;
IOStatus manifest_io_status;
std::unique_ptr<log::Writer> new_desc_log_ptr;
{
FileOptions opt_file_opts = fs_->OptimizeForManifestWrite(file_options_);
mu->Unlock();
@@ -5538,6 +5537,7 @@ Status VersionSet::ProcessManifestWrites(
}
}
log::Writer* raw_desc_log_ptr = descriptor_log_.get();
if (s.ok() && new_descriptor_log) {
// This is fine because everything inside of this block is serialized --
// only one thread can be here at the same time
@@ -5559,12 +5559,14 @@ Status VersionSet::ProcessManifestWrites(
db_options_->listeners, nullptr,
tmp_set.Contains(FileType::kDescriptorFile),
tmp_set.Contains(FileType::kDescriptorFile)));
descriptor_log_.reset(
new_desc_log_ptr.reset(
new log::Writer(std::move(file_writer), 0, false));
raw_desc_log_ptr = new_desc_log_ptr.get();
s = WriteCurrentStateToManifest(write_options, curr_state,
wal_additions, descriptor_log_.get(),
io_s);
} else {
wal_additions, raw_desc_log_ptr, io_s);
assert(s == io_s);
}
if (!io_s.ok()) {
manifest_io_status = io_s;
s = io_s;
}
@@ -5607,7 +5609,7 @@ Status VersionSet::ProcessManifestWrites(
}
++idx;
#endif /* !NDEBUG */
io_s = descriptor_log_->AddRecord(write_options, record);
io_s = raw_desc_log_ptr->AddRecord(write_options, record);
if (!io_s.ok()) {
s = io_s;
manifest_io_status = io_s;
@@ -5617,7 +5619,7 @@ Status VersionSet::ProcessManifestWrites(
if (s.ok()) {
io_s =
SyncManifest(db_options_, write_options, descriptor_log_->file());
SyncManifest(db_options_, write_options, raw_desc_log_ptr->file());
manifest_io_status = io_s;
TEST_SYNC_POINT_CALLBACK(
"VersionSet::ProcessManifestWrites:AfterSyncManifest", &io_s);
@@ -5650,7 +5652,7 @@ Status VersionSet::ProcessManifestWrites(
if (s.ok()) {
// find offset in manifest file where this version is stored.
new_manifest_file_size = descriptor_log_->file()->GetFileSize();
new_manifest_file_size = raw_desc_log_ptr->file()->GetFileSize();
}
if (first_writer.edit_list.front()->IsColumnFamilyDrop()) {
@@ -5696,6 +5698,7 @@ Status VersionSet::ProcessManifestWrites(
// Append the old manifest file to the obsolete_manifest_ list to be deleted
// by PurgeObsoleteFiles later.
if (s.ok() && new_descriptor_log) {
descriptor_log_ = std::move(new_desc_log_ptr);
obsolete_manifests_.emplace_back(
DescriptorFileName("", manifest_file_number_));
}
@@ -5773,6 +5776,7 @@ Status VersionSet::ProcessManifestWrites(
// corrupted. So we need to force the next version update to start a
// new manifest file.
descriptor_log_.reset();
new_desc_log_ptr.reset();
// If manifest operations failed, then we know the CURRENT file still
// points to the original MANIFEST. Therefore, we can safely delete the
// new MANIFEST.
@@ -7078,8 +7082,8 @@ InternalIterator* VersionSet::MakeInputIterator(
// that will be initialized to where CompactionMergingIterator stores
// pointer to its range tombstones. This is used by LevelIterator
// to update pointer to range tombstones as it traverse different SST files.
std::vector<
std::pair<TruncatedRangeDelIterator*, TruncatedRangeDelIterator***>>
std::vector<std::pair<std::unique_ptr<TruncatedRangeDelIterator>,
std::unique_ptr<TruncatedRangeDelIterator>**>>
range_tombstones;
size_t num = 0;
for (size_t which = 0; which < c->num_input_levels(); which++) {
@@ -7101,7 +7105,8 @@ InternalIterator* VersionSet::MakeInputIterator(
*end, fmd.smallest.user_key()) < 0) {
continue;
}
TruncatedRangeDelIterator* range_tombstone_iter = nullptr;
std::unique_ptr<TruncatedRangeDelIterator> range_tombstone_iter =
nullptr;
list[num++] = cfd->table_cache()->NewIterator(
read_options, file_options_compactions,
cfd->internal_comparator(), fmd, range_del_agg,
@@ -7118,11 +7123,13 @@ InternalIterator* VersionSet::MakeInputIterator(
c->mutable_cf_options()->block_protection_bytes_per_key,
/*range_del_read_seqno=*/nullptr,
/*range_del_iter=*/&range_tombstone_iter);
range_tombstones.emplace_back(range_tombstone_iter, nullptr);
range_tombstones.emplace_back(std::move(range_tombstone_iter),
nullptr);
}
} else {
// Create concatenating iterator for the files from this level
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr;
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr =
nullptr;
list[num++] = new LevelIterator(
cfd->table_cache(), read_options, file_options_compactions,
cfd->internal_comparator(), c->input_levels(which),
+24 -15
View File
@@ -43,16 +43,17 @@ Status WalManager::DeleteFile(const std::string& fname, uint64_t number) {
}
return s;
}
Status WalManager::GetSortedWalFiles(VectorWalPtr& files) {
Status WalManager::GetSortedWalFiles(VectorWalPtr& files, bool need_seqnos,
bool include_archived) {
// First get sorted files in db dir, then get sorted files from archived
// dir, to avoid a race condition where a log file is moved to archived
// dir in between.
Status s;
// list wal files in main db dir.
VectorWalPtr logs;
s = GetSortedWalsOfType(wal_dir_, logs, kAliveLogFile);
if (!s.ok()) {
s = GetSortedWalsOfType(wal_dir_, logs, kAliveLogFile, need_seqnos);
if (!include_archived || !s.ok()) {
return s;
}
@@ -67,7 +68,7 @@ Status WalManager::GetSortedWalFiles(VectorWalPtr& files) {
std::string archivedir = ArchivalDirectory(wal_dir_);
Status exists = env_->FileExists(archivedir);
if (exists.ok()) {
s = GetSortedWalsOfType(archivedir, files, kArchivedLogFile);
s = GetSortedWalsOfType(archivedir, files, kArchivedLogFile, need_seqnos);
if (!s.ok()) {
return s;
}
@@ -249,7 +250,8 @@ void WalManager::PurgeObsoleteWALFiles() {
size_t files_del_num = log_files_num - files_keep_num;
VectorWalPtr archived_logs;
s = GetSortedWalsOfType(archival_dir, archived_logs, kArchivedLogFile);
s = GetSortedWalsOfType(archival_dir, archived_logs, kArchivedLogFile,
/*need_seqno=*/false);
if (!s.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
"Unable to get archived WALs from: %s: %s",
@@ -284,6 +286,9 @@ void WalManager::ArchiveWALFile(const std::string& fname, uint64_t number) {
Status s = env_->RenameFile(fname, archived_log_name);
// The sync point below is used in (DBTest,TransactionLogIteratorRace)
TEST_SYNC_POINT("WalManager::PurgeObsoleteFiles:2");
// The sync point below is used in
// (CheckPointTest, CheckpointWithArchievedLog)
TEST_SYNC_POINT("WalManager::ArchiveWALFile");
ROCKS_LOG_INFO(db_options_.info_log, "Move log file %s to %s -- %s\n",
fname.c_str(), archived_log_name.c_str(),
s.ToString().c_str());
@@ -291,7 +296,7 @@ void WalManager::ArchiveWALFile(const std::string& fname, uint64_t number) {
Status WalManager::GetSortedWalsOfType(const std::string& path,
VectorWalPtr& log_files,
WalFileType log_type) {
WalFileType log_type, bool need_seqnos) {
std::vector<std::string> all_files;
const Status status = env_->GetChildren(path, &all_files);
if (!status.ok()) {
@@ -303,13 +308,17 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
FileType type;
if (ParseFileName(f, &number, &type) && type == kWalFile) {
SequenceNumber sequence;
Status s = ReadFirstRecord(log_type, number, &sequence);
if (!s.ok()) {
return s;
}
if (sequence == 0) {
// empty file
continue;
if (need_seqnos) {
Status s = ReadFirstRecord(log_type, number, &sequence);
if (!s.ok()) {
return s;
}
if (sequence == 0) {
// empty file
continue;
}
} else {
sequence = 0;
}
// Reproduce the race condition where a log file is moved
@@ -319,7 +328,7 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
TEST_SYNC_POINT("WalManager::GetSortedWalsOfType:2");
uint64_t size_bytes;
s = env_->GetFileSize(LogFileName(path, number), &size_bytes);
Status s = env_->GetFileSize(LogFileName(path, number), &size_bytes);
// re-try in case the alive log file has been moved to archive.
if (!s.ok() && log_type == kAliveLogFile) {
std::string archived_file = ArchivedLogFileName(path, number);
+3 -2
View File
@@ -49,7 +49,8 @@ class WalManager {
wal_in_db_path_(db_options_.IsWalDirSameAsDBPath()),
io_tracer_(io_tracer) {}
Status GetSortedWalFiles(VectorWalPtr& files);
Status GetSortedWalFiles(VectorWalPtr& files, bool need_seqnos = true,
bool include_archived = true);
// Allow user to tail transaction log to find all recent changes to the
// database that are newer than `seq_number`.
@@ -78,7 +79,7 @@ class WalManager {
private:
Status GetSortedWalsOfType(const std::string& path, VectorWalPtr& log_files,
WalFileType type);
WalFileType type, bool need_seqnos);
// Requires: all_logs should be sorted with earliest log file first
// Retains all log files in all_logs which contain updates with seq no.
// Greater Than or Equal to the requested SequenceNumber.
+19 -43
View File
@@ -73,16 +73,13 @@ class CfConsistencyStressTest : public StressTest {
status = db_->Write(write_opts, &batch);
}
if (IsRetryableInjectedError(status)) {
fprintf(stdout, "multi put or merge error: %s\n",
status.ToString().c_str());
} else if (!status.ok()) {
if (status.ok()) {
auto num = static_cast<long>(rand_column_families.size());
thread->stats.AddBytesForWrites(num, (sz + 1) * num);
} else if (!IsErrorInjectedAndRetryable(status)) {
fprintf(stderr, "multi put or merge error: %s\n",
status.ToString().c_str());
thread->stats.AddErrors(1);
} else {
auto num = static_cast<long>(rand_column_families.size());
thread->stats.AddBytesForWrites(num, (sz + 1) * num);
}
return status;
@@ -99,13 +96,11 @@ class CfConsistencyStressTest : public StressTest {
batch.Delete(cfh, key);
}
Status s = db_->Write(write_opts, &batch);
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "multidel error: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
if (s.ok()) {
thread->stats.AddDeletes(static_cast<long>(rand_column_families.size()));
} else if (!IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "multidel error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
} else {
thread->stats.AddDeletes(static_cast<long>(rand_column_families.size()));
}
return s;
}
@@ -130,14 +125,12 @@ class CfConsistencyStressTest : public StressTest {
batch.DeleteRange(cfh, key, end_key);
}
Status s = db_->Write(write_opts, &batch);
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "multi del range error: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
fprintf(stderr, "multi del range error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
} else {
if (s.ok()) {
thread->stats.AddRangeDeletions(
static_cast<long>(rand_column_families.size()));
} else if (!IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "multi del range error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
}
return s;
}
@@ -248,9 +241,7 @@ class CfConsistencyStressTest : public StressTest {
thread->stats.AddGets(1, 1);
} else if (s.IsNotFound()) {
thread->stats.AddGets(1, 0);
} else if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestGet error: %s\n", s.ToString().c_str());
} else {
} else if (!IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "TestGet error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
}
@@ -286,9 +277,7 @@ class CfConsistencyStressTest : public StressTest {
} else if (s.IsNotFound()) {
// not found case
thread->stats.AddGets(1, 0);
} else if (IsRetryableInjectedError(s)) {
fprintf(stdout, "MultiGet error: %s\n", s.ToString().c_str());
} else {
} else if (!IsErrorInjectedAndRetryable(s)) {
// errors case
fprintf(stderr, "MultiGet error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
@@ -324,12 +313,7 @@ class CfConsistencyStressTest : public StressTest {
PinnableWideColumns result;
s = db_->GetEntity(read_opts, cfh, key, &result);
if (IsRetryableInjectedError(s)) {
fprintf(stdout,
"GetEntity error: inconsistent columns for key %s, entity %s\n",
StringToHex(key).c_str(),
WideColumnsToHex(result.columns()).c_str());
} else if (s.ok()) {
if (s.ok()) {
if (!VerifyWideColumns(result.columns())) {
fprintf(
stderr,
@@ -359,10 +343,10 @@ class CfConsistencyStressTest : public StressTest {
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
}
if (s.ok() || s.IsNotFound()) {
const bool cmp_found = s.ok();
@@ -517,9 +501,7 @@ class CfConsistencyStressTest : public StressTest {
thread->stats.AddGets(1, 1);
} else if (s.IsNotFound()) {
thread->stats.AddGets(1, 0);
} else if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestGetEntity error: %s\n", s.ToString().c_str());
} else {
} else if (!IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "TestGetEntity error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
}
@@ -590,9 +572,7 @@ class CfConsistencyStressTest : public StressTest {
for (size_t j = 0; j < num_cfs; ++j) {
const Status& s = result[j].status();
const WideColumns& columns = result[j].columns();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestMultiGetEntity (AttributeGroup) error: %s\n",
s.ToString().c_str());
if (!s.ok() && IsErrorInjectedAndRetryable(s)) {
break;
} else if (!s.ok() && !s.IsNotFound()) {
fprintf(stderr, "TestMultiGetEntity (AttributeGroup) error: %s\n",
@@ -699,9 +679,7 @@ class CfConsistencyStressTest : public StressTest {
const Status& s = statuses[j];
const WideColumns& columns = results[j].columns();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestMultiGetEntity error: %s\n",
s.ToString().c_str());
if (!s.ok() && IsErrorInjectedAndRetryable(s)) {
break;
} else if (!s.ok() && !s.IsNotFound()) {
fprintf(stderr, "TestMultiGetEntity error: %s\n",
@@ -838,9 +816,7 @@ class CfConsistencyStressTest : public StressTest {
s = iter->status();
}
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestPrefixScan error: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
if (!s.ok() && !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "TestPrefixScan error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
-2
View File
@@ -19,8 +19,6 @@
ROCKSDB_NAMESPACE::Env* db_stress_listener_env = nullptr;
ROCKSDB_NAMESPACE::Env* db_stress_env = nullptr;
// If non-null, injects read error at a rate specified by the
// read_fault_one_in or write_fault_one_in flag
std::shared_ptr<ROCKSDB_NAMESPACE::FaultInjectionTestFS> fault_fs_guard;
std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> compressed_secondary_cache;
std::shared_ptr<ROCKSDB_NAMESPACE::Cache> block_cache;
+5 -5
View File
@@ -159,13 +159,13 @@ bool RunStressTestImpl(SharedState* shared) {
stress->TrackExpectedState(shared);
}
// Since wrie fault and sync fault implementations are coupled with each
// other in `TestFSWritableFile()`, we can not enable or disable only one
// of the two.
// TODO(hx235): decouple implementations of write fault injection and sync
// fault injection.
if (FLAGS_sync_fault_injection || FLAGS_write_fault_one_in > 0) {
fault_fs_guard->SetFilesystemDirectWritable(false);
fault_fs_guard->SetInjectUnsyncedDataLoss(FLAGS_sync_fault_injection);
if (FLAGS_exclude_wal_from_write_fault_injection) {
fault_fs_guard->SetFileTypesExcludedFromWriteFaultInjection(
{FileType::kWalFile});
}
}
now = clock->NowMicros();
fprintf(stdout, "%s Starting database operations\n",
+25 -88
View File
@@ -13,6 +13,20 @@
#include "monitoring/thread_status_util.h"
namespace ROCKSDB_NAMESPACE {
namespace {
void CheckIOActivity(const IOOptions& options) {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#else
(void)options;
#endif
}
} // namespace
class DbStressRandomAccessFileWrapper : public FSRandomAccessFileOwnerWrapper {
public:
explicit DbStressRandomAccessFileWrapper(
@@ -83,144 +97,67 @@ class DbStressWritableFileWrapper : public FSWritableFileOwnerWrapper {
IOStatus Append(const Slice& data, const IOOptions& options,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Append(data, options, dbg);
}
IOStatus Append(const Slice& data, const IOOptions& options,
const DataVerificationInfo& verification_info,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Append(data, options, verification_info, dbg);
}
IOStatus PositionedAppend(const Slice& data, uint64_t offset,
const IOOptions& options,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->PositionedAppend(data, offset, options, dbg);
}
IOStatus PositionedAppend(const Slice& data, uint64_t offset,
const IOOptions& options,
const DataVerificationInfo& verification_info,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->PositionedAppend(data, offset, options, verification_info,
dbg);
}
IOStatus Truncate(uint64_t size, const IOOptions& options,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Truncate(size, options, dbg);
}
IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Close(options, dbg);
}
IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Flush(options, dbg);
}
IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Sync(options, dbg);
}
IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Fsync(options, dbg);
}
#ifdef ROCKSDB_FALLOCATE_PRESENT
IOStatus Allocate(uint64_t offset, uint64_t len, const IOOptions& options,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->Allocate(offset, len, options, dbg);
}
#endif
IOStatus RangeSync(uint64_t offset, uint64_t nbytes, const IOOptions& options,
IODebugContext* dbg) override {
#ifndef NDEBUG
const ThreadStatus::OperationType thread_op =
ThreadStatusUtil::GetThreadOperation();
Env::IOActivity io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
assert(io_activity == Env::IOActivity::kUnknown ||
io_activity == options.io_activity);
#endif
CheckIOActivity(options);
return target()->RangeSync(offset, nbytes, options, dbg);
}
};
+3
View File
@@ -1095,6 +1095,9 @@ DEFINE_int32(write_fault_one_in, 0,
"On non-zero, enables fault injection on write. Currently only"
"injects write error when writing to SST files.");
DEFINE_bool(exclude_wal_from_write_fault_injection, false,
"If true, we won't inject write fault when writing to WAL file");
DEFINE_int32(metadata_write_fault_one_in, 1000,
"On non-zero, enables fault injection on metadata write (i.e, "
"directory and file metadata write)");
+4 -3
View File
@@ -284,12 +284,13 @@ class DbStressListener : public EventListener {
FaultInjectionIOType::kMetadataWrite);
// TODO(hx235): only exempt the flush thread during error recovery instead
// of all the flush threads from error injection
fault_fs_guard->SetIOActivtiesExemptedFromFaultInjection(
fault_fs_guard->SetIOActivtiesExcludedFromFaultInjection(
{Env::IOActivity::kFlush});
}
}
void OnErrorRecoveryCompleted(Status /* old_bg_error */) override {
void OnErrorRecoveryEnd(
const BackgroundErrorRecoveryInfo& /*info*/) override {
RandomSleep();
if (FLAGS_error_recovery_with_no_fault_injection && fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
@@ -300,7 +301,7 @@ class DbStressListener : public EventListener {
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->SetIOActivtiesExemptedFromFaultInjection({});
fault_fs_guard->SetIOActivtiesExcludedFromFaultInjection({});
}
}
+1
View File
@@ -37,6 +37,7 @@ DECLARE_int32(metadata_read_fault_one_in);
DECLARE_int32(metadata_write_fault_one_in);
DECLARE_int32(read_fault_one_in);
DECLARE_int32(write_fault_one_in);
DECLARE_bool(exclude_wal_from_write_fault_injection);
DECLARE_int32(open_metadata_read_fault_one_in);
DECLARE_int32(open_metadata_write_fault_one_in);
DECLARE_int32(open_write_fault_one_in);
+218 -104
View File
@@ -103,12 +103,16 @@ StressTest::StressTest()
}
}
StressTest::~StressTest() {
void StressTest::CleanUp() {
for (auto cf : column_families_) {
delete cf;
}
column_families_.clear();
if (db_) {
db_->Close();
}
delete db_;
db_ = nullptr;
for (auto* cf : cmp_cfhs_) {
delete cf;
@@ -286,19 +290,12 @@ bool StressTest::BuildOptionsTable() {
std::to_string(options_.write_buffer_size / 8),
}},
{"memtable_huge_page_size", {"0", std::to_string(2 * 1024 * 1024)}},
{"max_successive_merges", {"0", "2", "4"}},
{"strict_max_successive_merges", {"false", "true"}},
{"inplace_update_num_locks", {"100", "200", "300"}},
// TODO: re-enable once internal task T124324915 is fixed.
// {"experimental_mempurge_threshold", {"0.0", "1.0"}},
// TODO(ljin): enable test for this option
// {"disable_auto_compactions", {"100", "200", "300"}},
{"level0_file_num_compaction_trigger",
{
std::to_string(options_.level0_file_num_compaction_trigger),
std::to_string(options_.level0_file_num_compaction_trigger + 2),
std::to_string(options_.level0_file_num_compaction_trigger + 4),
}},
{"level0_slowdown_writes_trigger",
{
std::to_string(options_.level0_slowdown_writes_trigger),
@@ -343,6 +340,35 @@ bool StressTest::BuildOptionsTable() {
}},
{"max_sequential_skip_in_iterations", {"4", "8", "12"}},
};
if (FLAGS_compaction_style == kCompactionStyleUniversal &&
FLAGS_universal_max_read_amp > 0) {
// level0_file_num_compaction_trigger needs to be at most max_read_amp
options_tbl.emplace(
"level0_file_num_compaction_trigger",
std::vector<std::string>{
std::to_string(options_.level0_file_num_compaction_trigger),
std::to_string(
std::min(options_.level0_file_num_compaction_trigger + 2,
FLAGS_universal_max_read_amp)),
std::to_string(
std::min(options_.level0_file_num_compaction_trigger + 4,
FLAGS_universal_max_read_amp)),
});
} else {
options_tbl.emplace(
"level0_file_num_compaction_trigger",
std::vector<std::string>{
std::to_string(options_.level0_file_num_compaction_trigger),
std::to_string(options_.level0_file_num_compaction_trigger + 2),
std::to_string(options_.level0_file_num_compaction_trigger + 4),
});
}
if (FLAGS_unordered_write) {
options_tbl.emplace("max_successive_merges", std::vector<std::string>{"0"});
} else {
options_tbl.emplace("max_successive_merges",
std::vector<std::string>{"0", "2", "4"});
}
if (FLAGS_allow_setting_blob_options_dynamically) {
options_tbl.emplace("enable_blob_files",
@@ -436,7 +462,7 @@ void StressTest::FinishInitDb(SharedState* shared) {
void StressTest::TrackExpectedState(SharedState* shared) {
// When data loss is simulated, recovery from potential data loss is a prefix
// recovery that requires tracing
if (MightHaveDataLoss() && IsStateTracked()) {
if (MightHaveUnsyncedDataLoss() && IsStateTracked()) {
Status s = shared->SaveAtAndAfter(db_);
if (!s.ok()) {
fprintf(stderr, "Error enabling history tracing: %s\n",
@@ -516,9 +542,7 @@ void StressTest::ProcessStatus(SharedState* shared, std::string opname,
if (s.ok()) {
return;
}
if (ignore_injected_error && IsRetryableInjectedError(s)) {
fprintf(stdout, "%s failed: %s\n", opname.c_str(), s.ToString().c_str());
} else {
if (!ignore_injected_error || !IsErrorInjectedAndRetryable(s)) {
std::ostringstream oss;
oss << opname << " failed: " << s.ToString();
VerificationAbort(shared, oss.str());
@@ -1018,10 +1042,8 @@ void StressTest::OperateDb(ThreadState* thread) {
if (thread->rand.OneInOpt(FLAGS_manual_wal_flush_one_in)) {
bool sync = thread->rand.OneIn(2) ? true : false;
Status s = db_->FlushWAL(sync);
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "FlushWAL(sync=%s) failed: %s\n",
(sync ? "true" : "false"), s.ToString().c_str());
} else if (!s.ok() && !(sync && s.IsNotSupported())) {
if (!s.ok() && !IsErrorInjectedAndRetryable(s) &&
!(sync && s.IsNotSupported())) {
fprintf(stderr, "FlushWAL(sync=%s) failed: %s\n",
(sync ? "true" : "false"), s.ToString().c_str());
}
@@ -1029,17 +1051,19 @@ void StressTest::OperateDb(ThreadState* thread) {
if (thread->rand.OneInOpt(FLAGS_lock_wal_one_in)) {
Status s = db_->LockWAL();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "LockWAL() failed: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
if (!s.ok() && !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "LockWAL() failed: %s\n", s.ToString().c_str());
} else {
} else if (s.ok()) {
// Temporarily disable error injection for verification
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
}
// Verify no writes during LockWAL
@@ -1098,13 +1122,17 @@ void StressTest::OperateDb(ThreadState* thread) {
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
}
}
}
if (thread->rand.OneInOpt(FLAGS_sync_wal_one_in)) {
Status s = db_->SyncWAL();
if (!s.ok() && !s.IsNotSupported() && !IsRetryableInjectedError(s)) {
if (!s.ok() && !s.IsNotSupported() && !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "SyncWAL() failed: %s\n", s.ToString().c_str());
}
}
@@ -1144,8 +1172,12 @@ void StressTest::OperateDb(ThreadState* thread) {
ProcessStatus(shared, "GetLiveFiles", s_1);
Status s_2 = TestGetLiveFilesMetaData();
ProcessStatus(shared, "GetLiveFilesMetaData", s_2);
Status s_3 = TestGetLiveFilesStorageInfo();
ProcessStatus(shared, "GetLiveFilesStorageInfo", s_3);
// TODO: enable again after making `GetLiveFilesStorageInfo()`
// compatible with `Options::recycle_log_file_num`
if (FLAGS_recycle_log_file_num == 0) {
Status s_3 = TestGetLiveFilesStorageInfo();
ProcessStatus(shared, "GetLiveFilesStorageInfo", s_3);
}
}
if (thread->rand.OneInOpt(FLAGS_get_all_column_family_metadata_one_in)) {
@@ -1202,7 +1234,32 @@ void StressTest::OperateDb(ThreadState* thread) {
}
if (thread->rand.OneInOpt(FLAGS_get_property_one_in)) {
// TestGetProperty doesn't return status for us to tell whether it has
// failed due to injected error. So we disable fault injection to avoid
// false positive
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
TestGetProperty(thread);
if (fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
}
if (thread->rand.OneInOpt(FLAGS_get_properties_of_all_tables_one_in)) {
@@ -1229,17 +1286,29 @@ void StressTest::OperateDb(ThreadState* thread) {
}
if (total_size <= FLAGS_backup_max_size) {
// TODO(hx235): enable metadata error injection with
// TODO(hx235): enable error injection with
// backup/restore after fixing the various issues it surfaces
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
Status s = TestBackupRestore(thread, rand_column_families, rand_keys);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
if (fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
ProcessStatus(shared, "Backup/restore", s);
}
}
@@ -1674,7 +1743,7 @@ Status StressTest::TestIterateImpl(ThreadState* thread,
if (!(FLAGS_use_txn && FLAGS_txn_write_policy != 0 /* write committed */) &&
!FLAGS_use_multi_cf_iterator && thread->rand.OneIn(4)) {
Status s = iter->Refresh(snapshot_guard.snapshot());
if (!s.ok() && IsRetryableInjectedError(s)) {
if (!s.ok() && IsErrorInjectedAndRetryable(s)) {
return s;
}
assert(s.ok());
@@ -1704,10 +1773,13 @@ Status StressTest::TestIterateImpl(ThreadState* thread,
op_logs += "S " + key.ToString(true) + " ";
}
if (IsRetryableInjectedError(iter->status()) ||
IsRetryableInjectedError(cmp_iter->status())) {
return !iter->status().ok() ? iter->status() : cmp_iter->status();
if (!iter->status().ok() && IsErrorInjectedAndRetryable(iter->status())) {
return iter->status();
} else if (!cmp_iter->status().ok() &&
IsErrorInjectedAndRetryable(cmp_iter->status())) {
return cmp_iter->status();
}
VerifyIterator(thread, cmp_cfh, ro, iter.get(), cmp_iter.get(), last_op,
key, op_logs, verify_func, &diverged);
@@ -1732,10 +1804,13 @@ Status StressTest::TestIterateImpl(ThreadState* thread,
last_op = kLastOpNextOrPrev;
if (IsRetryableInjectedError(iter->status()) ||
IsRetryableInjectedError(cmp_iter->status())) {
return !iter->status().ok() ? iter->status() : cmp_iter->status();
if (!iter->status().ok() && IsErrorInjectedAndRetryable(iter->status())) {
return iter->status();
} else if (!cmp_iter->status().ok() &&
IsErrorInjectedAndRetryable(cmp_iter->status())) {
return cmp_iter->status();
}
VerifyIterator(thread, cmp_cfh, ro, iter.get(), cmp_iter.get(), last_op,
key, op_logs, verify_func, &diverged);
}
@@ -2279,7 +2354,7 @@ Status StressTest::TestBackupRestore(
FaultInjectionIOType::kMetadataWrite);
}
if (s.ok() || IsRetryableInjectedError(s)) {
if (s.ok() || IsErrorInjectedAndRetryable(s)) {
// Preserve directories on failure, or allowed persistent backup
if (!allow_persistent) {
s = DestroyDir(db_stress_env, backup_dir);
@@ -2289,7 +2364,7 @@ Status StressTest::TestBackupRestore(
}
}
if (s.ok() || IsRetryableInjectedError(s)) {
if (s.ok() || IsErrorInjectedAndRetryable(s)) {
s = DestroyDir(db_stress_env, restore_dir);
if (!s.ok()) {
from = "Destroy restore dir";
@@ -2308,7 +2383,7 @@ Status StressTest::TestBackupRestore(
FaultInjectionIOType::kMetadataWrite);
}
if (!s.ok() && !IsRetryableInjectedError(s)) {
if (!s.ok() && !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr,
"Failure in %s with: %s under specified BackupEngineOptions: %s, "
"CreateBackupOptions: %s, RestoreOptions: %s (Empty string or "
@@ -2470,23 +2545,32 @@ Status StressTest::TestCheckpoint(ThreadState* thread,
Status s = Checkpoint::Create(db_, &checkpoint);
if (s.ok()) {
s = checkpoint->CreateCheckpoint(checkpoint_dir);
if (!s.ok() && !IsRetryableInjectedError(s)) {
if (!s.ok() && !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "Fail to create checkpoint to %s\n",
checkpoint_dir.c_str());
std::vector<std::string> files;
// Temporarily disable error injection to print debugging information
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
}
Status my_s = db_stress_env->GetChildren(checkpoint_dir, &files);
// Enable back disable error injection disabled for printing debugging
// information
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
assert(my_s.ok());
for (const auto& f : files) {
fprintf(stderr, " %s\n", f.c_str());
if (fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
}
if (!my_s.ok()) {
fprintf(stderr, "Fail to GetChildren under %s due to %s\n",
checkpoint_dir.c_str(), my_s.ToString().c_str());
} else {
for (const auto& f : files) {
fprintf(stderr, " %s\n", f.c_str());
}
}
}
}
@@ -2573,7 +2657,7 @@ Status StressTest::TestCheckpoint(ThreadState* thread,
FaultInjectionIOType::kMetadataWrite);
}
if (!s.ok() && !IsRetryableInjectedError(s)) {
if (!s.ok() && !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "A checkpoint operation failed with: %s\n",
s.ToString().c_str());
} else {
@@ -2724,18 +2808,18 @@ void StressTest::TestCompactFiles(ThreadState* thread,
auto s = db_->CompactFiles(compact_options, column_family, input_files,
static_cast<int>(output_level));
if (!s.ok()) {
thread->stats.AddNumCompactFilesFailed(1);
// TOOD (hx235): allow an exact list of tolerable failures under stress
// test
bool non_ok_status_allowed =
s.IsManualCompactionPaused() || (IsRetryableInjectedError(s)) ||
s.IsManualCompactionPaused() || IsErrorInjectedAndRetryable(s) ||
s.IsAborted() || s.IsInvalidArgument() || s.IsNotSupported();
fprintf(non_ok_status_allowed ? stdout : stderr,
"Unable to perform CompactFiles(): %s under specified "
"CompactionOptions: %s (Empty string or "
"missing field indicates default option or value is used)\n",
s.ToString().c_str(), compact_opt_oss.str().c_str());
thread->stats.AddNumCompactFilesFailed(1);
if (!non_ok_status_allowed) {
fprintf(stderr,
"Unable to perform CompactFiles(): %s under specified "
"CompactionOptions: %s (Empty string or "
"missing field indicates default option or value is used)\n",
s.ToString().c_str(), compact_opt_oss.str().c_str());
thread->shared->SafeTerminate();
}
} else {
@@ -2759,11 +2843,12 @@ void StressTest::TestPromoteL0(ThreadState* thread,
s.ToString().find("VersionBuilder: Cannot delete table file") !=
std::string::npos &&
s.ToString().find("since it is on level") != std::string::npos);
fprintf(non_ok_status_allowed ? stdout : stderr,
"Unable to perform PromoteL0(): %s under specified "
"target_level: %d.\n",
s.ToString().c_str(), target_level);
if (!non_ok_status_allowed) {
fprintf(stderr,
"Unable to perform PromoteL0(): %s under specified "
"target_level: %d.\n",
s.ToString().c_str(), target_level);
thread->shared->SafeTerminate();
}
}
@@ -2852,10 +2937,8 @@ void StressTest::TestAcquireSnapshot(ThreadState* thread,
// will later read the same key before releasing the snapshot and
// verify that the results are the same.
Status status_at = db_->Get(ropt, column_family, key, &value_at);
if (IsRetryableInjectedError(status_at)) {
fprintf(stdout,
"Unable to perform Get() during TestAcquireSnapshot(): %s)\n",
status_at.ToString().c_str());
if (!status_at.ok() && IsErrorInjectedAndRetryable(status_at)) {
db_->ReleaseSnapshot(snapshot);
return;
}
std::vector<bool>* key_vec = nullptr;
@@ -2964,19 +3047,33 @@ void StressTest::TestCompactRange(ThreadState* thread, int64_t rand_key,
uint32_t pre_hash = 0;
if (thread->rand.OneIn(2)) {
// Temporarily disable error injection to for validation
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
// Declare a snapshot and compare the data before and after the compaction
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
pre_snapshot = db_->GetSnapshot();
pre_hash =
GetRangeHash(thread, pre_snapshot, column_family, start_key, end_key);
// Enable back error injection disabled for validation
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
if (fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
}
std::ostringstream compact_range_opt_oss;
compact_range_opt_oss << "exclusive_manual_compaction: "
@@ -2996,15 +3093,15 @@ void StressTest::TestCompactRange(ThreadState* thread, int64_t rand_key,
if (!status.ok()) {
// TOOD (hx235): allow an exact list of tolerable failures under stress test
bool non_ok_status_allowed =
status.IsManualCompactionPaused() || IsRetryableInjectedError(status) ||
status.IsAborted() || status.IsInvalidArgument() ||
status.IsNotSupported();
fprintf(non_ok_status_allowed ? stdout : stderr,
"Unable to perform CompactRange(): %s under specified "
"CompactRangeOptions: %s (Empty string or "
"missing field indicates default option or value is used)\n",
status.ToString().c_str(), compact_range_opt_oss.str().c_str());
status.IsManualCompactionPaused() ||
IsErrorInjectedAndRetryable(status) || status.IsAborted() ||
status.IsInvalidArgument() || status.IsNotSupported();
if (!non_ok_status_allowed) {
fprintf(stderr,
"Unable to perform CompactRange(): %s under specified "
"CompactRangeOptions: %s (Empty string or "
"missing field indicates default option or value is used)\n",
status.ToString().c_str(), compact_range_opt_oss.str().c_str());
// Fail fast to preserve the DB state.
thread->shared->SetVerificationFailure();
}
@@ -3012,11 +3109,16 @@ void StressTest::TestCompactRange(ThreadState* thread, int64_t rand_key,
if (pre_snapshot != nullptr) {
// Temporarily disable error injection for validation
// Declaring a snapshot and compare the data before and after the compaction
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
uint32_t post_hash =
GetRangeHash(thread, pre_snapshot, column_family, start_key, end_key);
if (pre_hash != post_hash) {
@@ -3032,11 +3134,17 @@ void StressTest::TestCompactRange(ThreadState* thread, int64_t rand_key,
thread->shared->SetVerificationFailure();
}
db_->ReleaseSnapshot(pre_snapshot);
// Enable back error injection disabled for validation
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
if (fault_fs_guard) {
// Enable back error injection disabled for validation
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
}
}
}
@@ -3426,17 +3534,23 @@ void StressTest::Open(SharedState* shared, bool reopen) {
}
// TODO; test transaction DB Open with fault injection
if (!FLAGS_use_txn) {
bool inject_sync_fault = FLAGS_sync_fault_injection;
bool inject_open_meta_read_error =
FLAGS_open_metadata_read_fault_one_in > 0;
bool inject_open_meta_write_error =
FLAGS_open_metadata_write_fault_one_in > 0;
bool inject_open_read_error = FLAGS_open_read_fault_one_in > 0;
bool inject_open_write_error = FLAGS_open_write_fault_one_in > 0;
if ((inject_open_meta_read_error || inject_open_meta_write_error ||
inject_open_read_error || inject_open_write_error) &&
if ((inject_sync_fault || inject_open_meta_read_error ||
inject_open_meta_write_error || inject_open_read_error ||
inject_open_write_error) &&
fault_fs_guard
->FileExists(FLAGS_db + "/CURRENT", IOOptions(), nullptr)
.ok()) {
if (inject_sync_fault || inject_open_write_error) {
fault_fs_guard->SetFilesystemDirectWritable(false);
fault_fs_guard->SetInjectUnsyncedDataLoss(inject_sync_fault);
}
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataRead,
static_cast<uint32_t>(FLAGS_seed),
@@ -3460,7 +3574,6 @@ void StressTest::Open(SharedState* shared, bool reopen) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->SetFilesystemDirectWritable(false);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kWrite, static_cast<uint32_t>(FLAGS_seed),
FLAGS_open_write_fault_one_in, false /* retryable */,
@@ -3495,11 +3608,11 @@ void StressTest::Open(SharedState* shared, bool reopen) {
}
}
if (inject_open_meta_read_error || inject_open_meta_write_error ||
inject_open_read_error || inject_open_write_error) {
if (inject_sync_fault || inject_open_meta_read_error ||
inject_open_meta_write_error || inject_open_read_error ||
inject_open_write_error) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->SetFilesystemDirectWritable(true);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
@@ -3522,9 +3635,10 @@ void StressTest::Open(SharedState* shared, bool reopen) {
}
}
if (!s.ok()) {
// After failure to opening a DB due to IO error, retry should
// successfully open the DB with correct data if no IO error shows
// up.
// After failure to opening a DB due to IO error or unsynced data
// loss, retry should successfully open the DB with correct data if
// no IO error shows up.
inject_sync_fault = false;
inject_open_meta_read_error = false;
inject_open_meta_write_error = false;
inject_open_read_error = false;
@@ -3684,7 +3798,7 @@ void StressTest::Reopen(ThreadState* thread) {
// crash-recovery verification does. Therefore it always expects no data loss
// and we should ensure no data loss in testing.
// TODO(hx235): eliminate the FlushWAL(true /* sync */)/SyncWAL() below
if (!FLAGS_disable_wal) {
if (!FLAGS_disable_wal && !FLAGS_avoid_flush_during_shutdown) {
Status s;
if (FLAGS_manual_wal_flush_one_in > 0) {
s = db_->FlushWAL(/*sync=*/true);
@@ -3721,7 +3835,7 @@ void StressTest::Reopen(ThreadState* thread) {
clock_->TimeToString(now / 1000000).c_str(), num_times_reopened_);
Open(thread->shared, /*reopen=*/true);
if (thread->shared->GetStressTest()->MightHaveDataLoss() &&
if (thread->shared->GetStressTest()->MightHaveUnsyncedDataLoss() &&
IsStateTracked()) {
Status s = thread->shared->SaveAtAndAfter(db_);
if (!s.ok()) {
+9 -7
View File
@@ -27,7 +27,7 @@ class StressTest {
public:
StressTest();
virtual ~StressTest();
virtual ~StressTest() {}
std::shared_ptr<Cache> NewCache(size_t capacity, int32_t num_shard_bits);
@@ -44,12 +44,13 @@ class StressTest {
virtual void VerifyDb(ThreadState* thread) const = 0;
virtual void ContinuouslyVerifyDb(ThreadState* /*thread*/) const = 0;
void PrintStatistics();
bool MightHaveDataLoss() {
return FLAGS_sync_fault_injection || FLAGS_write_fault_one_in > 0 ||
FLAGS_metadata_write_fault_one_in > 0 || FLAGS_disable_wal ||
bool MightHaveUnsyncedDataLoss() {
return FLAGS_sync_fault_injection || FLAGS_disable_wal ||
FLAGS_manual_wal_flush_one_in > 0;
}
void CleanUp();
protected:
static int GetMinInjectedErrorCount(int error_count_1, int error_count_2) {
if (error_count_1 > 0 && error_count_2 > 0) {
@@ -280,9 +281,10 @@ class StressTest {
return Status::NotSupported("TestCustomOperations() must be overridden");
}
bool IsRetryableInjectedError(const Status& s) const {
return s.getState() && std::strstr(s.getState(), "inject") &&
!status_to_io_status(Status(s)).GetDataLoss();
bool IsErrorInjectedAndRetryable(const Status& error_s) const {
assert(!error_s.ok());
return error_s.getState() && std::strstr(error_s.getState(), "inject") &&
!status_to_io_status(Status(error_s)).GetDataLoss();
}
void ProcessStatus(SharedState* shared, std::string msg, const Status& s,
+9 -9
View File
@@ -90,10 +90,10 @@ int db_stress_tool(int argc, char** argv) {
FaultInjectionTestFS* fs =
new FaultInjectionTestFS(raw_env->GetFileSystem());
fault_fs_guard.reset(fs);
// Set it to direct writable here to not lose files created during DB open
// when no open fault injection is not enabled.
// This will be overwritten in StressTest::Open() for open fault injection
// and in RunStressTestImpl() for proper write fault injection setup.
// Set it to direct writable here to initially bypass any fault injection
// during DB open This will correspondingly be overwritten in
// StressTest::Open() for open fault injection and in RunStressTestImpl()
// for proper fault injection setup.
fault_fs_guard->SetFilesystemDirectWritable(true);
fault_env_guard =
std::make_shared<CompositeEnvWrapper>(raw_env, fault_fs_guard);
@@ -362,11 +362,11 @@ int db_stress_tool(int argc, char** argv) {
// Initialize the Zipfian pre-calculated array
InitializeHotKeyGenerator(FLAGS_hot_key_alpha);
shared.reset(new SharedState(db_stress_env, stress.get()));
if (RunStressTest(shared.get())) {
return 0;
} else {
return 1;
}
bool run_stress_test = RunStressTest(shared.get());
// Close DB in CleanUp() before destructor to prevent race between destructor
// and operations in listener callbacks (e.g. MultiOpsTxnsStressListener).
stress->CleanUp();
return run_stress_test ? 0 : 1;
}
} // namespace ROCKSDB_NAMESPACE
+47 -87
View File
@@ -599,10 +599,7 @@ class NonBatchedOpsStressTest : public StressTest {
key.ToString(true).c_str());
}
}
} else if (injected_error_count > 0 && IsRetryableInjectedError(s)) {
fprintf(stdout, "error : Get() returns %s for key: %s.\n",
s.ToString().c_str(), key.ToString(true).c_str());
} else {
} else if (injected_error_count == 0 || !IsErrorInjectedAndRetryable(s)) {
thread->shared->SetVerificationFailure();
fprintf(stderr, "error : Get() returns %s for key: %s.\n",
s.ToString().c_str(), key.ToString(true).c_str());
@@ -876,15 +873,10 @@ class NonBatchedOpsStressTest : public StressTest {
} else if (s.IsMergeInProgress() && use_txn) {
// With txn this is sometimes expected.
thread->stats.AddGets(1, 1);
} else {
if (injected_error_count > 0 && IsRetryableInjectedError(s)) {
fprintf(stdout, "MultiGet error: %s\n", s.ToString().c_str());
thread->stats.AddVerifiedErrors(1);
} else {
fprintf(stderr, "MultiGet error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
shared->SetVerificationFailure();
}
} else if (injected_error_count == 0 || !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "MultiGet error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
shared->SetVerificationFailure();
}
// Enable back error injection disbled for checking results
@@ -1080,10 +1072,7 @@ class NonBatchedOpsStressTest : public StressTest {
StringToHex(key_str).c_str());
}
}
} else if (injected_error_count > 0 && IsRetryableInjectedError(s)) {
fprintf(stdout, "error : GetEntity() returns %s for key: %s.\n",
s.ToString().c_str(), StringToHex(key_str).c_str());
} else {
} else if (injected_error_count == 0 || !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "error : GetEntity() returns %s for key: %s.\n",
s.ToString().c_str(), StringToHex(key_str).c_str());
thread->shared->SetVerificationFailure();
@@ -1279,15 +1268,11 @@ class NonBatchedOpsStressTest : public StressTest {
thread->stats.AddGets(1, 1);
} else if (s.IsNotFound()) {
thread->stats.AddGets(1, 0);
} else {
if (injected_error_count > 0 && IsRetryableInjectedError(s)) {
fprintf(stderr, "MultiGetEntity error: %s\n", s.ToString().c_str());
thread->stats.AddVerifiedErrors(1);
} else {
fprintf(stderr, "MultiGetEntity error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
thread->shared->SetVerificationFailure();
}
} else if (injected_error_count == 0 ||
!IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "MultiGetEntity error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
thread->shared->SetVerificationFailure();
}
}
// Enable back error injection disbled for checking results
@@ -1549,13 +1534,7 @@ class NonBatchedOpsStressTest : public StressTest {
if (s.ok()) {
thread->stats.AddPrefixes(1, count);
} else if (injected_error_count > 0 && IsRetryableInjectedError(s)) {
fprintf(stdout, "TestPrefixScan error: %s\n", s.ToString().c_str());
} else {
fprintf(stderr, "TestPrefixScan is retryable error? %s\n",
IsRetryableInjectedError(s) ? "true" : "false");
fprintf(stderr, "TestPrefixScan injected_error_count: %d\n",
injected_error_count);
} else if (injected_error_count == 0 || !IsErrorInjectedAndRetryable(s)) {
fprintf(stderr, "TestPrefixScan error: %s\n", s.ToString().c_str());
thread->shared->SetVerificationFailure();
}
@@ -1607,14 +1586,13 @@ class NonBatchedOpsStressTest : public StressTest {
if (FLAGS_verify_before_write) {
// Temporarily disable error injection for preparation
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
}
std::string from_db;
Status s = db_->Get(read_opts, cfh, k, &from_db);
bool res = VerifyOrSyncValue(
@@ -1622,14 +1600,12 @@ class NonBatchedOpsStressTest : public StressTest {
/* msg_prefix */ "Pre-Put Get verification", from_db, s);
// Enable back error injection disabled for preparation
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
}
if (!res) {
return s;
}
@@ -1695,8 +1671,7 @@ class NonBatchedOpsStressTest : public StressTest {
if (!s.ok()) {
pending_expected_value.Rollback();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "put or merge error: %s\n", s.ToString().c_str());
if (IsErrorInjectedAndRetryable(s)) {
return s;
} else if (FLAGS_inject_error_severity == 2) {
if (!is_db_stopped_ && s.severity() >= Status::Severity::kFatalError) {
@@ -1756,8 +1731,7 @@ class NonBatchedOpsStressTest : public StressTest {
if (!s.ok()) {
pending_expected_value.Rollback();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "delete error: %s\n", s.ToString().c_str());
if (IsErrorInjectedAndRetryable(s)) {
return s;
} else if (FLAGS_inject_error_severity == 2) {
if (!is_db_stopped_ &&
@@ -1792,8 +1766,7 @@ class NonBatchedOpsStressTest : public StressTest {
if (!s.ok()) {
pending_expected_value.Rollback();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "delete error: %s\n", s.ToString().c_str());
if (IsErrorInjectedAndRetryable(s)) {
return s;
} else if (FLAGS_inject_error_severity == 2) {
if (!is_db_stopped_ &&
@@ -1863,8 +1836,7 @@ class NonBatchedOpsStressTest : public StressTest {
pending_expected_values) {
pending_expected_value.Rollback();
}
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "delete range error: %s\n", s.ToString().c_str());
if (IsErrorInjectedAndRetryable(s)) {
return s;
} else if (FLAGS_inject_error_severity == 2) {
if (!is_db_stopped_ && s.severity() >= Status::Severity::kFatalError) {
@@ -1897,19 +1869,24 @@ class NonBatchedOpsStressTest : public StressTest {
std::ostringstream ingest_options_oss;
// Temporarily disable error injection for preparation
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
}
if (db_stress_env->FileExists(sst_filename).ok()) {
// Maybe we terminated abnormally before, so cleanup to give this file
// ingestion a clean slate
s = db_stress_env->DeleteFile(sst_filename);
}
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
}
SstFileWriter sst_file_writer(EnvOptions(options_), options_);
if (s.ok()) {
@@ -1992,9 +1969,7 @@ class NonBatchedOpsStressTest : public StressTest {
pending_expected_value.Rollback();
}
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "file ingestion error: %s \n", s.ToString().c_str());
} else {
if (!IsErrorInjectedAndRetryable(s)) {
fprintf(stderr,
"file ingestion error: %s under specified "
"IngestExternalFileOptions: %s (Empty string or "
@@ -2183,11 +2158,7 @@ class NonBatchedOpsStressTest : public StressTest {
assert(last_key < ub);
if (!iter->Valid()) {
if (!iter->status().ok()) {
if (IsRetryableInjectedError(iter->status())) {
fprintf(
stdout,
"TestIterate against expected state forward scan error: %s\n",
iter->status().ToString().c_str());
if (IsErrorInjectedAndRetryable(iter->status())) {
return iter->status();
} else {
thread->shared->SetVerificationFailure();
@@ -2249,11 +2220,7 @@ class NonBatchedOpsStressTest : public StressTest {
assert(lb < last_key);
if (!iter->Valid()) {
if (!iter->status().ok()) {
if (IsRetryableInjectedError(iter->status())) {
fprintf(
stdout,
"TestIterate against expected state backward scan error: %s\n",
iter->status().ToString().c_str());
if (IsErrorInjectedAndRetryable(iter->status())) {
return iter->status();
} else {
thread->shared->SetVerificationFailure();
@@ -2317,10 +2284,7 @@ class NonBatchedOpsStressTest : public StressTest {
shared->Get(rand_column_family, i + lb));
}
Status rs = iter->Refresh();
if (!rs.ok() && IsRetryableInjectedError(rs)) {
fprintf(stdout,
"TestIterate against expected state refresh error: %s\n",
iter->status().ToString().c_str());
if (!rs.ok() && IsErrorInjectedAndRetryable(rs)) {
return rs;
}
assert(rs.ok());
@@ -2484,11 +2448,7 @@ class NonBatchedOpsStressTest : public StressTest {
}
if (!iter->status().ok()) {
if (IsRetryableInjectedError(iter->status())) {
fprintf(
stdout,
"TestIterate against expected state scan from middle error: %s\n",
iter->status().ToString().c_str());
if (IsErrorInjectedAndRetryable(iter->status())) {
return iter->status();
} else {
thread->shared->SetVerificationFailure();
+21
View File
@@ -18,6 +18,13 @@
namespace ROCKSDB_NAMESPACE {
// This class supports abstracting different types of an `Env`'s functionality
// into separate interfaces. It is constructed with a `FileSystem` and a
// `SystemClock` and delegates:
// * File system operations to member `file_system_`.
// * Time related misc operations to member `clock_`.
// A subclass needs to inherit `CompositeEnv` and provide implementations for
// the thread management related APIs.
class CompositeEnv : public Env {
public:
// Initialize a CompositeEnvWrapper that delegates all thread/time related
@@ -250,6 +257,20 @@ class CompositeEnv : public Env {
}
};
// A `CompositeEnvWrapper` is constructed with a target `Env` object, an
// optional `FileSystem` object and an optional `SystemClock` object.
// `Env::GetFileSystem()` is a fallback file system if no such object is
// explicitly provided. Similarly, `Env::GetSystemClock()` is a fallback system
// clock.
// Besides delegating corresponding functionality to `file_system_` and `clock_`
// which is inherited from `CompositeEnv`, it also implements the thread
// management APIs by delegating them to the target `Env` object.
//
// Effectively, this class helps to support using customized file system
// implementations such as a remote file system instead of the default file
// system provided by the operating system.
//
// Also see public API `NewCompositeEnv` in rocksdb/include/env.h
class CompositeEnvWrapper : public CompositeEnv {
public:
// Initialize a CompositeEnvWrapper that delegates all thread/time related
Vendored
+6
View File
@@ -355,6 +355,12 @@ class LegacyDirectoryWrapper : public FSDirectory {
std::unique_ptr<Directory> target_;
};
// A helper class to make legacy `Env` implementations be backward compatible
// now that all `Env` implementations are expected to have a `FileSystem` type
// member `file_system_` and a `SystemClock` type member `clock_`.
// This class wraps a legacy `Env` object and expose its file system related
// APIs as a `FileSystem` interface. Also check `LegacySystemClock` that does
// the same thing for the clock related APIs.
class LegacyFileSystemWrapper : public FileSystem {
public:
// Initialize an EnvWrapper that delegates all calls to *t
+12 -5
View File
@@ -233,13 +233,17 @@ class WritableFileWriter {
WritableFileWriter& operator=(const WritableFileWriter&) = delete;
~WritableFileWriter() {
ThreadStatus::OperationType cur_op_type =
IOOptions io_options;
#ifndef NDEBUG
// This is needed to pass the IOActivity related checks in stress test.
// See DbStressWritableFileWrapper.
ThreadStatus::OperationType op_type =
ThreadStatusUtil::GetThreadOperation();
ThreadStatusUtil::SetThreadOperation(
ThreadStatus::OperationType::OP_UNKNOWN);
auto s = Close(IOOptions());
io_options.io_activity =
ThreadStatusUtil::TEST_GetExpectedIOActivity(op_type);
#endif
auto s = Close(io_options);
s.PermitUncheckedError();
ThreadStatusUtil::SetThreadOperation(cur_op_type);
}
std::string file_name() const { return file_name_; }
@@ -304,6 +308,9 @@ class WritableFileWriter {
// operating on the file after an error happens.
void reset_seen_error() {
seen_error_.store(false, std::memory_order_relaxed);
#ifndef NDEBUG
seen_injected_error_.store(false, std::memory_order_relaxed);
#endif // NDEBUG
}
void set_seen_error(const Status& s) {
seen_error_.store(true, std::memory_order_relaxed);
+25
View File
@@ -650,6 +650,19 @@ extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf(
const size_t* range_start_key_len, const char* const* range_limit_key,
const size_t* range_limit_key_len, uint64_t* sizes, char** errptr);
enum {
rocksdb_size_approximation_flags_none = 0,
rocksdb_size_approximation_flags_include_memtable = 1 << 0,
rocksdb_size_approximation_flags_include_files = 1 << 1,
};
extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
int num_ranges, const char* const* range_start_key,
const size_t* range_start_key_len, const char* const* range_limit_key,
const size_t* range_limit_key_len, uint8_t include_flags, uint64_t* sizes,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_compact_range(rocksdb_t* db,
const char* start_key,
size_t start_key_len,
@@ -850,6 +863,13 @@ extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_iterate(
rocksdb_writebatch_t*, void* state,
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
void (*deleted)(void*, const char* k, size_t klen));
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_iterate_cf(
rocksdb_writebatch_t*, void* state,
void (*put_cf)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen),
void (*deleted_cf)(void*, uint32_t cfid, const char* k, size_t klen),
void (*merge_cf)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen));
extern ROCKSDB_LIBRARY_API const char* rocksdb_writebatch_data(
rocksdb_writebatch_t*, size_t* size);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_set_save_point(
@@ -1205,6 +1225,11 @@ extern ROCKSDB_LIBRARY_API int rocksdb_options_get_info_log_level(
rocksdb_options_t*);
extern ROCKSDB_LIBRARY_API rocksdb_logger_t*
rocksdb_logger_create_stderr_logger(int log_level, const char* prefix);
extern ROCKSDB_LIBRARY_API rocksdb_logger_t*
rocksdb_logger_create_callback_logger(int log_level,
void (*)(void* priv, unsigned lev,
char* msg, size_t len),
void* priv);
extern ROCKSDB_LIBRARY_API void rocksdb_logger_destroy(
rocksdb_logger_t* logger);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_write_buffer_size(
+4 -3
View File
@@ -1334,9 +1334,10 @@ class DB {
// DB implementations export properties about their state via this method.
// If "property" is a valid "string" property understood by this DB
// implementation (see Properties struct above for valid options), fills
// "*value" with its current value and returns true. Otherwise, returns
// false.
// implementation (see Properties struct above for valid options) and the DB
// is able to get and fill "*value" with its current value, then return true.
// In all the other cases (e.g, "property" is an invalid "string" property, IO
// errors ..), it returns false.
virtual bool GetProperty(ColumnFamilyHandle* column_family,
const Slice& property, std::string* value) = 0;
virtual bool GetProperty(const Slice& property, std::string* value) {
+5
View File
@@ -145,6 +145,11 @@ struct EnvOptions {
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
// because RocksDB is not exception-safe. This could cause undefined behavior
// including data loss, unreported corruption, deadlocks, and more.
// An interface that abstracts RocksDB's interactions with the operating system
// environment. There are three main types of APIs:
// 1) File system operations, like creating a file, writing to a file, etc.
// 2) Thread management.
// 3) Misc functions, like getting the current time.
class Env : public Customizable {
public:
static const char* kDefaultName() { return "DefaultEnv"; }
+19 -3
View File
@@ -645,9 +645,7 @@ struct DBOptions {
bool verify_sst_unique_id_in_manifest = true;
// Use the specified object to interact with the environment,
// e.g. to read/write files, schedule background work, etc. In the near
// future, support for doing storage operations such as read/write files
// through env will be deprecated in favor of file_system (see below)
// e.g. to read/write files, schedule background work, etc.
// Default: Env::Default()
Env* env = Env::Default();
@@ -2181,6 +2179,24 @@ struct IngestExternalFileOptions {
//
// XXX: "bottommost" is obsolete/confusing terminology to refer to last level
bool fail_if_not_bottommost_level = false;
// EXPERIMENTAL
// If set to true, ingestion will
// - allow the files to not be generated by SstFileWriter, and
// - ignore cf_id mismatch between cf_id in the files and the CF they are
// being ingested into.
//
// REQUIRES:
// - files to be ingested do not overlap with existing keys.
// - write_global_seqno = false
// - move_files = false
//
// Warning: This ONLY works for SST files where all keys have sequence number
// zero and with no duplicated user keys (this should be guaranteed if the
// file is generated by a DB with zero as the largest sequence number).
// We scan the entire SST files to validate sequence numbers.
// Warning: If a DB contains ingested files generated by another DB/CF,
// RepairDB() may not correctly recover these files. It may lose these files.
bool allow_db_generated_files = false;
};
enum TraceFilterType : uint64_t {
+5
View File
@@ -529,6 +529,11 @@ enum Tickers : uint32_t {
// Footer corruption detected when opening an SST file for reading
SST_FOOTER_CORRUPTION_COUNT,
// Counters for file read retries with the verify_and_reconstruct_read
// file system option after detecting a checksum mismatch
FILE_READ_CORRUPTION_RETRY_COUNT,
FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT,
TICKER_ENUM_MAX
};
+4 -2
View File
@@ -32,8 +32,10 @@ class Checkpoint {
// same filesystem as the database, and copied otherwise.
// (2) other required files (like MANIFEST) are always copied.
// log_size_for_flush: if the total log file size is equal or larger than
// this value, then a flush is triggered for all the column families. The
// default value is 0, which means flush is always triggered. If you move
// this value, then a flush is triggered for all the column families.
// The archived log size will not be included when calculating the total log
// size.
// The default value is 0, which means flush is always triggered. If you move
// away from the default, the checkpoint may not contain up-to-date data
// if WAL writing is not always enabled.
// Flush will always trigger if it is 2PC.
+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 9
#define ROCKSDB_MINOR 4
#define ROCKSDB_PATCH 1
#define ROCKSDB_MINOR 5
#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
+9
View File
@@ -5269,6 +5269,10 @@ class TickerTypeJni {
return -0x53;
case ROCKSDB_NAMESPACE::Tickers::SST_FOOTER_CORRUPTION_COUNT:
return -0x55;
case ROCKSDB_NAMESPACE::Tickers::FILE_READ_CORRUPTION_RETRY_COUNT:
return -0x56;
case ROCKSDB_NAMESPACE::Tickers::FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT:
return -0x57;
case ROCKSDB_NAMESPACE::Tickers::TICKER_ENUM_MAX:
// -0x54 is the max value at this time. Since these values are exposed
// directly to Java clients, we'll keep the value the same till the next
@@ -5726,6 +5730,11 @@ class TickerTypeJni {
return ROCKSDB_NAMESPACE::Tickers::PREFETCH_HITS;
case -0x55:
return ROCKSDB_NAMESPACE::Tickers::SST_FOOTER_CORRUPTION_COUNT;
case -0x56:
return ROCKSDB_NAMESPACE::Tickers::FILE_READ_CORRUPTION_RETRY_COUNT;
case -0x57:
return ROCKSDB_NAMESPACE::Tickers::
FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT;
case -0x54:
// -0x54 is the max value at this time. Since these values are exposed
// directly to Java clients, we'll keep the value the same till the next
@@ -45,7 +45,7 @@ public class RateLimiter extends RocksObject {
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
* lead to burstier writes while smaller value introduces more CPU
* overhead. The default of 100,000ms should work for most cases.
* overhead. The default of 100,000us should work for most cases.
*/
public RateLimiter(final long rateBytesPerSecond,
final long refillPeriodMicros) {
@@ -66,7 +66,7 @@ public class RateLimiter extends RocksObject {
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
* lead to burstier writes while smaller value introduces more CPU
* overhead. The default of 100,000ms should work for most cases.
* overhead. The default of 100,000us should work for most cases.
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
* A low-pri request is usually blocked in favor of hi-pri request.
* Currently, RocksDB assigns low-pri to request from compaction and
@@ -95,7 +95,7 @@ public class RateLimiter extends RocksObject {
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
* lead to burstier writes while smaller value introduces more CPU
* overhead. The default of 100,000ms should work for most cases.
* overhead. The default of 100,000us should work for most cases.
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
* A low-pri request is usually blocked in favor of hi-pri request.
* Currently, RocksDB assigns low-pri to request from compaction and
@@ -127,7 +127,7 @@ public class RateLimiter extends RocksObject {
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
* lead to burstier writes while smaller value introduces more CPU
* overhead. The default of 100,000ms should work for most cases.
* overhead. The default of 100,000us should work for most cases.
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
* A low-pri request is usually blocked in favor of hi-pri request.
* Currently, RocksDB assigns low-pri to request from compaction and
@@ -878,6 +878,10 @@ public enum TickerType {
SST_FOOTER_CORRUPTION_COUNT((byte) -0x55),
FILE_READ_CORRUPTION_RETRY_COUNT((byte) -0x56),
FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT((byte) -0x57),
TICKER_ENUM_MAX((byte) -0x54);
private final byte value;
+4
View File
@@ -266,6 +266,10 @@ const std::vector<std::pair<Tickers, std::string>> TickersNameMap = {
{PREFETCH_BYTES_USEFUL, "rocksdb.prefetch.bytes.useful"},
{PREFETCH_HITS, "rocksdb.prefetch.hits"},
{SST_FOOTER_CORRUPTION_COUNT, "rocksdb.footer.corruption.count"},
{FILE_READ_CORRUPTION_RETRY_COUNT,
"rocksdb.file.read.corruption.retry.count"},
{FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT,
"rocksdb.file.read.corruption.retry.success.count"},
};
const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
+8 -5
View File
@@ -98,17 +98,20 @@ struct ImmutableDBOptions {
std::string db_host_id;
FileTypeSet checksum_handoff_file_types;
CacheTier lowest_used_cache_tier;
// Convenience/Helper objects that are not part of the base DBOptions
std::shared_ptr<FileSystem> fs;
SystemClock* clock;
Statistics* stats;
Logger* logger;
std::shared_ptr<CompactionService> compaction_service;
bool enforce_single_del_contracts;
uint64_t follower_refresh_catchup_period_ms;
uint64_t follower_catchup_retry_count;
uint64_t follower_catchup_retry_wait_ms;
// Beginning convenience/helper objects that are not part of the base
// DBOptions
std::shared_ptr<FileSystem> fs;
SystemClock* clock;
Statistics* stats;
Logger* logger;
// End of convenience/helper objects.
bool IsWalDirSameAsDBPath() const;
bool IsWalDirSameAsDBPath(const std::string& path) const;
const std::string& GetWalDir() const;
+2 -2
View File
@@ -6,7 +6,7 @@
#pragma once
#if defined(__clang__) && defined(__GLIBC__)
// glibc's `posix_memalign()` declaration specifies `throw()` while clang's
// glibc's `posix_memalign()` declaration specifies `noexcept` while clang's
// declaration does not. There is a hack in clang to make its re-declaration
// compatible with glibc's if they are declared consecutively. That hack breaks
// if yet another `posix_memalign()` declaration comes between glibc's and
@@ -14,7 +14,7 @@
// declarations both come before "jemalloc.h"'s `posix_memalign()` declaration.
//
// This problem could also be avoided if "jemalloc.h"'s `posix_memalign()`
// declaration did not specify `throw()` when built with clang.
// declaration did not specify `noexcept` when built with clang.
#include <mm_malloc.h>
#endif
+3 -2
View File
@@ -101,8 +101,9 @@ bool CondVar::TimedWait(uint64_t abs_time_us) {
std::unique_lock<std::mutex> lk(mu_->getLock(), std::adopt_lock);
// Work around https://github.com/microsoft/STL/issues/369
#if defined(_MSC_VER) && \
(!defined(_MSVC_STL_UPDATE) || _MSVC_STL_UPDATE < 202008L)
// std::condition_variable_any::wait_for had a fix, but
// std::condition_variable still doesn't have a fix in STL yet
#if defined(_MSC_VER)
if (relTimeUs == std::chrono::microseconds::zero()) {
lk.unlock();
lk.lock();
@@ -823,6 +823,12 @@ void BlockBasedTableIterator::BlockCacheLookupForReadAheadSize(
read_options_, block_handle,
&(block_handle_info.cachable_entry_).As<Block_kData>());
if (!s.ok()) {
#ifndef NDEBUG
// To allow fault injection verification to pass since non-okay status in
// `BlockCacheLookupForReadAheadSize()` won't fail the read but to have
// less or no readahead
IGNORE_STATUS_IF_ERROR(s);
#endif
break;
}
@@ -852,6 +858,15 @@ void BlockBasedTableIterator::BlockCacheLookupForReadAheadSize(
is_index_at_curr_block_ = false;
}
#ifndef NDEBUG
// To allow fault injection verification to pass since non-okay status in
// `BlockCacheLookupForReadAheadSize()` won't fail the read but to have less
// or no readahead
if (!index_iter_->status().ok()) {
IGNORE_STATUS_IF_ERROR(index_iter_->status());
}
#endif
if (found_first_miss_block) {
// Iterate cache hit block handles from the end till a Miss is there, to
// truncate and update the end offset till that Miss.
+14 -4
View File
@@ -478,6 +478,7 @@ bool IsFeatureSupported(const TableProperties& table_properties,
}
// Caller has to ensure seqno is not nullptr.
// Set *seqno to the global sequence number for reading this file.
Status GetGlobalSequenceNumber(const TableProperties& table_properties,
SequenceNumber largest_seqno,
SequenceNumber* seqno) {
@@ -500,12 +501,17 @@ Status GetGlobalSequenceNumber(const TableProperties& table_properties,
}
uint32_t version = DecodeFixed32(version_pos->second.c_str());
if (version < 2) {
if (seqno_pos != props.end() || version != 1) {
std::array<char, 200> msg_buf;
if (version != 2) {
std::array<char, 200> msg_buf;
if (version != 1) {
snprintf(msg_buf.data(), msg_buf.max_size(),
"An external sst file has corrupted version %u.", version);
return Status::Corruption(msg_buf.data());
}
if (seqno_pos != props.end()) {
// This is a v1 external sst file, global_seqno is not supported.
snprintf(msg_buf.data(), msg_buf.max_size(),
"An external sst file with version %u have global seqno "
"An external sst file with version %u has global seqno "
"property with value %s",
version, seqno_pos->second.c_str());
return Status::Corruption(msg_buf.data());
@@ -687,6 +693,10 @@ Status BlockBasedTable::Open(
s = ReadFooterFromFile(retry_opts, file.get(), *ioptions.fs,
prefetch_buffer.get(), file_size, &footer,
kBlockBasedTableMagicNumber);
RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_COUNT);
if (s.ok()) {
RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
}
}
}
if (!s.ok()) {
@@ -223,13 +223,16 @@ DEFINE_SYNC_AND_ASYNC(void, BlockBasedTable::RetrieveMultipleBlocks)
s = VerifyBlockChecksum(footer, data, handle.size(),
rep_->file->file_name(), handle.offset());
RecordTick(ioptions.stats, BLOCK_CHECKSUM_COMPUTE_COUNT);
if (!s.ok()) {
RecordTick(ioptions.stats, BLOCK_CHECKSUM_MISMATCH_COUNT);
}
TEST_SYNC_POINT_CALLBACK("RetrieveMultipleBlocks:VerifyChecksum", &s);
if (!s.ok() &&
CheckFSFeatureSupport(ioptions.fs.get(),
FSSupportedOps::kVerifyAndReconstructRead)) {
assert(s.IsCorruption());
assert(!ioptions.allow_mmap_reads);
RecordTick(ioptions.stats, BLOCK_CHECKSUM_MISMATCH_COUNT);
RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_COUNT);
// Repeat the read for this particular block using the regular
// synchronous Read API. We can use the same chunk of memory
@@ -246,6 +249,10 @@ DEFINE_SYNC_AND_ASYNC(void, BlockBasedTable::RetrieveMultipleBlocks)
assert(result.size() == BlockSizeWithTrailer(handle));
s = VerifyBlockChecksum(footer, data, handle.size(),
rep_->file->file_name(), handle.offset());
if (s.ok()) {
RecordTick(ioptions.stats,
FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
}
} else {
s = io_s;
}
+12 -3
View File
@@ -20,7 +20,8 @@ namespace ROCKSDB_NAMESPACE {
FullFilterBlockBuilder::FullFilterBlockBuilder(
const SliceTransform* _prefix_extractor, bool whole_key_filtering,
FilterBitsBuilder* filter_bits_builder)
: prefix_extractor_(_prefix_extractor),
: need_last_prefix_(whole_key_filtering && _prefix_extractor != nullptr),
prefix_extractor_(_prefix_extractor),
whole_key_filtering_(whole_key_filtering),
last_whole_key_recorded_(false),
last_prefix_recorded_(false),
@@ -38,7 +39,7 @@ void FullFilterBlockBuilder::Add(const Slice& key_without_ts) {
const bool add_prefix =
prefix_extractor_ && prefix_extractor_->InDomain(key_without_ts);
if (!last_prefix_recorded_ && last_key_in_domain_) {
if (need_last_prefix_ && !last_prefix_recorded_ && last_key_in_domain_) {
// We can reach here when a new filter partition starts in partitioned
// filter. The last prefix in the previous partition should be added if
// necessary regardless of key_without_ts, to support prefix SeekForPrev.
@@ -82,7 +83,15 @@ inline void FullFilterBlockBuilder::AddKey(const Slice& key) {
void FullFilterBlockBuilder::AddPrefix(const Slice& key) {
assert(prefix_extractor_ && prefix_extractor_->InDomain(key));
Slice prefix = prefix_extractor_->Transform(key);
if (whole_key_filtering_) {
if (need_last_prefix_) {
// WART/FIXME: Because last_prefix_str_ is needed above to make
// SeekForPrev work with partitioned + prefix filters, we are currently
// use this inefficient code in that case (in addition to prefix+whole
// key). Hopefully this can be optimized with some refactoring up the call
// chain to BlockBasedTableBuilder. Even in PartitionedFilterBlockBuilder,
// we don't currently have access to the previous key/prefix by the time we
// know we are starting a new partition.
// if both whole_key and prefix are added to bloom then we will have whole
// key and prefix addition being interleaved and thus cannot rely on the
// bits builder to properly detect the duplicates by comparing with the last
+1
View File
@@ -69,6 +69,7 @@ class FullFilterBlockBuilder : public FilterBlockBuilder {
void AddPrefix(const Slice& key);
const SliceTransform* prefix_extractor() { return prefix_extractor_; }
const std::string& last_prefix_str() const { return last_prefix_str_; }
bool need_last_prefix_;
private:
// important: all of these might point to invalid addresses
@@ -45,6 +45,9 @@ PartitionedFilterBlockBuilder::PartitionedFilterBlockBuilder(
p_index_builder_(p_index_builder),
keys_added_to_partition_(0),
total_added_in_built_(0) {
// See FullFilterBlockBuilder::AddPrefix
need_last_prefix_ = prefix_extractor() != nullptr;
// Compute keys_per_partition_
keys_per_partition_ = static_cast<uint32_t>(
filter_bits_builder_->ApproximateNumEntries(partition_size));
if (keys_per_partition_ < 1) {
@@ -86,7 +89,7 @@ void PartitionedFilterBlockBuilder::MaybeCutAFilterBlock(
}
// Add the prefix of the next key before finishing the partition without
// updating last_prefix_str_. This hack, fixes a bug with format_verison=3
// updating last_prefix_str_. This hack fixes a bug with format_verison=3
// where seeking for the prefix would lead us to the previous partition.
const bool maybe_add_prefix =
next_key && prefix_extractor() && prefix_extractor()->InDomain(*next_key);
+13 -1
View File
@@ -84,7 +84,9 @@ inline bool BlockFetcher::TryGetFromPrefetchBuffer() {
if (io_status_.ok()) {
got_from_prefetch_buffer_ = true;
used_buf_ = const_cast<char*>(slice_.data());
} else if (!(io_status_.IsCorruption() && retry_corrupt_read_)) {
} else if (io_status_.IsCorruption()) {
// Returning true apparently indicates we either got some data from
// the prefetch buffer, or we tried and encountered an error.
return true;
}
}
@@ -334,9 +336,15 @@ void BlockFetcher::ReadBlock(bool retry) {
ProcessTrailerIfPresent();
}
if (retry) {
RecordTick(ioptions_.stats, FILE_READ_CORRUPTION_RETRY_COUNT);
}
if (io_status_.ok()) {
InsertCompressedBlockToPersistentCacheIfNeeded();
fs_buf_ = std::move(read_req.fs_scratch);
if (retry) {
RecordTick(ioptions_.stats, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
}
} else {
ReleaseFileSystemProvidedBuffer(&read_req);
direct_io_buf_.reset();
@@ -355,7 +363,11 @@ IOStatus BlockFetcher::ReadBlockContents() {
return IOStatus::OK();
}
if (TryGetFromPrefetchBuffer()) {
if (io_status_.IsCorruption() && retry_corrupt_read_) {
ReadBlock(/*retry=*/true);
}
if (!io_status_.ok()) {
assert(!fs_buf_);
return io_status_;
}
} else if (!TryGetSerializedBlockFromPersistentCache()) {
+9 -10
View File
@@ -11,8 +11,8 @@ class CompactionMergingIterator : public InternalIterator {
CompactionMergingIterator(
const InternalKeyComparator* comparator, InternalIterator** children,
int n, bool is_arena_mode,
std::vector<
std::pair<TruncatedRangeDelIterator*, TruncatedRangeDelIterator***>>
std::vector<std::pair<std::unique_ptr<TruncatedRangeDelIterator>,
std::unique_ptr<TruncatedRangeDelIterator>**>>&
range_tombstones)
: is_arena_mode_(is_arena_mode),
comparator_(comparator),
@@ -27,7 +27,7 @@ class CompactionMergingIterator : public InternalIterator {
}
assert(range_tombstones.size() == static_cast<size_t>(n));
for (auto& p : range_tombstones) {
range_tombstone_iters_.push_back(p.first);
range_tombstone_iters_.push_back(std::move(p.first));
}
pinned_heap_item_.resize(n);
for (int i = 0; i < n; ++i) {
@@ -47,10 +47,7 @@ class CompactionMergingIterator : public InternalIterator {
}
~CompactionMergingIterator() override {
// TODO: use unique_ptr for range_tombstone_iters_
for (auto child : range_tombstone_iters_) {
delete child;
}
range_tombstone_iters_.clear();
for (auto& child : children_) {
child.iter.DeleteIter(is_arena_mode_);
@@ -197,7 +194,8 @@ class CompactionMergingIterator : public InternalIterator {
// nullptr means the sorted run of children_[i] does not have range
// tombstones (or the current SSTable does not have range tombstones in the
// case of LevelIterator).
std::vector<TruncatedRangeDelIterator*> range_tombstone_iters_;
std::vector<std::unique_ptr<TruncatedRangeDelIterator>>
range_tombstone_iters_;
// Used as value for range tombstone keys
std::string dummy_tombstone_val{};
@@ -349,8 +347,9 @@ void CompactionMergingIterator::AddToMinHeapOrCheckStatus(HeapItem* child) {
InternalIterator* NewCompactionMergingIterator(
const InternalKeyComparator* comparator, InternalIterator** children, int n,
std::vector<std::pair<TruncatedRangeDelIterator*,
TruncatedRangeDelIterator***>>& range_tombstone_iters,
std::vector<std::pair<std::unique_ptr<TruncatedRangeDelIterator>,
std::unique_ptr<TruncatedRangeDelIterator>**>>&
range_tombstone_iters,
Arena* arena) {
assert(n >= 0);
if (n == 0) {
+3 -2
View File
@@ -38,7 +38,8 @@ class CompactionMergingIterator;
InternalIterator* NewCompactionMergingIterator(
const InternalKeyComparator* comparator, InternalIterator** children, int n,
std::vector<std::pair<TruncatedRangeDelIterator*,
TruncatedRangeDelIterator***>>& range_tombstone_iters,
std::vector<std::pair<std::unique_ptr<TruncatedRangeDelIterator>,
std::unique_ptr<TruncatedRangeDelIterator>**>>&
range_tombstone_iters,
Arena* arena = nullptr);
} // namespace ROCKSDB_NAMESPACE
+18 -13
View File
@@ -96,8 +96,9 @@ class MergingIterator : public InternalIterator {
// could be updated. In that case, this merging iterator is only responsible
// for freeing the new range tombstone iterator that it has pointers to in
// range_tombstone_iters_.
void AddRangeTombstoneIterator(TruncatedRangeDelIterator* iter) {
range_tombstone_iters_.emplace_back(iter);
void AddRangeTombstoneIterator(
std::unique_ptr<TruncatedRangeDelIterator>&& iter) {
range_tombstone_iters_.emplace_back(std::move(iter));
}
// Called by MergingIteratorBuilder when all point iterators and range
@@ -125,9 +126,7 @@ class MergingIterator : public InternalIterator {
}
~MergingIterator() override {
for (auto child : range_tombstone_iters_) {
delete child;
}
range_tombstone_iters_.clear();
for (auto& child : children_) {
child.iter.DeleteIter(is_arena_mode_);
@@ -624,7 +623,8 @@ class MergingIterator : public InternalIterator {
// Invariant(rti): pinned_heap_item_[i] is in minHeap_ iff
// range_tombstone_iters_[i]->Valid() and at most one pinned_heap_item_[i] is
// in minHeap_.
std::vector<TruncatedRangeDelIterator*> range_tombstone_iters_;
std::vector<std::unique_ptr<TruncatedRangeDelIterator>>
range_tombstone_iters_;
// Levels (indices into range_tombstone_iters_/children_ ) that currently have
// "active" range tombstones. See comments above MergingIterator for meaning
@@ -841,7 +841,8 @@ void MergingIterator::SeekImpl(const Slice& target, size_t starting_level,
prefetched_target.emplace_back(
level, current_search_key.GetInternalKey().ToString());
}
auto range_tombstone_iter = range_tombstone_iters_[level];
UnownedPtr<TruncatedRangeDelIterator> range_tombstone_iter =
range_tombstone_iters_[level].get();
if (range_tombstone_iter) {
range_tombstone_iter->SeekInternalKey(
current_search_key.GetInternalKey());
@@ -1125,7 +1126,8 @@ void MergingIterator::SeekForPrevImpl(const Slice& target,
prefetched_target.emplace_back(
level, current_search_key.GetInternalKey().ToString());
}
auto range_tombstone_iter = range_tombstone_iters_[level];
UnownedPtr<TruncatedRangeDelIterator> range_tombstone_iter =
range_tombstone_iters_[level].get();
if (range_tombstone_iter) {
range_tombstone_iter->SeekForPrev(current_search_key.GetUserKey());
if (range_tombstone_iter->Valid()) {
@@ -1349,7 +1351,8 @@ void MergingIterator::SwitchToForward() {
ParseInternalKey(target, &pik, false /* log_err_key */)
.PermitUncheckedError();
for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) {
auto iter = range_tombstone_iters_[i];
UnownedPtr<TruncatedRangeDelIterator> iter =
range_tombstone_iters_[i].get();
if (iter) {
iter->Seek(pik.user_key);
// The while loop is needed as the Seek() call above is only for user
@@ -1395,7 +1398,8 @@ void MergingIterator::SwitchToBackward() {
ParseInternalKey(target, &pik, false /* log_err_key */)
.PermitUncheckedError();
for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) {
auto iter = range_tombstone_iters_[i];
UnownedPtr<TruncatedRangeDelIterator> iter =
range_tombstone_iters_[i].get();
if (iter) {
iter->SeekForPrev(pik.user_key);
// Since the SeekForPrev() call above is only for user key,
@@ -1690,8 +1694,9 @@ void MergeIteratorBuilder::AddIterator(InternalIterator* iter) {
}
void MergeIteratorBuilder::AddPointAndTombstoneIterator(
InternalIterator* point_iter, TruncatedRangeDelIterator* tombstone_iter,
TruncatedRangeDelIterator*** tombstone_iter_ptr) {
InternalIterator* point_iter,
std::unique_ptr<TruncatedRangeDelIterator>&& tombstone_iter,
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr) {
// tombstone_iter_ptr != nullptr means point_iter is a LevelIterator.
bool add_range_tombstone = tombstone_iter ||
!merge_iter->range_tombstone_iters_.empty() ||
@@ -1711,7 +1716,7 @@ void MergeIteratorBuilder::AddPointAndTombstoneIterator(
merge_iter->children_.size() - 1) {
merge_iter->AddRangeTombstoneIterator(nullptr);
}
merge_iter->AddRangeTombstoneIterator(tombstone_iter);
merge_iter->AddRangeTombstoneIterator(std::move(tombstone_iter));
}
if (tombstone_iter_ptr) {
+5 -3
View File
@@ -70,8 +70,10 @@ class MergeIteratorBuilder {
// point iterators are not LevelIterator, then range tombstone iterator is
// only added to the merging iter if there is a non-null `tombstone_iter`.
void AddPointAndTombstoneIterator(
InternalIterator* point_iter, TruncatedRangeDelIterator* tombstone_iter,
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr);
InternalIterator* point_iter,
std::unique_ptr<TruncatedRangeDelIterator>&& tombstone_iter,
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr =
nullptr);
// Get arena used to build the merging iterator. It is called one a child
// iterator needs to be allocated.
@@ -91,7 +93,7 @@ class MergeIteratorBuilder {
Arena* arena;
// Used to set LevelIterator.range_tombstone_iter_.
// See AddRangeTombstoneIterator() implementation for more detail.
std::vector<std::pair<size_t, TruncatedRangeDelIterator***>>
std::vector<std::pair<size_t, std::unique_ptr<TruncatedRangeDelIterator>**>>
range_del_iter_ptrs_;
};
+1 -1
View File
@@ -125,7 +125,7 @@ EOF
# To check for DB forward compatibility with loading options (old version
# reading data from new), as well as backward compatibility
declare -a db_forward_with_options_refs=("8.6.fb" "8.7.fb" "8.8.fb" "8.9.fb" "8.10.fb" "8.11.fb" "9.0.fb" "9.1.fb" "9.2.fb" "9.3.fb")
declare -a db_forward_with_options_refs=("8.6.fb" "8.7.fb" "8.8.fb" "8.9.fb" "8.10.fb" "8.11.fb" "9.0.fb" "9.1.fb" "9.2.fb" "9.3.fb" "9.4.fb")
# To check for DB forward compatibility without loading options (in addition
# to the "with loading options" set), as well as backward compatibility
declare -a db_forward_no_options_refs=() # N/A at the moment
+122 -70
View File
@@ -48,13 +48,15 @@ default_params = {
"charge_filter_construction": lambda: random.choice([0, 1]),
"charge_table_reader": lambda: random.choice([0, 1]),
"charge_file_metadata": lambda: random.choice([0, 1]),
"checkpoint_one_in": lambda: random.choice([10000, 1000000]),
"checkpoint_one_in": lambda: random.choice([10000, 1000000]),
"compression_type": lambda: random.choice(
["none", "snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"]
),
"bottommost_compression_type": lambda: "disable"
if random.randint(0, 1) == 0
else random.choice(["none", "snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"]),
"bottommost_compression_type": lambda: (
"disable"
if random.randint(0, 1) == 0
else random.choice(["none", "snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"])
),
"checksum_type": lambda: random.choice(
["kCRC32c", "kxxHash", "kxxHash64", "kXXH3"]
),
@@ -65,13 +67,13 @@ default_params = {
"compression_use_zstd_dict_trainer": lambda: random.randint(0, 1),
"compression_checksum": lambda: random.randint(0, 1),
"clear_column_family_one_in": 0,
"compact_files_one_in": lambda: random.choice([1000, 1000000]),
"compact_range_one_in": lambda: random.choice([1000, 1000000]),
"compact_files_one_in": lambda: random.choice([1000, 1000000]),
"compact_range_one_in": lambda: random.choice([1000, 1000000]),
# Disabled because of various likely related failures with
# "Cannot delete table file #N from level 0 since it is on level X"
"promote_l0_one_in": 0,
"compaction_pri": random.randint(0, 4),
"key_may_exist_one_in": lambda: random.choice([100, 100000]),
"key_may_exist_one_in": lambda: random.choice([100, 100000]),
"data_block_index_type": lambda: random.choice([0, 1]),
"delpercent": 4,
"delrangepercent": 1,
@@ -147,10 +149,16 @@ default_params = {
"use_sqfc_for_range_queries": lambda: random.choice([0, 1, 1, 1]),
"mock_direct_io": False,
"cache_type": lambda: random.choice(
["lru_cache", "fixed_hyper_clock_cache", "auto_hyper_clock_cache",
"auto_hyper_clock_cache", "tiered_lru_cache",
"tiered_fixed_hyper_clock_cache", "tiered_auto_hyper_clock_cache",
"tiered_auto_hyper_clock_cache"]
[
"lru_cache",
"fixed_hyper_clock_cache",
"auto_hyper_clock_cache",
"auto_hyper_clock_cache",
"tiered_lru_cache",
"tiered_fixed_hyper_clock_cache",
"tiered_auto_hyper_clock_cache",
"tiered_auto_hyper_clock_cache",
]
),
"uncache_aggressiveness": lambda: int(math.pow(10, 4.0 * random.random()) - 1.0),
"use_full_merge_v1": lambda: random.randint(0, 1),
@@ -160,7 +168,9 @@ default_params = {
"use_attribute_group": lambda: random.randint(0, 1),
"use_multi_cf_iterator": lambda: random.randint(0, 1),
# 999 -> use Bloom API
"bloom_before_level": lambda: random.choice([random.randint(-1, 2), random.randint(-1, 10), 0x7fffffff - 1, 0x7fffffff]),
"bloom_before_level": lambda: random.choice(
[random.randint(-1, 2), random.randint(-1, 10), 0x7FFFFFFF - 1, 0x7FFFFFFF]
),
"value_size_mult": 32,
"verification_only": 0,
"verify_checksum": 1,
@@ -173,13 +183,7 @@ default_params = {
"use_multi_get_entity": lambda: random.choice([0] * 7 + [1]),
"periodic_compaction_seconds": lambda: random.choice([0, 0, 1, 2, 10, 100, 1000]),
"daily_offpeak_time_utc": lambda: random.choice(
[
"",
"",
"00:00-23:59",
"04:00-08:00",
"23:30-03:15"
]
["", "", "00:00-23:59", "04:00-08:00", "23:30-03:15"]
),
# 0 = never (used by some), 10 = often (for threading bugs), 600 = default
"stats_dump_period_sec": lambda: random.choice([0, 10, 600]),
@@ -196,8 +200,7 @@ default_params = {
# TODO(hx235): Enable `wal_bytes_per_sync` after fixing the DB recovery such
# that it won't recover past the WAL data hole created by this option
"wal_bytes_per_sync": 0,
"compaction_readahead_size": lambda: random.choice(
[0, 0, 1024 * 1024]),
"compaction_readahead_size": lambda: random.choice([0, 0, 1024 * 1024]),
"db_write_buffer_size": lambda: random.choice(
[0, 0, 0, 1024 * 1024, 8 * 1024 * 1024, 128 * 1024 * 1024]
),
@@ -222,13 +225,14 @@ default_params = {
"metadata_write_fault_one_in": lambda: random.choice([0, 128, 1000]),
"read_fault_one_in": lambda: random.choice([0, 32, 1000]),
"write_fault_one_in": lambda: random.choice([0, 128, 1000]),
"exclude_wal_from_write_fault_injection": 0,
"open_metadata_write_fault_one_in": lambda: random.choice([0, 0, 8]),
"open_metadata_read_fault_one_in": lambda: random.choice([0, 0, 8]),
"open_write_fault_one_in": lambda: random.choice([0, 0, 16]),
"open_read_fault_one_in": lambda: random.choice([0, 0, 32]),
"sync_fault_injection": lambda: random.randint(0, 1),
"get_property_one_in": lambda: random.choice([100000, 1000000]),
"get_properties_of_all_tables_one_in": lambda: random.choice([100000, 1000000]),
"get_property_one_in": lambda: random.choice([100000, 1000000]),
"get_properties_of_all_tables_one_in": lambda: random.choice([100000, 1000000]),
"paranoid_file_checks": lambda: random.choice([0, 1, 1, 1]),
"max_write_buffer_size_to_maintain": lambda: random.choice(
[0, 1024 * 1024, 2 * 1024 * 1024, 4 * 1024 * 1024, 8 * 1024 * 1024]
@@ -265,14 +269,16 @@ default_params = {
"bottommost_file_compaction_delay": lambda: random.choice(
[0, 0, 0, 600, 3600, 86400]
),
"auto_readahead_size" : lambda: random.choice([0, 1]),
"auto_readahead_size": lambda: random.choice([0, 1]),
"verify_iterator_with_expected_state_one_in": 5,
"allow_fallocate": lambda: random.choice([0, 1]),
"table_cache_numshardbits": lambda: random.choice([6] * 3 + [-1] * 2 + [0]),
"enable_write_thread_adaptive_yield": lambda: random.choice([0, 1]),
"log_readahead_size": lambda: random.choice([0, 16 * 1024 * 1024]),
"bgerror_resume_retry_interval": lambda: random.choice([100, 1000000]),
"delete_obsolete_files_period_micros": lambda: random.choice([6 * 60 * 60 * 1000000, 30 * 1000000]),
"delete_obsolete_files_period_micros": lambda: random.choice(
[6 * 60 * 60 * 1000000, 30 * 1000000]
),
"max_log_file_size": lambda: random.choice([0, 1024 * 1024]),
"log_file_time_to_roll": lambda: random.choice([0, 60]),
"use_adaptive_mutex": lambda: random.choice([0, 1]),
@@ -304,24 +310,34 @@ default_params = {
"max_total_wal_size": lambda: random.choice([0] * 4 + [64 * 1024 * 1024]),
"high_pri_pool_ratio": lambda: random.choice([0, 0.5]),
"low_pri_pool_ratio": lambda: random.choice([0, 0.5]),
"soft_pending_compaction_bytes_limit" : lambda: random.choice([1024 * 1024] + [64 * 1073741824] * 4),
"hard_pending_compaction_bytes_limit" : lambda: random.choice([2 * 1024 * 1024] + [256 * 1073741824] * 4),
"soft_pending_compaction_bytes_limit": lambda: random.choice(
[1024 * 1024] + [64 * 1073741824] * 4
),
"hard_pending_compaction_bytes_limit": lambda: random.choice(
[2 * 1024 * 1024] + [256 * 1073741824] * 4
),
"enable_sst_partitioner_factory": lambda: random.choice([0, 1]),
"enable_do_not_compress_roles": lambda: random.choice([0, 1]),
"block_align": lambda: random.choice([0, 1]),
"lowest_used_cache_tier": lambda: random.choice([0, 1, 2]),
"enable_custom_split_merge": lambda: random.choice([0, 1]),
"adm_policy": lambda: random.choice([0, 1, 2, 3]),
"last_level_temperature": lambda: random.choice(["kUnknown", "kHot", "kWarm", "kCold"]),
"default_write_temperature": lambda: random.choice(["kUnknown", "kHot", "kWarm", "kCold"]),
"default_temperature": lambda: random.choice(["kUnknown", "kHot", "kWarm", "kCold"]),
"last_level_temperature": lambda: random.choice(
["kUnknown", "kHot", "kWarm", "kCold"]
),
"default_write_temperature": lambda: random.choice(
["kUnknown", "kHot", "kWarm", "kCold"]
),
"default_temperature": lambda: random.choice(
["kUnknown", "kHot", "kWarm", "kCold"]
),
# TODO(hx235): enable `enable_memtable_insert_with_hint_prefix_extractor`
# after fixing the surfaced issue with delete range
"enable_memtable_insert_with_hint_prefix_extractor": 0,
"check_multiget_consistency": lambda: random.choice([0, 0, 0, 1]),
"check_multiget_entity_consistency": lambda: random.choice([0, 0, 0, 1]),
"use_timed_put_one_in": lambda: random.choice([0] * 7 + [1, 5, 10]),
"universal_max_read_amp": lambda : random.choice([-1] * 3 + [0, 4, 10]),
"universal_max_read_amp": lambda: random.choice([-1] * 3 + [0, 4, 10]),
}
_TEST_DIR_ENV_VAR = "TEST_TMPDIR"
# If TEST_TMPDIR_EXPECTED is not specified, default value will be TEST_TMPDIR
@@ -507,6 +523,8 @@ txn_params = {
"inplace_update_support": 0,
# TimedPut is not supported in transaction
"use_timed_put_one_in": 0,
# AttributeGroup not yet supported
"use_attribute_group": 0,
}
# For optimistic transaction db
@@ -520,6 +538,8 @@ optimistic_txn_params = {
"inplace_update_support": 0,
# TimedPut is not supported in transaction
"use_timed_put_one_in": 0,
# AttributeGroup not yet supported
"use_attribute_group": 0,
}
best_efforts_recovery_params = {
@@ -527,7 +547,7 @@ best_efforts_recovery_params = {
"disable_wal": 1,
"column_families": 1,
"skip_verifydb": 1,
"verify_db_one_in": 0
"verify_db_one_in": 0,
}
blob_params = {
@@ -623,10 +643,11 @@ multiops_txn_default_params = {
"create_timestamped_snapshot_one_in": 50,
"sync_fault_injection": 0,
"metadata_write_fault_one_in": 0,
"manual_wal_flush": 0,
"manual_wal_flush_one_in": 0,
# This test has aggressive flush frequency and small write buffer size.
# Disabling write fault to avoid writes being stopped.
"write_fault_one_in": 0,
"metadata_write_fault_one_in": 0,
# PutEntity in transactions is not yet implemented
"use_put_entity_one_in": 0,
"use_get_entity": 0,
@@ -666,6 +687,7 @@ multiops_wp_txn_params = {
"lock_wal_one_in": 0,
}
def finalize_and_sanitize(src_params):
dest_params = {k: v() if callable(v) else v for (k, v) in src_params.items()}
if is_release_mode():
@@ -727,23 +749,19 @@ def finalize_and_sanitize(src_params):
# inplace_update_support based on other option values, which may change
# across runs.
if dest_params["inplace_update_support"] == 1:
dest_params["delpercent"] += dest_params["delrangepercent"]
dest_params["delrangepercent"] = 0
dest_params["readpercent"] += dest_params["prefixpercent"]
dest_params["prefixpercent"] = 0
dest_params["allow_concurrent_memtable_write"] = 0
# inplace_update_support does not update sequence number. Our stress test recovery
# logic for unsynced data loss relies on max sequence number stored
# in MANIFEST, so they don't work together.
dest_params["sync_fault_injection"] = 0
dest_params["write_fault_one_in"] = 0
dest_params["metadata_write_fault_one_in"] = 0
dest_params["disable_wal"] = 0
dest_params["manual_wal_flush_one_in"] = 0
dest_params["delpercent"] += dest_params["delrangepercent"]
dest_params["delrangepercent"] = 0
dest_params["readpercent"] += dest_params["prefixpercent"]
dest_params["prefixpercent"] = 0
dest_params["allow_concurrent_memtable_write"] = 0
# inplace_update_support does not update sequence number. Our stress test recovery
# logic for unsynced data loss relies on max sequence number stored
# in MANIFEST, so they don't work together.
dest_params["sync_fault_injection"] = 0
dest_params["disable_wal"] = 0
dest_params["manual_wal_flush_one_in"] = 0
if (
dest_params.get("sync_fault_injection") == 1
or dest_params.get("write_fault_one_in") > 0
or dest_params.get("metadata_write_fault_one_in") > 0
or dest_params.get("disable_wal") == 1
or dest_params.get("manual_wal_flush_one_in") > 0
):
@@ -759,6 +777,14 @@ def finalize_and_sanitize(src_params):
# files, which would be problematic when unsynced data can be lost in
# crash recoveries.
dest_params["enable_compaction_filter"] = 0
# Prefix-recoverability relies on tracing successful user writes.
# Currently we trace all user writes regardless of whether it later succeeds or not.
# To simplify, we disable any user write failure injection.
# TODO(hx235): support tracing user writes with failure injection.
# TODO(hx235): support excluding WAL from metadata write fault injection so we don't
# have to disable metadata write fault injection to other file
dest_params["metadata_write_fault_one_in"] = 0
dest_params["exclude_wal_from_write_fault_injection"] = 1
# Only under WritePrepared txns, unordered_write would provide the same guarnatees as vanilla rocksdb
# unordered_write is only enabled with --txn, and txn_params disables inplace_update_support, so
# setting allow_concurrent_memtable_write=1 won't conflcit with inplace_update_support.
@@ -813,8 +839,6 @@ def finalize_and_sanitize(src_params):
# compatible with only write committed policy
if dest_params.get("use_txn") == 1 and dest_params.get("txn_write_policy", 0) != 0:
dest_params["sync_fault_injection"] = 0
dest_params["write_fault_one_in"] = 0
dest_params["metadata_write_fault_one_in"] = 0
dest_params["disable_wal"] = 0
dest_params["manual_wal_flush_one_in"] = 0
# Wide-column pessimistic transaction APIs are initially supported for
@@ -822,10 +846,10 @@ def finalize_and_sanitize(src_params):
dest_params["use_put_entity_one_in"] = 0
# TODO(hx235): enable test_multi_ops_txns with fault injection after stabilizing the CI
if dest_params.get("test_multi_ops_txns") == 1:
dest_params["write_fault_one_in"] = 0
dest_params["metadata_write_fault_one_in"] = 0
dest_params["read_fault_one_in"] = 0
dest_params["metadata_read_fault_one_in"] = 0
dest_params["write_fault_one_in"] = 0
dest_params["metadata_write_fault_one_in"] = 0
dest_params["read_fault_one_in"] = 0
dest_params["metadata_read_fault_one_in"] = 0
# Wide column stress tests require FullMergeV3
if dest_params["use_put_entity_one_in"] != 0:
dest_params["use_full_merge_v1"] = 0
@@ -833,15 +857,21 @@ def finalize_and_sanitize(src_params):
dest_params["verify_file_checksums_one_in"] = 0
if dest_params["write_fault_one_in"] > 0:
# background work may be disabled while DB is resuming after some error
dest_params["max_write_buffer_number"] = max(dest_params["max_write_buffer_number"], 10)
dest_params["max_write_buffer_number"] = max(
dest_params["max_write_buffer_number"], 10
)
if dest_params["secondary_cache_uri"].find("compressed_secondary_cache") >= 0:
dest_params["compressed_secondary_cache_size"] = 0
dest_params["compressed_secondary_cache_ratio"] = 0.0
if dest_params["cache_type"].find("tiered_") >= 0:
if dest_params["compressed_secondary_cache_size"] > 0:
dest_params["compressed_secondary_cache_ratio"] = \
float(dest_params["compressed_secondary_cache_size"]/ \
(dest_params["cache_size"] + dest_params["compressed_secondary_cache_size"]))
dest_params["compressed_secondary_cache_ratio"] = float(
dest_params["compressed_secondary_cache_size"]
/ (
dest_params["cache_size"]
+ dest_params["compressed_secondary_cache_size"]
)
)
dest_params["compressed_secondary_cache_size"] = 0
else:
dest_params["compressed_secondary_cache_ratio"] = 0.0
@@ -851,10 +881,12 @@ def finalize_and_sanitize(src_params):
dest_params["compressed_secondary_cache_size"] = 0
dest_params["compressed_secondary_cache_ratio"] = 0.0
if dest_params["use_write_buffer_manager"]:
if (dest_params["cache_size"] <= 0
or dest_params["db_write_buffer_size"] <= 0):
if dest_params["cache_size"] <= 0 or dest_params["db_write_buffer_size"] <= 0:
dest_params["use_write_buffer_manager"] = 0
if dest_params["user_timestamp_size"] > 0 and dest_params["persist_user_defined_timestamps"] == 0:
if (
dest_params["user_timestamp_size"] > 0
and dest_params["persist_user_defined_timestamps"] == 0
):
# Features that are not compatible with UDT in memtable only feature.
dest_params["enable_blob_files"] = 0
dest_params["allow_setting_blob_options_dynamically"] = 0
@@ -874,22 +906,36 @@ def finalize_and_sanitize(src_params):
# Only best efforts recovery test support disabling wal and
# disable atomic flush.
if dest_params["test_best_efforts_recovery"] == 0:
dest_params["disable_wal"] = 0
dest_params["disable_wal"] = 0
if dest_params.get("allow_concurrent_memtable_write", 1) == 1:
dest_params["memtablerep"] = "skip_list"
if (dest_params.get("enable_compaction_filter", 0) == 1
or dest_params.get("inplace_update_support", 0) == 1):
if (
dest_params.get("enable_compaction_filter", 0) == 1
or dest_params.get("inplace_update_support", 0) == 1
):
# Compaction filter, inplace update support are incompatible with snapshots. Need to avoid taking
# snapshots, as well as avoid operations that use snapshots for
# verification.
dest_params["acquire_snapshot_one_in"] = 0
dest_params["compact_range_one_in"] = 0
# Redistribute to maintain 100% total
dest_params["readpercent"] += dest_params.get("iterpercent", 10) + dest_params.get("prefixpercent", 20)
dest_params["readpercent"] += dest_params.get(
"iterpercent", 10
) + dest_params.get("prefixpercent", 20)
dest_params["iterpercent"] = 0
dest_params["prefixpercent"] = 0
dest_params["check_multiget_consistency"] = 0
dest_params["check_multiget_entity_consistency"] = 0
if dest_params.get("disable_wal") == 0 and dest_params.get("reopen") > 0:
# Reopen with WAL currently requires persisting WAL data before closing for reopen.
# Previous injected WAL write errors may not be cleared by the time of closing and ready
# for persisting WAL.
# To simplify, we disable any WAL write error injection.
# TODO(hx235): support WAL write error injection with reopen
# TODO(hx235): support excluding WAL from metadata write fault injection so we don't
# have to disable metadata write fault injection to other file
dest_params["exclude_wal_from_write_fault_injection"] = 1
dest_params["metadata_write_fault_one_in"] = 0
if dest_params.get("disable_wal") == 1:
# disableWAL and recycle_log_file_num options are not mutually
# compatible at the moment
@@ -905,11 +951,14 @@ def finalize_and_sanitize(src_params):
# `use_timed_put_one_in` option so that they make sense together.
if dest_params.get("use_put_entity_one_in") == 1:
dest_params["use_timed_put_one_in"] = 0
elif (dest_params.get("use_put_entity_one_in") > 1 and
dest_params.get("use_timed_put_one_in") == 1):
elif (
dest_params.get("use_put_entity_one_in") > 1
and dest_params.get("use_timed_put_one_in") == 1
):
dest_params["use_timed_put_one_in"] = 3
return dest_params
def gen_cmd_params(args):
params = {}
@@ -984,7 +1033,7 @@ def gen_cmd(params, unknown_params):
"test_tiered_storage",
"cleanup_cmd",
"skip_tmpdir_check",
"print_stderr_separately"
"print_stderr_separately",
}
and v is not None
]
@@ -1022,6 +1071,7 @@ def print_output_and_exit_on_error(stdout, stderr, print_stderr_separately=False
sys.exit(2)
def cleanup_after_success(dbname):
shutil.rmtree(dbname, True)
if cleanup_cmd is not None:
@@ -1031,6 +1081,7 @@ def cleanup_after_success(dbname):
print("TEST FAILED. DB cleanup returned error %d\n" % ret)
sys.exit(1)
# This script runs and kills db_stress multiple times. It checks consistency
# in case of unsafe crashes in RocksDB.
def blackbox_crash_main(args, unknown_args):
@@ -1222,7 +1273,9 @@ def whitebox_crash_main(args, unknown_args):
)
print(msg)
print_output_and_exit_on_error(stdoutdata, stderrdata, args.print_stderr_separately)
print_output_and_exit_on_error(
stdoutdata, stderrdata, args.print_stderr_separately
)
if hit_timeout:
print("Killing the run for running too long")
@@ -1257,7 +1310,6 @@ def whitebox_crash_main(args, unknown_args):
time.sleep(1) # time to stabilize after a kill
# If successfully finished or timed out (we currently treat timed out test as passing)
# Clean up after ourselves
if succeeded or hit_timeout:
+1 -1
View File
@@ -17,7 +17,7 @@ namespace ROCKSDB_NAMESPACE {
class StderrLogger : public Logger {
public:
explicit StderrLogger(const InfoLogLevel log_level = InfoLogLevel::INFO_LEVEL)
: Logger(log_level), log_prefix(nullptr) {}
: Logger(log_level), log_prefix(nullptr), log_prefix_len(0) {}
explicit StderrLogger(const InfoLogLevel log_level, const std::string prefix)
: Logger(log_level),
log_prefix(strdup(prefix.c_str())),
+1 -1
View File
@@ -1303,7 +1303,7 @@ struct XXH3_state_s {
* Note that this doesn't prepare the state for a streaming operation,
* it's still necessary to use XXH3_NNbits_reset*() afterwards.
*/
#define XXH3_INITSTATE(XXH3_state_ptr) { (XXH3_state_ptr)->seed = 0; }
#define XXH3_INITSTATE(XXH3_state_ptr) do { (XXH3_state_ptr)->seed = 0; } while (0)
/*!
+1 -1
View File
@@ -3962,7 +3962,7 @@ TEST_P(BackupEngineTestWithParam, BackupUsingDirectIO) {
OpenDBAndBackupEngine(true /* destroy_old_data */);
for (int i = 0; i < kNumBackups; ++i) {
FillDB(db_.get(), i * kNumKeysPerBackup /* from */,
(i + 1) * kNumKeysPerBackup /* to */, kFlushAll);
(i + 1) * kNumKeysPerBackup /* to */);
// Clear the file open counters and then do a bunch of backup engine ops.
// For all ops, files should be opened in direct mode.
+38
View File
@@ -13,6 +13,7 @@
#ifndef OS_WIN
#include <unistd.h>
#endif
#include <cstdlib>
#include <iostream>
#include <thread>
#include <utility>
@@ -994,6 +995,43 @@ TEST_F(CheckpointTest, CheckpointWithDbPath) {
delete checkpoint;
}
TEST_F(CheckpointTest, CheckpointWithArchievedLog) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"WalManager::ArchiveWALFile",
"CheckpointTest:CheckpointWithArchievedLog"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.WAL_ttl_seconds = 3600;
options.disable_auto_compactions = true;
DestroyAndReopen(options);
ASSERT_OK(Put("key1", std::string(1024 * 1024, 'a')));
// flush and archive the first log
ASSERT_OK(Flush());
ASSERT_OK(Put("key2", std::string(1024, 'a')));
Checkpoint* checkpoint;
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
TEST_SYNC_POINT("CheckpointTest:CheckpointWithArchievedLog");
ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_, 1024 * 1024));
// unflushed log size < 1024 * 1024 < total file size including archived log,
// so flush shouldn't occur, there is only one file at level 0
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
delete checkpoint;
checkpoint = nullptr;
DB* snapshot_db;
ASSERT_OK(DB::Open(options, snapshot_name_, &snapshot_db));
ReadOptions read_opts;
std::string get_result;
ASSERT_OK(snapshot_db->Get(read_opts, "key1", &get_result));
ASSERT_EQ(std::string(1024 * 1024, 'a'), get_result);
get_result.clear();
ASSERT_OK(snapshot_db->Get(read_opts, "key2", &get_result));
ASSERT_EQ(std::string(1024, 'a'), get_result);
delete snapshot_db;
}
class CheckpointDestroyTest : public CheckpointTest,
public testing::WithParamInterface<bool> {};
+228 -81
View File
@@ -148,9 +148,11 @@ TestFSWritableFile::TestFSWritableFile(const std::string& fname,
file_opts_(file_opts),
target_(std::move(f)),
writable_file_opened_(true),
fs_(fs) {
fs_(fs),
unsync_data_loss_(fs_->InjectUnsyncedDataLoss()) {
assert(target_ != nullptr);
state_.pos_at_last_append_ = 0;
assert(state_.pos_at_last_append_ == 0);
assert(state_.pos_at_last_sync_ == 0);
}
TestFSWritableFile::~TestFSWritableFile() {
@@ -166,13 +168,13 @@ IOStatus TestFSWritableFile::Append(const Slice& data, const IOOptions& options,
return fs_->GetError();
}
IOStatus s =
fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite, options);
IOStatus s = fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
options, state_.filename_);
if (!s.ok()) {
return s;
}
if (target_->use_direct_io()) {
if (target_->use_direct_io() || !unsync_data_loss_) {
// TODO(hx235): buffer data for direct IO write to simulate data loss like
// non-direct IO write
s = target_->Append(data, options, dbg);
@@ -201,8 +203,8 @@ IOStatus TestFSWritableFile::Append(
return IOStatus::Corruption("Data is corrupted!");
}
IOStatus s =
fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite, options);
IOStatus s = fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
options, state_.filename_);
if (!s.ok()) {
return s;
}
@@ -220,7 +222,7 @@ IOStatus TestFSWritableFile::Append(
return IOStatus::Corruption(msg);
}
if (target_->use_direct_io()) {
if (target_->use_direct_io() || !unsync_data_loss_) {
// TODO(hx235): buffer data for direct IO write to simulate data loss like
// non-direct IO write
s = target_->Append(data, options, dbg);
@@ -240,8 +242,8 @@ IOStatus TestFSWritableFile::Truncate(uint64_t size, const IOOptions& options,
if (!fs_->IsFilesystemActive()) {
return fs_->GetError();
}
IOStatus s =
fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite, options);
IOStatus s = fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
options, state_.filename_);
if (!s.ok()) {
return s;
}
@@ -264,8 +266,8 @@ IOStatus TestFSWritableFile::PositionedAppend(const Slice& data,
if (fs_->ShouldDataCorruptionBeforeWrite()) {
return IOStatus::Corruption("Data is corrupted!");
}
IOStatus s =
fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite, options);
IOStatus s = fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
options, state_.filename_);
if (!s.ok()) {
return s;
}
@@ -290,8 +292,8 @@ IOStatus TestFSWritableFile::PositionedAppend(
if (fs_->ShouldDataCorruptionBeforeWrite()) {
return IOStatus::Corruption("Data is corrupted!");
}
IOStatus s =
fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite, options);
IOStatus s = fs_->MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
options, state_.filename_);
if (!s.ok()) {
return s;
}
@@ -376,15 +378,13 @@ IOStatus TestFSWritableFile::RangeSync(uint64_t offset, uint64_t nbytes,
}
// Assumes caller passes consecutive byte ranges.
uint64_t sync_limit = offset + nbytes;
uint64_t buf_begin =
state_.pos_at_last_sync_ < 0 ? 0 : state_.pos_at_last_sync_;
IOStatus io_s;
if (sync_limit < buf_begin) {
if (sync_limit < state_.pos_at_last_sync_) {
return io_s;
}
uint64_t num_to_sync = std::min(static_cast<uint64_t>(state_.buffer_.size()),
sync_limit - buf_begin);
sync_limit - state_.pos_at_last_sync_);
Slice buf_to_sync(state_.buffer_.data(), num_to_sync);
io_s = target_->Append(buf_to_sync, options, dbg);
state_.buffer_ = state_.buffer_.substr(num_to_sync);
@@ -468,7 +468,7 @@ IOStatus TestFSRandomAccessFile::Read(uint64_t offset, size_t n,
return fs_->GetError();
}
IOStatus s = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, options,
FaultInjectionIOType::kRead, options, "",
FaultInjectionTestFS::ErrorOperation::kRead, result, use_direct_io(),
scratch, /*need_count_increase=*/true,
/*fault_injected=*/nullptr);
@@ -485,26 +485,35 @@ IOStatus TestFSRandomAccessFile::ReadAsync(
FSReadRequest& req, const IOOptions& opts,
std::function<void(FSReadRequest&, void*)> cb, void* cb_arg,
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* /*dbg*/) {
IOStatus s;
IOStatus res_status;
FSReadRequest res;
IOStatus s;
if (!fs_->IsFilesystemActive()) {
s = fs_->GetError();
res_status = fs_->GetError();
}
if (s.ok()) {
s = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, opts,
if (res_status.ok()) {
res_status = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, opts, "",
FaultInjectionTestFS::ErrorOperation::kRead, &res.result,
use_direct_io(), req.scratch, /*need_count_increase=*/true,
/*fault_injected=*/nullptr);
}
if (s.ok()) {
if (res_status.ok()) {
s = target_->ReadAsync(req, opts, cb, cb_arg, io_handle, del_fn, nullptr);
// TODO (low priority): fs_->ReadUnsyncedData()
}
if (!s.ok()) {
res.status = s;
} else {
// If there’s no injected error, then cb will be called asynchronously when
// target_ actually finishes the read. But if there’s an injected error, it
// needs to immediately call cb(res, cb_arg) s since target_->ReadAsync()
// isn’t invoked at all.
res.status = res_status;
cb(res, cb_arg);
}
// We return ReadAsync()'s status intead of injected error status here since
// the return status is not supposed to be the status of the actual IO (i.e,
// the actual async read). The actual status of the IO will be passed to cb()
// callback upon the actual read finishes or like above when injected error
// happens.
return s;
}
@@ -524,7 +533,7 @@ IOStatus TestFSRandomAccessFile::MultiRead(FSReadRequest* reqs, size_t num_reqs,
}
bool this_injected_error;
reqs[i].status = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, options,
FaultInjectionIOType::kRead, options, "",
FaultInjectionTestFS::ErrorOperation::kRead, &(reqs[i].result),
use_direct_io(), reqs[i].scratch,
/*need_count_increase=*/true,
@@ -533,7 +542,7 @@ IOStatus TestFSRandomAccessFile::MultiRead(FSReadRequest* reqs, size_t num_reqs,
}
if (s.ok()) {
s = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, options,
FaultInjectionIOType::kRead, options, "",
FaultInjectionTestFS::ErrorOperation::kMultiRead, nullptr,
use_direct_io(), nullptr, /*need_count_increase=*/!injected_error,
/*fault_injected=*/nullptr);
@@ -549,38 +558,44 @@ size_t TestFSRandomAccessFile::GetUniqueId(char* id, size_t max_size) const {
}
}
void FaultInjectionTestFS::AddUnsyncedToRead(const std::string& fname,
size_t pos, size_t n,
Slice* result, char* scratch) {
// Should be checked prior
assert(result->size() < n);
size_t pos_after = pos + result->size();
namespace {
// Modifies `result` to start at the beginning of `scratch` if not already,
// copying data there if needed.
void MoveToScratchIfNeeded(Slice* result, char* scratch) {
if (result->data() != scratch) {
// NOTE: might overlap, where result is later in scratch
std::copy(result->data(), result->data() + result->size(), scratch);
*result = Slice(scratch, result->size());
}
}
} // namespace
void FaultInjectionTestFS::ReadUnsynced(const std::string& fname,
uint64_t offset, size_t n,
Slice* result, char* scratch,
int64_t* pos_at_last_sync) {
*result = Slice(scratch, 0); // default empty result
assert(*pos_at_last_sync == -1); // default "unknown"
MutexLock l(&mutex_);
auto it = db_file_state_.find(fname);
if (it != db_file_state_.end()) {
auto& st = it->second;
if (st.pos_at_last_append_ > static_cast<ssize_t>(pos_after)) {
size_t remaining_requested = n - result->size();
size_t to_copy =
std::min(remaining_requested,
static_cast<size_t>(st.pos_at_last_append_) - pos_after);
size_t buffer_offset = pos_after - static_cast<size_t>(std::max(
st.pos_at_last_sync_, ssize_t{0}));
// Data might have been dropped from buffer
if (st.buffer_.size() > buffer_offset) {
to_copy = std::min(to_copy, st.buffer_.size() - buffer_offset);
if (result->data() != scratch) {
// TODO: this will be needed when supporting random reads
// but not currently used
abort();
// NOTE: might overlap
// std::copy_n(result->data(), result->size(), scratch);
}
std::copy_n(st.buffer_.data() + buffer_offset, to_copy,
scratch + result->size());
*result = Slice(scratch, result->size() + to_copy);
}
*pos_at_last_sync = static_cast<int64_t>(st.pos_at_last_sync_);
// Find overlap between [offset, offset + n) and
// [*pos_at_last_sync, *pos_at_last_sync + st.buffer_.size())
int64_t begin = std::max(static_cast<int64_t>(offset), *pos_at_last_sync);
int64_t end =
std::min(static_cast<int64_t>(offset + n),
*pos_at_last_sync + static_cast<int64_t>(st.buffer_.size()));
// Copy and return overlap if there is any
if (begin < end) {
size_t offset_in_buffer = static_cast<size_t>(begin - *pos_at_last_sync);
size_t offset_in_scratch = static_cast<size_t>(begin - offset);
std::copy_n(st.buffer_.data() + offset_in_buffer, end - begin,
scratch + offset_in_scratch);
*result = Slice(scratch + offset_in_scratch, end - begin);
}
}
}
@@ -589,20 +604,146 @@ IOStatus TestFSSequentialFile::Read(size_t n, const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) {
IOStatus s = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, options,
FaultInjectionIOType::kRead, options, "",
FaultInjectionTestFS::ErrorOperation::kRead, result, use_direct_io(),
scratch, true /*need_count_increase=*/, nullptr /* fault_injected*/);
if (!s.ok()) {
return s;
}
s = target()->Read(n, options, result, scratch, dbg);
if (!s.ok()) {
return s;
// Some complex logic is needed to deal with concurrent write to the same
// file, while keeping good performance (e.g. not holding FS mutex during
// I/O op), especially in common cases.
if (read_pos_ == target_read_pos_) {
// Normal case: start by reading from underlying file
s = target()->Read(n, options, result, scratch, dbg);
if (!s.ok()) {
return s;
}
target_read_pos_ += result->size();
} else {
// We must have previously read buffered data (unsynced) not written to
// target. Deal with this case (and more) below.
*result = {};
}
if (fs_->ReadUnsyncedData() && result->size() < n) {
fs_->AddUnsyncedToRead(fname_, read_pos_, n, result, scratch);
// We need to check if there's unsynced data to fill out the rest of the
// read.
// First, ensure target read data is in scratch for easy handling.
MoveToScratchIfNeeded(result, scratch);
assert(result->data() == scratch);
// If we just did a target Read, we only want unsynced data after it
// (target_read_pos_). Otherwise (e.g. if target is behind because of
// unsynced data) we want unsynced data starting at the current read pos
// (read_pos_, not yet updated).
const uint64_t unsynced_read_pos = std::max(target_read_pos_, read_pos_);
const size_t offset_from_read_pos =
static_cast<size_t>(unsynced_read_pos - read_pos_);
Slice unsynced_result;
int64_t pos_at_last_sync = -1;
fs_->ReadUnsynced(fname_, unsynced_read_pos, n - offset_from_read_pos,
&unsynced_result, scratch + offset_from_read_pos,
&pos_at_last_sync);
assert(unsynced_result.data() >= scratch + offset_from_read_pos);
assert(unsynced_result.data() < scratch + n);
// Now, there are several cases to consider (some grouped together):
if (pos_at_last_sync <= static_cast<int64_t>(unsynced_read_pos)) {
// 1. We didn't get any unsynced data because nothing has been written
// to the file beyond unsynced_read_pos (including untracked
// pos_at_last_sync == -1)
// 2. We got some unsynced data starting at unsynced_read_pos (possibly
// on top of some synced data from target). We don't need to try reading
// any more from target because we established a "point in time" for
// completing this Read in which we read as much tail data (unsynced) as
// we could.
// We got pos_at_last_sync info if we got any unsynced data.
assert(pos_at_last_sync >= 0 || unsynced_result.size() == 0);
// Combined data is already lined up in scratch.
assert(result->data() + result->size() == unsynced_result.data());
assert(result->size() + unsynced_result.size() <= n);
// Combine results
*result = Slice(result->data(), result->size() + unsynced_result.size());
} else {
// 3. Any unsynced data we got was after unsynced_read_pos because the
// file was synced some time since our last target Read (either from this
// Read or a prior Read). We need to read more data from target to ensure
// this Read is filled out, even though we might have already read some
// (but not all due to a race). This code handles:
//
// * Catching up target after prior read(s) of unsynced data
// * Racing Sync in another thread since we called target Read above
//
// And merging potentially three results together for this Read:
// * The original target Read above
// * The following (non-throw-away) target Read
// * The ReadUnsynced above, which is always last if it returned data,
// so that we have a "point in time" for completing this Read in which we
// read as much tail data (unsynced) as we could.
//
// Deeper note about the race: we cannot just treat the original target
// Read as a "point in time" view of available data in the file, because
// there might have been unsynced data at that time, which became synced
// data by the time we read unsynced data. That is the race we are
// resolving with this "double check"-style code.
const size_t supplemental_read_pos = unsynced_read_pos;
// First, if there's any data from target that we know we would need to
// throw away to catch up, try to do it.
if (target_read_pos_ < supplemental_read_pos) {
Slice throw_away_result;
size_t throw_away_n = supplemental_read_pos - target_read_pos_;
std::unique_ptr<char[]> throw_away_scratch{new char[throw_away_n]};
s = target()->Read(throw_away_n, options, &throw_away_result,
throw_away_scratch.get(), dbg);
if (!s.ok()) {
read_pos_ += result->size();
return s;
}
target_read_pos_ += throw_away_result.size();
if (target_read_pos_ < supplemental_read_pos) {
// Because of pos_at_last_sync > supplemental_read_pos, we should
// have been able to catch up
read_pos_ += result->size();
return IOStatus::IOError(
"Unexpected truncation or short read of file " + fname_);
}
}
// Now we can do a productive supplemental Read from target
assert(target_read_pos_ == supplemental_read_pos);
Slice supplemental_result;
size_t supplemental_n =
unsynced_result.size() == 0
? n - offset_from_read_pos
: unsynced_result.data() - (scratch + offset_from_read_pos);
s = target()->Read(supplemental_n, options, &supplemental_result,
scratch + offset_from_read_pos, dbg);
if (!s.ok()) {
read_pos_ += result->size();
return s;
}
target_read_pos_ += supplemental_result.size();
MoveToScratchIfNeeded(&supplemental_result,
scratch + offset_from_read_pos);
// Combined data is already lined up in scratch.
assert(result->data() + result->size() == supplemental_result.data());
assert(unsynced_result.size() == 0 ||
supplemental_result.data() + supplemental_result.size() ==
unsynced_result.data());
assert(result->size() + supplemental_result.size() +
unsynced_result.size() <=
n);
// Combine results
*result =
Slice(result->data(), result->size() + supplemental_result.size() +
unsynced_result.size());
}
}
read_pos_ += result->size();
@@ -614,7 +755,7 @@ IOStatus TestFSSequentialFile::PositionedRead(uint64_t offset, size_t n,
Slice* result, char* scratch,
IODebugContext* dbg) {
IOStatus s = fs_->MaybeInjectThreadLocalError(
FaultInjectionIOType::kRead, options,
FaultInjectionIOType::kRead, options, "",
FaultInjectionTestFS::ErrorOperation::kRead, result, use_direct_io(),
scratch, true /*need_count_increase=*/, nullptr /* fault_injected */);
if (!s.ok()) {
@@ -698,12 +839,12 @@ IOStatus FaultInjectionTestFS::NewWritableFile(
return GetError();
}
if (ShouldUseDiretWritable(fname)) {
if (IsFilesystemDirectWritable()) {
return target()->NewWritableFile(fname, file_opts, result, dbg);
}
IOStatus io_s = MaybeInjectThreadLocalError(
FaultInjectionIOType::kMetadataWrite, file_opts.io_options);
IOStatus io_s = MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
file_opts.io_options, fname);
if (!io_s.ok()) {
return io_s;
}
@@ -735,11 +876,11 @@ IOStatus FaultInjectionTestFS::ReopenWritableFile(
if (!IsFilesystemActive()) {
return GetError();
}
if (ShouldUseDiretWritable(fname)) {
if (IsFilesystemDirectWritable()) {
return target()->ReopenWritableFile(fname, file_opts, result, dbg);
}
IOStatus io_s = MaybeInjectThreadLocalError(
FaultInjectionIOType::kMetadataWrite, file_opts.io_options);
IOStatus io_s = MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
file_opts.io_options, fname);
if (!io_s.ok()) {
return io_s;
}
@@ -811,11 +952,11 @@ IOStatus FaultInjectionTestFS::NewRandomRWFile(
if (!IsFilesystemActive()) {
return GetError();
}
if (ShouldUseDiretWritable(fname)) {
if (IsFilesystemDirectWritable()) {
return target()->NewRandomRWFile(fname, file_opts, result, dbg);
}
IOStatus io_s = MaybeInjectThreadLocalError(
FaultInjectionIOType::kMetadataWrite, file_opts.io_options);
IOStatus io_s = MaybeInjectThreadLocalError(FaultInjectionIOType::kWrite,
file_opts.io_options, fname);
if (!io_s.ok()) {
return io_s;
}
@@ -847,7 +988,10 @@ IOStatus FaultInjectionTestFS::NewRandomAccessFile(
return GetError();
}
IOStatus io_s = MaybeInjectThreadLocalError(
FaultInjectionIOType::kMetadataWrite, file_opts.io_options);
FaultInjectionIOType::kRead, file_opts.io_options, fname,
ErrorOperation::kOpen, nullptr /* result */, false /* direct_io */,
nullptr /* scratch */, true /*need_count_increase*/,
nullptr /*fault_injected*/);
if (!io_s.ok()) {
return io_s;
}
@@ -867,7 +1011,10 @@ IOStatus FaultInjectionTestFS::NewSequentialFile(
return GetError();
}
IOStatus io_s = MaybeInjectThreadLocalError(
FaultInjectionIOType::kMetadataWrite, file_opts.io_options);
FaultInjectionIOType::kRead, file_opts.io_options, fname,
ErrorOperation::kOpen, nullptr /* result */, false /* direct_io */,
nullptr /* scratch */, true /*need_count_increase*/,
nullptr /*fault_injected*/);
if (!io_s.ok()) {
return io_s;
}
@@ -985,7 +1132,6 @@ IOStatus FaultInjectionTestFS::RenameFile(const std::string& s,
auto tdn = TestFSGetDirAndName(t);
if (dir_to_new_files_since_last_sync_[sdn.first].erase(sdn.second) != 0) {
auto& tlist = dir_to_new_files_since_last_sync_[tdn.first];
assert(tlist.find(tdn.second) == tlist.end());
tlist[tdn.second] = previous_contents;
}
}
@@ -1228,8 +1374,7 @@ IOStatus FaultInjectionTestFS::MaybeInjectThreadLocalReadError(
ErrorContext* ctx =
static_cast<ErrorContext*>(injected_thread_local_read_error_.Get());
if (ctx == nullptr || !ctx->enable_error_injection || !ctx->one_in ||
io_activties_exempted_from_fault_injection.find(io_options.io_activity) !=
io_activties_exempted_from_fault_injection.end()) {
ShouldIOActivtiesExcludedFromFaultInjection(io_options.io_activity)) {
return IOStatus::OK();
}
@@ -1295,8 +1440,9 @@ bool FaultInjectionTestFS::TryParseFileName(const std::string& file_name,
}
IOStatus FaultInjectionTestFS::MaybeInjectThreadLocalError(
FaultInjectionIOType type, const IOOptions& io_options, ErrorOperation op,
Slice* result, bool direct_io, char* scratch, bool need_count_increase,
FaultInjectionIOType type, const IOOptions& io_options,
const std::string& file_name, ErrorOperation op, Slice* result,
bool direct_io, char* scratch, bool need_count_increase,
bool* fault_injected) {
if (type == FaultInjectionIOType::kRead) {
return MaybeInjectThreadLocalReadError(io_options, op, result, direct_io,
@@ -1306,8 +1452,9 @@ IOStatus FaultInjectionTestFS::MaybeInjectThreadLocalError(
ErrorContext* ctx = GetErrorContextFromFaultInjectionIOType(type);
if (ctx == nullptr || !ctx->enable_error_injection || !ctx->one_in ||
io_activties_exempted_from_fault_injection.find(io_options.io_activity) !=
io_activties_exempted_from_fault_injection.end()) {
ShouldIOActivtiesExcludedFromFaultInjection(io_options.io_activity) ||
(type == FaultInjectionIOType::kWrite &&
ShouldExcludeFromWriteFaultInjection(file_name))) {
return IOStatus::OK();
}
+60 -35
View File
@@ -41,17 +41,15 @@ enum class FaultInjectionIOType {
struct FSFileState {
std::string filename_;
ssize_t pos_at_last_append_;
ssize_t pos_at_last_sync_;
uint64_t pos_at_last_append_ = 0;
uint64_t pos_at_last_sync_ = 0;
std::string buffer_;
explicit FSFileState(const std::string& filename)
: filename_(filename), pos_at_last_append_(-1), pos_at_last_sync_(-1) {}
FSFileState() : pos_at_last_append_(-1), pos_at_last_sync_(-1) {}
explicit FSFileState(const std::string& filename = {})
: filename_(filename) {}
bool IsFullySynced() const {
return pos_at_last_append_ <= 0 || pos_at_last_append_ == pos_at_last_sync_;
return pos_at_last_append_ == pos_at_last_sync_;
}
IOStatus DropUnsyncedData();
@@ -105,6 +103,7 @@ class TestFSWritableFile : public FSWritableFile {
bool writable_file_opened_;
FaultInjectionTestFS* fs_;
port::Mutex mutex_;
const bool unsync_data_loss_;
};
// A wrapper around WritableFileWriter* file
@@ -177,7 +176,8 @@ class TestFSSequentialFile : public FSSequentialFileOwnerWrapper {
private:
FaultInjectionTestFS* fs_;
std::string fname_;
size_t read_pos_ = 0;
uint64_t read_pos_ = 0;
uint64_t target_read_pos_ = 0;
};
class TestFSDirectory : public FSDirectory {
@@ -207,6 +207,7 @@ class FaultInjectionTestFS : public FileSystemWrapper {
: FileSystemWrapper(base),
filesystem_active_(true),
filesystem_writable_(false),
inject_unsynced_data_loss_(false),
read_unsynced_data_(true),
allow_link_open_file_(false),
injected_thread_local_read_error_(DeleteThreadLocalErrorContext),
@@ -359,19 +360,6 @@ class FaultInjectionTestFS : public FileSystemWrapper {
MutexLock l(&mutex_);
return filesystem_writable_;
}
bool ShouldUseDiretWritable(const std::string& file_name) {
MutexLock l(&mutex_);
if (filesystem_writable_) {
return true;
}
FileType file_type = kTempFile;
uint64_t file_number = 0;
if (!TryParseFileName(file_name, &file_number, &file_type)) {
return false;
}
return direct_writable_types_.find(file_type) !=
direct_writable_types_.end();
}
void SetFilesystemActiveNoLock(
bool active, IOStatus error = IOStatus::Corruption("Not active")) {
error.PermitUncheckedError();
@@ -391,6 +379,18 @@ class FaultInjectionTestFS : public FileSystemWrapper {
filesystem_writable_ = writable;
}
// If true, we buffer write data in memory to simulate data loss upon system
// crash by only having process crashes
void SetInjectUnsyncedDataLoss(bool inject) {
MutexLock l(&mutex_);
inject_unsynced_data_loss_ = inject;
}
bool InjectUnsyncedDataLoss() {
MutexLock l(&mutex_);
return inject_unsynced_data_loss_;
}
// In places (e.g. GetSortedWals()) RocksDB relies on querying the file size
// or even reading the contents of files currently open for writing, and
// as in POSIX semantics, expects to see the flushed size and contents
@@ -414,15 +414,10 @@ class FaultInjectionTestFS : public FileSystemWrapper {
allow_link_open_file_ = allow_link_open_file;
}
void SetDirectWritableTypes(const std::set<FileType>& types) {
bool ShouldIOActivtiesExcludedFromFaultInjection(Env::IOActivity io_activty) {
MutexLock l(&mutex_);
direct_writable_types_ = types;
}
void SetIOActivtiesExemptedFromFaultInjection(
const std::set<Env::IOActivity>& io_activties) {
MutexLock l(&mutex_);
io_activties_exempted_from_fault_injection = io_activties;
return io_activties_excluded_from_fault_injection.find(io_activty) !=
io_activties_excluded_from_fault_injection.end();
}
void AssertNoOpenFile() { assert(open_managed_files_.empty()); }
@@ -499,8 +494,8 @@ class FaultInjectionTestFS : public FileSystemWrapper {
IOStatus MaybeInjectThreadLocalError(
FaultInjectionIOType type, const IOOptions& io_options,
ErrorOperation op = kUnknown, Slice* slice = nullptr,
bool direct_io = false, char* scratch = nullptr,
const std::string& file_name = "", ErrorOperation op = kUnknown,
Slice* slice = nullptr, bool direct_io = false, char* scratch = nullptr,
bool need_count_increase = false, bool* fault_injected = nullptr);
int GetAndResetInjectedThreadLocalErrorCount(FaultInjectionIOType type) {
@@ -513,6 +508,29 @@ class FaultInjectionTestFS : public FileSystemWrapper {
return count;
}
void SetIOActivtiesExcludedFromFaultInjection(
const std::set<Env::IOActivity>& io_activties) {
MutexLock l(&mutex_);
io_activties_excluded_from_fault_injection = io_activties;
}
void SetFileTypesExcludedFromWriteFaultInjection(
const std::set<FileType>& types) {
MutexLock l(&mutex_);
file_types_excluded_from_write_fault_injection_ = types;
}
bool ShouldExcludeFromWriteFaultInjection(const std::string& file_name) {
MutexLock l(&mutex_);
FileType file_type = kTempFile;
uint64_t file_number = 0;
if (!TryParseFileName(file_name, &file_number, &file_type)) {
return false;
}
return file_types_excluded_from_write_fault_injection_.find(file_type) !=
file_types_excluded_from_write_fault_injection_.end();
}
void EnableThreadLocalErrorInjection(FaultInjectionIOType type) {
ErrorContext* ctx = GetErrorContextFromFaultInjectionIOType(type);
if (ctx) {
@@ -529,8 +547,14 @@ class FaultInjectionTestFS : public FileSystemWrapper {
void PrintInjectedThreadLocalErrorBacktrace(FaultInjectionIOType type);
void AddUnsyncedToRead(const std::string& fname, size_t offset, size_t n,
Slice* result, char* scratch);
// If there is unsynced data in the specified file within the specified
// range [offset, offset + n), return the unsynced data overlapping with
// that range, in a corresponding range of scratch. When known, also return
// the position of the last sync, so that the caller can determine whether
// more data is available from the target file when not available from
// unsynced.
void ReadUnsynced(const std::string& fname, uint64_t offset, size_t n,
Slice* result, char* scratch, int64_t* pos_at_last_sync);
private:
port::Mutex mutex_;
@@ -545,6 +569,7 @@ class FaultInjectionTestFS : public FileSystemWrapper {
bool filesystem_active_; // Record flushes, syncs, writes
bool filesystem_writable_; // Bypass FaultInjectionTestFS and go directly
// to underlying FS for writable files
bool inject_unsynced_data_loss_; // See InjectUnsyncedDataLoss()
bool read_unsynced_data_; // See SetReadUnsyncedData()
bool allow_link_open_file_; // See SetAllowLinkOpenFile()
IOStatus fs_error_;
@@ -582,8 +607,8 @@ class FaultInjectionTestFS : public FileSystemWrapper {
}
};
std::set<FileType> direct_writable_types_;
std::set<Env::IOActivity> io_activties_exempted_from_fault_injection;
std::set<FileType> file_types_excluded_from_write_fault_injection_;
std::set<Env::IOActivity> io_activties_excluded_from_fault_injection;
ThreadLocalPtr injected_thread_local_read_error_;
ThreadLocalPtr injected_thread_local_write_error_;
ThreadLocalPtr injected_thread_local_metadata_read_error_;
@@ -139,8 +139,6 @@ std::shared_ptr<CompactForTieringCollectorFactory>
NewCompactForTieringCollectorFactory(double compaction_trigger_ratio) {
return std::make_shared<CompactForTieringCollectorFactory>(
compaction_trigger_ratio);
return std::shared_ptr<CompactForTieringCollectorFactory>(
new CompactForTieringCollectorFactory(compaction_trigger_ratio));
}
} // namespace ROCKSDB_NAMESPACE
@@ -1227,14 +1227,15 @@ Status PessimisticTransaction::SetName(const TransactionName& name) {
if (txn_state_ == STARTED) {
if (name_.length()) {
s = Status::InvalidArgument("Transaction has already been named.");
} else if (txn_db_impl_->GetTransactionByName(name) != nullptr) {
s = Status::InvalidArgument("Transaction name must be unique.");
} else if (name.length() < 1 || name.length() > 512) {
s = Status::InvalidArgument(
"Transaction name length must be between 1 and 512 chars.");
} else {
name_ = name;
txn_db_impl_->RegisterTransaction(this);
s = txn_db_impl_->RegisterTransaction(this);
if (!s.ok()) {
name_.clear();
}
}
} else {
s = Status::InvalidArgument("Transaction is beyond state for naming.");
@@ -723,6 +723,11 @@ void PessimisticTransactionDB::ReinitializeTransaction(
Transaction* PessimisticTransactionDB::GetTransactionByName(
const TransactionName& name) {
std::lock_guard<std::mutex> lock(name_map_mutex_);
return GetTransactionByNameLocked(name);
}
Transaction* PessimisticTransactionDB::GetTransactionByNameLocked(
const TransactionName& name) {
auto it = transactions_.find(name);
if (it == transactions_.end()) {
return nullptr;
@@ -755,13 +760,15 @@ void PessimisticTransactionDB::SetDeadlockInfoBufferSize(uint32_t target_size) {
lock_manager_->Resize(target_size);
}
void PessimisticTransactionDB::RegisterTransaction(Transaction* txn) {
Status PessimisticTransactionDB::RegisterTransaction(Transaction* txn) {
assert(txn);
assert(txn->GetName().length() > 0);
assert(GetTransactionByName(txn->GetName()) == nullptr);
assert(txn->GetState() == Transaction::STARTED);
std::lock_guard<std::mutex> lock(name_map_mutex_);
transactions_[txn->GetName()] = txn;
if (!transactions_.insert({txn->GetName(), txn}).second) {
return Status::InvalidArgument("Duplicate txn name " + txn->GetName());
}
return Status::OK();
}
void PessimisticTransactionDB::UnregisterTransaction(Transaction* txn) {
@@ -173,7 +173,7 @@ class PessimisticTransactionDB : public TransactionDB {
Transaction* GetTransactionByName(const TransactionName& name) override;
void RegisterTransaction(Transaction* txn);
Status RegisterTransaction(Transaction* txn);
void UnregisterTransaction(Transaction* txn);
// not thread safe. current use case is during recovery (single thread)
@@ -239,6 +239,7 @@ class PessimisticTransactionDB : public TransactionDB {
friend class WriteUnpreparedTransactionTest_MarkLogWithPrepSection_Test;
Transaction* BeginInternalTransaction(const WriteOptions& options);
Transaction* GetTransactionByNameLocked(const TransactionName& name);
std::shared_ptr<LockManager> lock_manager_;
+25 -5
View File
@@ -832,11 +832,13 @@ Status TransactionBaseImpl::RebuildFromWriteBatch(WriteBatch* src_batch) {
}
Status PutCF(uint32_t cf, const Slice& key, const Slice& val) override {
return txn_->Put(db_->GetColumnFamilyHandle(cf), key, val);
Slice user_key = GetUserKey(cf, key);
return txn_->Put(db_->GetColumnFamilyHandle(cf), user_key, val);
}
Status PutEntityCF(uint32_t cf, const Slice& key,
const Slice& entity) override {
Slice user_key = GetUserKey(cf, key);
Slice entity_copy = entity;
WideColumns columns;
const Status s =
@@ -845,19 +847,22 @@ Status TransactionBaseImpl::RebuildFromWriteBatch(WriteBatch* src_batch) {
return s;
}
return txn_->PutEntity(db_->GetColumnFamilyHandle(cf), key, columns);
return txn_->PutEntity(db_->GetColumnFamilyHandle(cf), user_key, columns);
}
Status DeleteCF(uint32_t cf, const Slice& key) override {
return txn_->Delete(db_->GetColumnFamilyHandle(cf), key);
Slice user_key = GetUserKey(cf, key);
return txn_->Delete(db_->GetColumnFamilyHandle(cf), user_key);
}
Status SingleDeleteCF(uint32_t cf, const Slice& key) override {
return txn_->SingleDelete(db_->GetColumnFamilyHandle(cf), key);
Slice user_key = GetUserKey(cf, key);
return txn_->SingleDelete(db_->GetColumnFamilyHandle(cf), user_key);
}
Status MergeCF(uint32_t cf, const Slice& key, const Slice& val) override {
return txn_->Merge(db_->GetColumnFamilyHandle(cf), key, val);
Slice user_key = GetUserKey(cf, key);
return txn_->Merge(db_->GetColumnFamilyHandle(cf), user_key, val);
}
// this is used for reconstructing prepared transactions upon
@@ -880,6 +885,21 @@ Status TransactionBaseImpl::RebuildFromWriteBatch(WriteBatch* src_batch) {
Status MarkRollback(const Slice&) override {
return Status::InvalidArgument();
}
size_t GetTimestampSize(uint32_t cf_id) {
auto cfd = db_->versions_->GetColumnFamilySet()->GetColumnFamily(cf_id);
const Comparator* ucmp = cfd->user_comparator();
assert(ucmp);
return ucmp->timestamp_size();
}
Slice GetUserKey(uint32_t cf_id, const Slice& key) {
size_t ts_sz = GetTimestampSize(cf_id);
if (ts_sz == 0) {
return key;
}
assert(key.size() >= ts_sz);
return Slice(key.data(), key.size() - ts_sz);
}
};
IndexedWriteBatchBuilder copycat(this, dbimpl_);
@@ -288,12 +288,25 @@ TEST_P(WriteCommittedTxnWithTsTest, RecoverFromWal) {
txn0.reset();
std::unique_ptr<Transaction> txn3(
NewTxn(WriteOptions(), TransactionOptions()));
assert(txn3);
ASSERT_OK(txn3->Put(handles_[1], "baz", "baz_value"));
ASSERT_OK(txn3->SetName("txn3"));
ASSERT_OK(txn3->Prepare());
txn3.reset();
ASSERT_OK(ReOpenNoDelete(cf_descs, &handles_));
{
Transaction* recovered_txn0 = db->GetTransactionByName("txn0");
ASSERT_OK(recovered_txn0->SetCommitTimestamp(23));
ASSERT_OK(recovered_txn0->Commit());
delete recovered_txn0;
std::string value;
Status s = GetFromDb(ReadOptions(), handles_[1], "foo", /*ts=*/23, &value);
ASSERT_TRUE(s.IsNotFound());
ASSERT_OK(s);
ASSERT_EQ("foo_value", value);
s = db->Get(ReadOptions(), handles_[0], "bar", &value);
ASSERT_OK(s);
@@ -314,6 +327,9 @@ TEST_P(WriteCommittedTxnWithTsTest, RecoverFromWal) {
s = GetFromDb(ReadOptions(), handles_[1], "key1", /*ts=*/24, &value);
ASSERT_OK(s);
ASSERT_EQ("value_3", value);
s = GetFromDb(ReadOptions(), handles_[1], "baz", /*ts=*/24, &value);
ASSERT_TRUE(s.IsNotFound());
}
}