Compare commits

..
5 Commits
Author SHA1 Message Date
Anand Ananthabhotla ec93377878 Make InjectedErrorLog::Entry 256 bytes on 32-bit targets (#15388)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15388

`InjectedErrorLog::Entry` in `utilities/fault_injection_fs.h` is written to the injected error log in its native layout, and a `static_assert` requires `sizeof(Entry) == 256` so the record size stays stable. That assert fails on i386 builds.

Root cause: the fields add up to only 252 bytes. On targets where `alignof(uint64_t)` is 8 (x86_64, aarch64, armv7), the compiler adds 4 bytes of implicit tail padding, so the size is 256. On i386, `uint64_t` members are 4-byte aligned inside a struct, so there is no tail padding and the size is 252.

Fix: add an explicit trailing `uint8_t padding[4]` field so the size no longer depends on `uint64_t` alignment.

Compatibility: on 64-bit targets the new field takes the place of the old implicit tail padding, so every field offset and the 256-byte record layout are unchanged. `Record()` value-initializes the entry (`Entry entry{}`), so the padding bytes are always written as zero. No decoder in the repo depends on the padding bytes.

___

landed-with-radar-review

Differential Revision: D124873369

fbshipit-source-id: f5c6f43ca85013b0e769853ad13b8d4f3f89b958
2026-10-13 10:29:39 -07:00
xingbowang 0e1c7f9e62 Fix big-endian builds for the fault-injection filesystem (#15297)
Summary:
The persistent injected-error log writes `InjectedErrorLog::Entry` objects using their native in-memory layout, while the log format's consumer expects little-endian multi-byte fields. The existing `static_assert(port::kLittleEndian)` protected that format by rejecting every big-endian build that included the fault-injection filesystem, even though the log is an optional diagnostic.

This change keeps RocksDB and the fault-injection filesystem available on supported big-endian platforms while disabling only the incompatible persistent log. Log-file creation now returns an inactive descriptor when `port::kLittleEndian` is false, which makes `Record()` and `Flush()` remain safe no-ops. The fixed 256-byte entry-size assertion remains in place, and format-dependent tests are bypassed on big-endian targets.

The change also adds a release note and documents big-endian support plus the preferred capability-check pattern for optional architecture-specific features.

## Compatibility and limitations

- Little-endian behavior and the on-disk log layout are unchanged.
- On big-endian platforms, configuring an injected-error log path does not create or populate a log file; the rest of fault injection remains available.
- Windows already leaves this POSIX-backed diagnostic disabled.
- This does not introduce a portable log encoding. Supporting persistent injected-error logs on big-endian platforms would require a format change.

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

Test Plan:
- `build_tools/rockstest.sh fault_injection_fs_test '--gtest_filter=InjectedErrorLogTest.*'` (little-endian macOS): all 5 tests passed.
- `make check-sources`
- `git diff --check upstream/main...2026_10_01_endian_fix`

Native big-endian build and test execution were not available locally; that path relies on the compile-time `port::kLittleEndian` capability and should be covered by big-endian CI.

Reviewed By: jaykorean

Differential Revision: D122880760

Pulled By: xingbowang

fbshipit-source-id: 59dae9661a47608aa12f41078ad1b3ef0fa7231e
2026-10-13 10:29:33 -07:00
Jay Huh c12d364ad1 Update version and HISTORY.md for 11.11.1 release 2026-09-24 13:22:58 -07:00
Jay Huh 766ca8f985 Make SecondaryDB multi-CF iterator reads non-blocking (#15259)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15259

Publish immutable SecondaryDB read views after open and successful catch-up so `NewIterators()` can acquire one cross-CF sequence and SuperVersion set without waiting for `DBImpl::mutex_`. Keep failed catch-ups on the previous view, reject refresh for batch iterators, and exercise concurrent catch-up plus batch scans in the CF-consistency stress profile.

Reviewed By: xingbowang

Differential Revision: D121318547

fbshipit-source-id: 3198f4766c90ab0bb39400963df3c329b4ecf6bf
2026-09-24 13:12:24 -07:00
meta-rocksdb-release-automation[bot] 3c8f8f417d Update HISTORY.md for 11.11.0 release 2026-09-23 00:40:33 +00:00
254 changed files with 3382 additions and 17345 deletions

No files matched your search

-1
View File
@@ -12,7 +12,6 @@ jobs:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
with:
+2 -2
View File
@@ -1,4 +1,4 @@
name: facebook/rocksdb/nightly-candidate
name: facebook/rocksdb/nightly
on: workflow_dispatch
permissions: {}
jobs:
@@ -10,7 +10,7 @@ jobs:
labels: arm64large
container:
image: ubuntu-2004:202111-02
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
+8 -8
View File
@@ -11,7 +11,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
with:
@@ -33,7 +33,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
env:
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
steps:
@@ -47,7 +47,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=32gb
options: --shm-size=16gb
env:
CC: clang-21
CXX: clang++-21
@@ -84,7 +84,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -103,7 +103,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -161,7 +161,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -174,7 +174,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -189,7 +189,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
+18 -18
View File
@@ -92,7 +92,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -110,7 +110,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -135,7 +135,7 @@ jobs:
labels: 32-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=64gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -160,7 +160,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -180,7 +180,7 @@ jobs:
labels: 32-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=64gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -209,7 +209,7 @@ jobs:
shard: [0, 1, 2, 3]
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -232,7 +232,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -252,7 +252,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/setup-ccache"
@@ -306,7 +306,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -373,7 +373,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -394,7 +394,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: gcc:latest
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- run: apt-get update -y && apt-get install -y libgflags-dev
@@ -422,7 +422,7 @@ jobs:
crash_duration: 240
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=8gb --ulimit nofile=1048576:1048576
options: --shm-size=16gb --ulimit nofile=1048576:1048576
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -457,7 +457,7 @@ jobs:
shard: [0, 1, 2]
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=64gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -484,7 +484,7 @@ jobs:
shard: [0, 1, 2]
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=64gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -507,7 +507,7 @@ jobs:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -614,7 +614,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -637,7 +637,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -740,7 +740,7 @@ jobs:
labels: 4-core-ubuntu
container:
image: evolvedbinary/rocksjava:alpine3_x64-be
options: --shm-size=8gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/install-maven"
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
timeout-minutes: 840
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=32gb
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
+4 -60
View File
@@ -241,7 +241,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"table/plain/plain_table_index.cc",
"table/plain/plain_table_key_coding.cc",
"table/plain/plain_table_reader.cc",
"table/range_del_block.cc",
"table/sst_file_dumper.cc",
"table/sst_file_reader.cc",
"table/sst_file_writer.cc",
@@ -416,27 +415,13 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
"table/mock_table.cc",
"test_util/mock_time_env.cc",
"test_util/secondary_cache_test_util.cc",
"test_util/simple_external_table_factory.cc",
"test_util/testharness.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
"utilities/agg_merge/test_agg_merge.cc",
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_lib"], headers=[
"db/db_test_util.h",
"db/db_with_timestamp_test_util.h",
"table/mock_table.h",
"test_util/mock_time_env.h",
"test_util/secondary_cache_test_util.h",
"test_util/simple_external_table_factory.h",
"test_util/testharness.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
"utilities/agg_merge/test_agg_merge.h",
"utilities/cassandra/test_utils.h",
], link_whole=False, extra_test_libs=True)
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
"db/db_test_util.cc",
@@ -444,27 +429,13 @@ cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
"table/mock_table.cc",
"test_util/mock_time_env.cc",
"test_util/secondary_cache_test_util.cc",
"test_util/simple_external_table_factory.cc",
"test_util/testharness.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
"utilities/agg_merge/test_agg_merge.cc",
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_with_faiss_lib"], headers=[
"db/db_test_util.h",
"db/db_with_timestamp_test_util.h",
"table/mock_table.h",
"test_util/mock_time_env.h",
"test_util/secondary_cache_test_util.h",
"test_util/simple_external_table_factory.h",
"test_util/testharness.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
"utilities/agg_merge/test_agg_merge.h",
"utilities/cassandra/test_utils.h",
], link_whole=False, extra_test_libs=True)
], deps=[":rocksdb_with_faiss_lib"], headers=[], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
"test_util/testutil.cc",
@@ -473,12 +444,7 @@ cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
"tools/simulated_hybrid_file_system.cc",
"tools/tool_hooks.cc",
"tools/trace_analyzer_tool.cc",
], deps=[":rocksdb_lib"], headers=[
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/simulated_hybrid_file_system.h",
"tools/trace_analyzer_tool.h",
], link_whole=False, extra_test_libs=False)
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
@@ -502,28 +468,10 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
"db_stress_tool/expected_value.cc",
"db_stress_tool/multi_ops_txns_stress.cc",
"db_stress_tool/no_batched_ops_stress.cc",
"test_util/simple_external_table_factory.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
], headers=[
"db_stress_tool/db_stress_common.h",
"db_stress_tool/db_stress_compaction_service.h",
"db_stress_tool/db_stress_compression_manager.h",
"db_stress_tool/db_stress_driver.h",
"db_stress_tool/db_stress_filters.h",
"db_stress_tool/db_stress_listener.h",
"db_stress_tool/db_stress_shared_state.h",
"db_stress_tool/db_stress_test_base.h",
"db_stress_tool/db_stress_wide_merge_operator.h",
"db_stress_tool/expected_state.h",
"db_stress_tool/expected_value.h",
"db_stress_tool/multi_ops_txns_stress.h",
"test_util/simple_external_table_factory.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
])
], headers=[])
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
@@ -4813,7 +4761,6 @@ cpp_unittest_wrapper(name="compression_test",
cpp_unittest_wrapper(name="configurable_test",
srcs=["options/configurable_test.cc"],
source_headers=["options/configurable_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -5518,7 +5465,6 @@ cpp_unittest_wrapper(name="periodic_task_scheduler_test",
cpp_unittest_wrapper(name="persistent_cache_test",
srcs=["utilities/persistent_cache/persistent_cache_test.cc"],
source_headers=["utilities/persistent_cache/persistent_cache_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -5537,7 +5483,6 @@ cpp_unittest_wrapper(name="point_lock_manager_stress_test",
cpp_unittest_wrapper(name="point_lock_manager_test",
srcs=["utilities/transactions/lock/point/point_lock_manager_test.cc"],
source_headers=["utilities/transactions/lock/point/point_lock_manager_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -5748,7 +5693,6 @@ cpp_unittest_wrapper(name="trace_analyzer_test",
cpp_unittest_wrapper(name="transaction_test",
srcs=["utilities/transactions/transaction_test.cc"],
source_headers=["utilities/transactions/transaction_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
-5
View File
@@ -244,11 +244,6 @@ from an implementation detail instead of an explicit option.
## Important tips
### Pull request and diff summaries
* Before writing or updating an external RocksDB pull request summary or its
corresponding internal Phabricator diff summary, read and follow
`claude_md/pull_request_summary_guideline.md`.
### Build system
* There are 3 build system. Make for git clones, BUCK (meta internal) for hg
clones, and CMake for some special cases.
-2
View File
@@ -972,7 +972,6 @@ set(SOURCES
table/plain/plain_table_index.cc
table/plain/plain_table_key_coding.cc
table/plain/plain_table_reader.cc
table/range_del_block.cc
table/sst_file_dumper.cc
table/sst_file_reader.cc
table/sst_file_writer.cc
@@ -1689,7 +1688,6 @@ if(WITH_TESTS)
db/db_with_timestamp_test_util.cc
monitoring/thread_status_updater_debug.cc
table/mock_table.cc
test_util/simple_external_table_factory.cc
utilities/agg_merge/test_agg_merge.cc
utilities/cassandra/test_utils.cc
)
+1 -30
View File
@@ -1,41 +1,13 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 11.12.0 (10/07/2026)
### New Features
* Added `FullExternalTableFactory` and related full-mode external table APIs that preserve RocksDB internal keys, enabling external tables to support nonzero sequence numbers, point and single deletions, merge operands, blob indexes, wide-column entities, snapshots, live DB writes, and multi-level compactions.
### Public API Changes
* Added experimental coroutine capability interfaces for basic and full `ExternalTableReader` implementations. Coroutine-capable external readers can initiate file reads through `FSRandomAccessFile::SubmitReadAsync()`.
* `ExternalTableFactory::NewTableReader()` now receives an already-opened `FSRandomAccessFile` and its file size instead of requiring implementations to reopen `file_path`. The supplied file preserves RocksDB's direct-I/O alignment handling, rate limiting, read statistics and listener notifications, prefetching, and synchronous, asynchronous, and batched reads.
* Added `PutRangeDeletionBlock()`, `GetRangeDeletionBlock()`, and `IsDeleteRangeSupported()` to the external table interfaces so formats can opt in to range deletions using RocksDB's raw range-deletion metadata block.
* Opaque `PreparedFileInfo` metadata can now be serialized to a checksummed representation with `SerializePreparedFileInfo()` and restored with `DeserializePreparedFileInfo()` for reuse across processes.
* Removed the deprecated `IngestExternalFileOptions::write_global_seqno` option and its C, Java, and `ldb` APIs, `TableProperties::external_sst_file_global_seqno_offset`, and the `file_offset` output from `ExternalTableReader::GetPropertiesBlock()`. Existing external SST files whose global sequence number was rewritten without updating the properties-block checksum remain readable.
* `TablePropertiesNames` has no apparent use in the public API so has been deprecated there.
* `TableProperties::creation_time` is renamed to `oldest_ancestor_time`, which better describes its meaning, along with `TablePropertiesNames::kCreationTime` to `kOldestAncestorTime`. The misspelled `SstFileMetaData::oldest_ancester_time` is renamed to `oldest_ancestor_time`. The old names remain available as deprecated aliases and will be removed in a future release.
* Added `WriteBufferFlushPolicy` and `WriteBufferManager` policy selection for flushing the oldest or largest column family, including configurable batched flushing of the largest column families across DBs sharing a manager, with C and Java bindings.
## 11.11.1 (09/24/2026)
### Behavior Changes
* When `ExternalTableBuilderBase::PutPropertiesBlock()` returns `NotSupported`, RocksDB now overrides `num_entries`, `raw_key_size`, and `raw_value_size` from `ExternalTableBuilderBase::GetTableProperties()` with the logical totals tracked by the external-table adapter. Format-specific properties such as `data_size` remain unchanged.
* The `FSWritableFile` passed to `ExternalTableFactory::NewTableBuilder()` now reports logical file size including RocksDB-buffered data and exposes the underlying file's direct-I/O mode and required buffer alignment.
* `Iterator::Refresh()` now returns `NotSupported` on iterators from a secondary DB's `DB::NewIterators()` only, because those iterators are pinned to one cross-column-family read view. Iterators from `DB::NewIterator()` are unaffected; to advance a batch, call `DB::NewIterators()` again after `TryCatchUpWithPrimary()`.
* `SstFileReader` now reports standard SST read statistics and file I/O listener notifications, and honors the configured read rate limiter.
### Bug Fixes
* Fixed full recovery from retryable background errors to atomically advance WAL replay boundaries for all column families, preventing partial recovery of multi-column-family write batches after a failed recovery flush.
* Fixed RocksDB builds on big-endian architectures such as s390x by disabling the optional persistent fault-injection log, whose binary format is little-endian-only.
* Fixed `DB::ClipColumnFamily()` deleting a non-L0 SST file whose largest key equals `begin_key`, which lost keys at the start of the kept range.
* Fixed swapped `oldest_key_time` and `newest_key_time` values in the `table_file_creation` event log entry.
* Fixed `ExternalTableReader::VerifyChecksum()` results not being honored by the external table adapter.
* Fixed a bug in manifest handling that could lead to a hang or memory corruption in the case of LSM sanity checks being fatally violated (exceptional; see `force_consistency_checks`).
* Fixed `MultiGet()` with `ReadOptions::read_tier == kBlockCacheTier` reading data blocks from disk when `allow_mmap_reads` is false. Keys whose data block is not in the block cache now return `Status::Incomplete()`, the same as `Get()`.
* Fixed trace replay (`DB::NewDefaultReplayer()`, db_bench `replay`) failing every read record (Get, MultiGet, iterator seek) on the default column family with `Corruption: Invalid Column Family ID` when the default column family handle was not passed, which is always the case in db_bench with `--num_column_families=1`.
* Fixed multi-threaded `Replayer::Replay()` ignoring errors from executing trace records and returning OK. It now stops and returns the error, including `Status::NotSupported()` from executing a record, as single-threaded replay does.
* Fixed secondary DB `NewIterators()` returning iterators from different database states or blocking for `TryCatchUpWithPrimary()` by serving the last completed catch-up view.
### Performance Improvements
* Single Get coroutine reads using non-mmap, non-direct I/O on a `kFSBuffer`-capable `FileSystem` now use `FSRandomAccessFile::SubmitReadAsync()` instead of falling back to synchronous `Read()`. When rate-limited, these reads acquire their full token budget before submission to preserve a single filesystem-owned buffer, which can make physical I/O burstier than reads using caller-provided buffers.
## 11.11.0 (09/21/2026)
### New Features
* External tables now support DB-assigned global sequence numbers when `IngestExternalFileOptions::allow_global_seqno` is enabled, allowing overlapping external tables to be ingested across multiple levels when compactions are disabled. This includes the deprecated `write_global_seqno` path when the reader supplies the properties block's file offset.
@@ -66,7 +38,6 @@
### Performance Improvements
* `DB::MultiGetEntityLazy()` now reads all keys of a batch under a single shared per-column-family SuperVersion pin, and a single `LazyWideColumnsBatch::MultiResolve()` coalesces the resulting blob reads across keys: one read per blob file (and one per SST for embedded blobs), for whole-column and byte-range reads alike, instead of reading each key's blobs separately.
## 11.10.0 (08/28/2026)
### New Features
* Added `BlockBasedTableOptions::optimize_key_common_prefix` as part of new `format_version=8`. When enabled, each data block and index block stores the common user-key prefix of its keys once instead of at every restart point, reducing block size and block-cache footprint for prefix-heavy data and speeding up Seek for bytewise-ordered keys.
-1
View File
@@ -591,7 +591,6 @@ ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
FOLLY_DEPENDENT_HEADERS := \
include/rocksdb/coro_db.h \
include/rocksdb/coro_external_table.h \
include/rocksdb/utilities/coro_stackable_db.h
# Some headers like in port/ are platform-specific
DEV_HEADERS_TO_CHECK := $(filter-out $(FOLLY_DEPENDENT_HEADERS), $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|range_tree/|secondary_index/'))
+11 -77
View File
@@ -35,49 +35,6 @@ from util import ColorString
# tests to export as libraries for inclusion in other projects
_EXPORTED_TEST_LIBS = ["env_basic_test"]
_TEST_LIB_HEADERS = [
"db/db_test_util.h",
"db/db_with_timestamp_test_util.h",
"table/mock_table.h",
"test_util/mock_time_env.h",
"test_util/secondary_cache_test_util.h",
"test_util/simple_external_table_factory.h",
"test_util/testharness.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
"utilities/agg_merge/test_agg_merge.h",
"utilities/cassandra/test_utils.h",
]
_TEST_HEADERS = {
"configurable_test": ["options/configurable_test.h"],
"persistent_cache_test": ["utilities/persistent_cache/persistent_cache_test.h"],
"point_lock_manager_test": [
"utilities/transactions/lock/point/point_lock_manager_test.h",
],
"transaction_test": ["utilities/transactions/transaction_test.h"],
}
_STRESS_LIB_HEADERS = [
"db_stress_tool/db_stress_common.h",
"db_stress_tool/db_stress_compaction_service.h",
"db_stress_tool/db_stress_compression_manager.h",
"db_stress_tool/db_stress_driver.h",
"db_stress_tool/db_stress_filters.h",
"db_stress_tool/db_stress_listener.h",
"db_stress_tool/db_stress_shared_state.h",
"db_stress_tool/db_stress_test_base.h",
"db_stress_tool/db_stress_wide_merge_operator.h",
"db_stress_tool/expected_state.h",
"db_stress_tool/expected_value.h",
"db_stress_tool/multi_ops_txns_stress.h",
"test_util/simple_external_table_factory.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h"
]
# Parse src.mk files as a Dictionary of
# VAR_NAME => list of files
def parse_src_mk(repo_path):
@@ -226,34 +183,27 @@ def generate_buck(repo_path, deps_map):
":rocksdb_lib",
],
)
test_lib_sources = (
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
+ src_mk.get("EXP_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
)
# Older release source lists may not compile every test helper yet.
test_lib_headers = [
header for header in _TEST_LIB_HEADERS
if header[:-2] + ".cc" in test_lib_sources
]
# rocksdb_test_lib
BUCK.add_library(
"rocksdb_test_lib",
test_lib_sources,
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
+ src_mk.get("EXP_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
[":rocksdb_lib"],
extra_test_libs=True,
headers=test_lib_headers,
)
# rocksdb_with_faiss_test_lib
BUCK.add_library(
"rocksdb_with_faiss_test_lib",
test_lib_sources,
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
+ src_mk.get("EXP_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
deps=[
":rocksdb_with_faiss_lib",
],
extra_test_libs=True,
headers=test_lib_headers,
)
# rocksdb_tools_lib
BUCK.add_library(
@@ -262,12 +212,6 @@ def generate_buck(repo_path, deps_map):
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
+ ["test_util/testutil.cc"],
[":rocksdb_lib"],
headers=[
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/simulated_hybrid_file_system.h",
"tools/trace_analyzer_tool.h",
],
)
# rocksdb_cache_bench_tools_lib
BUCK.add_library(
@@ -281,19 +225,12 @@ def generate_buck(repo_path, deps_map):
src_mk.get("POINT_LOCK_BENCH_LIB_SOURCES", []),
[":rocksdb_lib"],
)
stress_sources = (
src_mk.get("ANALYZER_LIB_SOURCES", [])
+ src_mk.get("STRESS_LIB_SOURCES", [])
+ ["test_util/testutil.cc"]
)
# rocksdb_stress_lib
BUCK.add_rocksdb_library(
"rocksdb_stress_lib",
stress_sources,
headers=[
header for header in _STRESS_LIB_HEADERS
if header[:-2] + ".cc" in stress_sources
],
src_mk.get("ANALYZER_LIB_SOURCES", [])
+ src_mk.get("STRESS_LIB_SOURCES", [])
+ ["test_util/testutil.cc"],
)
# ldb binary
BUCK.add_binary(
@@ -416,7 +353,6 @@ def generate_buck(repo_path, deps_map):
test_src,
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
source_headers=_TEST_HEADERS.get(test, []),
)
else:
if with_faiss:
@@ -425,7 +361,6 @@ def generate_buck(repo_path, deps_map):
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
source_headers=_TEST_HEADERS.get(test, []),
)
else:
BUCK.register_test(
@@ -433,7 +368,6 @@ def generate_buck(repo_path, deps_map):
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
source_headers=_TEST_HEADERS.get(test, []),
)
BUCK.export_file("tools/db_crashtest.py")
BUCK.export_file("tools/fault_injection_log_parser.py")
+1 -6
View File
@@ -162,17 +162,12 @@ add_c_test_wrapper()
).encode("utf-8")
)
def register_test(self, test_name, src, deps, extra_compiler_flags, source_headers):
def register_test(self, test_name, src, deps, extra_compiler_flags):
with open(self.path, "ab") as targets_file:
targets_file.write(
targets_cfg.unittests_template.format(
test_name=test_name,
test_cc=str(src),
source_headers=(
f" source_headers=[{pretty_list(source_headers, 16)}],\n"
if source_headers
else ""
),
deps=deps,
extra_compiler_flags=extra_compiler_flags,
).encode("utf-8")
+1 -1
View File
@@ -30,7 +30,7 @@ cpp_binary_wrapper(name="{name}", srcs=[{srcs}], deps=[{deps}], extra_preprocess
unittests_template = """
cpp_unittest_wrapper(name="{test_name}",
srcs=["{test_cc}"],
{source_headers} deps={deps},
deps={deps},
extra_compiler_flags={extra_compiler_flags})
"""
@@ -142,11 +142,6 @@ uint64_t rocksdb_table_properties_column_family_id(
return props->rep.column_family_id;
}
uint64_t rocksdb_table_properties_oldest_ancestor_time(
const rocksdb_table_properties_t* props) {
return props->rep.oldest_ancestor_time;
}
uint64_t rocksdb_table_properties_creation_time(
const rocksdb_table_properties_t* props) {
return props->rep.creation_time;
@@ -177,6 +172,11 @@ uint64_t rocksdb_table_properties_fast_compression_estimated_data_size(
return props->rep.fast_compression_estimated_data_size;
}
uint64_t rocksdb_table_properties_external_sst_file_global_seqno_offset(
const rocksdb_table_properties_t* props) {
return props->rep.external_sst_file_global_seqno_offset;
}
uint64_t rocksdb_table_properties_tail_start_offset(
const rocksdb_table_properties_t* props) {
return props->rep.tail_start_offset;
@@ -102,10 +102,6 @@ rocksdb_table_properties_fixed_key_len(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_column_family_id(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_oldest_ancestor_time(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_creation_time(const rocksdb_table_properties_t* props);
@@ -126,6 +122,10 @@ extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_fast_compression_estimated_data_size(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_external_sst_file_global_seqno_offset(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_tail_start_offset(
const rocksdb_table_properties_t* props);
@@ -2057,6 +2057,16 @@ unsigned char rocksdb_ingestexternalfileoptions_get_ingest_behind(
return opt->rep.ingest_behind;
}
void rocksdb_ingestexternalfileoptions_set_write_global_seqno(
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
opt->rep.write_global_seqno = v;
}
unsigned char rocksdb_ingestexternalfileoptions_get_write_global_seqno(
rocksdb_ingestexternalfileoptions_t* opt) {
return opt->rep.write_global_seqno;
}
void rocksdb_ingestexternalfileoptions_set_verify_checksums_before_ingest(
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
opt->rep.verify_checksums_before_ingest = v;
@@ -816,6 +816,14 @@ extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_ingestexternalfileoptions_get_ingest_behind(
rocksdb_ingestexternalfileoptions_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_ingestexternalfileoptions_set_write_global_seqno(
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_ingestexternalfileoptions_get_write_global_seqno(
rocksdb_ingestexternalfileoptions_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_ingestexternalfileoptions_set_verify_checksums_before_ingest(
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v);
@@ -556,6 +556,12 @@ static void rocksdb_roundtrip_test_rocksdb_ingestexternalfileoptions(void) {
rocksdb_ingestexternalfileoptions_set_verify_file_checksum(obj, 0);
CheckCondition(
rocksdb_ingestexternalfileoptions_get_verify_file_checksum(obj) == 0);
rocksdb_ingestexternalfileoptions_set_write_global_seqno(obj, 1);
CheckCondition(
rocksdb_ingestexternalfileoptions_get_write_global_seqno(obj) == 1);
rocksdb_ingestexternalfileoptions_set_write_global_seqno(obj, 0);
CheckCondition(
rocksdb_ingestexternalfileoptions_get_write_global_seqno(obj) == 0);
rocksdb_ingestexternalfileoptions_destroy(obj);
}
-104
View File
@@ -28,98 +28,6 @@ A behavior change is not automatically a defect. Report a finding when the
intent, documentation, downstream-impact analysis, or mitigation has a concrete
gap or ambiguity that could lead to an unintended compatibility regression.
## DB Mutex Review Perspective
For every non-trivial change, determine whether the changed code, or any
transitive callee it adds or changes, can run while `DBImpl::mutex_` is held.
State the result explicitly, including when the DB mutex is not affected.
`DBImpl::mutex_` is the DB-wide metadata and coordination mutex. It protects
cross-object invariants involving `ColumnFamilyData`, memtable and
`SuperVersion` publication, `VersionSet`, snapshots, flush/compaction queues,
background-job state, and file lifetime. It is shared by all column families
and can be a hot lock. It is not the filesystem `db_lock_`, and it should not
replace data-path synchronization already provided by `WriteThread`,
`wal_write_mutex_`, atomics, referenced `SuperVersion`s, or component-local
synchronization.
When a DB-mutex critical section is affected:
### Locking and correctness
- Trace the full call path and mark the exact lock and unlock boundaries. Do
not inspect only the lines that directly acquire the mutex.
- State the locking contract. Helpers that require the lock should document it
and normally use `mutex_.AssertHeld()`. Check whether each caller expects the
lock to be held on return.
- Treat calls that can release and re-acquire the mutex as concurrency
boundaries. Examples include `VersionSet::LogAndApply()`, condition-variable
waits, write-thread coordination, and notification helpers. Revalidate state
inspected before such a call.
- Keep atomic metadata transitions under the mutex. Examples include moving a
memtable between mutable and immutable state, reserving compaction inputs,
installing a `Version` or `SuperVersion`, updating scheduling counters and
queues, and changing file-lifetime protection.
- For expensive work, prefer a prepare/run/install structure: reserve and pin
the required state while locked, do CPU or I/O work while unlocked, then
re-lock, validate, and publish. Before unlocking, copy mutable options and
acquire every required object reference or lifetime reservation.
- Check object lifetime across every unlocked region. Do not use a raw
`ColumnFamilyData*`, `Version*`, memtable, file metadata pointer, or DB-owned
container element after unlocking unless an explicit reference,
`SuperVersion`, job object, or pending-output reservation keeps it alive.
- Preserve the snapshot/input-selection boundary. Flush and compaction must not
miss a snapshot whose visible key versions they may remove. Missing a newly
created snapshot is a correctness bug; retaining a recently released
snapshot is conservative but safe.
- Never invoke untrusted or re-entrant code while holding the mutex. The mutex
is non-reentrant, so a callback into RocksDB can self-deadlock.
- Check lock ordering. `options_mutex_` is acquired before the DB mutex. When
both the DB mutex and `wal_write_mutex_` are needed, the DB mutex is acquired
first. Establish and document the order for other locks.
- Keep cleanup outside the critical section when possible. File deletion,
`JobContext::Clean()`, expensive `SuperVersion` destruction, logging, and
listener callbacks should normally run after protected state is detached or
snapshotted.
- Do not add the DB mutex to steady-state point reads or the normal write data
phase without strong justification. Reads normally use a referenced
`SuperVersion`; writes use `WriteThread` and component-specific
synchronization.
### Critical-section performance
- Compare the CPU and I/O performed under the mutex before and after the
change. Include call frequency, loop bounds, input-size scaling, hidden work
in callees, and worst-case cost.
- Account for filesystem or network I/O, waits, callbacks, logging and
formatting, allocation, container growth, copies, sorting, scans, reference
release and destructors, cache misses, and repeated metadata lookups.
- Do not allow a significant regression in mutex-held work without clear
justification, hold-time or contention measurements, and representative
benchmarks.
- Treat unavoidable work under the mutex as hot-path code and keep it highly
optimized. Precompute, allocate, copy, and perform I/O outside the lock;
remove repeated scans and lookups; cache or batch work where practical; and
keep locked operations O(1) or tightly bounded when possible.
- Treat new blocking I/O, waits, callbacks into user or plugin code, unbounded
loops, or work that scales with DB size, files, column families, snapshots,
or pending jobs as a blocker unless it is unavoidable and supported by strong
measurements and explicit design justification.
### Validation and evidence
- Test unlock windows with sync points rather than sleeps. Cover relevant races
with memtable switches, snapshot create/release, column-family drop,
flush/compaction install, shutdown, and background errors. Run TSAN where
appropriate.
- Distinguish wait time from hold time. `PerfContext::db_mutex_lock_nanos` and
`DB_MUTEX_WAIT_MICROS` primarily expose acquisition wait; use contention
profiles or scoped instrumentation to find long critical sections.
- Report affected critical sections, before/after cost, workload assumptions,
optimization choices, and remaining risk. If exact measurement is not
practical, provide a conservative static bound and do not claim no regression
without evidence.
IMPORTANT — Where the review goes: Your FINAL RESPONSE TEXT is the review.
The PR comment is generated verbatim from your final response, so the
complete, polished review MUST appear there in full. Do NOT end your turn with
@@ -399,9 +307,6 @@ in isolation may be a critical bug when you consider the full call chain.
- **Callee side effects**: Does the change call functions that mutate shared
state (counters, seqnos, metadata)? Are the mutations correct for the new
calling context? (Use the callee-chain analysis from context.md)
- Apply the DB Mutex Review Perspective above to locking contracts, unlocked
object lifetimes, stale-state validation, lock ordering, and snapshot
boundaries.
#### Agent: Cross-Component & Adversarial Reviewer
@@ -505,9 +410,6 @@ Write findings to `findings-caller-audit.md`.
- Loop optimization opportunities
- Branch prediction (LIKELY/UNLIKELY)
- Zero-overhead design verification
- Apply the DB Mutex Review Perspective above, with special focus on transitive
CPU and I/O, before/after cost, critical-section hold time, and benchmark
evidence
#### Agent: API & Compatibility Reviewer
- Public API backwards compatibility
@@ -691,8 +593,6 @@ Rules for this structure:
- [ ] Multi-component interactions (compaction, recovery, snapshots, iterators)
- [ ] Configuration dependencies and unexpected option combinations
- [ ] Potential pitfalls from caller-chain analysis
- [ ] DB-mutex reachability checked through transitive callees; affected lock
and unlock boundaries documented
### Review Phase (verified by agents)
- [ ] Database semantics preserved (snapshot isolation, key ordering)
@@ -706,10 +606,6 @@ Rules for this structure:
- [ ] Code follows RocksDB style conventions
- [ ] Behavioral contracts with upstream callers preserved
- [ ] All callers enumerated with parameter range table
- [ ] Before/after CPU and I/O under the DB mutex compared, with call frequency,
scaling, hidden callee work, and worst-case cost included
- [ ] No significant DB-mutex-held work regression; unavoidable work is highly
optimized, bounded, and supported by measurements when material
## Output Structure
Your **final response text is the primary output** — it becomes the PR comment
-106
View File
@@ -1,106 +0,0 @@
# RocksDB Diff and Pull Request Summary Guidance
This guidance applies to both internal Phabricator diff summaries and external
GitHub pull request summaries for changes under `fbcode/internal_repo_rocksdb/`.
## Goal
Write each diff or pull request summary for a reviewer who has not read the
patch. The summary must explain the behavioral story and the evidence for it,
not merely inventory the edited files or symbols.
Every new or updated diff or pull request summary must clearly cover:
1. Why the change is required.
2. What the previous behavior was, including the failure mode or limitation.
3. What the new behavior is.
4. What changed in the implementation.
5. Safety boundaries, compatibility, and unsupported or intentionally deferred
cases.
6. Tests performed and known limitations of the validation.
Use explicit headings for a broad, subtle, or high-risk change. A small,
focused change may combine these points into a few paragraphs, but none may be
left implicit when it affects a review decision.
## What Good RocksDB Summaries Make Concrete
- Name the exact conditions that activate a bug or behavior: relevant options,
call ordering, storage mode, concurrency mode, file format, platform, or
workload shape. Also state important cases that are not affected.
- Explain the causal chain from the old behavior to its externally observable
result. For correctness fixes, distinguish a symptom from the underlying
invariant violation.
- Describe the new contract before implementation mechanics. Then identify the
essential implementation choices and why they are sufficient.
- Call out API, ABI, serialized-format, behavioral, and performance
compatibility where relevant. For additive persisted fields, state how older
data is interpreted and whether older readers remain supported.
- State safety boundaries precisely: defaults, feature gates, fallback paths,
ownership and lifetime constraints, memory-ordering assumptions, and cases
deliberately postponed to a follow-up.
- Support performance claims with a baseline, workload and configuration,
measurement method, and results. Report meaningful tradeoffs, not only the
winning metric.
- Make the test plan evidence-based. Name the tests and configurations, say
what behavior each proves, and report failures, timeouts, untested platforms,
or stale pre-rebase results honestly. For a regression test, say how it fails
without the fix when that was verified.
- Separate incidental cleanup or a pre-existing test fix from the primary
change, and explain why it belongs in the diff.
Avoid vague claims such as "improves performance," "fixes an issue," or "tests
pass." Avoid copying the patch into prose, exhaustive file-by-file narration,
raw command output, and claims broader than the evidence.
## Suggested Structure
Adapt this structure to the change; do not keep empty or irrelevant sections.
```text
Summary:
Why the change is required:
<User or system impact, and why the current behavior is inadequate.>
Previous behavior:
<Old contract or mechanism, triggering conditions, and observable result.>
New behavior:
<New contract and externally visible effect.>
Implementation:
<Essential design and code changes; omit routine edit-by-edit narration.>
Safety, compatibility, and limitations:
<Defaults, unaffected paths, compatibility, unsupported cases, and follow-ups.>
Test Plan:
<Exact tests/benchmarks, configurations, what they demonstrate, and gaps.>
```
For a focused bug fix, a cohesive narrative can be clearer than six headings:
problem and root cause, trigger and unaffected cases, fix, then quantified or
counterfactual validation. For a multi-part change, labeled bullets such as
`Fix`, `Add`, and `Change` can make the implementation and review surface easy
to scan.
## Reference Pull Request Summaries
- [PR #15216](https://github.com/facebook/rocksdb/pull/15216) is a strong
focused bug-fix example. It traces stale iterator state to readahead behavior
and dropped keys, identifies the exact prerequisites and unaffected mmap
case, gives before/after measurements, and explains how the regression test
fails when the fix is reverted.
- [PR #15218](https://github.com/facebook/rocksdb/pull/15218) is a concise
example of explicit `Why`, `Previous behavior`, `New behavior`,
`Implementation`, and `Safety boundaries and unsupported cases` sections.
- Peter Dillinger's [PR #15231](https://github.com/facebook/rocksdb/pull/15231)
is a strong recent multi-part example. Its `Fix`/`Add`/`Change` bullets
separate the behaviors, its compatibility sentence explains how old files
read back, and its test plan maps each new persisted property to concrete
coverage while explaining an adjacent test adjustment.
Use these pull requests as models for reasoning, specificity, and evidence. Do
not copy their length or headings mechanically; scale the summary to the risk
and complexity of the change.
-7
View File
@@ -25,7 +25,6 @@ CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --
crash_test_with_multiops_wup_txn \
crash_test_with_optimistic_txn \
crash_test_with_tiered_storage \
crash_test_with_full_external_table \
liveness_crash_test \
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
blackbox_crash_test_with_wc_txn blackbox_crash_test_with_wp_txn \
@@ -37,7 +36,6 @@ CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --
blackbox_crash_test_with_multiops_wup_txn \
blackbox_crash_test_with_optimistic_txn \
blackbox_crash_test_with_tiered_storage \
blackbox_crash_test_with_full_external_table \
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
whitebox_crash_test_with_wc_txn whitebox_crash_test_with_wp_txn \
whitebox_crash_test_with_wup_txn \
@@ -88,8 +86,6 @@ crash_test_with_tiered_storage: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) whitebox_crash_test_with_tiered_storage
$(CRASHTEST_MAKE) blackbox_crash_test_with_tiered_storage
crash_test_with_full_external_table: blackbox_crash_test_with_full_external_table
crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wc_txn
@@ -136,9 +132,6 @@ blackbox_crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_full_external_table: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_full_external_table blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --optimistic_txn blackbox $(CRASH_TEST_EXT_ARGS)
+23 -87
View File
@@ -38,14 +38,12 @@ BlobFileCache::BlobFileCache(Cache* cache,
}
Status BlobFileCache::GetBlobFileReader(
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
const ReadOptions& read_options, uint64_t blob_file_number,
CacheHandleGuard<BlobFileReader>* blob_file_reader,
bool allow_footer_skip_retry) {
assert(blob_file_reader);
assert(blob_file_reader->IsEmpty());
const uint64_t blob_file_number = blob_file.file_number;
// NOTE: sharing same Cache with table_cache
const Slice key = GetSliceForKey(&blob_file_number);
@@ -76,8 +74,18 @@ Status BlobFileCache::GetBlobFileReader(
std::unique_ptr<BlobFileReader> reader;
{
Status s = OpenBlobFileReader(read_options, blob_file,
allow_footer_skip_retry, &reader);
assert(file_options_);
Status s = BlobFileReader::Create(
*immutable_options_, read_options, *file_options_, column_family_id_,
blob_file_read_hist_, blob_file_number, io_tracer_,
/*skip_footer_validation=*/false, &reader);
if (!s.ok() && s.IsCorruption() && allow_footer_skip_retry) {
reader.reset();
s = BlobFileReader::Create(
*immutable_options_, read_options, *file_options_, column_family_id_,
blob_file_read_hist_, blob_file_number, io_tracer_,
/*skip_footer_validation=*/true, &reader);
}
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_options_->logger,
"BlobFileCache open failed for blob file %" PRIu64
@@ -106,7 +114,7 @@ Status BlobFileCache::GetBlobFileReader(
}
Status BlobFileCache::OpenBlobFileReaderUncached(
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
const ReadOptions& read_options, uint64_t blob_file_number,
std::unique_ptr<BlobFileReader>* blob_file_reader,
bool allow_footer_skip_retry) {
assert(blob_file_reader);
@@ -115,77 +123,21 @@ Status BlobFileCache::OpenBlobFileReaderUncached(
Statistics* const statistics = immutable_options_->stats;
RecordTick(statistics, NO_FILE_OPENS);
Status s = OpenBlobFileReader(read_options, blob_file,
allow_footer_skip_retry, blob_file_reader);
if (!s.ok()) {
RecordTick(statistics, NO_FILE_ERRORS);
}
return s;
}
void BlobFileCache::RegisterBlobFileChecksum(
const BlobFileOpenInfo& blob_file) {
assert(blob_file.file_checksum.empty() ==
blob_file.file_checksum_func_name.empty());
if (blob_file.file_checksum.empty()) {
return;
}
const Slice key = GetSliceForKey(&blob_file.file_number);
MutexLock cache_lock(&mutex_.Get(key));
{
MutexLock checksum_lock(&blob_file_checksums_mutex_);
BlobFileChecksum& checksum = blob_file_checksums_[blob_file.file_number];
checksum.value = blob_file.file_checksum.ToString();
checksum.function_name = blob_file.file_checksum_func_name.ToString();
}
// Readers opened while the file was active did not carry finalized metadata
// and may also have observed a footer-less file size.
cache_.get()->Erase(key);
}
void BlobFileCache::UnregisterBlobFileChecksum(uint64_t blob_file_number) {
MutexLock checksum_lock(&blob_file_checksums_mutex_);
blob_file_checksums_.erase(blob_file_number);
}
Status BlobFileCache::OpenBlobFileReader(
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
bool allow_footer_skip_retry,
std::unique_ptr<BlobFileReader>* blob_file_reader) {
assert(file_options_);
assert(blob_file.file_checksum.empty() ==
blob_file.file_checksum_func_name.empty());
FileOptions file_options = *file_options_;
if (!blob_file.file_checksum.empty()) {
file_options.file_checksum = blob_file.file_checksum.ToString();
file_options.file_checksum_func_name =
blob_file.file_checksum_func_name.ToString();
} else {
MutexLock checksum_lock(&blob_file_checksums_mutex_);
const auto it = blob_file_checksums_.find(blob_file.file_number);
if (it != blob_file_checksums_.end()) {
file_options.file_checksum = it->second.value;
file_options.file_checksum_func_name = it->second.function_name;
} else {
file_options.file_checksum.clear();
file_options.file_checksum_func_name.clear();
}
}
Status s = BlobFileReader::Create(
*immutable_options_, read_options, file_options, column_family_id_,
blob_file_read_hist_, blob_file.file_number, io_tracer_,
*immutable_options_, read_options, *file_options_, column_family_id_,
blob_file_read_hist_, blob_file_number, io_tracer_,
/*skip_footer_validation=*/false, blob_file_reader);
if (!s.ok() && s.IsCorruption() && allow_footer_skip_retry) {
blob_file_reader->reset();
s = BlobFileReader::Create(
*immutable_options_, read_options, file_options, column_family_id_,
blob_file_read_hist_, blob_file.file_number, io_tracer_,
*immutable_options_, read_options, *file_options_, column_family_id_,
blob_file_read_hist_, blob_file_number, io_tracer_,
/*skip_footer_validation=*/true, blob_file_reader);
}
if (!s.ok()) {
RecordTick(statistics, NO_FILE_ERRORS);
}
return s;
}
@@ -226,7 +178,7 @@ Status BlobFileCache::InsertBlobFileReader(
}
Status BlobFileCache::RefreshBlobFileReader(
const BlobFileOpenInfo& blob_file,
uint64_t blob_file_number,
std::unique_ptr<BlobFileReader>* blob_file_reader,
CacheHandleGuard<BlobFileReader>* cached_blob_file_reader) {
assert(blob_file_reader);
@@ -234,26 +186,10 @@ Status BlobFileCache::RefreshBlobFileReader(
assert(cached_blob_file_reader);
assert(cached_blob_file_reader->IsEmpty());
const Slice key = GetSliceForKey(&blob_file.file_number);
const Slice key = GetSliceForKey(&blob_file_number);
MutexLock lock(&mutex_.Get(key));
TypedHandle* handle = cache_.Lookup(key);
if (blob_file.file_checksum.empty()) {
MutexLock checksum_lock(&blob_file_checksums_mutex_);
if (blob_file_checksums_.find(blob_file.file_number) !=
blob_file_checksums_.end()) {
// This reader can have been opened just before finalization registered
// checksum metadata. Never let such a racing refresh repopulate the
// cache.
if (handle) {
*cached_blob_file_reader = cache_.Guard(handle);
}
blob_file_reader->reset();
return Status::OK();
}
}
if (handle) {
BlobFileReader* const cached_reader = cache_.Value(handle);
assert(cached_reader != nullptr);
+6 -31
View File
@@ -6,12 +6,9 @@
#pragma once
#include <cinttypes>
#include <string>
#include <unordered_map>
#include "cache/typed_cache.h"
#include "db/blob/blob_file_reader.h"
#include "db/blob/blob_read_request.h"
#include "rocksdb/rocksdb_namespace.h"
#include "util/mutexlock.h"
@@ -35,30 +32,20 @@ class BlobFileCache {
BlobFileCache(const BlobFileCache&) = delete;
BlobFileCache& operator=(const BlobFileCache&) = delete;
// Returns a cached reader for `blob_file`, opening and caching it on miss.
// Checksum metadata is forwarded to the FileSystem through FileOptions when
// the file is opened. If `allow_footer_skip_retry` is true, a
// footer-validation corruption retries once without requiring a footer.
// Returns a cached reader for `blob_file_number`, opening and caching it on
// miss. If `allow_footer_skip_retry` is true, a footer-validation corruption
// retries once without requiring a footer.
Status GetBlobFileReader(const ReadOptions& read_options,
const BlobFileOpenInfo& blob_file,
uint64_t blob_file_number,
CacheHandleGuard<BlobFileReader>* blob_file_reader,
bool allow_footer_skip_retry = false);
// Opens a blob file reader without inserting it into the cache.
Status OpenBlobFileReaderUncached(
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
const ReadOptions& read_options, uint64_t blob_file_number,
std::unique_ptr<BlobFileReader>* blob_file_reader,
bool allow_footer_skip_retry = false);
// Makes finalized checksum metadata available to direct-write fallback reads
// whose pinned Version predates the blob-file addition. Any cached
// footer-less reader is evicted as part of the same per-file transition.
void RegisterBlobFileChecksum(const BlobFileOpenInfo& blob_file);
// Stops retaining checksum metadata once no direct-write fallback can refer
// to the file. Manifest-backed callers continue to provide it directly.
void UnregisterBlobFileChecksum(uint64_t blob_file_number);
// Inserts a freshly opened uncached reader unless another thread already
// cached the same blob file.
Status InsertBlobFileReader(
@@ -71,7 +58,7 @@ class BlobFileCache {
// reader instead so a racing refresh cannot reintroduce an older active-file
// view after the blob file grows.
Status RefreshBlobFileReader(
const BlobFileOpenInfo& blob_file,
uint64_t blob_file_number,
std::unique_ptr<BlobFileReader>* blob_file_reader,
CacheHandleGuard<BlobFileReader>* cached_blob_file_reader);
@@ -85,11 +72,6 @@ class BlobFileCache {
}
private:
Status OpenBlobFileReader(const ReadOptions& read_options,
const BlobFileOpenInfo& blob_file,
bool allow_footer_skip_retry,
std::unique_ptr<BlobFileReader>* blob_file_reader);
using CacheInterface =
BasicTypedCacheInterface<BlobFileReader, CacheEntryRole::kMisc>;
using TypedHandle = CacheInterface::TypedHandle;
@@ -103,13 +85,6 @@ class BlobFileCache {
HistogramImpl* blob_file_read_hist_;
std::shared_ptr<IOTracer> io_tracer_;
struct BlobFileChecksum {
std::string value;
std::string function_name;
};
std::unordered_map<uint64_t, BlobFileChecksum> blob_file_checksums_;
mutable port::Mutex blob_file_checksums_mutex_;
static constexpr size_t kNumberOfMutexStripes = 1 << 7;
};
+18 -21
View File
@@ -115,13 +115,13 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
&file_options, column_family_id,
blob_file_read_hist, nullptr /*IOTracer*/);
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
// First try: reader should be opened and put in cache
CacheHandleGuard<BlobFileReader> first;
const ReadOptions read_options;
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file, &first));
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
&first));
ASSERT_NE(first.GetValue(), nullptr);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
@@ -129,8 +129,8 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
// Second try: reader should be served from cache
CacheHandleGuard<BlobFileReader> second;
ASSERT_OK(
blob_file_cache.GetBlobFileReader(read_options, blob_file, &second));
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
&second));
ASSERT_NE(second.GetValue(), nullptr);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
@@ -163,7 +163,6 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
&file_options, column_family_id,
blob_file_read_hist, nullptr /*IOTracer*/);
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
CacheHandleGuard<BlobFileReader> first;
CacheHandleGuard<BlobFileReader> second;
@@ -173,15 +172,16 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
"BlobFileCache::GetBlobFileReader:DoubleCheck", [&](void* /* arg */) {
// Disabling sync points to prevent infinite recursion
SyncPoint::GetInstance()->DisableProcessing();
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file,
&second));
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options,
blob_file_number, &second));
ASSERT_NE(second.GetValue(), nullptr);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file, &first));
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
&first));
ASSERT_NE(first.GetValue(), nullptr);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
@@ -245,22 +245,21 @@ TEST_F(BlobFileCacheTest, RefreshBlobFileReaderPrefersLargestObservedFileSize) {
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
&file_options, column_family_id,
blob_file_read_hist, nullptr /*IOTracer*/);
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
const ReadOptions read_options;
std::unique_ptr<BlobFileReader> stale_reader;
ASSERT_OK(blob_file_cache.OpenBlobFileReaderUncached(
read_options, blob_file, &stale_reader,
read_options, blob_file_number, &stale_reader,
/*allow_footer_skip_retry=*/true));
std::unique_ptr<BlobFileReader> initial_cached_reader;
ASSERT_OK(blob_file_cache.OpenBlobFileReaderUncached(
read_options, blob_file, &initial_cached_reader,
read_options, blob_file_number, &initial_cached_reader,
/*allow_footer_skip_retry=*/true));
CacheHandleGuard<BlobFileReader> cached_reader;
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(
blob_file, &initial_cached_reader, &cached_reader));
blob_file_number, &initial_cached_reader, &cached_reader));
ASSERT_NE(cached_reader.GetValue(), nullptr);
const uint64_t initial_file_size = cached_reader.GetValue()->GetFileSize();
@@ -270,20 +269,20 @@ TEST_F(BlobFileCacheTest, RefreshBlobFileReaderPrefersLargestObservedFileSize) {
std::unique_ptr<BlobFileReader> fresh_reader;
ASSERT_OK(blob_file_cache.OpenBlobFileReaderUncached(
read_options, blob_file, &fresh_reader,
read_options, blob_file_number, &fresh_reader,
/*allow_footer_skip_retry=*/true));
ASSERT_GT(fresh_reader->GetFileSize(), initial_file_size);
CacheHandleGuard<BlobFileReader> refreshed_reader;
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(blob_file, &fresh_reader,
&refreshed_reader));
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(
blob_file_number, &fresh_reader, &refreshed_reader));
ASSERT_NE(refreshed_reader.GetValue(), nullptr);
ASSERT_GT(refreshed_reader.GetValue()->GetFileSize(), initial_file_size);
BlobFileReader* const largest_reader = refreshed_reader.GetValue();
CacheHandleGuard<BlobFileReader> preserved_reader;
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(blob_file, &stale_reader,
&preserved_reader));
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(
blob_file_number, &stale_reader, &preserved_reader));
ASSERT_NE(preserved_reader.GetValue(), nullptr);
ASSERT_EQ(preserved_reader.GetValue(), largest_reader);
ASSERT_EQ(stale_reader.get(), nullptr);
@@ -313,13 +312,12 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_IOError) {
// Note: there is no blob file with the below number
constexpr uint64_t blob_file_number = 123;
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
CacheHandleGuard<BlobFileReader> reader;
const ReadOptions read_options;
ASSERT_TRUE(
blob_file_cache.GetBlobFileReader(read_options, blob_file, &reader)
blob_file_cache.GetBlobFileReader(read_options, blob_file_number, &reader)
.IsIOError());
ASSERT_EQ(reader.GetValue(), nullptr);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
@@ -354,7 +352,6 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
&file_options, column_family_id,
blob_file_read_hist, nullptr /*IOTracer*/);
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
// Insert into cache should fail since it has zero capacity and
// strict_capacity_limit is set
@@ -362,7 +359,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
const ReadOptions read_options;
ASSERT_TRUE(
blob_file_cache.GetBlobFileReader(read_options, blob_file, &reader)
blob_file_cache.GetBlobFileReader(read_options, blob_file_number, &reader)
.IsMemoryLimit());
ASSERT_EQ(reader.GetValue(), nullptr);
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
+5 -16
View File
@@ -330,11 +330,6 @@ Status BlobFilePartitionManager::FinalizeBlobFile(
*addition =
BlobFileAddition(file_number, blob_count, total_blob_bytes,
std::move(checksum_method), std::move(checksum_value));
if (blob_file_cache_ != nullptr) {
const BlobFileOpenInfo blob_file{file_number, addition->GetChecksumValue(),
addition->GetChecksumMethod()};
blob_file_cache_->RegisterBlobFileChecksum(blob_file);
}
return Status::OK();
}
@@ -758,9 +753,6 @@ void BlobFilePartitionManager::UnprotectSealedBlobFileNumbers(
// manifest-visible path. Evict it here so delayed protection does not
// leave an obsolete blob reader behind until DB close.
blob_file_cache_->Evict(file_number);
if (file_to_partition_.find(file_number) == file_to_partition_.end()) {
blob_file_cache_->UnregisterBlobFileChecksum(file_number);
}
}
}
}
@@ -780,10 +772,6 @@ void BlobFilePartitionManager::RemoveFilePartitionMappings(
// this file before it was sealed. Drop it now so future manifest-visible
// reads reopen against the finalized on-disk size and footer state.
blob_file_cache_->Evict(file_number);
if (protected_blob_file_refs_.find(file_number) ==
protected_blob_file_refs_.end()) {
blob_file_cache_->UnregisterBlobFileChecksum(file_number);
}
}
}
}
@@ -821,9 +809,9 @@ Status BlobFilePartitionManager::ResolveBlobDirectWriteIndex(
}
Status s;
const BlobFileOpenInfo blob_file{blob_idx.file_number(), Slice(), Slice()};
CacheHandleGuard<BlobFileReader> reader;
s = blob_file_cache->GetBlobFileReader(read_options, blob_file, &reader,
s = blob_file_cache->GetBlobFileReader(read_options, blob_idx.file_number(),
&reader,
/*allow_footer_skip_retry=*/true);
if (!s.ok()) {
return s;
@@ -848,7 +836,7 @@ Status BlobFilePartitionManager::ResolveBlobDirectWriteIndex(
std::unique_ptr<BlobFileReader> fresh_reader;
s = blob_file_cache->OpenBlobFileReaderUncached(
read_options, blob_file, &fresh_reader,
read_options, blob_idx.file_number(), &fresh_reader,
/*allow_footer_skip_retry=*/true);
if (!s.ok()) {
return s;
@@ -862,7 +850,8 @@ Status BlobFilePartitionManager::ResolveBlobDirectWriteIndex(
if (s.ok()) {
blob_value->PinSelf(fresh_contents->data());
CacheHandleGuard<BlobFileReader> ignored;
blob_file_cache->RefreshBlobFileReader(blob_file, &fresh_reader, &ignored)
blob_file_cache
->RefreshBlobFileReader(blob_idx.file_number(), &fresh_reader, &ignored)
.PermitUncheckedError();
}
+7 -9
View File
@@ -278,24 +278,22 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
return s;
}
FSReadRequest read_req;
read_req.offset = read_offset;
read_req.len = read_size;
if (file_reader->use_direct_io()) {
read_req.scratch = nullptr;
constexpr char* scratch = nullptr;
AlignedBufferAllocationContext direct_io_context{direct_io_buffer};
file_reader->Read(io_options, &read_req, &direct_io_context, &dbg);
s = file_reader->Read(io_options, read_offset, read_size, slice, scratch,
&direct_io_context, &dbg);
} else {
buf->reset(new char[read_size]);
read_req.scratch = buf->get();
file_reader->Read(io_options, &read_req, nullptr, &dbg);
s = file_reader->Read(io_options, read_offset, read_size, slice, buf->get(),
nullptr, &dbg);
}
s = std::move(read_req.status);
if (!s.ok()) {
return s;
}
*slice = read_req.result;
if (slice->size() != read_size) {
return Status::Corruption("Incomplete blob read from " +
+6 -5
View File
@@ -82,7 +82,8 @@ Status ReadAndVerifySimpleGen2BlobRecord(
IODebugContext dbg;
Status s = file->PrepareIOOptions(read_options, opts, &dbg);
if (s.ok()) {
s = file->Read(record_offset, record_size, opts, &result, buf, &dbg);
s = file->Read(opts, record_offset, record_size, &result, buf, nullptr,
&dbg);
}
if (!s.ok()) {
return s;
@@ -170,8 +171,8 @@ Status ReadSimpleGen2BlobRange(const ReadOptions& read_options,
IODebugContext dbg;
Status s = file->PrepareIOOptions(read_options, opts, &dbg);
if (s.ok()) {
s = file->Read(record_offset + range_offset, range_length, opts, &result,
buf, &dbg);
s = file->Read(opts, record_offset + range_offset, range_length, &result,
buf, nullptr, &dbg);
}
if (!s.ok()) {
return s;
@@ -423,12 +424,12 @@ IOStatus WriteSimpleGen2BlobRecord(WritableFileWriter* file,
return io_s;
}
if (!payload.empty()) {
io_s = file->Append(payload, opts);
io_s = file->Append(opts, payload);
if (!io_s.ok()) {
return io_s;
}
}
return file->Append(Slice(trailer.data(), trailer.size()), opts);
return file->Append(opts, Slice(trailer.data(), trailer.size()));
}
} // namespace ROCKSDB_NAMESPACE
+1 -1
View File
@@ -29,7 +29,7 @@ Status BlobLogSequentialReader::ReadSlice(uint64_t size, Slice* slice,
StopWatch read_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
// TODO: rate limit `BlobLogSequentialReader` reads (it appears unused?)
Status s = file_->Read(next_byte_, static_cast<size_t>(size), IOOptions(),
Status s = file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size),
slice, buf, nullptr);
next_byte_ += size;
if (!s.ok()) {
+6 -6
View File
@@ -40,7 +40,7 @@ Status BlobLogWriter::Sync(const WriteOptions& write_options) {
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = use_fsync_ ? dest_->Fsync(opts) : dest_->Sync(opts);
s = dest_->Sync(opts, use_fsync_);
}
if (s.ok()) {
RecordTick(statistics_, BLOB_DB_BLOB_FILE_SYNCED);
@@ -58,7 +58,7 @@ Status BlobLogWriter::WriteHeader(const WriteOptions& write_options,
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = dest_->Append(Slice(str), opts);
s = dest_->Append(opts, Slice(str));
}
if (s.ok()) {
block_offset_ += str.size();
@@ -92,7 +92,7 @@ Status BlobLogWriter::AppendFooter(const WriteOptions& write_options,
IOOptions opts;
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = dest_->Append(Slice(str), opts);
s = dest_->Append(opts, Slice(str));
}
if (s.ok()) {
block_offset_ += str.size();
@@ -181,13 +181,13 @@ Status BlobLogWriter::EmitPhysicalRecord(const WriteOptions& write_options,
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = dest_->Append(Slice(headerbuf), opts);
s = dest_->Append(opts, Slice(headerbuf));
}
if (s.ok()) {
s = dest_->Append(key, opts);
s = dest_->Append(opts, key);
}
if (s.ok()) {
s = dest_->Append(val, opts);
s = dest_->Append(opts, val);
}
if (do_flush_ && s.ok()) {
s = dest_->Flush(opts);
+2 -12
View File
@@ -14,16 +14,6 @@
namespace ROCKSDB_NAMESPACE {
// Non-owning metadata needed when opening a blob file for reading. The
// checksum slices must remain valid while this object is in use. A file without
// finalized checksum metadata, such as an active direct-write blob file, uses
// empty slices for both checksum fields.
struct BlobFileOpenInfo {
uint64_t file_number = 0;
Slice file_checksum;
Slice file_checksum_func_name;
};
// A read Blob request structure for use in BlobSource::MultiGetBlob and
// BlobFileReader::MultiGetBlob.
struct BlobReadRequest {
@@ -63,7 +53,7 @@ struct BlobReadRequest {
};
using BlobFileReadRequests =
std::tuple<BlobFileOpenInfo, uint64_t /* file_size */,
std::tuple<uint64_t /* file_number */, uint64_t /* file_size */,
autovector<BlobReadRequest>>;
// A byte-range (partial) blob read request for the lazy blob-read multi
@@ -107,7 +97,7 @@ struct BlobRangeReadRequest {
};
using BlobFileRangeReadRequests =
std::tuple<BlobFileOpenInfo, uint64_t /* file_size */,
std::tuple<uint64_t /* file_number */, uint64_t /* file_size */,
autovector<BlobRangeReadRequest>>;
} // namespace ROCKSDB_NAMESPACE
+25 -29
View File
@@ -50,10 +50,9 @@ Status AppendBlobRefreshRetryFailure(const Status& stale_status,
template <typename ReadFn>
Status ReadBlobWithReaderRetry(BlobFileCache* blob_file_cache,
const ReadOptions& read_options,
const BlobFileOpenInfo& blob_file,
const ReadFn& read) {
uint64_t file_number, const ReadFn& read) {
CacheHandleGuard<BlobFileReader> blob_file_reader;
Status s = blob_file_cache->GetBlobFileReader(read_options, blob_file,
Status s = blob_file_cache->GetBlobFileReader(read_options, file_number,
&blob_file_reader);
if (!s.ok()) {
return s;
@@ -67,11 +66,11 @@ Status ReadBlobWithReaderRetry(BlobFileCache* blob_file_cache,
const Status stale_status = s;
blob_file_reader.Reset();
blob_file_cache->Evict(blob_file.file_number);
blob_file_cache->Evict(file_number);
std::unique_ptr<BlobFileReader> fresh_reader;
s = blob_file_cache->OpenBlobFileReaderUncached(
read_options, blob_file, &fresh_reader,
read_options, file_number, &fresh_reader,
/*allow_footer_skip_retry=*/false);
if (!s.ok()) {
return AppendBlobRefreshRetryFailure(stale_status, s);
@@ -84,7 +83,7 @@ Status ReadBlobWithReaderRetry(BlobFileCache* blob_file_cache,
CacheHandleGuard<BlobFileReader> ignored_reader;
blob_file_cache
->RefreshBlobFileReader(blob_file, &fresh_reader, &ignored_reader)
->RefreshBlobFileReader(file_number, &fresh_reader, &ignored_reader)
.PermitUncheckedError();
return s;
}
@@ -273,9 +272,9 @@ Status BlobSource::InsertEntryIntoCache(const Slice& key, BlobContents* value,
}
Status BlobSource::GetBlob(const ReadOptions& read_options,
const Slice& user_key,
const BlobFileOpenInfo& blob_file, uint64_t offset,
uint64_t file_size, uint64_t value_size,
const Slice& user_key, uint64_t file_number,
uint64_t offset, uint64_t file_size,
uint64_t value_size,
CompressionType compression_type,
FilePrefetchBuffer* prefetch_buffer,
PinnableSlice* value, uint64_t* bytes_read) {
@@ -283,8 +282,7 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
Status s;
const CacheKey cache_key =
GetCacheKey(blob_file.file_number, file_size, offset);
const CacheKey cache_key = GetCacheKey(file_number, file_size, offset);
CacheHandleGuard<BlobContents> blob_handle;
@@ -334,7 +332,8 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
uint64_t read_size = 0;
s = ReadBlobWithReaderRetry(
blob_file_cache_, read_options, blob_file, [&](BlobFileReader* reader) {
blob_file_cache_, read_options, file_number,
[&](BlobFileReader* reader) {
if (compression_type != reader->GetCompressionType()) {
return Status::Corruption(
"Compression type mismatch when reading blob");
@@ -378,8 +377,7 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
}
Status BlobSource::GetBlobRange(const ReadOptions& read_options,
const Slice& user_key,
const BlobFileOpenInfo& blob_file,
const Slice& user_key, uint64_t file_number,
uint64_t offset, uint64_t file_size,
uint64_t value_size,
CompressionType compression_type,
@@ -396,8 +394,7 @@ Status BlobSource::GetBlobRange(const ReadOptions& read_options,
Status s;
const CacheKey cache_key =
GetCacheKey(blob_file.file_number, file_size, offset);
const CacheKey cache_key = GetCacheKey(file_number, file_size, offset);
CacheHandleGuard<BlobContents> blob_handle;
@@ -438,7 +435,8 @@ Status BlobSource::GetBlobRange(const ReadOptions& read_options,
// case (offset/size mismatch surfacing as Corruption), not a payload
// checksum failure.
s = ReadBlobWithReaderRetry(
blob_file_cache_, read_options, blob_file, [&](BlobFileReader* reader) {
blob_file_cache_, read_options, file_number,
[&](BlobFileReader* reader) {
if (compression_type != reader->GetCompressionType()) {
return Status::Corruption(
"Compression type mismatch when reading blob");
@@ -829,7 +827,7 @@ void BlobSource::MultiGetBlob(const ReadOptions& read_options,
uint64_t total_bytes_read = 0;
uint64_t bytes_read_in_file = 0;
for (auto& [blob_file, file_size, blob_reqs_in_file] : blob_reqs) {
for (auto& [file_number, file_size, blob_reqs_in_file] : blob_reqs) {
// sort blob_reqs_in_file by file offset.
std::sort(
blob_reqs_in_file.begin(), blob_reqs_in_file.end(),
@@ -837,7 +835,7 @@ void BlobSource::MultiGetBlob(const ReadOptions& read_options,
return lhs.offset < rhs.offset;
});
MultiGetBlobFromOneFile(read_options, blob_file, file_size,
MultiGetBlobFromOneFile(read_options, file_number, file_size,
blob_reqs_in_file, &bytes_read_in_file);
total_bytes_read += bytes_read_in_file;
@@ -849,7 +847,7 @@ void BlobSource::MultiGetBlob(const ReadOptions& read_options,
}
void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
const BlobFileOpenInfo& blob_file,
uint64_t file_number,
uint64_t /*file_size*/,
autovector<BlobReadRequest>& blob_reqs,
uint64_t* bytes_read) {
@@ -867,7 +865,6 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
Mask cache_hit_mask = 0;
uint64_t total_bytes = 0;
const uint64_t file_number = blob_file.file_number;
const OffsetableCacheKey base_cache_key(db_id_, db_session_id_, file_number);
if (blob_cache_) {
@@ -939,7 +936,7 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
}
CacheHandleGuard<BlobFileReader> blob_file_reader;
Status s = blob_file_cache_->GetBlobFileReader(read_options, blob_file,
Status s = blob_file_cache_->GetBlobFileReader(read_options, file_number,
&blob_file_reader);
if (!s.ok()) {
for (size_t i = 0; i < _blob_reqs.size(); ++i) {
@@ -979,7 +976,7 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
std::unique_ptr<BlobFileReader> fresh_reader;
s = blob_file_cache_->OpenBlobFileReaderUncached(
read_options, blob_file, &fresh_reader,
read_options, file_number, &fresh_reader,
/*allow_footer_skip_retry=*/false);
if (!s.ok()) {
for (const auto& blob_req : _blob_reqs) {
@@ -1040,7 +1037,7 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
if (install_fresh_reader) {
CacheHandleGuard<BlobFileReader> ignored_reader;
blob_file_cache_
->RefreshBlobFileReader(blob_file, &fresh_reader, &ignored_reader)
->RefreshBlobFileReader(file_number, &fresh_reader, &ignored_reader)
.PermitUncheckedError();
}
}
@@ -1106,7 +1103,7 @@ void BlobSource::MultiGetBlobRange(
uint64_t total_bytes_read = 0;
uint64_t bytes_read_in_file = 0;
for (auto& [blob_file, file_size, blob_reqs_in_file] : blob_reqs) {
for (auto& [file_number, file_size, blob_reqs_in_file] : blob_reqs) {
// Sort by effective read offset so the file system layer can coalesce
// adjacent sub-range reads within one MultiRead.
std::sort(blob_reqs_in_file.begin(), blob_reqs_in_file.end(),
@@ -1116,7 +1113,7 @@ void BlobSource::MultiGetBlobRange(
rhs.offset + rhs.range_offset;
});
MultiGetBlobRangeFromOneFile(read_options, blob_file, file_size,
MultiGetBlobRangeFromOneFile(read_options, file_number, file_size,
blob_reqs_in_file, &bytes_read_in_file);
total_bytes_read += bytes_read_in_file;
@@ -1129,7 +1126,7 @@ void BlobSource::MultiGetBlobRange(
}
void BlobSource::MultiGetBlobRangeFromOneFile(
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
const ReadOptions& read_options, uint64_t file_number,
uint64_t /*file_size*/, autovector<BlobRangeReadRequest>& blob_reqs,
uint64_t* bytes_read) {
const size_t num_blobs = blob_reqs.size();
@@ -1143,7 +1140,6 @@ void BlobSource::MultiGetBlobRangeFromOneFile(
Mask cache_hit_mask = 0;
uint64_t total_bytes = 0;
const uint64_t file_number = blob_file.file_number;
const OffsetableCacheKey base_cache_key(db_id_, db_session_id_, file_number);
// Probe the whole-value cache per request; on a hit, slice the requested
@@ -1200,7 +1196,7 @@ void BlobSource::MultiGetBlobRangeFromOneFile(
// is simply surfaced per request. Hence no reader-refresh retry and no
// cache-fill (a partial value cannot represent the whole-record cache entry).
CacheHandleGuard<BlobFileReader> blob_file_reader;
Status s = blob_file_cache_->GetBlobFileReader(read_options, blob_file,
Status s = blob_file_cache_->GetBlobFileReader(read_options, file_number,
&blob_file_reader);
if (!s.ok()) {
for (auto& blob_req : _blob_reqs) {
+10 -14
View File
@@ -80,9 +80,8 @@ class BlobSource {
// sets "*bytes_read" to the size of on-disk (possibly compressed) blob
// record.
Status GetBlob(const ReadOptions& read_options, const Slice& user_key,
const BlobFileOpenInfo& blob_file, uint64_t offset,
uint64_t file_size, uint64_t value_size,
CompressionType compression_type,
uint64_t file_number, uint64_t offset, uint64_t file_size,
uint64_t value_size, CompressionType compression_type,
FilePrefetchBuffer* prefetch_buffer, PinnableSlice* value,
uint64_t* bytes_read);
@@ -106,11 +105,10 @@ class BlobSource {
// *bytes_read (when non-null) is the number of bytes read from the file; on a
// hit it is 0.
Status GetBlobRange(const ReadOptions& read_options, const Slice& user_key,
const BlobFileOpenInfo& blob_file, uint64_t offset,
uint64_t file_size, uint64_t value_size,
CompressionType compression_type, uint64_t range_offset,
size_t range_length, PinnableSlice* value,
uint64_t* bytes_read);
uint64_t file_number, uint64_t offset, uint64_t file_size,
uint64_t value_size, CompressionType compression_type,
uint64_t range_offset, size_t range_length,
PinnableSlice* value, uint64_t* bytes_read);
// Reads a SimpleGen2Blob payload (see db/blob/blob_gen2_format.h) through the
// blob value cache and BLOB_DB_* statistics. This is the counterpart to
@@ -234,8 +232,7 @@ class BlobSource {
// "*bytes_read" to the total size of on-disk (possibly compressed) blob
// records.
void MultiGetBlobFromOneFile(const ReadOptions& read_options,
const BlobFileOpenInfo& blob_file,
uint64_t file_size,
uint64_t file_number, uint64_t file_size,
autovector<BlobReadRequest>& blob_reqs,
uint64_t* bytes_read);
@@ -253,15 +250,14 @@ class BlobSource {
// Byte-range (partial) multi-read counterpart of MultiGetBlobFromOneFile for
// a single blob file. See MultiGetBlobRange.
void MultiGetBlobRangeFromOneFile(const ReadOptions& read_options,
const BlobFileOpenInfo& blob_file,
uint64_t file_size,
uint64_t file_number, uint64_t file_size,
autovector<BlobRangeReadRequest>& blob_reqs,
uint64_t* bytes_read);
inline Status GetBlobFileReader(
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
const ReadOptions& read_options, uint64_t blob_file_number,
CacheHandleGuard<BlobFileReader>* blob_file_reader) {
return blob_file_cache_->GetBlobFileReader(read_options, blob_file,
return blob_file_cache_->GetBlobFileReader(read_options, blob_file_number,
blob_file_reader);
}
+75 -93
View File
@@ -104,10 +104,6 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
&checksum_method, &checksum_value));
}
BlobFileOpenInfo OpenInfoWithoutChecksum(uint64_t file_number) {
return BlobFileOpenInfo{file_number, Slice(), Slice()};
}
} // anonymous namespace
class BlobSourceTest : public DBTestBase {
@@ -216,10 +212,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
blob_offsets[i]));
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
prefetch_buffer, &values[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer, &values[i],
&bytes_read));
ASSERT_EQ(values[i], blobs[i]);
ASSERT_TRUE(values[i].IsPinned());
ASSERT_EQ(bytes_read,
@@ -255,10 +251,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
blob_offsets[i]));
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
prefetch_buffer, &values[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer, &values[i],
&bytes_read));
ASSERT_EQ(values[i], blobs[i]);
ASSERT_TRUE(values[i].IsPinned());
ASSERT_EQ(bytes_read,
@@ -303,10 +299,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
blob_offsets[i]));
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
prefetch_buffer, &values[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer, &values[i],
&bytes_read));
ASSERT_EQ(values[i], blobs[i]);
ASSERT_TRUE(values[i].IsPinned());
ASSERT_EQ(bytes_read,
@@ -343,10 +339,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
blob_offsets[i]));
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
prefetch_buffer, &values[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer, &values[i],
&bytes_read));
ASSERT_EQ(values[i], blobs[i]);
ASSERT_TRUE(values[i].IsPinned());
ASSERT_EQ(bytes_read,
@@ -389,8 +385,7 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
blob_offsets[i]));
ASSERT_TRUE(blob_source
.GetBlob(read_options, keys[i],
OpenInfoWithoutChecksum(blob_file_number),
.GetBlob(read_options, keys[i], blob_file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer, &values[i],
&bytes_read)
@@ -432,8 +427,7 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
blob_offsets[i]));
ASSERT_TRUE(blob_source
.GetBlob(read_options, keys[i],
OpenInfoWithoutChecksum(file_number),
.GetBlob(read_options, keys[i], file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer, &values[i],
&bytes_read)
@@ -528,8 +522,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
compression, blob_offsets, blob_sizes);
CacheHandleGuard<BlobFileReader> blob_file_reader;
ASSERT_OK(blob_source.GetBlobFileReader(
read_options, OpenInfoWithoutChecksum(file_number), &blob_file_reader));
ASSERT_OK(blob_source.GetBlobFileReader(read_options, file_number,
&blob_file_reader));
ASSERT_NE(blob_file_reader.GetValue(), nullptr);
const uint64_t file_size = blob_file_reader.GetValue()->GetFileSize();
@@ -547,10 +541,10 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
for (size_t i = 0; i < num_blobs; ++i) {
ASSERT_FALSE(blob_source.TEST_BlobInCache(file_number, file_size,
blob_offsets[i]));
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(file_number),
blob_offsets[i], file_size, blob_sizes[i], compression,
nullptr /*prefetch_buffer*/, &values[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], file_number,
blob_offsets[i], file_size, blob_sizes[i],
compression, nullptr /*prefetch_buffer*/,
&values[i], &bytes_read));
ASSERT_EQ(values[i], blobs[i] /*uncompressed blob*/);
ASSERT_NE(values[i].size(), blob_sizes[i] /*compressed size*/);
ASSERT_EQ(bytes_read,
@@ -570,10 +564,10 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
blob_offsets[i]));
// Compressed blob size is passed in GetBlob
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(file_number),
blob_offsets[i], file_size, blob_sizes[i], compression,
nullptr /*prefetch_buffer*/, &values[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], file_number,
blob_offsets[i], file_size, blob_sizes[i],
compression, nullptr /*prefetch_buffer*/,
&values[i], &bytes_read));
ASSERT_EQ(values[i], blobs[i] /*uncompressed blob*/);
ASSERT_NE(values[i].size(), blob_sizes[i] /*compressed size*/);
ASSERT_EQ(bytes_read,
@@ -680,8 +674,7 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
keys[j], blob_offsets[j], blob_sizes[j], kNoCompression,
&value_buf[i * num_blobs + j], &statuses_buf[i * num_blobs + j]);
}
blob_reqs.emplace_back(OpenInfoWithoutChecksum(file_number), file_size,
blob_reqs_in_file[i]);
blob_reqs.emplace_back(file_number, file_size, blob_reqs_in_file[i]);
}
get_perf_context()->Reset();
@@ -738,8 +731,7 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
}
// Add a fake multi-get blob request.
blob_reqs.emplace_back(OpenInfoWithoutChecksum(fake_file_number), file_size,
fake_blob_reqs_in_file);
blob_reqs.emplace_back(fake_file_number, file_size, fake_blob_reqs_in_file);
blob_source.MultiGetBlob(read_options, blob_reqs, &bytes_read);
@@ -873,9 +865,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
statistics->Reset().PermitUncheckedError();
// Get half of blobs
blob_source.MultiGetBlobFromOneFile(
read_options, OpenInfoWithoutChecksum(blob_file_number), file_size,
blob_reqs, &bytes_read);
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number,
file_size, blob_reqs, &bytes_read);
uint64_t fs_read_bytes = 0;
uint64_t ca_read_bytes = 0;
@@ -920,10 +911,10 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
blob_offsets[i]));
ASSERT_OK(blob_source.GetBlob(
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
prefetch_buffer, &value_buf[i], &bytes_read));
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
blob_offsets[i], file_size, blob_sizes[i],
kNoCompression, prefetch_buffer,
&value_buf[i], &bytes_read));
ASSERT_EQ(value_buf[i], blobs[i]);
ASSERT_TRUE(value_buf[i].IsPinned());
ASSERT_EQ(bytes_read,
@@ -944,9 +935,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
kNoCompression, &value_buf[i], &statuses_buf[i]);
}
blob_source.MultiGetBlobFromOneFile(
read_options, OpenInfoWithoutChecksum(blob_file_number), file_size,
blob_reqs, &bytes_read);
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number,
file_size, blob_reqs, &bytes_read);
uint64_t blob_bytes = 0;
for (size_t i = 0; i < num_blobs; ++i) {
@@ -995,9 +985,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
get_perf_context()->Reset();
statistics->Reset().PermitUncheckedError();
blob_source.MultiGetBlobFromOneFile(
read_options, OpenInfoWithoutChecksum(blob_file_number), file_size,
blob_reqs, &bytes_read);
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number,
file_size, blob_reqs, &bytes_read);
for (size_t i = 0; i < num_blobs; ++i) {
ASSERT_TRUE(statuses_buf[i].IsIncomplete());
@@ -1040,9 +1029,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
get_perf_context()->Reset();
statistics->Reset().PermitUncheckedError();
blob_source.MultiGetBlobFromOneFile(
read_options, OpenInfoWithoutChecksum(non_existing_file_number),
file_size, blob_reqs, &bytes_read);
blob_source.MultiGetBlobFromOneFile(read_options, non_existing_file_number,
file_size, blob_reqs, &bytes_read);
for (size_t i = 0; i < num_blobs; ++i) {
ASSERT_TRUE(statuses_buf[i].IsIOError());
@@ -1116,9 +1104,8 @@ TEST_F(BlobSourceTest, GetBlobPreservesCorruptionDetailsWhenRefreshOpenFails) {
PinnableSlice value;
uint64_t bytes_read = 0;
ASSERT_OK(blob_source.GetBlob(
read_options, keys[0], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[0], file_size, blob_sizes[0], kNoCompression,
prefetch_buffer, &value, &bytes_read));
read_options, keys[0], blob_file_number, blob_offsets[0], file_size,
blob_sizes[0], kNoCompression, prefetch_buffer, &value, &bytes_read));
ASSERT_EQ(value, blobs[0]);
const std::string blob_file_path =
@@ -1127,10 +1114,10 @@ TEST_F(BlobSourceTest, GetBlobPreservesCorruptionDetailsWhenRefreshOpenFails) {
PinnableSlice invalid_value;
bytes_read = 0;
Status s = blob_source.GetBlob(
read_options, keys[0], OpenInfoWithoutChecksum(blob_file_number),
file_size, file_size, blob_sizes[0], kNoCompression, prefetch_buffer,
&invalid_value, &bytes_read);
Status s =
blob_source.GetBlob(read_options, keys[0], blob_file_number, file_size,
file_size, blob_sizes[0], kNoCompression,
prefetch_buffer, &invalid_value, &bytes_read);
ASSERT_TRUE(s.IsCorruption());
ASSERT_EQ(bytes_read, 0);
@@ -1191,9 +1178,8 @@ TEST_F(BlobSourceTest,
PinnableSlice value;
uint64_t bytes_read = 0;
ASSERT_OK(blob_source.GetBlob(
read_options, keys[0], OpenInfoWithoutChecksum(blob_file_number),
blob_offsets[0], file_size, blob_sizes[0], kNoCompression,
prefetch_buffer, &value, &bytes_read));
read_options, keys[0], blob_file_number, blob_offsets[0], file_size,
blob_sizes[0], kNoCompression, prefetch_buffer, &value, &bytes_read));
ASSERT_EQ(value, blobs[0]);
const std::string blob_file_path =
@@ -1207,9 +1193,8 @@ TEST_F(BlobSourceTest,
&values[0], &statuses[0]);
bytes_read = 0;
blob_source.MultiGetBlobFromOneFile(read_options,
OpenInfoWithoutChecksum(blob_file_number),
file_size, blob_reqs, &bytes_read);
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number, file_size,
blob_reqs, &bytes_read);
ASSERT_TRUE(statuses[0].IsCorruption());
ASSERT_EQ(bytes_read, 0);
@@ -1318,8 +1303,8 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
CacheHandleGuard<BlobFileReader> file_reader;
ReadOptions read_options;
ASSERT_OK(blob_source.GetBlobFileReader(
read_options, OpenInfoWithoutChecksum(file_number), &file_reader));
ASSERT_OK(
blob_source.GetBlobFileReader(read_options, file_number, &file_reader));
ASSERT_NE(file_reader.GetValue(), nullptr);
const uint64_t file_size = file_reader.GetValue()->GetFileSize();
ASSERT_EQ(file_reader.GetValue()->GetCompressionType(), kNoCompression);
@@ -1337,8 +1322,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
get_perf_context()->Reset();
// key0 should be filled to the primary cache from the blob file.
ASSERT_OK(blob_source.GetBlob(read_options, keys[0],
OpenInfoWithoutChecksum(file_number),
ASSERT_OK(blob_source.GetBlob(read_options, keys[0], file_number,
blob_offsets[0], file_size, blob_sizes[0],
kNoCompression, nullptr /* prefetch_buffer */,
values.data(), nullptr /* bytes_read */));
@@ -1347,18 +1331,17 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
// key0 should be evicted and key0's dummy item is inserted into secondary
// cache. key1 should be filled to the primary cache from the blob file.
ASSERT_OK(blob_source.GetBlob(
read_options, keys[1], OpenInfoWithoutChecksum(file_number),
blob_offsets[1], file_size, blob_sizes[1], kNoCompression,
nullptr /* prefetch_buffer */, &values[1], nullptr /* bytes_read */));
ASSERT_OK(blob_source.GetBlob(read_options, keys[1], file_number,
blob_offsets[1], file_size, blob_sizes[1],
kNoCompression, nullptr /* prefetch_buffer */,
&values[1], nullptr /* bytes_read */));
// Release cache handle
values[1].Reset();
// key0 should be filled to the primary cache from the blob file. key1
// should be evicted and key1's dummy item is inserted into secondary cache.
ASSERT_OK(blob_source.GetBlob(read_options, keys[0],
OpenInfoWithoutChecksum(file_number),
ASSERT_OK(blob_source.GetBlob(read_options, keys[0], file_number,
blob_offsets[0], file_size, blob_sizes[0],
kNoCompression, nullptr /* prefetch_buffer */,
values.data(), nullptr /* bytes_read */));
@@ -1371,10 +1354,10 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
// key0 should be evicted and is inserted into secondary cache.
// key1 should be filled to the primary cache from the blob file.
ASSERT_OK(blob_source.GetBlob(
read_options, keys[1], OpenInfoWithoutChecksum(file_number),
blob_offsets[1], file_size, blob_sizes[1], kNoCompression,
nullptr /* prefetch_buffer */, &values[1], nullptr /* bytes_read */));
ASSERT_OK(blob_source.GetBlob(read_options, keys[1], file_number,
blob_offsets[1], file_size, blob_sizes[1],
kNoCompression, nullptr /* prefetch_buffer */,
&values[1], nullptr /* bytes_read */));
ASSERT_EQ(values[1], blobs[1]);
ASSERT_TRUE(
blob_source.TEST_BlobInCache(file_number, file_size, blob_offsets[1]));
@@ -1439,10 +1422,9 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
// fetch key0 from the blob file to the primary cache.
// key1 is evicted and inserted into the secondary cache.
ASSERT_OK(blob_source.GetBlob(
read_options, keys[0], OpenInfoWithoutChecksum(file_number),
blob_offsets[0], file_size, blob_sizes[0], kNoCompression,
nullptr /* prefetch_buffer */, values.data(),
nullptr /* bytes_read */));
read_options, keys[0], file_number, blob_offsets[0], file_size,
blob_sizes[0], kNoCompression, nullptr /* prefetch_buffer */,
values.data(), nullptr /* bytes_read */));
ASSERT_EQ(values[0], blobs[0]);
// Release cache handle
@@ -1624,8 +1606,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
for (size_t i = 0; i < kNumBlobs; ++i) {
ASSERT_OK(blob_source.GetBlob(
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
blob_file_size_, blob_sizes[i], kNoCompression,
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
ASSERT_EQ(cache_res_mgr->GetTotalReservedCacheSize(), 0);
ASSERT_EQ(cache_res_mgr->GetTotalMemoryUsed(), 0);
@@ -1643,8 +1625,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
uint64_t blob_bytes = 0;
for (size_t i = 0; i < kNumBlobs; ++i) {
ASSERT_OK(blob_source.GetBlob(
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
blob_file_size_, blob_sizes[i], kNoCompression,
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
size_t charge = 0;
@@ -1746,8 +1728,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
for (size_t i = 0; i < kNumBlobs; ++i) {
ASSERT_OK(blob_source.GetBlob(
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
blob_file_size_, blob_sizes[i], kNoCompression,
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
ASSERT_EQ(cache_res_mgr->GetTotalReservedCacheSize(), 0);
ASSERT_EQ(cache_res_mgr->GetTotalMemoryUsed(), 0);
@@ -1762,8 +1744,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
uint64_t blob_bytes = 0;
for (size_t i = 0; i < kNumBlobs; ++i) {
ASSERT_OK(blob_source.GetBlob(
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
blob_file_size_, blob_sizes[i], kNoCompression,
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
// Release cache handle
-92
View File
@@ -7,7 +7,6 @@
#include <array>
#include <atomic>
#include <cstdint>
#include <mutex>
#include <set>
#include <sstream>
#include <string>
@@ -20,19 +19,15 @@
#include "db/column_family.h"
#include "db/db_test_util.h"
#include "db/db_with_timestamp_test_util.h"
#include "env/composite_env_wrapper.h"
#include "file/filename.h"
#include "file/random_access_file_reader.h"
#include "port/stack_trace.h"
#include "rocksdb/convenience.h"
#include "rocksdb/file_system.h"
#include "rocksdb/trace_reader_writer.h"
#include "rocksdb/trace_record.h"
#include "rocksdb/utilities/replayer.h"
#include "test_util/sync_point.h"
#include "util/compression.h"
#include "util/defer.h"
#include "util/file_checksum_helper.h"
#include "utilities/fault_injection_env.h"
namespace ROCKSDB_NAMESPACE {
@@ -43,49 +38,6 @@ class DBBlobBasicTest : public DBTestBase {
: DBTestBase("db_blob_basic_test", /* env_do_fsync */ false) {}
};
class BlobFileChecksumCapturingFS : public FileSystemWrapper {
public:
explicit BlobFileChecksumCapturingFS(const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {}
static const char* kClassName() { return "BlobFileChecksumCapturingFS"; }
const char* Name() const override { return kClassName(); }
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& opts,
std::unique_ptr<FSRandomAccessFile>* result,
IODebugContext* dbg) override {
if (fname.find(".blob") != std::string::npos) {
std::lock_guard<std::mutex> lock(mu_);
file_checksum_ = opts.file_checksum;
file_checksum_func_name_ = opts.file_checksum_func_name;
++capture_count_;
}
return target()->NewRandomAccessFile(fname, opts, result, dbg);
}
std::string GetFileChecksum() {
std::lock_guard<std::mutex> lock(mu_);
return file_checksum_;
}
std::string GetFileChecksumFuncName() {
std::lock_guard<std::mutex> lock(mu_);
return file_checksum_func_name_;
}
int GetCaptureCount() {
std::lock_guard<std::mutex> lock(mu_);
return capture_count_;
}
private:
std::mutex mu_;
std::string file_checksum_;
std::string file_checksum_func_name_;
int capture_count_ = 0;
};
TEST_F(DBBlobBasicTest, GetBlob) {
Options options = GetDefaultOptions();
options.enable_blob_files = true;
@@ -113,41 +65,6 @@ TEST_F(DBBlobBasicTest, GetBlob) {
.IsIncomplete());
}
TEST_F(DBBlobBasicTest, BlobFileChecksumInFileOptions) {
auto capturing_fs =
std::make_shared<BlobFileChecksumCapturingFS>(env_->GetFileSystem());
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, capturing_fs));
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.env = env.get();
options.enable_blob_files = true;
options.min_blob_size = 0;
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
Defer close_db([this]() { Close(); });
Reopen(options);
constexpr char key[] = "key";
constexpr char blob_value[] = "blob_value";
ASSERT_OK(Put(key, blob_value));
ASSERT_OK(Flush());
std::vector<ColumnFamilyMetaData> column_family_metadata;
db_->GetAllColumnFamilyMetaData(&column_family_metadata);
ASSERT_EQ(column_family_metadata.size(), 1);
ASSERT_EQ(column_family_metadata[0].blob_files.size(), 1);
const BlobMetaData& blob_metadata = column_family_metadata[0].blob_files[0];
ASSERT_FALSE(blob_metadata.checksum_value.empty());
ASSERT_EQ(blob_metadata.checksum_method, "FileChecksumCrc32c");
ASSERT_EQ(Get(key), blob_value);
ASSERT_GT(capturing_fs->GetCaptureCount(), 0);
ASSERT_EQ(capturing_fs->GetFileChecksum(), blob_metadata.checksum_value);
ASSERT_EQ(capturing_fs->GetFileChecksumFuncName(),
blob_metadata.checksum_method);
}
TEST_F(DBBlobBasicTest, BlobFileWritableFileMaxBufferSize) {
constexpr uint64_t kBlobWriterBufferSize = 128 * 1024;
@@ -837,15 +754,6 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
ASSERT_EQ(values[2], third_value);
}
// Load the SST blocks into the block cache. The inlined key does not read
// the blob file, so the blob cache stays empty.
{
ReadOptions fill_block_cache_options;
std::string value;
ASSERT_OK(db_->Get(fill_block_cache_options, first_key, &value));
ASSERT_EQ(value, first_value);
}
// Try again with no I/O allowed. The first (inlined) value should be
// successfully read; however, the two blob values could only be read from the
// blob file, so for those the read should return Incomplete.
-103
View File
@@ -40,8 +40,6 @@
#include "rocksdb/utilities/replayer.h"
#include "test_util/sync_point.h"
#include "util/compression.h"
#include "util/defer.h"
#include "util/file_checksum_helper.h"
#include "utilities/fault_injection_env.h"
namespace ROCKSDB_NAMESPACE {
@@ -123,59 +121,6 @@ bool IsBlobFilePath(const std::string& fname) {
file_type == kBlobFile;
}
class BlobFileChecksumCapturingFileSystem : public FileSystemWrapper {
public:
explicit BlobFileChecksumCapturingFileSystem(
const std::shared_ptr<FileSystem>& fs)
: FileSystemWrapper(fs) {}
static const char* kClassName() {
return "BlobFileChecksumCapturingFileSystem";
}
const char* Name() const override { return kClassName(); }
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& opts,
std::unique_ptr<FSRandomAccessFile>* result,
IODebugContext* dbg) override {
if (IsBlobFilePath(fname)) {
std::lock_guard<std::mutex> lock(mu_);
file_checksum_ = opts.file_checksum;
file_checksum_func_name_ = opts.file_checksum_func_name;
++capture_count_;
}
return target()->NewRandomAccessFile(fname, opts, result, dbg);
}
void Reset() {
std::lock_guard<std::mutex> lock(mu_);
file_checksum_.clear();
file_checksum_func_name_.clear();
capture_count_ = 0;
}
std::string GetFileChecksum() const {
std::lock_guard<std::mutex> lock(mu_);
return file_checksum_;
}
std::string GetFileChecksumFuncName() const {
std::lock_guard<std::mutex> lock(mu_);
return file_checksum_func_name_;
}
int GetCaptureCount() const {
std::lock_guard<std::mutex> lock(mu_);
return capture_count_;
}
private:
mutable std::mutex mu_;
std::string file_checksum_;
std::string file_checksum_func_name_;
int capture_count_ = 0;
};
// Keeps track of when an active blob file stops being writer-visible so the
// test filesystem can model current-size visibility only while the file remains
// active.
@@ -1810,54 +1755,6 @@ TEST_F(
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->value().ToString(), values[target_idx]);
}
TEST_F(DBBlobDirectWriteTest, DirectWriteDelayedReadUsesFinalizedFileChecksum) {
auto capturing_fs = std::make_shared<BlobFileChecksumCapturingFileSystem>(
env_->GetFileSystem());
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, capturing_fs));
Options options = GetDirectWriteOptions();
options.env = env.get();
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
Defer close_db([this]() { Close(); });
Reopen(options);
const std::string key = "delayed-read-key";
const std::string value(96, 'v');
ASSERT_OK(Put(key, value));
ReadOptions read_options;
read_options.allow_unprepared_value = true;
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
iter->Seek(key);
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key(), key);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(Flush());
std::vector<ColumnFamilyMetaData> column_family_metadata;
db_->GetAllColumnFamilyMetaData(&column_family_metadata);
ASSERT_EQ(column_family_metadata.size(), 1);
ASSERT_EQ(column_family_metadata[0].blob_files.size(), 1);
const BlobMetaData& blob_metadata = column_family_metadata[0].blob_files[0];
ASSERT_FALSE(blob_metadata.checksum_value.empty());
ASSERT_EQ(blob_metadata.checksum_method, "FileChecksumCrc32c");
capturing_fs->Reset();
ASSERT_TRUE(iter->PrepareValue());
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->value().ToString(), value);
ASSERT_GT(capturing_fs->GetCaptureCount(), 0);
ASSERT_EQ(capturing_fs->GetFileChecksum(), blob_metadata.checksum_value);
ASSERT_EQ(capturing_fs->GetFileChecksumFuncName(),
blob_metadata.checksum_method);
const int capture_count = capturing_fs->GetCaptureCount();
ASSERT_EQ(Get(key), value);
ASSERT_EQ(capturing_fs->GetCaptureCount(), capture_count);
}
TEST_F(DBBlobDirectWriteTest, OrderedTraceUsesLogicalBatchForBlobDirectWrite) {
AssertOrderedTraceStoresLogicalPut(GetDirectWriteOptions(),
/*expect_blob_files=*/true);
+2 -3
View File
@@ -396,7 +396,7 @@ Status BuildTable(
relevant_mapping,
ioptions.compaction_style == CompactionStyle::kCompactionStyleFIFO
? meta->file_creation_time
: meta->oldest_ancestor_time);
: meta->oldest_ancester_time);
s = builder->Finish();
}
if (io_status->ok()) {
@@ -452,8 +452,7 @@ Status BuildTable(
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
if (s.ok() && io_status->ok() && !empty) {
StopWatch sw(ioptions.clock, ioptions.stats, TABLE_SYNC_MICROS);
*io_status = ioptions.use_fsync ? file_writer->Fsync(opts)
: file_writer->Sync(opts);
*io_status = file_writer->Sync(opts, ioptions.use_fsync);
}
TEST_SYNC_POINT("BuildTable:BeforeCloseTableFile");
if (s.ok() && io_status->ok() && !empty) {
+15 -62
View File
@@ -195,7 +195,6 @@ using ROCKSDB_NAMESPACE::WALRecoveryMode;
using ROCKSDB_NAMESPACE::WritableFile;
using ROCKSDB_NAMESPACE::WriteBatch;
using ROCKSDB_NAMESPACE::WriteBatchWithIndex;
using ROCKSDB_NAMESPACE::WriteBufferFlushPolicy;
using ROCKSDB_NAMESPACE::WriteBufferManager;
using ROCKSDB_NAMESPACE::WriteOptions;
using ROCKSDB_NAMESPACE::WriteStallCondition;
@@ -5198,11 +5197,6 @@ uint64_t rocksdb_table_properties_column_family_id(
return props->rep.column_family_id;
}
uint64_t rocksdb_table_properties_oldest_ancestor_time(
const rocksdb_table_properties_t* props) {
return props->rep.oldest_ancestor_time;
}
uint64_t rocksdb_table_properties_creation_time(
const rocksdb_table_properties_t* props) {
return props->rep.creation_time;
@@ -5233,6 +5227,11 @@ uint64_t rocksdb_table_properties_fast_compression_estimated_data_size(
return props->rep.fast_compression_estimated_data_size;
}
uint64_t rocksdb_table_properties_external_sst_file_global_seqno_offset(
const rocksdb_table_properties_t* props) {
return props->rep.external_sst_file_global_seqno_offset;
}
uint64_t rocksdb_table_properties_tail_start_offset(
const rocksdb_table_properties_t* props) {
return props->rep.tail_start_offset;
@@ -7762,35 +7761,6 @@ rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create(
return wbm;
}
static bool rocksdb_write_buffer_manager_parse_flush_policy(
int value, WriteBufferFlushPolicy* policy) {
switch (value) {
case rocksdb_write_buffer_manager_flush_policy_oldest:
*policy = WriteBufferFlushPolicy::kFlushOldest;
return true;
case rocksdb_write_buffer_manager_flush_policy_largest:
*policy = WriteBufferFlushPolicy::kFlushLargest;
return true;
case rocksdb_write_buffer_manager_flush_policy_largest_across_dbs:
*policy = WriteBufferFlushPolicy::kFlushLargestAcrossDBs;
return true;
default:
return false;
}
}
rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_policy(
size_t buffer_size, bool allow_stall, int flush_policy) {
WriteBufferFlushPolicy policy;
if (!rocksdb_write_buffer_manager_parse_flush_policy(flush_policy, &policy)) {
return nullptr;
}
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
wbm->rep = std::make_shared<WriteBufferManager>(
buffer_size, std::shared_ptr<Cache>{}, allow_stall, policy);
return wbm;
}
rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_cache(
size_t buffer_size, const rocksdb_cache_t* cache, bool allow_stall) {
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
@@ -7799,20 +7769,6 @@ rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_cache(
return wbm;
}
rocksdb_write_buffer_manager_t*
rocksdb_write_buffer_manager_create_with_cache_and_policy(
size_t buffer_size, const rocksdb_cache_t* cache, bool allow_stall,
int flush_policy) {
WriteBufferFlushPolicy policy;
if (!rocksdb_write_buffer_manager_parse_flush_policy(flush_policy, &policy)) {
return nullptr;
}
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
wbm->rep = std::make_shared<WriteBufferManager>(buffer_size, cache->rep,
allow_stall, policy);
return wbm;
}
void rocksdb_write_buffer_manager_destroy(rocksdb_write_buffer_manager_t* wbm) {
delete wbm;
}
@@ -7853,19 +7809,6 @@ ROCKSDB_LIBRARY_API void rocksdb_write_buffer_manager_set_allow_stall(
wbm->rep->SetAllowStall(new_allow_stall);
}
int rocksdb_write_buffer_manager_flush_policy(
rocksdb_write_buffer_manager_t* wbm) {
return static_cast<int>(wbm->rep->flush_policy());
}
void rocksdb_write_buffer_manager_set_flush_policy(
rocksdb_write_buffer_manager_t* wbm, int flush_policy) {
WriteBufferFlushPolicy policy;
if (rocksdb_write_buffer_manager_parse_flush_policy(flush_policy, &policy)) {
wbm->rep->SetFlushPolicy(policy);
}
}
rocksdb_sst_file_manager_t* rocksdb_sst_file_manager_create(
rocksdb_env_t* env) {
rocksdb_sst_file_manager_t* sfm = new rocksdb_sst_file_manager_t;
@@ -11333,6 +11276,16 @@ unsigned char rocksdb_ingestexternalfileoptions_get_ingest_behind(
return opt->rep.ingest_behind;
}
void rocksdb_ingestexternalfileoptions_set_write_global_seqno(
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
opt->rep.write_global_seqno = v;
}
unsigned char rocksdb_ingestexternalfileoptions_get_write_global_seqno(
rocksdb_ingestexternalfileoptions_t* opt) {
return opt->rep.write_global_seqno;
}
void rocksdb_ingestexternalfileoptions_set_verify_checksums_before_ingest(
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
opt->rep.verify_checksums_before_ingest = v;
-30
View File
@@ -6716,36 +6716,6 @@ int main(int argc, char** argv) {
300 == rocksdb_write_buffer_manager_buffer_size(write_buffer_manager));
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
write_buffer_manager = rocksdb_write_buffer_manager_create_with_policy(
200, true,
rocksdb_write_buffer_manager_flush_policy_largest_across_dbs);
CheckCondition(write_buffer_manager != NULL);
CheckCondition(
rocksdb_write_buffer_manager_flush_policy_largest_across_dbs ==
rocksdb_write_buffer_manager_flush_policy(write_buffer_manager));
rocksdb_write_buffer_manager_set_flush_policy(
write_buffer_manager,
rocksdb_write_buffer_manager_flush_policy_largest);
CheckCondition(
rocksdb_write_buffer_manager_flush_policy_largest ==
rocksdb_write_buffer_manager_flush_policy(write_buffer_manager));
rocksdb_write_buffer_manager_set_flush_policy(write_buffer_manager, 999);
CheckCondition(
rocksdb_write_buffer_manager_flush_policy_largest ==
rocksdb_write_buffer_manager_flush_policy(write_buffer_manager));
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
write_buffer_manager =
rocksdb_write_buffer_manager_create_with_cache_and_policy(
200, lru, true,
rocksdb_write_buffer_manager_flush_policy_largest_across_dbs);
CheckCondition(write_buffer_manager != NULL);
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
write_buffer_manager =
rocksdb_write_buffer_manager_create_with_policy(200, true, 999);
CheckCondition(write_buffer_manager == NULL);
rocksdb_cache_destroy(lru);
}
+2 -116
View File
@@ -33,7 +33,6 @@
#include "db/write_controller.h"
#include "file/sst_file_manager_impl.h"
#include "logging/logging.h"
#include "memtable/flush_initiator.h"
#include "monitoring/thread_status_util.h"
#include "options/options_helper.h"
#include "port/port.h"
@@ -673,7 +672,7 @@ ColumnFamilyData::ColumnFamilyData(
new InternalStats(ioptions_.num_levels, ioptions_.clock, this));
table_cache_.reset(new TableCache(ioptions_, file_options, _table_cache,
block_cache_tracer, io_tracer,
db_session_id, fast_sst_open, name_));
db_session_id, fast_sst_open));
blob_file_cache_.reset(
new BlobFileCache(_table_cache, &ioptions(), soptions(), id_,
internal_stats_->GetBlobFileReadHist(), io_tracer));
@@ -835,11 +834,6 @@ void ColumnFamilyData::SetDropped() {
// can't drop default CF
assert(id_ != 0);
dropped_ = true;
// A tracker can still contain accounting published while a different WBM
// policy was active. Cleanup therefore cannot depend on the current policy.
if (mem_ != nullptr) {
mem_->StopWBMTracking();
}
write_controller_token_.reset();
// remove from column_family_set
@@ -1252,10 +1246,7 @@ uint64_t ColumnFamilyData::GetLiveSstFilesSize() const {
MemTable* ColumnFamilyData::ConstructNewMemtable(
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
write_buffer_manager_, earliest_seq, id_,
column_family_set_ != nullptr
? column_family_set_->flush_initiator()
: nullptr);
write_buffer_manager_, earliest_seq, id_);
}
void ColumnFamilyData::CreateNewMemtable(SequenceNumber earliest_seq) {
@@ -1908,102 +1899,6 @@ size_t ColumnFamilySet::NumberOfColumnFamilies() const {
return column_families_.size();
}
void ColumnFamilySet::RefreshFlushableMemAccounting() {
if (flush_initiator_ == nullptr || write_buffer_manager_ == nullptr ||
!write_buffer_manager_->ShouldTrackFlushInitiator()) {
return;
}
if (bulk_flushable_mem_accounting_depth_ > 0) {
bulk_flushable_mem_accounting_dirty_ = true;
flush_initiator_->InvalidateLargestFlushableCFMem();
return;
}
// Keep candidate readers from consuming a mixture of the previous snapshot
// and the values published by this rebuild.
flush_initiator_->InvalidateLargestFlushableCFMem();
constexpr int kMaxRefreshAttempts = 3;
for (int attempt = 0; attempt < kMaxRefreshAttempts; ++attempt) {
const uint64_t update_seq =
flush_initiator_->GetFlushableMemUpdateSequence();
bool has_mutable_mem = false;
bool has_ineligible_cf = false;
size_t largest = 0;
size_t waiting_immutable_mem = 0;
for (auto cfd : *this) {
if (cfd->IsDropped() || !cfd->initialized()) {
continue;
}
const bool mutable_not_empty = !cfd->mem()->IsEmpty();
if (mutable_not_empty) {
cfd->mem()->RefreshWBMTracking();
}
const bool has_mutable =
mutable_not_empty && cfd->mem()->IsWBMTrackingActive();
has_mutable_mem = has_mutable_mem || has_mutable;
if (flush_initiator_->UsesTotalMutableMem()) {
waiting_immutable_mem +=
cfd->imm()->WBMTrackedUnstartedMemTablesMemoryUsage();
continue;
}
if (cfd->imm()->IsFlushPendingOrRunning()) {
has_ineligible_cf = true;
continue;
}
const size_t mutable_mem =
has_mutable ? cfd->mem()->WBMTrackedMemoryUsage() : 0;
const size_t immutable_mem =
cfd->imm()->WBMTrackedUnflushedMemTablesMemoryUsage();
waiting_immutable_mem += immutable_mem;
largest = std::max(largest, mutable_mem + immutable_mem);
}
if (flush_initiator_->UsesTotalMutableMem()) {
flush_initiator_->SetHasIneligibleCF(false);
flush_initiator_->SetHasFlushableCF(has_mutable_mem ||
waiting_immutable_mem > 0);
if (flush_initiator_->TrySetLargestFlushableCFMem(
0, waiting_immutable_mem, update_seq)) {
write_buffer_manager_->NotifyFlushInitiatorChanged();
return;
}
continue;
}
flush_initiator_->SetHasIneligibleCF(has_ineligible_cf);
flush_initiator_->SetHasFlushableCF(largest > 0);
if (flush_initiator_->TrySetLargestFlushableCFMem(
largest, waiting_immutable_mem, update_seq)) {
write_buffer_manager_->NotifyFlushInitiatorChanged();
return;
}
}
flush_initiator_->InvalidateLargestFlushableCFMem();
}
void ColumnFamilySet::BeginBulkFlushableMemAccountingUpdate() {
++bulk_flushable_mem_accounting_depth_;
if (bulk_flushable_mem_accounting_depth_ == 1) {
bulk_flushable_mem_accounting_dirty_ = true;
if (flush_initiator_ != nullptr && write_buffer_manager_ != nullptr &&
write_buffer_manager_->ShouldTrackFlushInitiator()) {
flush_initiator_->InvalidateLargestFlushableCFMem();
write_buffer_manager_->NotifyFlushInitiatorChanged();
}
}
}
void ColumnFamilySet::EndBulkFlushableMemAccountingUpdate() {
assert(bulk_flushable_mem_accounting_depth_ > 0);
--bulk_flushable_mem_accounting_depth_;
if (bulk_flushable_mem_accounting_depth_ == 0 &&
bulk_flushable_mem_accounting_dirty_) {
bulk_flushable_mem_accounting_dirty_ = false;
TEST_SYNC_POINT(
"ColumnFamilySet::EndBulkFlushableMemAccountingUpdate:Refresh");
RefreshFlushableMemAccounting();
}
}
// under a DB mutex AND write thread
ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
const std::string& name, uint32_t id, Version* dummy_versions,
@@ -2044,15 +1939,6 @@ void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
column_families_.erase(cfd->GetName());
running_ts_sz_.erase(cf_id);
ts_sz_for_record_.erase(cf_id);
if (bulk_flushable_mem_accounting_depth_ > 0) {
bulk_flushable_mem_accounting_dirty_ = true;
if (flush_initiator_ != nullptr) {
flush_initiator_->InvalidateLargestFlushableCFMem();
}
return;
}
TEST_SYNC_POINT("ColumnFamilySet::RemoveColumnFamily:Refresh");
RefreshFlushableMemAccounting();
}
// under a DB mutex OR from a write thread
-18
View File
@@ -52,7 +52,6 @@ struct SuperVersionContext;
class BlobFileCache;
class BlobFilePartitionManager;
class BlobSource;
class FlushInitiator;
extern const double kIncSlowdownRatio;
// This file contains a list of data structures for managing column family
@@ -872,19 +871,6 @@ class ColumnFamilySet {
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
void SetFlushInitiator(FlushInitiator* flush_initiator) {
flush_initiator_ = flush_initiator;
}
FlushInitiator* flush_initiator() const { return flush_initiator_; }
void RefreshFlushableMemAccounting();
// Coalesces accounting rebuilds while a caller removes multiple column
// families. Both methods require the DB mutex.
void BeginBulkFlushableMemAccountingUpdate();
void EndBulkFlushableMemAccountingUpdate();
WriteController* write_controller() { return write_controller_; }
private:
@@ -912,9 +898,6 @@ class ColumnFamilySet {
// column families with non-zero user-defined timestamp size.
UnorderedMap<uint32_t, size_t> ts_sz_for_record_;
int bulk_flushable_mem_accounting_depth_ = 0;
bool bulk_flushable_mem_accounting_dirty_ = false;
uint32_t max_column_family_;
const FileOptions file_options_;
@@ -929,7 +912,6 @@ class ColumnFamilySet {
const ImmutableDBOptions* const db_options_;
Cache* table_cache_;
WriteBufferManager* write_buffer_manager_;
FlushInitiator* flush_initiator_ = nullptr;
WriteController* write_controller_;
BlockCacheTracer* const block_cache_tracer_;
std::shared_ptr<IOTracer> io_tracer_;
-46
View File
@@ -774,8 +774,6 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
constexpr int kNumCF = 1000;
ColumnFamilyOptions cf_options;
WriteOptions write_options;
db_options_.write_buffer_manager = std::make_shared<WriteBufferManager>(
1 << 30, nullptr, false, WriteBufferFlushPolicy::kFlushLargestAcrossDBs);
Open();
std::vector<std::string> cf_names;
std::vector<ColumnFamilyHandle*> cf_handles;
@@ -786,22 +784,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
for (int i = 1; i <= kNumCF; i++) {
ASSERT_OK(db_->Put(write_options, cf_handles[i - 1], "foo", "bar"));
}
std::atomic<int> per_removal_refreshes{0};
std::atomic<int> bulk_refreshes{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"ColumnFamilySet::RemoveColumnFamily:Refresh",
[&](void*) { ++per_removal_refreshes; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"ColumnFamilySet::EndBulkFlushableMemAccountingUpdate:Refresh",
[&](void*) { ++bulk_refreshes; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_points([] {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
});
ASSERT_OK(db_->DropColumnFamilies(cf_handles));
EXPECT_EQ(0, per_removal_refreshes.load());
EXPECT_EQ(1, bulk_refreshes.load());
std::vector<ColumnFamilyDescriptor> cf_descriptors;
for (auto* handle : cf_handles) {
delete handle;
@@ -815,10 +798,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
for (int i = 1; i <= kNumCF; i++) {
ASSERT_OK(db_->Put(write_options, cf_handles[i - 1], "foo", "bar"));
}
bulk_refreshes.store(0);
ASSERT_OK(db_->DropColumnFamilies(cf_handles));
EXPECT_EQ(0, per_removal_refreshes.load());
EXPECT_EQ(1, bulk_refreshes.load());
for (auto* handle : cf_handles) {
delete handle;
}
@@ -3187,32 +3167,6 @@ TEST_P(ColumnFamilyTest, CreateDropAndDestroy) {
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
}
TEST_P(ColumnFamilyTest, DropWaitsForUnorderedMemtableWrite) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL",
"ColumnFamilyTest::DropWaitsForUnorderedMemtableWrite:AfterWAL"},
{"DBImpl::WaitForPendingWrites:BeforeBlock",
"DBImpl::WriteImpl:BeforeUnorderedWriteMemtable"}});
db_options_.unordered_write = true;
Open();
ColumnFamilyHandle* cfh;
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "yoyo", &cfh));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
port::Thread writer(
[&] { ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar")); });
TEST_SYNC_POINT(
"ColumnFamilyTest::DropWaitsForUnorderedMemtableWrite:AfterWAL");
ASSERT_OK(db_->DropColumnFamily(cfh));
writer.join();
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(ColumnFamilyTest, CreateDropAndDestroyWithoutFileDeletion) {
ColumnFamilyHandle* cfh;
Open();
+7 -7
View File
@@ -986,9 +986,9 @@ uint64_t Compaction::MaxInputFileNewestKeyTime(const InternalKey* start,
return newest_key_time;
}
uint64_t Compaction::MinInputFileOldestAncestorTime(
uint64_t Compaction::MinInputFileOldestAncesterTime(
const InternalKey* start, const InternalKey* end) const {
uint64_t min_oldest_ancestor_time = std::numeric_limits<uint64_t>::max();
uint64_t min_oldest_ancester_time = std::numeric_limits<uint64_t>::max();
const InternalKeyComparator& icmp =
column_family_data()->internal_comparator();
for (const auto& level_files : inputs_) {
@@ -999,14 +999,14 @@ uint64_t Compaction::MinInputFileOldestAncestorTime(
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
continue;
}
uint64_t oldest_ancestor_time = file->TryGetOldestAncestorTime();
if (oldest_ancestor_time != 0) {
min_oldest_ancestor_time =
std::min(min_oldest_ancestor_time, oldest_ancestor_time);
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
if (oldest_ancester_time != 0) {
min_oldest_ancester_time =
std::min(min_oldest_ancester_time, oldest_ancester_time);
}
}
}
return min_oldest_ancestor_time;
return min_oldest_ancester_time;
}
uint64_t Compaction::MinInputFileEpochNumber() const {
+2 -2
View File
@@ -438,8 +438,8 @@ class Compaction {
const InternalKey* end) const;
// start and end are sub compact range. Null if no boundary.
// This is used to filter out some input files' ancestor's time range.
uint64_t MinInputFileOldestAncestorTime(const InternalKey* start,
// This is used to filter out some input files' ancester's time range.
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
const InternalKey* end) const;
// Return the minimum epoch number among
// input files' associated with this compaction
+15 -16
View File
@@ -2160,20 +2160,20 @@ Status CompactionJob::FinishCompactionOutputFile(
if (s.ok()) {
// With accurate smallest and largest key, we can get a slightly more
// accurate oldest ancestor time.
// This makes oldest ancestor time in manifest more accurate than in
// accurate oldest ancester time.
// This makes oldest ancester time in manifest more accurate than in
// table properties. Not sure how to resolve it.
if (meta->smallest.size() > 0 && meta->largest.size() > 0) {
uint64_t refined_oldest_ancestor_time;
uint64_t refined_oldest_ancester_time;
Slice new_smallest = meta->smallest.user_key();
Slice new_largest = meta->largest.user_key();
if (!new_largest.empty() && !new_smallest.empty()) {
refined_oldest_ancestor_time =
sub_compact->compaction->MinInputFileOldestAncestorTime(
refined_oldest_ancester_time =
sub_compact->compaction->MinInputFileOldestAncesterTime(
&(meta->smallest), &(meta->largest));
if (refined_oldest_ancestor_time !=
if (refined_oldest_ancester_time !=
std::numeric_limits<uint64_t>::max()) {
meta->oldest_ancestor_time = refined_oldest_ancestor_time;
meta->oldest_ancester_time = refined_oldest_ancester_time;
}
}
}
@@ -2555,7 +2555,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
return s;
}
// Try to figure out the output file's oldest ancestor time.
// Try to figure out the output file's oldest ancester time.
int64_t temp_current_time = 0;
auto get_time_status = db_options_.clock->GetCurrentTime(&temp_current_time);
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
@@ -2572,14 +2572,14 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
if (sub_compact->end.has_value()) {
tmp_end.SetMinPossibleForUserKey(*(sub_compact->end));
}
uint64_t oldest_ancestor_time =
sub_compact->compaction->MinInputFileOldestAncestorTime(
uint64_t oldest_ancester_time =
sub_compact->compaction->MinInputFileOldestAncesterTime(
sub_compact->start.has_value() ? &tmp_start : nullptr,
sub_compact->end.has_value() ? &tmp_end : nullptr);
if (oldest_ancestor_time == std::numeric_limits<uint64_t>::max()) {
if (oldest_ancester_time == std::numeric_limits<uint64_t>::max()) {
// TODO: fix DBSSTTest.GetTotalSstFilesSize and use
// kUnknownOldestAncestorTime
oldest_ancestor_time = current_time;
// kUnknownOldestAncesterTime
oldest_ancester_time = current_time;
}
uint64_t newest_key_time = sub_compact->compaction->MaxInputFileNewestKeyTime(
@@ -2592,7 +2592,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
FileMetaData meta;
meta.fd = FileDescriptor(file_number,
sub_compact->compaction->output_path_id(), 0);
meta.oldest_ancestor_time = oldest_ancestor_time;
meta.oldest_ancester_time = oldest_ancester_time;
meta.file_creation_time = current_time;
meta.epoch_number = epoch_number;
meta.temperature = temperature;
@@ -3268,8 +3268,7 @@ Status CompactionJob::PersistSubcompactionProgress(
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
}
if (s.ok()) {
s = db_options_.use_fsync ? compaction_progress_writer_->file()->Fsync(opts)
: compaction_progress_writer_->file()->Sync(opts);
s = compaction_progress_writer_->file()->Sync(opts, db_options_.use_fsync);
}
if (!s.ok()) {
+4 -4
View File
@@ -508,7 +508,7 @@ class CompactionJob {
std::atomic<int>* num_running_remote_compactions_;
// Stores the sequence number to time mapping gathered from all input files
// it also collects the smallest_seqno -> oldest_ancestor_time from the SST.
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
SeqnoToTimeMapping seqno_to_time_mapping_;
// Max seqno that can be zeroed out in last level, including for preserving
@@ -641,7 +641,7 @@ struct CompactionServiceOutputFile {
SequenceNumber largest_seqno{};
std::string smallest_internal_key;
std::string largest_internal_key;
uint64_t oldest_ancestor_time = kUnknownOldestAncestorTime;
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
uint64_t file_creation_time = kUnknownFileCreationTime;
uint64_t epoch_number = kUnknownEpochNumber;
std::string file_checksum = kUnknownFileChecksum;
@@ -657,7 +657,7 @@ struct CompactionServiceOutputFile {
CompactionServiceOutputFile(
const std::string& name, uint64_t size, SequenceNumber smallest,
SequenceNumber largest, std::string _smallest_internal_key,
std::string _largest_internal_key, uint64_t _oldest_ancestor_time,
std::string _largest_internal_key, uint64_t _oldest_ancester_time,
uint64_t _file_creation_time, uint64_t _epoch_number,
const std::string& _file_checksum,
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
@@ -670,7 +670,7 @@ struct CompactionServiceOutputFile {
largest_seqno(largest),
smallest_internal_key(std::move(_smallest_internal_key)),
largest_internal_key(std::move(_largest_internal_key)),
oldest_ancestor_time(_oldest_ancestor_time),
oldest_ancester_time(_oldest_ancester_time),
file_creation_time(_file_creation_time),
epoch_number(_epoch_number),
file_checksum(_file_checksum),
+2 -2
View File
@@ -387,7 +387,7 @@ class CompactionJobTestBase : public testing::Test {
edit.AddFile(
level, file_number, 0, file_size, smallest_key, largest_key,
smallest_seqno, largest_seqno, false, Temperature::kUnknown,
oldest_blob_file_number, kUnknownOldestAncestorTime,
oldest_blob_file_number, kUnknownOldestAncesterTime,
kUnknownFileCreationTime,
versions_->GetColumnFamilySet()->GetDefault()->NewEpochNumber(),
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
@@ -1702,7 +1702,7 @@ TEST_F(CompactionJobTest, ResultSerialization) {
rnd.Uniform(kStrMaxLen)) /* smallest_internal_key */,
rnd.RandomBinaryString(
rnd.Uniform(kStrMaxLen)) /* largest_internal_key */,
rnd64.Uniform(UINT64_MAX) /* oldest_ancestor_time */,
rnd64.Uniform(UINT64_MAX) /* oldest_ancester_time */,
rnd64.Uniform(UINT64_MAX) /* file_creation_time */,
rnd64.Uniform(UINT64_MAX) /* epoch_number */,
file_checksum /* file_checksum */,
+4 -4
View File
@@ -32,7 +32,7 @@ Status CompactionOutputs::Finish(
meta->fd.largest_seqno);
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
builder_->SetSeqnoTimeTableProperties(relevant_mapping,
meta->oldest_ancestor_time);
meta->oldest_ancester_time);
s = builder_->Finish();
} else {
@@ -73,7 +73,7 @@ IOStatus CompactionOutputs::WriterSyncClose(const Status& input_status,
WriteOptions(Env::IOActivity::kCompaction), opts);
if (input_status.ok() && io_s.ok()) {
StopWatch sw(clock, statistics, COMPACTION_OUTFILE_SYNC_MICROS);
io_s = use_fsync ? file_writer_->Fsync(opts) : file_writer_->Sync(opts);
io_s = file_writer_->Sync(opts, use_fsync);
}
if (input_status.ok() && io_s.ok()) {
io_s = file_writer_->Close(opts);
@@ -817,10 +817,10 @@ void CompactionOutputs::FillFilesToCutForTtl() {
*(compaction_->inputs(compaction_->num_input_levels() - 1));
for (FileMetaData* file : olevel) {
// Worth filtering out by start and end?
uint64_t oldest_ancestor_time = file->TryGetOldestAncestorTime();
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
// We put old files if they are not too small to prevent a flood
// of small files.
if (oldest_ancestor_time < old_age_thres &&
if (oldest_ancester_time < old_age_thres &&
file->fd.GetFileSize() >
compaction_->mutable_cf_options().target_file_size_base / 2) {
files_to_cut_for_ttl_.push_back(file);
+1 -1
View File
@@ -371,7 +371,7 @@ class CompactionOutputs {
// repetitive comparison in ShouldStopBefore()
const InternalKey* local_output_split_key_ = nullptr;
// Some identified files with old oldest ancestor time and the range should be
// Some identified files with old oldest ancester time and the range should be
// isolated out so that the output file(s) in that range can be merged down
// for TTL and clear the timestamps for the range.
std::vector<FileMetaData*> files_to_cut_for_ttl_;
+5 -6
View File
@@ -105,10 +105,9 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
TableReader* reader = f->fd.pinned_reader.Get();
if (reader != nullptr && reader->GetTableProperties() != nullptr) {
uint64_t newest_key_time = f->TryGetNewestKeyTime();
uint64_t oldest_ancestor_time =
reader->GetTableProperties()->oldest_ancestor_time;
uint64_t creation_time = reader->GetTableProperties()->creation_time;
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
? oldest_ancestor_time
? creation_time
: newest_key_time;
if (est_newest_key_time == kUnknownNewestKeyTime ||
est_newest_key_time >= (current_time - mutable_cf_options.ttl)) {
@@ -166,13 +165,13 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
for (const auto& f : inputs[0].files) {
assert(f);
uint64_t newest_key_time = f->TryGetNewestKeyTime();
uint64_t oldest_ancestor_time = 0;
uint64_t creation_time = 0;
TableReader* reader = f->fd.pinned_reader.Get();
if (reader != nullptr && reader->GetTableProperties() != nullptr) {
oldest_ancestor_time = reader->GetTableProperties()->oldest_ancestor_time;
creation_time = reader->GetTableProperties()->creation_time;
}
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
? oldest_ancestor_time
? creation_time
: newest_key_time;
ROCKS_LOG_BUFFER(log_buffer,
"[%s] FIFO compaction: picking file %" PRIu64
+76 -76
View File
@@ -120,7 +120,7 @@ class CompactionPickerTestBase : public testing::Test {
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
size_t compensated_file_size = 0, bool marked_for_compact = false,
Temperature temperature = Temperature::kUnknown,
uint64_t oldest_ancestor_time = kUnknownOldestAncestorTime,
uint64_t oldest_ancestor_time = kUnknownOldestAncesterTime,
uint64_t newest_key_time = kUnknownNewestKeyTime,
Slice ts_of_smallest = Slice(), Slice ts_of_largest = Slice(),
uint64_t epoch_number = kUnknownEpochNumber) {
@@ -162,15 +162,15 @@ class CompactionPickerTestBase : public testing::Test {
FileMetaData* f = new FileMetaData(
file_number, path_id, file_size, smallest_ikey, largest_ikey,
smallest_seq, largest_seq, marked_for_compact, temperature,
kInvalidBlobFileNumber, kUnknownOldestAncestorTime,
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, epoch_number, kUnknownFileChecksum,
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
true /* user_defined_timestamps_persisted */, "" /* min timestamp */,
"" /* max timestamp */);
f->compensated_file_size =
(compensated_file_size != 0) ? compensated_file_size : file_size;
// oldest_ancestor_time is only used if newest_key_time is not available
f->oldest_ancestor_time = oldest_ancestor_time;
// oldest_ancester_time is only used if newest_key_time is not available
f->oldest_ancester_time = oldest_ancestor_time;
// Set min/max timestamps for UDT support
if (!ts_of_smallest.empty()) {
f->min_timestamp = ts_of_smallest.ToString();
@@ -325,7 +325,7 @@ class CompactionPickerU64TsTest : public CompactionPickerTestBase {
/*smallest_seq=*/10, /*largest_seq=*/40,
/*compensated_file_size=*/1000,
/*marked_for_compact=*/false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
std::string full_history_ts_low = MakeU64Timestamp(full_history_ts_low_val);
@@ -350,12 +350,12 @@ class CompactionPickerU64TsTest : public CompactionPickerTestBase {
/*smallest_seq=*/100, /*largest_seq=*/100,
/*compensated_file_size=*/file_size,
/*marked_for_compact=*/false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts1, ts2);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts1, ts2);
Add(0, 2U, "150", "250", file_size, /*path_id=*/0,
/*smallest_seq=*/200, /*largest_seq=*/200,
/*compensated_file_size=*/file_size,
/*marked_for_compact=*/false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts1, ts2);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts1, ts2);
}
};
@@ -1452,14 +1452,14 @@ TEST_F(CompactionPickerTest, FIFOToCold1) {
static_cast<uint64_t>(current_time) - kColdThreshold;
Add(0 /* level */, 4U /* file_number */, "260", "300", 1 * kFileSize, 0,
2500, 2600, 0, true, Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: threshold_time - 2000 /* oldest_ancestor_time */,
newestKeyTimeKnown ? threshold_time - 2000
: kUnknownNewestKeyTime /* newest_key_time */);
// Qualifies for compaction to kCold.
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
newestKeyTimeKnown ? threshold_time - 3000
: kUnknownNewestKeyTime /* newest_key_time */);
UpdateVersionStorageInfo();
@@ -1504,32 +1504,32 @@ TEST_F(CompactionPickerTest, FIFOToColdMaxCompactionSize) {
static_cast<uint64_t>(current_time) - kColdThreshold;
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: static_cast<uint64_t>(current_time) - 100,
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
: kUnknownNewestKeyTime);
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time + 100,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time + 100,
newestKeyTimeKnown ? threshold_time + 100 : kUnknownNewestKeyTime);
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 2000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 2000,
newestKeyTimeKnown ? threshold_time - 2000 : kUnknownNewestKeyTime);
// The following two files qualify for compaction to kCold.
// But only the last two should be included to respect
// `max_compaction_bytes`.
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
newestKeyTimeKnown ? threshold_time - 3000 : kUnknownNewestKeyTime);
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 4000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 4000,
newestKeyTimeKnown ? threshold_time - 4000 : kUnknownNewestKeyTime);
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 5000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 5000,
newestKeyTimeKnown ? threshold_time - 5000 : kUnknownNewestKeyTime);
UpdateVersionStorageInfo();
@@ -1574,30 +1574,30 @@ TEST_F(CompactionPickerTest, FIFOToColdWithExistingCold) {
static_cast<uint64_t>(current_time) - kColdThreshold;
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: static_cast<uint64_t>(current_time) - 100,
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
: kUnknownNewestKeyTime);
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time + 100,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time + 100,
newestKeyTimeKnown ? threshold_time + 100 : kUnknownNewestKeyTime);
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 2000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 2000,
newestKeyTimeKnown ? threshold_time - 2000 : kUnknownNewestKeyTime);
// The following two files qualify for compaction to kCold.
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
newestKeyTimeKnown ? threshold_time - 3000 : kUnknownNewestKeyTime);
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 4000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 4000,
newestKeyTimeKnown ? threshold_time - 4000 : kUnknownNewestKeyTime);
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
Temperature::kCold,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 5000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 5000,
newestKeyTimeKnown ? threshold_time - 5000 : kUnknownNewestKeyTime);
UpdateVersionStorageInfo();
@@ -1642,30 +1642,30 @@ TEST_F(CompactionPickerTest, FIFOToColdWithHotBetweenCold) {
static_cast<uint64_t>(current_time) - kColdThreshold;
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: static_cast<uint64_t>(current_time) - 100,
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
: kUnknownNewestKeyTime);
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time + 100,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time + 100,
newestKeyTimeKnown ? threshold_time + 100 : kUnknownNewestKeyTime);
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 2000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 2000,
newestKeyTimeKnown ? threshold_time - 2000 : kUnknownNewestKeyTime);
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
Temperature::kCold,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
newestKeyTimeKnown ? threshold_time - 3000 : kUnknownNewestKeyTime);
// Qualifies for compaction to kCold.
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 4000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 4000,
newestKeyTimeKnown ? threshold_time - 4000 : kUnknownNewestKeyTime);
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
Temperature::kCold,
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 5000,
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 5000,
newestKeyTimeKnown ? threshold_time - 5000 : kUnknownNewestKeyTime);
UpdateVersionStorageInfo();
@@ -1714,36 +1714,36 @@ TEST_F(CompactionPickerTest, FIFOToHotAndWarm) {
static_cast<uint64_t>(current_time) - kWarmThreshold;
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: static_cast<uint64_t>(current_time) - 100,
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
: kUnknownNewestKeyTime);
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: hot_threshold_time + 100,
newestKeyTimeKnown ? hot_threshold_time + 100 : kUnknownNewestKeyTime);
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: hot_threshold_time - 200,
newestKeyTimeKnown ? hot_threshold_time - 200 : kUnknownNewestKeyTime);
// Qualifies for Hot
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: warm_threshold_time - 100,
newestKeyTimeKnown ? warm_threshold_time - 100 : kUnknownNewestKeyTime);
// Qualifies for Warm
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: warm_threshold_time - 4000,
newestKeyTimeKnown ? warm_threshold_time - 4000
: kUnknownNewestKeyTime);
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
Temperature::kUnknown,
newestKeyTimeKnown ? kUnknownOldestAncestorTime
newestKeyTimeKnown ? kUnknownOldestAncesterTime
: warm_threshold_time - 5000,
newestKeyTimeKnown ? warm_threshold_time - 5000
: kUnknownNewestKeyTime);
@@ -2490,7 +2490,7 @@ TEST_F(CompactionPickerTest, FileTtlBoosterLargeNumLevels) {
FileTtlBooster booster(kCurrentTime, /*ttl=*/2048,
/*num_non_empty_levels=*/100, /*level=*/1);
FileMetaData meta;
meta.oldest_ancestor_time = kCurrentTime - 1023;
meta.oldest_ancester_time = kCurrentTime - 1023;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
}
@@ -2509,47 +2509,47 @@ TEST_F(CompactionPickerTest, FileTtlBooster) {
FileTtlBooster booster(kCurrentTime, 2048, 7, 3);
// Not triggering if the file is younger than ttl/2
meta.oldest_ancestor_time = kCurrentTime - 1023;
meta.oldest_ancester_time = kCurrentTime - 1023;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancestor_time = kCurrentTime - 1024;
meta.oldest_ancester_time = kCurrentTime - 1024;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancestor_time = kCurrentTime + 10;
meta.oldest_ancester_time = kCurrentTime + 10;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
// Within one boosting step
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 6);
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 6);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
// One boosting step
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 7);
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 7);
ASSERT_EQ(2, booster.GetBoostScore(&meta));
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 8);
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 8);
ASSERT_EQ(2, booster.GetBoostScore(&meta));
// Multiple boosting steps
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 30);
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 30);
ASSERT_EQ(5, booster.GetBoostScore(&meta));
// Very high boosting steps
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 700);
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 700);
ASSERT_EQ(101, booster.GetBoostScore(&meta));
}
{
// Test second last level
FileTtlBooster booster(kCurrentTime, 2048, 7, 5);
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480);
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480 + 60);
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
ASSERT_EQ(3, booster.GetBoostScore(&meta));
}
{
// Test last level
FileTtlBooster booster(kCurrentTime, 2048, 7, 6);
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480);
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480 + 60);
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancestor_time = kCurrentTime - 3000;
meta.oldest_ancester_time = kCurrentTime - 3000;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
}
}
@@ -3577,21 +3577,21 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550, /*compensated_file_size*/ 0,
/*marked_for_compact*/ false, /* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 3);
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450,
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 2);
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300,
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 1);
@@ -3619,7 +3619,7 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
// Simulate a flush and mark the file for compaction
Add(0, 7U, "150", "200", kFileSize, 0, 551, 600, 0, true,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 4);
@@ -3647,7 +3647,7 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
// Mark file number 4 for compaction
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 1);
@@ -3674,14 +3674,14 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
AddVersionStorage();
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550, /*compensated_file_size*/ 0,
/*marked_for_compact*/ false, /* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 3);
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450,
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 2);
@@ -3859,27 +3859,27 @@ TEST_F(CompactionPickerTest, UniversalMarkedL0Overlap2) {
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 260, 300,
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 4);
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 201, 250, 0, true,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 3);
Add(0, 3U, "301", "350", 4 * kFileSize, 0, 101, 150,
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 2);
Add(0, 6U, "501", "750", 8 * kFileSize, 0, 50, 100,
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 1);
@@ -3906,14 +3906,14 @@ TEST_F(CompactionPickerTest, UniversalMarkedL0Overlap2) {
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550, /*compensated_file_size*/ 0,
/*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 6);
Add(0, 2U, "201", "250", kFileSize, 0, 401, 450, /*compensated_file_size*/ 0,
/*marked_for_compact*/ false,
/* temperature*/ Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime,
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
/*epoch_number*/ 5);
@@ -4149,7 +4149,7 @@ TEST_F(CompactionPickerU64TsTest, Overlap) {
/*file_size=*/1U, /*path_id=*/0,
/*smallest_seq=*/100, /*largest_seq=*/100, /*compensated_file_size=*/0,
/*marked_for_compact=*/false, /*temperature=*/Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime, ts1, ts2);
UpdateVersionStorageInfo();
}
@@ -4231,12 +4231,12 @@ TEST_F(CompactionPickerU64TsTest, CannotTrivialMoveUniversal) {
Add(1, 1U, "150", "150", kFileSize, /*path_id=*/0, /*smallest_seq=*/100,
/*largest_seq=*/100, /*compensated_file_size=*/kFileSize,
/*marked_for_compact=*/false, Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime, ts1, ts2);
Add(2, 2U, "150", "150", kFileSize, /*path_id=*/0, /*smallest_seq=*/100,
/*largest_seq=*/100, /*compensated_file_size=*/kFileSize,
/*marked_for_compact=*/false, Temperature::kUnknown,
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
/*newest_key_time*/ kUnknownNewestKeyTime, ts3, ts4);
UpdateVersionStorageInfo();
@@ -5033,12 +5033,12 @@ TEST_F(CompactionPickerTest, StandaloneRangeDeletionOnlyPicksOlderFiles) {
// Create L0 files with overlapping ranges
// File 1: newest regular file (epoch 5), keys [100, 200]
Add(0, 1U, "100", "200", 1U, 0, 100, 100, 0, false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, Slice(), Slice(), 5);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, Slice(), Slice(), 5);
// File 2: standalone range deletion (epoch 4), keys [150, 250]
// This file should be marked as having only range deletions
Add(0, 2U, "150", "250", 1U, 0, 200, 200, 0, true, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, Slice(), Slice(), 4);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, Slice(), Slice(), 4);
// Manually set file 2 as standalone range deletion
FileMetaData* range_del_file = file_map_[2U].first;
@@ -5169,7 +5169,7 @@ TEST_F(CompactionPickerU64TsTest,
/*smallest_seq=*/10, /*largest_seq=*/40,
/*compensated_file_size=*/1000,
/*marked_for_compact=*/false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
// Update version storage with empty full_history_ts_low
UpdateVersionStorageInfo();
@@ -5209,7 +5209,7 @@ TEST_F(CompactionPickerU64TsTest,
/*smallest_seq=*/10, /*largest_seq=*/40,
/*compensated_file_size=*/1000,
/*marked_for_compact=*/false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
// Simulate a file whose min/max timestamp was never recorded.
ASSERT_EQ(1U, vstorage_->LevelFiles(5).size());
@@ -5249,7 +5249,7 @@ TEST_F(CompactionPickerU64TsTest,
/*smallest_seq=*/10, /*largest_seq=*/40,
/*compensated_file_size=*/1000,
/*marked_for_compact=*/false, Temperature::kUnknown,
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
// Simulate a file whose min/max timestamp was never recorded.
ASSERT_EQ(1U, vstorage_->LevelFiles(5).size());
@@ -5715,13 +5715,13 @@ TEST_F(CompactionPickerTest, FIFOTTLBlobEstimationSingleLevel) {
uint64_t recent_time = static_cast<uint64_t>(time(nullptr));
Add(0, 1U, "100", "200", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
Add(0, 2U, "200", "300", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
Add(0, 3U, "300", "400", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
Add(0, 4U, "400", "500", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
if (blob_total > 0) {
AddBlobFile(100, blob_total / 2);
AddBlobFile(101, blob_total / 2);
@@ -5794,13 +5794,13 @@ TEST_F(CompactionPickerTest, FIFOTTLBlobEstimationMultiLevel) {
uint64_t recent_time = static_cast<uint64_t>(time(nullptr));
// L0 files: 2 recent, 2 expired
Add(0, 1U, "100", "200", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
Add(0, 2U, "200", "300", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
Add(0, 3U, "300", "400", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
Add(0, 4U, "400", "500", 50 * 1024, 0, 100, 100, 0, false,
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
// L2 legacy migration file
Add(2, 10U, "100", "600", 200 * 1024);
// Blob files (associated with ALL levels)
+21 -9
View File
@@ -271,7 +271,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
file.smallest_seqno, file.largest_seqno);
meta.smallest.DecodeFrom(file.smallest_internal_key);
meta.largest.DecodeFrom(file.largest_internal_key);
meta.oldest_ancestor_time = file.oldest_ancestor_time;
meta.oldest_ancester_time = file.oldest_ancester_time;
meta.file_creation_time = file.file_creation_time;
meta.epoch_number = file.epoch_number;
meta.file_checksum = file.file_checksum;
@@ -469,7 +469,7 @@ Status CompactionServiceCompactionJob::Run() {
MakeTableFileName(meta.fd.GetNumber()), meta.fd.GetFileSize(),
meta.fd.smallest_seqno, meta.fd.largest_seqno,
meta.smallest.Encode().ToString(), meta.largest.Encode().ToString(),
meta.oldest_ancestor_time, meta.file_creation_time, meta.epoch_number,
meta.oldest_ancester_time, meta.file_creation_time, meta.epoch_number,
meta.file_checksum, meta.file_checksum_func_name,
output_file.validator.GetHash(), meta.marked_for_compaction,
meta.unique_id, *output_file.table_properties,
@@ -601,9 +601,8 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct CompactionServiceOutputFile, largest_internal_key),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
// Misspelled key kept for compatibility with other versions
{"oldest_ancester_time",
{offsetof(struct CompactionServiceOutputFile, oldest_ancestor_time),
{offsetof(struct CompactionServiceOutputFile, oldest_ancester_time),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_creation_time",
@@ -921,11 +920,24 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct InternalStats::CompactionStats, count),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"counts", SizeTolerantDeserializeArray<
int, static_cast<int>(CompactionReason::kNumOfReasons)>(
offsetof(struct InternalStats::CompactionStats, counts),
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
{0, OptionType::kInt})},
// The remote worker may serialize a different number of
// CompactionReason counts than this primary expects (RocksDB versions
// can disagree on kNumOfReasons). Deserialization tolerates the
// mismatch: extra trailing elements are dropped, missing ones are
// zero-filled.
//
// The serialized width is intentionally kept at kNumOfReasons - 1 (the
// reduced width) so this change does NOT alter what is written -- only
// the read path becomes tolerant. Widening the serialized width to the
// full kNumOfReasons is a separate, later change that must not land
// until this tolerant read has been deployed to every primary,
// including rollback targets.
{"counts",
SizeTolerantDeserializeArray<
int, static_cast<int>(CompactionReason::kNumOfReasons) - 1>(
offsetof(struct InternalStats::CompactionStats, counts),
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
{0, OptionType::kInt})},
};
static std::unordered_map<std::string, OptionTypeInfo>
+1 -10
View File
@@ -1900,7 +1900,7 @@ TEST_F(CompactionServiceTest, InvalidResultFallsBackToLocal) {
TEST_F(CompactionServiceTest, CompatCheckCountsWidthTolerance) {
// Number of per-CompactionReason counts this binary serializes and expects.
constexpr size_t kExpected =
static_cast<size_t>(CompactionReason::kNumOfReasons);
static_cast<size_t>(CompactionReason::kNumOfReasons) - 1;
constexpr size_t kLastIndex = kExpected - 1;
constexpr int kFirstCount = 3;
constexpr int kLastCount = 5;
@@ -2452,8 +2452,6 @@ TEST_F(CompactionServiceTest, RemoteCompactionManifestFloorKillSwitchOff) {
Options options = CurrentOptions();
options.num_levels = 7;
options.max_open_files = -1;
options.open_files_async = true;
options.skip_stats_update_on_db_open = true;
options.level0_file_num_compaction_trigger = 100; // no auto compaction
options.remote_compaction_manifest_floor = false; // kill switch OFF
ReopenWithCompactionService(&options);
@@ -2466,15 +2464,8 @@ TEST_F(CompactionServiceTest, RemoteCompactionManifestFloorKillSwitchOff) {
ASSERT_OK(Flush()); // L0: Delete(k)
// Reopen so the MANIFEST is rewritten as current state before we perturb it.
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BGWorkAsyncFileOpen:Done",
"CompactionServiceTest::ManifestFloorKillSwitchOff:OpenDone"}});
SyncPoint::GetInstance()->EnableProcessing();
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
options);
TEST_SYNC_POINT("CompactionServiceTest::ManifestFloorKillSwitchOff:OpenDone");
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
GetCompactionService()->SetCanceled(false);
bool floor_provided_seen = true;
+3 -3
View File
@@ -68,11 +68,11 @@ class FileTtlBooster {
if (!enabled_) {
return 1;
}
uint64_t oldest_ancestor_time = f->TryGetOldestAncestorTime();
if (oldest_ancestor_time >= current_time_) {
uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime();
if (oldest_ancester_time >= current_time_) {
return 1;
}
uint64_t age = current_time_ - oldest_ancestor_time;
uint64_t age = current_time_ - oldest_ancester_time;
if (age > boost_age_start_) {
// Use integer just for convenience.
// We could make all file_to_order double if we want.
-7
View File
@@ -23,7 +23,6 @@
#include "rocksdb/table.h"
#include "rocksdb/utilities/checkpoint.h"
#include "rocksdb/utilities/debug.h"
#include "rocksdb/write_buffer_manager.h"
#include "table/block_based/block_based_table_reader.h"
#include "table/block_based/block_builder.h"
#include "table/format.h"
@@ -5768,9 +5767,6 @@ INSTANTIATE_TEST_CASE_P(BestEffortsRecoverIncompleteVersionTest,
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
Options options = CurrentOptions();
options.env = env_;
options.write_buffer_manager = std::make_shared<WriteBufferManager>(
64 << 20, nullptr /* cache */, false /* allow_stall */,
WriteBufferFlushPolicy::kFlushLargestAcrossDBs);
DestroyAndReopen(options);
ASSERT_OK(Put("foo", "value0"));
ASSERT_OK(Flush());
@@ -5792,9 +5788,6 @@ TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
Reopen(options);
ASSERT_OK(Put("bar", "value"));
options.write_buffer_manager->TEST_RefreshFlushInitiatorCandidate();
ASSERT_TRUE(
options.write_buffer_manager->TEST_ScheduleFlushOnLargestDB(nullptr));
}
TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
-81
View File
@@ -3,11 +3,8 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include <atomic>
#include "db/db_test_util.h"
#include "port/port.h"
#include "test_util/sync_point.h"
#include "util/random.h"
namespace ROCKSDB_NAMESPACE {
@@ -17,62 +14,6 @@ class DBClipTest : public DBTestBase {
DBClipTest() : DBTestBase("db_clip_test", /*env_do_fsync=*/true) {}
};
// ClipColumnFamily reads the CF's boundary keys under the DB mutex, then drops
// it and issues several writes with those keys. Those keys must not point into
// Version-owned FileMetaData, which a background job can replace and free in
// that window. Here a full compaction runs inside the window and obsoletes the
// Version the keys were read from.
TEST_F(DBClipTest, BoundaryKeysSurviveConcurrentVersionChange) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.num_levels = 3;
DestroyAndReopen(options);
Random rnd(301);
std::map<int32_t, std::string> values;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 100; j++) {
int k = i * 100 + j;
values[k] = rnd.RandomString(1024);
ASSERT_OK(Put(Key(k), values[k]));
}
ASSERT_OK(Flush());
}
std::atomic<bool> fired{false};
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::ClipColumnFamily:PostBoundaryKeys", [&](void*) {
if (fired.exchange(true)) {
return;
}
// Replace and free the Version the boundary keys were read from, on
// another thread so this does not re-enter Clip's own call stack.
port::Thread compactor([&] {
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForPurge());
});
compactor.join();
});
SyncPoint::GetInstance()->EnableProcessing();
const auto begin_key = Key(251);
const auto end_key = Key(751);
ASSERT_OK(
db_->ClipColumnFamily(db_->DefaultColumnFamily(), begin_key, end_key));
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
ASSERT_TRUE(fired.load());
for (int i = 251; i < 751; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
for (int i : {0, 250, 751, 999}) {
std::string result;
ASSERT_TRUE(db_->Get(ReadOptions(), Key(i), &result).IsNotFound());
}
}
TEST_F(DBClipTest, TestClipRange) {
Options options = CurrentOptions();
options.write_buffer_size = 10 * 1024 * 1024;
@@ -192,28 +133,6 @@ TEST_F(DBClipTest, TestClipRange) {
ASSERT_TRUE(in_range);
}
}
TEST_F(DBClipTest, KeepFileEndingAtBeginKey) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
DestroyAndReopen(options);
ASSERT_OK(Put("a", "va"));
ASSERT_OK(Put("b", "vb"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_OK(Put("c", "vc"));
ASSERT_OK(Put("d", "vd"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_EQ("0,2", FilesPerLevel(0));
ASSERT_OK(db_->ClipColumnFamily(db_->DefaultColumnFamily(), "b", "d"));
ASSERT_EQ("NOT_FOUND", Get("a"));
ASSERT_EQ("vb", Get("b"));
ASSERT_EQ("vc", Get("c"));
ASSERT_EQ("NOT_FOUND", Get("d"));
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+10 -10
View File
@@ -5350,10 +5350,10 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
*max_open_files = 2;
});
// In the case where all files are opened and doing DB restart
// forcing the oldest ancestor time in manifest file to be 0 to
// forcing the oldest ancester time in manifest file to be 0 to
// simulate the case of reading from an old version.
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionEdit::EncodeTo:VarintOldestAncestorTime", [&](void* arg) {
"VersionEdit::EncodeTo:VarintOldestAncesterTime", [&](void* arg) {
if (if_restart && if_open_all_files) {
std::string* encoded_field = static_cast<std::string*>(arg);
*encoded_field = "";
@@ -5390,7 +5390,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
std::vector<std::vector<FileMetaData>> level_to_files;
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
uint64_t oldest_time = level_to_files[0][0].oldest_ancestor_time;
uint64_t oldest_time = level_to_files[0][0].oldest_ancester_time;
// Add 1 hour and do another flush.
env_->MockSleepForSeconds(1 * 60 * 60);
for (int i = 101; i <= 200; ++i) {
@@ -5458,7 +5458,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
ASSERT_EQ(oldest_time, level_to_files[6][0].oldest_ancestor_time);
ASSERT_EQ(oldest_time, level_to_files[6][0].oldest_ancester_time);
env_->MockSleepForSeconds(25 * 60 * 60);
ASSERT_OK(Put(Key(2), "1"));
@@ -5881,7 +5881,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
props->file_creation_time = 0;
}
if (set_creation_time_to_zero) {
props->oldest_ancestor_time = 0;
props->creation_time = 0;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
@@ -12417,7 +12417,7 @@ TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
ASSERT_EQ(newest_key_time, file_metadatas[idx]
->fd.pinned_reader.Get()
->GetTableProperties()
->oldest_ancestor_time);
->creation_time);
}
// The delta between the first and last newest_key_times is 15s
uint64_t last_newest_key_time = prev_newest_key_time;
@@ -12436,14 +12436,14 @@ TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
->GetTableProperties()
->newest_key_time,
first_newest_key_time);
// Contrast newest_key_time with oldest_ancestor_time (15s older than
// newest_key_time)
// Contrast newest_key_time with creation_time, which records the oldest
// ancestor time (15s older than newest_key_time)
ASSERT_EQ(file_metadatas[0]
->fd.pinned_reader.Get()
->GetTableProperties()
->oldest_ancestor_time,
->creation_time,
last_newest_key_time);
ASSERT_EQ(file_metadatas[0]->oldest_ancestor_time, last_newest_key_time);
ASSERT_EQ(file_metadatas[0]->oldest_ancester_time, last_newest_key_time);
// Make sure TTL of 5s causes compaction
env_->MockSleepForSeconds(6);
-183
View File
@@ -4271,147 +4271,6 @@ TEST_F(DBTest2, TraceWithLimit) {
ASSERT_OK(DestroyDB(dbname2, options));
}
namespace {
Status ReplayTraceFile(Env* env, const std::string& trace_filename, DB* db,
const std::vector<ColumnFamilyHandle*>& handles,
uint32_t num_threads,
TraceExecutionResultHandler* res_handler = nullptr) {
std::unique_ptr<TraceReader> trace_reader;
Status s =
NewFileTraceReader(env, EnvOptions(), trace_filename, &trace_reader);
if (!s.ok()) {
return s;
}
std::unique_ptr<Replayer> replayer;
s = db->NewDefaultReplayer(handles, std::move(trace_reader), &replayer);
if (!s.ok()) {
return s;
}
s = replayer->Prepare();
if (!s.ok()) {
return s;
}
// Count the replayed records by type in `res_handler`, if given.
std::function<void(Status, std::unique_ptr<TraceRecordResult>&&)> res_cb;
if (res_handler != nullptr) {
res_cb = [res_handler](const Status& exec_s,
std::unique_ptr<TraceRecordResult>&& res) {
exec_s.PermitUncheckedError();
if (res != nullptr) {
EXPECT_OK(res->Accept(res_handler));
}
};
}
return replayer->Replay(ReplayOptions(num_threads, 1.0), res_cb);
}
} // anonymous namespace
TEST_F(DBTest2, TraceReplayDefaultColumnFamilyWithoutHandles) {
Options options = CurrentOptions();
std::string trace_filename = dbname_ + "/rocksdb.trace";
std::unique_ptr<TraceWriter> trace_writer;
ASSERT_OK(
NewFileTraceWriter(env_, EnvOptions(), trace_filename, &trace_writer));
ASSERT_OK(db_->StartTrace(TraceOptions(), std::move(trace_writer)));
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Put("b", "2"));
ASSERT_EQ("1", Get("a"));
std::vector<std::string> values;
for (const Status& s :
db_->MultiGet(ReadOptions(), {Slice("a"), Slice("b")}, &values)) {
ASSERT_OK(s);
}
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
iter->Seek("a");
iter->SeekForPrev("b");
ASSERT_OK(iter->status());
iter.reset();
ASSERT_OK(db_->EndTrace());
std::string dbname2 = test::PerThreadDBPath(env_, "/db_replay2");
options.create_if_missing = true;
for (uint32_t num_threads : {1U, 2U}) {
SCOPED_TRACE("num_threads=" + std::to_string(num_threads));
ASSERT_OK(DestroyDB(dbname2, options));
std::unique_ptr<DB> db2;
ASSERT_OK(DB::Open(options, dbname2, &db2));
// No handles: every read record on the default column family must still
// replay.
TraceExecutionResultHandler res_handler;
ASSERT_OK(ReplayTraceFile(env_, trace_filename, db2.get(),
/*handles=*/{}, num_threads, &res_handler));
// Each record replayed and produced a result.
ASSERT_EQ(2, res_handler.GetNumWrites());
ASSERT_EQ(1, res_handler.GetNumGets());
ASSERT_EQ(1, res_handler.GetNumMultiGets());
ASSERT_EQ(2, res_handler.GetNumIterSeeks());
std::string value;
ASSERT_OK(db2->Get(ReadOptions(), "a", &value));
ASSERT_EQ("1", value);
ASSERT_OK(db2->Get(ReadOptions(), "b", &value));
ASSERT_EQ("2", value);
}
ASSERT_OK(DestroyDB(dbname2, options));
}
TEST_F(DBTest2, TraceReplayReturnsExecutionError) {
Options options = CurrentOptions();
CreateAndReopenWithCF({"pikachu"}, options);
std::string trace_filename = dbname_ + "/rocksdb.trace";
std::unique_ptr<TraceWriter> trace_writer;
ASSERT_OK(
NewFileTraceWriter(env_, EnvOptions(), trace_filename, &trace_writer));
ASSERT_OK(db_->StartTrace(TraceOptions(), std::move(trace_writer)));
ASSERT_EQ("NOT_FOUND", Get(1, "a"));
ASSERT_EQ("NOT_FOUND", Get(1, "b"));
ASSERT_OK(db_->EndTrace());
// The replay DB has no "pikachu" column family, so both Get records fail.
std::string dbname2 = test::PerThreadDBPath(env_, "/db_replay2");
options.create_if_missing = true;
ASSERT_OK(DestroyDB(dbname2, options));
std::unique_ptr<DB> db2;
ASSERT_OK(DB::Open(options, dbname2, &db2));
for (uint32_t num_threads : {1U, 2U}) {
SCOPED_TRACE("num_threads=" + std::to_string(num_threads));
Status s = ReplayTraceFile(env_, trace_filename, db2.get(),
{db2->DefaultColumnFamily()}, num_threads);
ASSERT_TRUE(s.IsCorruption()) << s.ToString();
}
db2.reset();
ASSERT_OK(DestroyDB(dbname2, options));
}
TEST_F(DBTest2, TraceReplayReturnsNotSupportedExecutionError) {
Options options = CurrentOptions();
std::string trace_filename = dbname_ + "/rocksdb.trace";
std::unique_ptr<TraceWriter> trace_writer;
ASSERT_OK(
NewFileTraceWriter(env_, EnvOptions(), trace_filename, &trace_writer));
ASSERT_OK(db_->StartTrace(TraceOptions(), std::move(trace_writer)));
ASSERT_OK(Put("a", "1"));
ASSERT_OK(
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
ASSERT_OK(db_->EndTrace());
// DeleteRange is not compatible with row_cache, so replaying the DeleteRange
// write fails with NotSupported. Unlike an unsupported trace type, this must
// not be skipped.
std::string dbname2 = test::PerThreadDBPath(env_, "/db_replay2");
options.create_if_missing = true;
options.row_cache = NewLRUCache(1 << 20);
for (uint32_t num_threads : {1U, 2U}) {
SCOPED_TRACE("num_threads=" + std::to_string(num_threads));
ASSERT_OK(DestroyDB(dbname2, options));
std::unique_ptr<DB> db2;
ASSERT_OK(DB::Open(options, dbname2, &db2));
Status s = ReplayTraceFile(env_, trace_filename, db2.get(),
/*handles=*/{}, num_threads);
ASSERT_TRUE(s.IsNotSupported()) << s.ToString();
}
ASSERT_OK(DestroyDB(dbname2, options));
}
TEST_F(DBTest2, TraceWithSampling) {
Options options = CurrentOptions();
ReadOptions ro;
@@ -7792,48 +7651,6 @@ TEST_F(DBTest2, TableCacheMissDuringReadFromBlockCacheTier) {
ASSERT_EQ(orig_num_file_opens, TestGetTickerCount(options, NO_FILE_OPENS));
}
TEST_F(DBTest2, MultiGetBlockCacheTierSkipsColdSstData) {
Options options = CurrentOptions();
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
// Use buffered reads to reach the non-mmap RetrieveMultipleBlocks path.
options.allow_mmap_reads = false;
BlockBasedTableOptions table_options;
// Data blocks use the block cache. Index and filter blocks stay in the table
// reader (cache_index_and_filter_blocks is false by default), so a
// kBlockCacheTier read gets to the data block without metadata I/O.
table_options.block_cache = NewLRUCache(1 << 20);
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
DestroyAndReopen(options);
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Put("k2", "v2"));
ASSERT_OK(Flush());
// Open the table reader so that kBlockCacheTier does not fail at table
// open. Do not add the data block to the block cache.
ReadOptions warm_ro;
warm_ro.fill_cache = false;
std::string value;
ASSERT_OK(db_->Get(warm_ro, "k1", &value));
ASSERT_EQ(value, "v1");
// Make sure the data block is not in the block cache.
table_options.block_cache->EraseUnRefEntries();
ReadOptions no_io_ro;
no_io_ro.read_tier = kBlockCacheTier;
ASSERT_TRUE(db_->Get(no_io_ro, "k1", &value).IsIncomplete());
ASSERT_TRUE(db_->Get(no_io_ro, "k2", &value).IsIncomplete());
Slice keys[2] = {Slice("k1"), Slice("k2")};
PinnableSlice values[2];
Status statuses[2];
db_->MultiGet(no_io_ro, db_->DefaultColumnFamily(), 2, keys, values,
statuses);
ASSERT_TRUE(statuses[0].IsIncomplete());
ASSERT_TRUE(statuses[1].IsIncomplete());
}
TEST_F(DBTest2, GetFileChecksumsFromCurrentManifest_CRC32) {
Options opts = CurrentOptions();
opts.create_if_missing = true;
-28
View File
@@ -2982,34 +2982,6 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
}
}
// Two handles naming the same column family make the selected-cfd list longer
// than the per-CF memtable-id list, which is keyed by CF. The wait loop must
// not read the id list past its own length.
TEST_P(DBAtomicFlushTest, ManualAtomicFlushDuplicateColumnFamilyHandles) {
if (!GetParam()) {
// Only the atomic path reaches the shared wait list; without atomic_flush
// each handle is flushed independently.
return;
}
Options options = CurrentOptions();
options.create_if_missing = true;
options.atomic_flush = true;
options.write_buffer_size = (static_cast<size_t>(64) << 20);
DestroyAndReopen(options);
WriteOptions wopts;
wopts.disableWAL = true;
ASSERT_OK(Put("key", "value", wopts));
FlushOptions flush_options; // wait == true
ASSERT_OK(db_->Flush(
flush_options, {db_->DefaultColumnFamily(), db_->DefaultColumnFamily()}));
auto cfh = static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily());
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
}
TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
Options options = CurrentOptions();
options.create_if_missing = true;
+56 -419
View File
@@ -70,7 +70,6 @@
#include "db/write_batch_internal.h"
#include "db/write_callback.h"
#include "env/unique_id_gen.h"
#include "file/delete_scheduler.h"
#include "file/file_util.h"
#include "file/filename.h"
#include "file/random_access_file_reader.h"
@@ -276,11 +275,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
SetDbSessionId();
assert(!db_session_id_.empty());
if (write_buffer_manager_ != nullptr && !read_only_) {
wbm_flush_initiator_ = std::make_unique<WBMFlushInitiator>(
this, immutable_db_options_.atomic_flush);
}
periodic_task_functions_.emplace(PeriodicTaskType::kDumpStats,
[this]() { this->DumpStats(); });
periodic_task_functions_.emplace(PeriodicTaskType::kPersistStats,
@@ -299,8 +293,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
table_cache_.get(), write_buffer_manager_, &write_controller_,
&block_cache_tracer_, io_tracer_, db_id_, db_session_id_,
options.daily_offpeak_time_utc, &error_handler_, read_only));
versions_->GetColumnFamilySet()->SetFlushInitiator(
wbm_flush_initiator_.get());
column_family_memtables_.reset(
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
@@ -327,10 +319,6 @@ Status DBImpl::Resume() {
return Status::OK();
}
if (read_only_) {
return Status::NotSupported("Cannot resume a read-only DB");
}
if (error_handler_.IsRecoveryInProgress()) {
// Don't allow a mix of manual and automatic recovery
return Status::Busy("Recovery in progress");
@@ -342,23 +330,18 @@ Status DBImpl::Resume() {
return s;
}
// This function implements both manual and automatic background-error
// recovery. It discards stale queued flush requests, waits for scheduled work
// to exit, rebuilds and runs the flush work required by the recovery context,
// purges obsolete files, and schedules any follow-up compactions.
//
// A file-scoped WAL write error needs a stronger protocol because the failed
// record might be absent, partial, or complete, and the failed file handle must
// never be used again:
// 1. Keep every WAL through failed_wal_number quarantined from concurrent sync.
// 2. Fence WAL iterators at the failed sequence range and persist its end so a
// possibly complete record is neither exposed nor allowed to collide with a
// later write after recovery.
// 3. Replace the failed WAL and atomically flush all live column families.
// 4. Verify every column family advanced beyond the failed WAL.
// 5. Detach the quarantined handles, then clear the cutoff only if a concurrent
// failure did not extend it. Any failure before completion leaves the hard
// background error and quarantine in place.
// This function implements the guts of recovery from a background error. It
// is eventually called for both manual as well as automatic recovery. It does
// the following -
// 1. Wait for currently scheduled background flush/compaction to exit, in
// order to inadvertently causing an error and thinking recovery failed
// 2. Flush memtables if there's any data for all the CFs. This may result
// another error, which will be saved by error_handler_ and reported later
// as the recovery status
// 3. Find and delete any obsolete files
// 4. Schedule compactions if needed for all the CFs. This is needed as the
// flush in the prior step might have been a no-op for some CFs, which
// means a new super version wouldn't have been installed
Status DBImpl::ResumeImpl(DBRecoverContext context,
Env::IOActivity io_activity) {
mutex_.AssertHeld();
@@ -386,10 +369,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
TEST_SYNC_POINT("DBImpl::ResumeImpl:Start");
if (context.IsWALWriteErrorRecovery()) {
ExtendWALRecoveryCutoff(context.failed_wal_number);
}
// With two_write_queues=true, sequence numbers are allocated via
// FetchAddLastAllocatedSequence() before writes complete, but only
// published via SetLastSequence() after success. If we're recovering from
@@ -420,44 +399,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
s = Status::ShutdownInProgress();
}
if (s.ok() && context.IsWALWriteErrorRecovery()) {
const bool has_blob_direct_write =
HasAnyBlobDirectWriteColumnFamilyWithLockHeld();
if (immutable_db_options_.manual_wal_flush ||
immutable_db_options_.allow_2pc || two_write_queues_ ||
immutable_db_options_.enable_pipelined_write ||
immutable_db_options_.unordered_write ||
immutable_db_options_.recycle_log_file_num != 0 ||
immutable_db_options_.WAL_ttl_seconds != 0 ||
immutable_db_options_.WAL_size_limit_MB != 0 ||
immutable_db_options_.track_and_verify_wals_in_manifest ||
immutable_db_options_.track_and_verify_wals || has_blob_direct_write) {
ROCKS_LOG_WARN(
immutable_db_options_.info_log,
"[WAL recovery] Unsupported configuration; DB remains write-stopped: "
"manual_wal_flush=%d, allow_2pc=%d, two_write_queues=%d, "
"pipelined_write=%d, unordered_write=%d, wal_recycling=%d, "
"wal_ttl=%d, wal_size_limit=%d, track_wals_in_manifest=%d, "
"track_wals=%d, blob_direct_write=%d",
static_cast<int>(immutable_db_options_.manual_wal_flush),
static_cast<int>(immutable_db_options_.allow_2pc),
static_cast<int>(two_write_queues_),
static_cast<int>(immutable_db_options_.enable_pipelined_write),
static_cast<int>(immutable_db_options_.unordered_write),
static_cast<int>(immutable_db_options_.recycle_log_file_num != 0),
static_cast<int>(immutable_db_options_.WAL_ttl_seconds != 0),
static_cast<int>(immutable_db_options_.WAL_size_limit_MB != 0),
static_cast<int>(
immutable_db_options_.track_and_verify_wals_in_manifest),
static_cast<int>(immutable_db_options_.track_and_verify_wals),
static_cast<int>(has_blob_direct_write));
s = Status::NotSupported(
"file-scoped WAL write error recovery is not supported with manual "
"WAL flush, 2PC, two write queues, pipelined or unordered writes, "
"WAL recycling, retention or tracking, or blob direct write");
}
}
if (s.ok()) {
Status bg_error = error_handler_.GetBGError();
if (bg_error.severity() > Status::Severity::kHardError) {
@@ -498,56 +439,7 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
}
}
if (s.ok() && context.IsWALWriteErrorRecovery() &&
context.failed_wal_sequence > 0) {
// A WAL error can be reported after the whole record reached storage but
// before its sequence range was published. Persist the reservation before
// admitting later writes so they cannot reuse an ambiguous sequence. This
// guarantee belongs to file-scoped recovery, which also atomically advances
// every column family past the failed WAL before retiring it. Unsupported
// configurations fail the check above and remain write-stopped.
assert(context.IsWALWriteErrorRecovery());
assert(context.failed_wal_sequence_start != 0);
assert(context.failed_wal_sequence_start <= context.failed_wal_sequence);
wal_manager_.RecordIndeterminateWriteSequence(
context.failed_wal_number, context.failed_wal_sequence_start,
context.failed_wal_sequence);
if (context.failed_wal_sequence > versions_->LastAllocatedSequence()) {
versions_->SetLastAllocatedSequence(context.failed_wal_sequence);
}
if (context.failed_wal_sequence > versions_->LastPublishedSequence()) {
versions_->SetLastPublishedSequence(context.failed_wal_sequence);
}
if (context.failed_wal_sequence > versions_->LastSequence()) {
versions_->SetLastSequence(context.failed_wal_sequence);
}
VersionEdit edit;
edit.SetLastSequence(context.failed_wal_sequence);
auto cfh =
static_cast_with_check<ColumnFamilyHandleImpl>(default_cf_handle_);
assert(cfh);
s = versions_->LogAndApply(cfh->cfd(), read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
if (!s.ok() && versions_->io_status().IsIOError()) {
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
if (s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[WAL recovery] Persisted sequence reservation through "
"#%" PRIu64,
context.failed_wal_sequence);
} else {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"[WAL recovery] Failed to persist sequence reservation "
"through #%" PRIu64 ": %s",
context.failed_wal_sequence, s.ToString().c_str());
}
}
if (s.ok()) {
recovering_from_wal_write_error_ = context.IsWALWriteErrorRecovery();
recover_wal_through_number_ = context.failed_wal_number;
if (context.flush_reason == FlushReason::kErrorRecoveryRetryFlush) {
s = RetryFlushesForErrorRecovery(FlushReason::kErrorRecoveryRetryFlush,
true /* wait */);
@@ -557,26 +449,8 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
FlushOptions flush_opts;
// We allow flush to stall write since we are trying to resume from error.
flush_opts.allow_write_stall = true;
// Keep the per-column-family WAL replay boundaries in one MANIFEST
// commit. Otherwise a crash between sequential flushes can recover only
// part of a write batch spanning multiple column families.
flush_opts.force_atomic_flush = true;
if (context.IsWALWriteErrorRecovery()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[WAL recovery] Starting atomic flush of all live "
"column families through WAL #%" PRIu64,
context.failed_wal_number);
}
s = FlushAllColumnFamilies(flush_opts, context.flush_reason);
if (s.ok() && context.IsWALWriteErrorRecovery()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[WAL recovery] Atomic flush completed through WAL "
"#%" PRIu64,
context.failed_wal_number);
}
}
recovering_from_wal_write_error_ = false;
recover_wal_through_number_ = 0;
if (!s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"DB resume requested but failed due to Flush failure [%s]",
@@ -584,71 +458,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
}
}
if (s.ok() && context.IsWALWriteErrorRecovery()) {
for (auto* cfd : *versions_->GetColumnFamilySet()) {
if (!cfd->IsDropped() &&
cfd->GetLogNumber() <= context.failed_wal_number) {
s = Status::Corruption(
"WAL write error recovery did not advance column family " +
cfd->GetName() + " past WAL " +
std::to_string(context.failed_wal_number));
break;
}
}
}
if (s.ok() && context.IsWALWriteErrorRecovery()) {
// The flush and MANIFEST transition above make the abandoned WALs
// unnecessary for recovery. Detach their writers even when physical file
// deletion is disabled so no later SyncWAL() can touch a poisoned handle.
mutex_.Unlock();
{
InstrumentedMutexLock wal_lock(&wal_write_mutex_);
size_t retired_wal_count = 0;
if (logs_.empty() || logs_.back().number <= context.failed_wal_number) {
s = Status::Corruption(
"WAL write error recovery did not install a replacement WAL "
"after WAL " +
std::to_string(context.failed_wal_number));
} else {
for (auto it = logs_.begin();
it != logs_.end() && it->number <= context.failed_wal_number;) {
if (it->IsSyncing()) {
wal_sync_cv_.Wait();
it = logs_.begin();
continue;
}
wals_to_free_.push_back(it->ReleaseWriter());
it = logs_.erase(it);
++retired_wal_count;
}
uint64_t expected_cutoff = context.failed_wal_number;
// A failed compare-exchange means a concurrent WAL failure extended the
// cutoff. That failure also sets recovery_error_, so the background
// error remains set and a later recovery attempt owns the larger range.
const bool cutoff_cleared =
wal_recovery_cutoff_.compare_exchange_strong(
expected_cutoff, 0, std::memory_order_acq_rel,
std::memory_order_acquire);
if (cutoff_cleared) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[WAL recovery] Retired %" ROCKSDB_PRIszt
" WAL handle(s) through "
"#%" PRIu64 " and cleared the quarantine cutoff",
retired_wal_count, context.failed_wal_number);
} else {
ROCKS_LOG_WARN(
immutable_db_options_.info_log,
"[WAL recovery] Retired %" ROCKSDB_PRIszt
" WAL handle(s) through "
"#%" PRIu64 "; quarantine cutoff advanced to #%" PRIu64,
retired_wal_count, context.failed_wal_number, expected_cutoff);
}
}
}
mutex_.Lock();
}
JobContext job_context(0);
FindObsoleteFiles(&job_context, true);
mutex_.Unlock();
@@ -663,21 +472,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
if (s.ok()) {
// Will notify and unblock threads waiting for error recovery to finish.
s = error_handler_.ClearBGError();
if (s.ok() && AsyncWALPrecreateEnabled()) {
size_t max_write_buffer_size = 0;
for (ColumnFamilyData* cfd : *versions_->GetColumnFamilySet()) {
if (!cfd->IsDropped()) {
max_write_buffer_size =
std::max(max_write_buffer_size,
cfd->GetLatestMutableCFOptions().write_buffer_size);
}
}
// WAL rotation during recovery cannot schedule its successor while the
// hard background error is set. Refill the async slot only after
// ClearBGError() has made normal background work eligible again.
MaybeScheduleAsyncWALPrecreate(
GetWalPreallocateBlockSize(max_write_buffer_size));
}
} else {
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
// in the DBSSTTest.DBWithMaxSpaceAllowedRandomized test.
@@ -685,6 +479,13 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
error_handler_.GetRecoveryError().PermitUncheckedError();
}
if (s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
} else {
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Failed to resume DB [%s]",
s.ToString().c_str());
}
// Check for shutdown again before scheduling further compactions,
// since we released and re-acquired the lock above
if (shutdown_initiated_) {
@@ -715,23 +516,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
MaybeScheduleFlushOrCompaction();
}
if (s.ok() && context.IsWALWriteErrorRecovery()) {
ROCKS_LOG_INFO(
immutable_db_options_.info_log,
"[WAL recovery] Successfully resumed DB through WAL #%" PRIu64,
context.failed_wal_number);
} else if (s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
} else if (context.IsWALWriteErrorRecovery()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"[WAL recovery] Failed to resume DB through WAL #%" PRIu64
": %s",
context.failed_wal_number, s.ToString().c_str());
} else {
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Failed to resume DB [%s]",
s.ToString().c_str());
}
// Wake up any waiters - in this case, it could be the shutdown thread
bg_cv_.SignalAll();
@@ -743,9 +527,7 @@ Status DBImpl::ResumeImpl(DBRecoverContext context,
void DBImpl::WaitForBackgroundWork() {
// Wait for background work to finish
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
bg_flush_scheduled_ || bg_wbm_flush_scheduled_ ||
bg_pressure_callback_in_progress_ ||
bg_async_file_open_state_ == AsyncFileOpenState::kScheduled) {
bg_flush_scheduled_ || bg_pressure_callback_in_progress_) {
bg_cv_.Wait();
}
}
@@ -828,7 +610,6 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
}
shutting_down_.store(true, std::memory_order_release);
bg_cv_.SignalAll();
env_->UnSchedule(&bg_async_file_open_state_, Env::Priority::HIGH);
if (!wait) {
return;
}
@@ -1037,18 +818,6 @@ Status DBImpl::MaybeWriteWalMarkersToManifestOnClose() {
/*new_descriptor_log=*/false);
}
void DBImpl::MaybeRegisterFlushInitiator() {
// Register only fully opened read-write DBs. Do not hold mutex_: the lock
// order is registry mutex before DB mutex.
if (write_buffer_manager_ == nullptr || read_only_ || !opened_successfully_) {
return;
}
if (wbm_flush_initiator_ == nullptr || wbm_flush_initiator_->IsRegistered()) {
return;
}
write_buffer_manager_->RegisterFlushInitiator(wbm_flush_initiator_.get());
}
Status DBImpl::CloseHelper() {
// Guarantee that there is no background error recovery in progress before
// continuing with the shutdown
@@ -1060,13 +829,6 @@ Status DBImpl::CloseHelper() {
}
mutex_.Unlock();
// Deregister before teardown and without mutex_ to preserve registry-to-DB
// lock ordering.
if (write_buffer_manager_ && wbm_flush_initiator_ &&
wbm_flush_initiator_->IsRegistered()) {
write_buffer_manager_->DeregisterFlushInitiator(wbm_flush_initiator_.get());
}
// Below check is added as recovery_error_ is not checked and it causes crash
// in DBSSTTest.DBWithMaxSpaceAllowedWithBlobFiles when space limit is
// reached.
@@ -1093,8 +855,8 @@ Status DBImpl::CloseHelper() {
// Wait for background work to finish
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
bg_flush_scheduled_ || bg_wbm_flush_scheduled_ ||
bg_purge_scheduled_ || bg_pressure_callback_in_progress_ ||
bg_flush_scheduled_ || bg_purge_scheduled_ ||
bg_pressure_callback_in_progress_ ||
bg_async_file_open_state_ == AsyncFileOpenState::kScheduled ||
async_wal_precreate_state_ == AsyncWALPrecreateState::kScheduled ||
pending_purge_obsolete_files_ ||
@@ -1283,10 +1045,6 @@ Status DBImpl::CloseHelper() {
#endif // !NDEBUG
table_cache_->EraseUnRefEntries();
// The flush initiator was deregistered before background-work teardown.
// Detach it before ColumnFamilySet destroys its column families one by one;
// otherwise each removal rebuilds accounting by scanning all survivors.
versions_->GetColumnFamilySet()->SetFlushInitiator(nullptr);
versions_.reset();
mutex_.Unlock();
if (db_lock_ != nullptr) {
@@ -2323,14 +2081,6 @@ Status DBImpl::SyncWAL() {
s = ApplyWALToManifest(read_options, write_options, &synced_wals);
}
if (s.ok() && wal_recovery_cutoff_.load(std::memory_order_acquire) != 0) {
InstrumentedMutexLock l(&mutex_);
Status bg_error = error_handler_.GetBGError();
if (!bg_error.ok()) {
s = bg_error;
}
}
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
return s;
}
@@ -2372,17 +2122,6 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
auto& log = *it;
// Ensure the head of logs_ is marked as getting_synced if any is.
log.PrepareForSync();
if (log.number <= wal_recovery_cutoff_.load(std::memory_order_acquire)) {
// The failed file handle is permanently unusable. Recovery will make
// this WAL obsolete only after atomically persisting all accepted
// memtable contents, so do not retry any operation on the handle.
// PrepareForSync() pins the entry until MarkLogsSynced() or
// MarkLogsNotSynced() calls FinishSync(), which prevents recovery from
// clearing the cutoff while this call still relies on it.
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncWalImpl:SkipFailedWAL",
&log.number);
continue;
}
// If last sync failed on a later WAL, this could be a fully synced
// and closed WAL that just needs to be recorded as synced in the
// manifest.
@@ -2401,15 +2140,8 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
IOOptions opts;
IOStatus io_s = WritableFileWriter::PrepareIOOptions(write_options, opts);
std::list<log::Writer*> wals_internally_closed;
uint64_t failed_wal_number = 0;
bool failed_with_previous_error = false;
if (io_s.ok()) {
for (log::Writer* log : wals_to_sync) {
if (log->get_log_number() <=
wal_recovery_cutoff_.load(std::memory_order_acquire)) {
continue;
}
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncWalImpl:BeforeSyncWAL", log);
if (job_context) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
@@ -2420,17 +2152,12 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
}
if (log->get_log_number() >= maybe_active_number) {
assert(log->get_log_number() == maybe_active_number);
io_s = log->file()->SyncWithoutFlush(
opts, immutable_db_options_.use_fsync, &failed_with_previous_error);
io_s = log->file()->SyncWithoutFlush(opts,
immutable_db_options_.use_fsync);
} else {
io_s = log->file()->Sync(opts, immutable_db_options_.use_fsync,
&failed_with_previous_error);
io_s = log->file()->Sync(opts, immutable_db_options_.use_fsync);
}
std::pair<log::Writer*, IOStatus*> wal_and_status(log, &io_s);
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncWalImpl:AfterSyncWAL",
&wal_and_status);
if (!io_s.ok()) {
failed_wal_number = log->get_log_number();
break;
}
// WALs can be closed when purging obsolete files, but if recycling is
@@ -2450,7 +2177,6 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
io_s = log->file()->Close(opts);
wals_internally_closed.push_back(log);
if (!io_s.ok()) {
failed_wal_number = log->get_log_number();
break;
}
}
@@ -2461,16 +2187,7 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
io_s.ToString().c_str());
// In case there is a fs error we should set it globally to prevent the
// future writes
// The live-WAL operation that first observed the I/O failure owns
// classifying its original status, either through WALIOStatusCheck() or a
// direct SetBGError() call. With paranoid checks enabled, a concurrent sync
// must not classify the synthetic "previous error" status because it has
// lost the original scope and could incorrectly make the error fatal. With
// paranoid checks disabled, WALIOStatusCheck() instead preserves the
// legacy behavior of resetting the writer so a later operation can retry.
if (!failed_with_previous_error || !immutable_db_options_.paranoid_checks) {
WALIOStatusCheck(io_s, failed_wal_number);
}
WALIOStatusCheck(io_s);
}
if (io_s.ok() && need_wal_dir_sync) {
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
@@ -2617,12 +2334,6 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
auto& wal = *it;
assert(wal.IsSyncing());
if (wal.number <= wal_recovery_cutoff_.load(std::memory_order_acquire)) {
wal.FinishSync();
++it;
continue;
}
if (wal.number < logs_.back().number) {
// Inactive WAL
if (immutable_db_options_.track_and_verify_wals_in_manifest &&
@@ -4699,29 +4410,12 @@ Status DBImpl::DropColumnFamilies(
InstrumentedMutexLock ol(&options_mutex_);
Status s;
bool success_once = false;
ColumnFamilySet* const column_family_set = versions_->GetColumnFamilySet();
const bool batch_flushable_mem_accounting =
write_buffer_manager_ != nullptr &&
write_buffer_manager_->ShouldTrackFlushInitiator();
if (batch_flushable_mem_accounting) {
InstrumentedMutexLock lock(&mutex_);
column_family_set->BeginBulkFlushableMemAccountingUpdate();
}
{
Defer finish_bulk_accounting(
[this, column_family_set, batch_flushable_mem_accounting] {
if (batch_flushable_mem_accounting) {
InstrumentedMutexLock lock(&mutex_);
column_family_set->EndBulkFlushableMemAccountingUpdate();
}
});
for (auto* handle : column_families) {
s = DropColumnFamilyImpl(handle);
if (!s.ok()) {
break;
}
success_once = true;
for (auto* handle : column_families) {
s = DropColumnFamilyImpl(handle);
if (!s.ok()) {
break;
}
success_once = true;
}
if (success_once) {
// TODO: plumb Env::IOActivity, Env::IOPriority
@@ -4766,7 +4460,6 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
// we drop column family from a single write thread
WriteThread::Writer w;
write_thread_.EnterUnbatched(&w, &mutex_);
WaitForPendingWrites();
s = versions_->LogAndApply(cfd, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
write_thread_.ExitUnbatched(&w);
@@ -6442,36 +6135,21 @@ Status DestroyDB(const std::string& dbname, const Options& options,
// log file and prevents cleanup and directory removal
soptions.info_log.reset();
IOOptions io_opts;
// Ignore error in case directory does not exist
soptions.fs
->GetChildren(dbname, io_opts, &filenames,
/*IODebugContext*=*/nullptr)
.PermitUncheckedError();
std::set<std::string> paths_to_delete;
FileLock* lock;
const std::string lockname = LockFileName(dbname);
Status result = env->LockFile(lockname, &lock);
if (result.ok()) {
if (sfm) {
sfm->WaitForEmptyTrashInDirectory(dbname);
}
// Ignore error in case directory does not exist
soptions.fs
->GetChildren(dbname, io_opts, &filenames,
/*IODebugContext*=*/nullptr)
.PermitUncheckedError();
uint64_t number;
FileType type;
InfoLogPrefix info_log_prefix(!soptions.db_log_dir.empty(), dbname);
for (const auto& fname : filenames) {
if (DeleteScheduler::IsTrashFile(fname)) {
std::string trash_file_path = dbname;
trash_file_path.append("/").append(fname);
Status del = DeleteExistingTrashDBFile(&soptions, trash_file_path,
dbname, /*force_bg=*/false,
/*force_fg=*/false, bucket);
if (!del.ok() && result.ok()) {
result = del;
}
continue;
}
if (ParseFileName(fname, &number, info_log_prefix.prefix, &type) &&
type != kDBLockFile) { // Lock file will be deleted at end
Status del;
@@ -6511,26 +6189,14 @@ Status DestroyDB(const std::string& dbname, const Options& options,
}
for (const auto& path : paths) {
if (sfm) {
sfm->WaitForEmptyTrashInDirectory(path);
}
if (soptions.fs
->GetChildren(path, io_opts, &filenames,
/*IODebugContext*=*/nullptr)
.ok()) {
for (const auto& fname : filenames) {
if (DeleteScheduler::IsTrashFile(fname)) {
std::string trash_file_path = path;
trash_file_path.append("/").append(fname);
Status del = DeleteExistingTrashDBFile(&soptions, trash_file_path,
path, /*force_bg=*/false,
/*force_fg=*/false, bucket);
if (!del.ok() && result.ok()) {
result = del;
}
} else if (ParseFileName(fname, &number, &type) &&
(type == kTableFile ||
type == kBlobFile)) { // Lock file will be deleted at end
if (ParseFileName(fname, &number, &type) &&
(type == kTableFile ||
type == kBlobFile)) { // Lock file will be deleted at end
std::string file_path = path + "/" + fname;
Status del = DeleteUnaccountedDBFile(&soptions, file_path, dbname,
/*force_bg=*/false,
@@ -6549,9 +6215,6 @@ Status DestroyDB(const std::string& dbname, const Options& options,
std::string archivedir = ArchivalDirectory(dbname);
bool wal_dir_exists = false;
if (!soptions.IsWalDirSameAsDBPath(dbname)) {
if (sfm) {
sfm->WaitForEmptyTrashInDirectory(soptions.wal_dir);
}
wal_dir_exists =
soptions.fs
->GetChildren(soptions.wal_dir, io_opts, &walDirFiles,
@@ -6564,25 +6227,13 @@ Status DestroyDB(const std::string& dbname, const Options& options,
// processed and removed before those otherwise we have issues
// removing them
std::vector<std::string> archiveFiles;
if (sfm) {
sfm->WaitForEmptyTrashInDirectory(archivedir);
}
if (soptions.fs
->GetChildren(archivedir, io_opts, &archiveFiles,
/*IODebugContext*=*/nullptr)
.ok()) {
// Delete archival files.
for (const auto& file : archiveFiles) {
if (DeleteScheduler::IsTrashFile(file)) {
std::string trash_file_path = archivedir;
trash_file_path.append("/").append(file);
Status del = DeleteExistingTrashDBFile(
&soptions, trash_file_path, archivedir, /*force_bg=*/false,
/*force_fg=*/!wal_in_db_path, bucket);
if (!del.ok() && result.ok()) {
result = del;
}
} else if (ParseFileName(file, &number, &type) && type == kWalFile) {
if (ParseFileName(file, &number, &type) && type == kWalFile) {
Status del = DeleteUnaccountedDBFile(
&soptions, archivedir + "/" + file, archivedir,
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path, bucket);
@@ -6597,16 +6248,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
// Delete log files in the WAL dir
if (wal_dir_exists) {
for (const auto& file : walDirFiles) {
if (DeleteScheduler::IsTrashFile(file)) {
std::string trash_file_path = soptions.wal_dir;
trash_file_path.append("/").append(file);
Status del = DeleteExistingTrashDBFile(
&soptions, trash_file_path, soptions.wal_dir,
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path, bucket);
if (!del.ok() && result.ok()) {
result = del;
}
} else if (ParseFileName(file, &number, &type) && type == kWalFile) {
if (ParseFileName(file, &number, &type) && type == kWalFile) {
Status del = DeleteUnaccountedDBFile(
&soptions, LogFileName(soptions.wal_dir, number),
soptions.wal_dir, /*force_bg=*/false,
@@ -7174,6 +6816,10 @@ Status DBImpl::PrepareFileIngestion(
"] size must match external_files[" +
std::to_string(i) + "] size");
}
if (args[i].options.write_global_seqno) {
return Status::InvalidArgument(
"write_global_seqno is not supported when file_infos is set");
}
}
if (i && args[i].options.fill_cache != args[i - 1].options.fill_cache) {
return Status::InvalidArgument(
@@ -7220,6 +6866,13 @@ Status DBImpl::PrepareFileIngestion(
}
}
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 && ingest_opts.link_files) {
return Status::InvalidArgument(
"`move_files` and `link_files` can not both be true.");
@@ -7814,35 +7467,19 @@ Status DBImpl::ClipColumnFamily(ColumnFamilyHandle* column_family,
std::vector<RangeOpt> ranges;
ranges.emplace_back(OptSlice{}, begin_key);
ranges.emplace_back(end_key, OptSlice{});
// begin_key is kept, so a file whose largest key is begin_key must stay.
status = DeleteFilesInRanges(column_family, ranges.data(), ranges.size(),
false /* include_end */);
status = DeleteFilesInRanges(column_family, ranges.data(), ranges.size());
}
// DeleteRange the remaining overlapping keys
bool empty_after_delete = false;
if (status.ok()) {
// GetSstFilesBoundaryKeys returns Slices into the current Version's
// FileMetaData. Copy them out while holding the mutex: the deletes below
// drop the mutex and each is a full write, during which a background
// compaction can install a new Version and free that metadata.
std::string smallest_buf;
std::string largest_buf;
Slice smallest_user_key, largest_user_key;
{
// Lock db mutex
InstrumentedMutexLock l(&mutex_);
Slice smallest_slice;
Slice largest_slice;
cfd->current()->GetSstFilesBoundaryKeys(&smallest_slice, &largest_slice);
smallest_buf.assign(smallest_slice.data(), smallest_slice.size());
largest_buf.assign(largest_slice.data(), largest_slice.size());
cfd->current()->GetSstFilesBoundaryKeys(&smallest_user_key,
&largest_user_key);
}
Slice smallest_user_key(smallest_buf);
Slice largest_user_key(largest_buf);
// Opens the window in which the current Version (and the FileMetaData the
// boundary keys were read from) can be replaced and freed by a background
// job before the deletes below run.
TEST_SYNC_POINT("DBImpl::ClipColumnFamily:PostBoundaryKeys");
// all the files has been deleted after DeleteFilesInRanges;
if (smallest_user_key.empty() && largest_user_key.empty()) {
empty_after_delete = true;
+3 -128
View File
@@ -9,7 +9,6 @@
#pragma once
#include <atomic>
#include <chrono>
#include <cstdint>
#include <deque>
#include <functional>
@@ -52,7 +51,6 @@
#include "db/write_controller.h"
#include "db/write_thread.h"
#include "logging/event_logger.h"
#include "memtable/flush_initiator.h"
#include "memtable/wbwi_memtable.h"
#include "monitoring/instrumented_mutex.h"
#include "options/db_options.h"
@@ -1344,9 +1342,6 @@ class DBImpl : public DB
Status TEST_SwitchMemtable(ColumnFamilyData* cfd = nullptr);
size_t TEST_GetWBMFlushableMemUsage();
bool TEST_IsWBMFlushInitiatorFlushable() const;
// Force current memtable contents to be flushed.
Status TEST_FlushMemTable(bool wait = true, bool allow_write_stall = false,
ColumnFamilyHandle* cfh = nullptr);
@@ -1367,10 +1362,7 @@ class DBImpl : public DB
// finishes. For example in CompactRange.
Status TEST_AtomicFlushMemTables(
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
const FlushOptions& flush_opts, bool non_blocking_write_thread = false);
void TEST_BeginWriteStall();
void TEST_EndWriteStall();
const FlushOptions& flush_opts);
// Wait for background threads to complete scheduled work.
Status TEST_WaitForBackgroundWork();
@@ -1563,21 +1555,6 @@ class DBImpl : public DB
}
}
// Returns false when the wait times out while the DB is still blocked.
bool BlockFor(uint64_t timeout_micros) {
MutexLock lock(&state_mutex_);
const std::chrono::microseconds deadline(
SystemClock::Default()->NowMicros() + timeout_micros);
while (state_ == State::BLOCKED) {
TEST_SYNC_POINT("WBMStallInterface::BlockDB");
if (SystemClock::Default()->TimedWait(&state_cv_, deadline) &&
state_ == State::BLOCKED) {
return false;
}
}
return true;
}
// Called from WriteBufferManager. This function changes the state_
// to State::RUNNING indicating the stall is cleared and DB can proceed.
void Signal() override {
@@ -1598,24 +1575,6 @@ class DBImpl : public DB
State state_;
};
// Adapts DBImpl to the shared WriteBufferManager flush registry.
class WBMFlushInitiator : public FlushInitiator {
public:
WBMFlushInitiator(DBImpl* db, bool atomic_flush)
: FlushInitiator(atomic_flush), db_(db) {}
bool ScheduleFlush() override {
return db_->ScheduleWriteBufferManagerFlush();
}
bool TryRefreshMemoryAccounting() override {
return db_->TryRefreshFlushableMemAccounting();
}
private:
DBImpl* const db_;
};
static void TEST_ResetDbSessionIdGen();
static std::string GenerateDbSessionId(Env* env);
@@ -1633,9 +1592,6 @@ class DBImpl : public DB
// db_session_id_ is an identifier that gets reset
// every time the DB is opened
std::string db_session_id_;
// Declared before versions_ so memtables cannot outlive their allocation
// accounting target during destruction.
std::unique_ptr<FlushInitiator> wbm_flush_initiator_;
std::unique_ptr<VersionSet> versions_;
// Flag to check whether we allocated and own the info log file
bool own_info_log_;
@@ -2675,7 +2631,7 @@ class DBImpl : public DB
// Begin stalling of writes when memory usage increases beyond a certain
// threshold.
Status WriteBufferManagerStallWrites(WriteContext* write_context);
void WriteBufferManagerStallWrites();
Status ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
WriteBatch* my_batch);
@@ -2703,16 +2659,6 @@ class DBImpl : public DB
ReadOnlyMemTable* new_imm = nullptr,
SequenceNumber last_seqno = 0);
// `refresh_wbm_accounting` can be false when a caller switches a batch of
// memtables and refreshes the DB-wide accounting once after the batch.
Status SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
ReadOnlyMemTable* new_imm,
SequenceNumber last_seqno,
bool refresh_wbm_accounting);
// REQUIRES: mutex_ is held
void UpdateWBMAccountingAfterMemtableSwitch(bool refresh_now);
// Select and output column families qualified for atomic flush in
// `selected_cfds`. If `provided_candidate_cfds` is non-empty, it will be used
// as candidate CFs to select qualified ones from. Otherwise, all column
@@ -2737,18 +2683,6 @@ class DBImpl : public DB
const autovector<ColumnFamilyData*>& provided_candidate_cfds = {},
bool entered_write_thread = false);
enum class WriteThreadJoinMode { kBlocking, kNonBlocking };
Status FlushMemTableImpl(ColumnFamilyData* cfd, const FlushOptions& options,
FlushReason flush_reason, bool entered_write_thread,
WriteThreadJoinMode join_mode, bool* made_progress);
Status AtomicFlushMemTablesImpl(
const FlushOptions& options, FlushReason flush_reason,
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
bool entered_write_thread, WriteThreadJoinMode join_mode,
bool* made_progress);
// REQUIRES: mutex locked and write queues drained up to the recovery flush
// fence that is about to switch memtables.
void MaybeSyncLastSequenceWithAllocatedForRecovery(FlushReason flush_reason);
@@ -2835,18 +2769,6 @@ class DBImpl : public DB
return static_cast<uint8_t*>(static_cast<void*>(this)) + type;
}
// Checks DB-local write queues; manager-wide stall state would reject healthy
// DBs. REQUIRES: mutex_ held.
bool WouldBlockJoiningWriteThread() {
mutex_.AssertHeld();
return write_thread_.GetBegunCountOfOutstandingStall() != 0 ||
(two_write_queues_ &&
nonmem_write_thread_.GetBegunCountOfOutstandingStall() != 0);
}
Status EnterWriteThreadForNonBlockingFlush(WriteThread::Writer* w,
WriteThread::Writer* nonmem_w);
// REQUIRES: mutex locked and in write thread.
void AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds);
@@ -2856,31 +2778,6 @@ class DBImpl : public DB
// REQUIRES: mutex locked and in write thread.
Status HandleWriteBufferManagerFlush(WriteContext* write_context);
// Column families and memory eligible for a WriteBufferManager flush.
struct FlushableCFs {
ColumnFamilyData* largest = nullptr; // most reclaimable memtable memory
ColumnFamilyData* oldest = nullptr; // smallest memtable creation seq
size_t largest_mem = 0;
size_t total_mem = 0;
};
// Shared selection for bidding and flushing. REQUIRES: mutex_ held.
FlushableCFs CollectFlushableCFs(bool include_waiting_immutable);
// Registers an opened read-write DB without holding mutex_.
void MaybeRegisterFlushInitiator();
// Refreshes the published mutable-memory counters after memtables change.
// REQUIRES: mutex_ held.
void RefreshFlushableMemAccounting();
bool TryRefreshFlushableMemAccounting();
// Queues one WBM flush; false lets the coordinator try another DB.
bool ScheduleWriteBufferManagerFlush();
void BackgroundCallWBMFlush();
// REQUIRES: mutex locked
Status PreprocessWrite(const WriteOptions& write_options,
WalContext* log_context, WriteContext* write_context);
@@ -2919,13 +2816,7 @@ class DBImpl : public DB
// Used by WriteImpl to update bg_error_ when IO error happens, e.g., write
// WAL, sync WAL fails, if paranoid check is enabled.
void WALIOStatusCheck(const IOStatus& status, uint64_t failed_wal_number = 0,
SequenceNumber failed_wal_sequence = 0,
SequenceNumber failed_wal_sequence_start = 0);
// Extends the inclusive WAL-number cutoff that must not receive more I/O.
// This is lock-free because a WAL error can race with public SyncWAL().
void ExtendWALRecoveryCutoff(uint64_t failed_wal_number);
void WALIOStatusCheck(const IOStatus& status);
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
void HandleMemTableInsertFailure(const Status& nonok_memtable_insert_status);
@@ -3511,16 +3402,6 @@ class DBImpl : public DB
// * whenever a column family is dropped.
InstrumentedCondVar bg_cv_;
// Set only while ResumeImpl performs a file-scoped WAL recovery. Protected
// by mutex_; SwitchMemtable uses it to avoid touching the failed writer.
bool recovering_from_wal_write_error_ = false;
uint64_t recover_wal_through_number_ = 0;
// Once a file-scoped WAL error is reported, all writers through this
// inclusive cutoff are quarantined until recovery detaches them. A
// concurrent SyncWAL() reads this without taking the DB mutex.
std::atomic<uint64_t> wal_recovery_cutoff_{0};
ColumnFamilyHandleImpl* persist_stats_cf_handle_ = nullptr;
bool persistent_stats_cfd_exists_ = true;
@@ -3784,12 +3665,6 @@ class DBImpl : public DB
// number of background memtable flush jobs, submitted to the HIGH pool
int bg_flush_scheduled_ = 0;
// LOW avoids consuming a flush worker while joining the target write thread.
static constexpr Env::Priority kWBMFlushPriority = Env::Priority::LOW;
// Outstanding cross-DB WBM flush jobs, drained during shutdown.
int bg_wbm_flush_scheduled_ = 0;
// stores the number of flushes are currently running
int num_running_flushes_ = 0;
+50 -438
View File
@@ -9,7 +9,6 @@
#include <algorithm>
#include <cinttypes>
#include <deque>
#include <memory>
#include <unordered_map>
#include "db/blob/blob_file_partition_manager.h"
@@ -1713,7 +1712,7 @@ void DBImpl::PrepareTrivialMoveEdit(Compaction& c, LogBuffer* log_buffer,
c.output_level(), f->fd.GetNumber(), f->fd.GetPathId(),
f->fd.GetFileSize(), f->smallest, f->largest, f->fd.smallest_seqno,
f->fd.largest_seqno, f->marked_for_compaction, f->temperature,
f->oldest_blob_file_number, f->oldest_ancestor_time,
f->oldest_blob_file_number, f->oldest_ancester_time,
f->file_creation_time, f->epoch_number, f->file_checksum,
f->file_checksum_func_name, f->unique_id,
f->compensated_range_deletion_size, f->tail_size,
@@ -2034,7 +2033,7 @@ Status DBImpl::PauseBackgroundWork() {
InstrumentedMutexLock guard_lock(&mutex_);
bg_compaction_paused_++;
while (bg_bottom_compaction_scheduled_ > 0 || bg_compaction_scheduled_ > 0 ||
bg_flush_scheduled_ > 0 || bg_wbm_flush_scheduled_ > 0) {
bg_flush_scheduled_ > 0) {
bg_cv_.Wait();
}
bg_work_paused_++;
@@ -2290,7 +2289,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
to_level, f->fd.GetNumber(), f->fd.GetPathId(), f->fd.GetFileSize(),
f->smallest, f->largest, f->fd.smallest_seqno, f->fd.largest_seqno,
f->marked_for_compaction, f->temperature, f->oldest_blob_file_number,
f->oldest_ancestor_time, f->file_creation_time, f->epoch_number,
f->oldest_ancester_time, f->file_creation_time, f->epoch_number,
f->file_checksum, f->file_checksum_func_name, f->unique_id,
f->compensated_range_deletion_size, f->tail_size,
f->user_defined_timestamps_persisted, f->min_timestamp,
@@ -2725,54 +2724,12 @@ void DBImpl::MaybeSyncLastSequenceWithAllocatedForRecovery(
}
}
Status DBImpl::EnterWriteThreadForNonBlockingFlush(
WriteThread::Writer* w, WriteThread::Writer* nonmem_w) {
mutex_.AssertHeld();
if (!write_thread_.EnterUnbatchedNonBlocking(w, &mutex_)) {
return Status::Incomplete(
"Write stall in progress, unable to join the write thread to switch "
"memtables");
}
if (two_write_queues_ &&
!nonmem_write_thread_.EnterUnbatchedNonBlocking(nonmem_w, &mutex_)) {
write_thread_.ExitUnbatched(w);
return Status::Incomplete(
"Write stall in progress, unable to join the write thread to switch "
"memtables");
}
if (!error_handler_.IsBGWorkStopped() && !write_controller_.IsStopped() &&
!WouldBlockJoiningWriteThread()) {
return Status::OK();
}
write_thread_.ExitUnbatched(w);
if (two_write_queues_) {
nonmem_write_thread_.ExitUnbatched(nonmem_w);
}
return Status::Incomplete(
"Write stop started while joining the write thread to switch memtables");
}
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
const FlushOptions& flush_options,
FlushReason flush_reason,
bool entered_write_thread) {
return FlushMemTableImpl(cfd, flush_options, flush_reason,
entered_write_thread, WriteThreadJoinMode::kBlocking,
nullptr /* made_progress */);
}
Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
const FlushOptions& flush_options,
FlushReason flush_reason,
bool entered_write_thread,
WriteThreadJoinMode join_mode,
bool* made_progress) {
if (made_progress != nullptr) {
*made_progress = false;
}
// This method should not be called if atomic_flush is true.
assert(!immutable_db_options_.atomic_flush);
assert(!IsRecoveryFlush(flush_reason));
if (!flush_options.wait && write_controller_.IsStopped()) {
std::ostringstream oss;
oss << "Writes have been stopped, thus unable to perform manual flush. "
@@ -2806,45 +2763,13 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
WriteThread::Writer w;
WriteThread::Writer nonmem_w;
if (needs_to_join_write_thread) {
if (join_mode == WriteThreadJoinMode::kNonBlocking) {
s = EnterWriteThreadForNonBlockingFlush(&w, &nonmem_w);
if (!s.ok()) {
return s;
}
} else {
write_thread_.EnterUnbatched(&w, &mutex_);
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
write_thread_.EnterUnbatched(&w, &mutex_);
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
}
if (join_mode == WriteThreadJoinMode::kNonBlocking && cfd->IsDropped()) {
TEST_SYNC_POINT("DBImpl::FlushMemTableImpl:Dropped");
if (needs_to_join_write_thread) {
write_thread_.ExitUnbatched(&w);
if (two_write_queues_) {
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
}
}
s.PermitUncheckedOk();
return Status::ColumnFamilyDropped();
}
WaitForPendingWrites();
if (join_mode == WriteThreadJoinMode::kNonBlocking &&
flush_reason == FlushReason::kWriteBufferManager &&
cfd->imm()->IsFlushPendingOrRunning()) {
TEST_SYNC_POINT("DBImpl::FlushMemTableImpl:AlreadyFlushing");
if (needs_to_join_write_thread) {
write_thread_.ExitUnbatched(&w);
if (two_write_queues_) {
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
}
}
return Status::Incomplete(
"Column family started flushing before the WBM handoff completed");
}
// Recovery may have released `mutex_` after the earlier `ResumeImpl()`
// sync. Refresh sequence state at the actual memtable-switch fence, after
// both write queues are drained and before `SwitchMemtable()` consumes
@@ -2854,9 +2779,6 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load() ||
IsRecoveryFlush(flush_reason)) {
s = SwitchMemtable(cfd, &context);
if (s.ok() && made_progress != nullptr) {
*made_progress = true;
}
}
const uint64_t flush_memtable_id = std::numeric_limits<uint64_t>::max();
if (s.ok()) {
@@ -2929,11 +2851,7 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
req.cfd_to_max_mem_id_to_persist.begin()->first;
bool already_queued_for_flush = loop_cfd->queued_for_flush();
bool flush_req_enqueued = EnqueuePendingFlush(req);
if (flush_req_enqueued && made_progress != nullptr) {
*made_progress = true;
}
if (flush_reason != FlushReason::kWriteBufferManager &&
(already_queued_for_flush || flush_req_enqueued)) {
if (already_queued_for_flush || flush_req_enqueued) {
loop_cfd->SetFlushSkipReschedule();
}
}
@@ -2948,9 +2866,7 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
}
}
if (flush_reason != FlushReason::kWriteBufferManager) {
NotifyOnManualFlushScheduled({cfd}, flush_reason);
}
NotifyOnManualFlushScheduled({cfd}, flush_reason);
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
if (s.ok() && flush_options.wait) {
@@ -2979,20 +2895,6 @@ Status DBImpl::AtomicFlushMemTables(
const FlushOptions& flush_options, FlushReason flush_reason,
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
bool entered_write_thread) {
return AtomicFlushMemTablesImpl(flush_options, flush_reason,
provided_candidate_cfds, entered_write_thread,
WriteThreadJoinMode::kBlocking,
nullptr /* made_progress */);
}
Status DBImpl::AtomicFlushMemTablesImpl(
const FlushOptions& flush_options, FlushReason flush_reason,
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
bool entered_write_thread, WriteThreadJoinMode join_mode,
bool* made_progress) {
if (made_progress != nullptr) {
*made_progress = false;
}
if (!flush_options.wait && write_controller_.IsStopped()) {
std::ostringstream oss;
oss << "Writes have been stopped, thus unable to perform manual flush. "
@@ -3023,30 +2925,32 @@ Status DBImpl::AtomicFlushMemTablesImpl(
candidate_cfds = provided_candidate_cfds;
}
const auto unref_generated_candidates = [&]() {
if (!provided_candidate_cfds.empty()) {
return;
}
for (auto candidate_cfd : candidate_cfds) {
candidate_cfd->UnrefAndTryDelete();
}
candidate_cfds.clear();
};
if (!flush_options.allow_write_stall) {
int num_cfs_to_flush = 0;
for (auto cfd : candidate_cfds) {
bool flush_needed = true;
s = WaitUntilFlushWouldNotStallWrites(cfd, &flush_needed);
if (!s.ok()) {
unref_generated_candidates();
// Unref the newly generated candidate cfds (when not provided) in
// `candidate_cfds`
if (provided_candidate_cfds.empty()) {
for (auto candidate_cfd : candidate_cfds) {
candidate_cfd->UnrefAndTryDelete();
}
}
return s;
} else if (flush_needed) {
++num_cfs_to_flush;
}
}
if (0 == num_cfs_to_flush) {
unref_generated_candidates();
// Unref the newly generated candidate cfds (when not provided) in
// `candidate_cfds`
if (provided_candidate_cfds.empty()) {
for (auto candidate_cfd : candidate_cfds) {
candidate_cfd->UnrefAndTryDelete();
}
}
return s;
}
}
@@ -3061,54 +2965,39 @@ Status DBImpl::AtomicFlushMemTablesImpl(
WriteThread::Writer w;
WriteThread::Writer nonmem_w;
if (needs_to_join_write_thread) {
if (join_mode == WriteThreadJoinMode::kNonBlocking) {
s = EnterWriteThreadForNonBlockingFlush(&w, &nonmem_w);
if (!s.ok()) {
unref_generated_candidates();
return s;
}
} else {
write_thread_.EnterUnbatched(&w, &mutex_);
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
write_thread_.EnterUnbatched(&w, &mutex_);
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
}
WaitForPendingWrites();
// Recovery may have released `mutex_` after the earlier `ResumeImpl()`
// sync. Refresh sequence state at the atomic memtable-switch fence, after
// both write queues are drained and before `SwitchMemtable()` consumes
// `LastSequence()`.
// Keep atomic recovery flushes consistent with the single-CF path: the
// sequence sync must happen after both write queues are drained and before
// any recovery memtable switch reads `LastSequence()`.
MaybeSyncLastSequenceWithAllocatedForRecovery(flush_reason);
SelectColumnFamiliesForAtomicFlush(&cfds, candidate_cfds, flush_reason);
unref_generated_candidates();
// Unref the newly generated candidate cfds (when not provided) in
// `candidate_cfds`
if (provided_candidate_cfds.empty()) {
for (auto candidate_cfd : candidate_cfds) {
candidate_cfd->UnrefAndTryDelete();
}
}
const bool defer_wbm_accounting_refresh = cfds.size() > 1;
bool switch_attempted = false;
for (auto cfd : cfds) {
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load() &&
!IsRecoveryFlush(flush_reason)) {
continue;
}
cfd->Ref();
switch_attempted = true;
s = defer_wbm_accounting_refresh
? SwitchMemtableImpl(cfd, &context, nullptr, 0,
false /* refresh_wbm_accounting */)
: SwitchMemtable(cfd, &context);
s = SwitchMemtable(cfd, &context);
cfd->UnrefAndTryDelete();
if (!s.ok()) {
break;
}
if (made_progress != nullptr) {
*made_progress = true;
}
}
if (defer_wbm_accounting_refresh && switch_attempted) {
UpdateWBMAccountingAfterMemtableSwitch(true /* refresh_now */);
}
if (s.ok()) {
AssignAtomicFlushSeq(cfds);
@@ -3125,9 +3014,7 @@ Status DBImpl::AtomicFlushMemTablesImpl(
}
}
GenerateFlushRequest(cfds, flush_reason, &flush_req);
if (EnqueuePendingFlush(flush_req) && made_progress != nullptr) {
*made_progress = true;
}
EnqueuePendingFlush(flush_req);
MaybeScheduleFlushOrCompaction();
}
@@ -3138,39 +3025,14 @@ Status DBImpl::AtomicFlushMemTablesImpl(
}
}
}
if (flush_reason != FlushReason::kWriteBufferManager) {
NotifyOnManualFlushScheduled(cfds, flush_reason);
}
NotifyOnManualFlushScheduled(cfds, flush_reason);
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
if (s.ok() && flush_options.wait) {
// Walk cfds and look each one up, so the ids line up with cfds by
// construction. Iterating the map instead pairs ids with the wrong CF
// (its order is unrelated to cfds), and a CF that dropped out of the map
// (an idle CF de-selected during the switch loop, or a duplicate handle
// collapsed by the map) would make the two vectors different lengths.
autovector<const uint64_t*> flush_memtable_ids;
flush_memtable_ids.reserve(cfds.size());
for (auto* cfd : cfds) {
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfd);
flush_memtable_ids.push_back(
iter == flush_req.cfd_to_max_mem_id_to_persist.end()
? nullptr
: &(iter->second));
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
flush_memtable_ids.push_back(&(iter.second));
}
#ifndef NDEBUG
// WaitForFlushMemTables pairs flush_memtable_ids[i] with cfds[i], so each
// entry must point at this cfd's own map id (or be null when absent). Fires
// if the ids are ever rebuilt in the request map's order.
assert(flush_memtable_ids.size() == cfds.size());
for (size_t i = 0; i < cfds.size(); ++i) {
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfds[i]);
assert(flush_memtable_ids[i] ==
(iter == flush_req.cfd_to_max_mem_id_to_persist.end()
? nullptr
: &(iter->second)));
}
#endif // NDEBUG
s = WaitForFlushMemTables(
cfds, flush_memtable_ids,
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */,
@@ -3210,29 +3072,9 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
flush_req.atomic_flush = true;
GenerateFlushRequest(cfds, flush_reason, &flush_req);
EnqueuePendingFlush(flush_req);
// Keep flush_memtable_ids aligned with cfds by walking cfds, since the
// request map's order is unrelated to cfds. A CF not in the map waits on
// its whole imm list, matching the max-bound the non-atomic branch uses.
for (auto* cfd : cfds) {
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfd);
flush_memtable_ids.push_back(
iter == flush_req.cfd_to_max_mem_id_to_persist.end()
? std::numeric_limits<uint64_t>::max()
: iter->second);
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
flush_memtable_ids.push_back(iter.second);
}
#ifndef NDEBUG
// WaitForFlushMemTables pairs flush_memtable_ids[i] with cfds[i], so each
// entry must be this cfd's own max id. Fires if the ids are ever rebuilt in
// the request map's order, which is unrelated to cfds order.
assert(flush_memtable_ids.size() == cfds.size());
for (size_t i = 0; i < cfds.size(); ++i) {
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfds[i]);
assert(flush_memtable_ids[i] ==
(iter == flush_req.cfd_to_max_mem_id_to_persist.end()
? std::numeric_limits<uint64_t>::max()
: iter->second));
}
#endif // NDEBUG
} else {
for (auto cfd : cfds) {
flush_memtable_ids.push_back(
@@ -3993,34 +3835,11 @@ bool DBImpl::EnqueuePendingFlush(const FlushRequest& flush_req) {
enqueued = true;
}
} else {
if (flush_req.flush_reason == FlushReason::kWriteBufferManager &&
write_buffer_manager_->ShouldTrackFlushInitiator()) {
for (const FlushRequest& queued : flush_queue_) {
if (!queued.atomic_flush) {
continue;
}
bool covered = true;
for (const auto& requested : flush_req.cfd_to_max_mem_id_to_persist) {
const auto queued_entry =
queued.cfd_to_max_mem_id_to_persist.find(requested.first);
if (queued_entry == queued.cfd_to_max_mem_id_to_persist.end() ||
queued_entry->second < requested.second) {
covered = false;
break;
}
}
if (covered) {
TEST_SYNC_POINT(
"DBImpl::EnqueuePendingFlush:AtomicWBMRequestDeduplicated");
return false;
}
}
}
// Atomic flush requests normally bypass the per-CF queued_for_flush()
// guard. Cross-DB WBM requests are deduplicated above because its
// autonomous retry loop must not repeatedly enqueue the same atomic cut
// while HIGH workers are unavailable. Other atomic requests retain their
// existing behavior.
// Atomic flush requests bypass the queued_for_flush() deduplication guard.
// This means an atomic request and a non-atomic request for the same CF
// can coexist in the flush queue. This is safe because PickMemtablesToFlush
// uses flush_in_progress_ to prevent double-picking of memtables, and
// BackgroundFlush filters out CFs where !IsFlushPending().
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
ColumnFamilyData* cfd = iter.first;
cfd->Ref();
@@ -4145,178 +3964,6 @@ void DBImpl::UnscheduleFlushCallback(void* arg) {
TEST_SYNC_POINT("DBImpl::UnscheduleFlushCallback");
}
void DBImpl::RefreshFlushableMemAccounting() {
mutex_.AssertHeld();
versions_->GetColumnFamilySet()->RefreshFlushableMemAccounting();
}
bool DBImpl::TryRefreshFlushableMemAccounting() {
if (!mutex_.TryLock()) {
return false;
}
Defer unlock_mutex([this] { mutex_.Unlock(); });
RefreshFlushableMemAccounting();
return wbm_flush_initiator_ != nullptr &&
wbm_flush_initiator_->HasAccurateFlushableMemUsage();
}
DBImpl::FlushableCFs DBImpl::CollectFlushableCFs(
bool include_waiting_immutable) {
mutex_.AssertHeld();
FlushableCFs result;
SequenceNumber oldest_seq = kMaxSequenceNumber;
for (auto cfd : *versions_->GetColumnFamilySet()) {
if (cfd->IsDropped() || !cfd->initialized()) {
continue;
}
const size_t mutable_mem =
cfd->mem()->IsEmpty() ? 0 : cfd->mem()->WBMTrackedMemoryUsage();
size_t waiting_immutable_mem = 0;
if (include_waiting_immutable) {
if (immutable_db_options_.atomic_flush) {
waiting_immutable_mem =
cfd->imm()->WBMTrackedUnstartedMemTablesMemoryUsage();
} else if (!cfd->imm()->IsFlushPendingOrRunning()) {
waiting_immutable_mem =
cfd->imm()->WBMTrackedUnflushedMemTablesMemoryUsage();
}
}
result.total_mem += mutable_mem + waiting_immutable_mem;
// Non-atomic flushes skip CFs already flushing.
if (cfd->imm()->IsFlushPendingOrRunning()) {
continue;
}
size_t mem = mutable_mem;
if (include_waiting_immutable) {
mem += waiting_immutable_mem;
}
if (mem == 0) {
continue;
}
if (result.largest == nullptr || mem > result.largest_mem) {
result.largest = cfd;
result.largest_mem = mem;
}
const SequenceNumber seq = cfd->mem()->GetCreationSeq();
if (result.oldest == nullptr || seq < oldest_seq) {
result.oldest = cfd;
oldest_seq = seq;
}
}
return result;
}
bool DBImpl::ScheduleWriteBufferManagerFlush() {
{
if (!mutex_.TryLock()) {
return false;
}
Defer unlock_mutex([this] { mutex_.Unlock(); });
// Recheck bid eligibility because DB state may have changed.
if (!write_buffer_manager_->ShouldTrackFlushInitiator() ||
shutdown_initiated_.load(std::memory_order_acquire) ||
shutting_down_.load(std::memory_order_acquire) ||
reject_new_background_jobs_ || !opened_successfully_ || read_only_ ||
error_handler_.IsBGWorkStopped() || write_controller_.IsStopped() ||
// Reject both the pause drain and fully paused states.
bg_work_paused_ > 0 || bg_compaction_paused_ > 0 ||
// Never queue work that would wait behind this DB's stall.
WouldBlockJoiningWriteThread()) {
return false;
}
// A queued or running job does not count as new progress. Returning false
// lets the coordinator try another DB instead of queuing more work behind
// a busy LOW pool.
if (bg_wbm_flush_scheduled_ > 0) {
return false;
}
const FlushableCFs flushable =
CollectFlushableCFs(true /* include_waiting_immutable */);
const bool has_flushable_cf = immutable_db_options_.atomic_flush
? flushable.total_mem > 0
: flushable.largest != nullptr;
if (!has_flushable_cf) {
wbm_flush_initiator_->SetHasFlushableCF(false);
write_buffer_manager_->NotifyFlushInitiatorChanged();
return false;
}
wbm_flush_initiator_->SetHasFlushableCF(false);
write_buffer_manager_->NotifyFlushInitiatorChanged();
++bg_wbm_flush_scheduled_;
}
// The WBM-owned executor provides bounded capacity independent of Env::LOW.
// Run outside mutex_ so DB-local work cannot block other DB mutex users.
BackgroundCallWBMFlush();
return true;
}
void DBImpl::BackgroundCallWBMFlush() {
const bool atomic_flush = immutable_db_options_.atomic_flush;
ColumnFamilyData* cfd_to_flush = nullptr;
bool has_flushable_cf = false;
{
InstrumentedMutexLock l(&mutex_);
if (!shutdown_initiated_.load(std::memory_order_acquire) &&
!shutting_down_.load(std::memory_order_acquire) &&
!reject_new_background_jobs_ && opened_successfully_ && !read_only_ &&
!error_handler_.IsBGWorkStopped() && !write_controller_.IsStopped() &&
bg_work_paused_ == 0 && bg_compaction_paused_ == 0 &&
!WouldBlockJoiningWriteThread()) {
const FlushableCFs flushable =
CollectFlushableCFs(true /* include_waiting_immutable */);
has_flushable_cf =
atomic_flush ? flushable.total_mem > 0 : flushable.largest != nullptr;
if (!atomic_flush) {
cfd_to_flush = flushable.largest;
if (cfd_to_flush != nullptr) {
cfd_to_flush->Ref();
}
}
}
}
TEST_SYNC_POINT("DBImpl::BackgroundCallWBMFlush:AfterPick");
FlushOptions flush_options;
flush_options.wait = false;
flush_options.allow_write_stall = true;
bool made_progress = false;
if (has_flushable_cf) {
Status s;
if (atomic_flush) {
s = AtomicFlushMemTablesImpl(
flush_options, FlushReason::kWriteBufferManager,
{} /* provided_candidate_cfds */, false /* entered_write_thread */,
WriteThreadJoinMode::kNonBlocking, &made_progress);
} else {
s = FlushMemTableImpl(cfd_to_flush, flush_options,
FlushReason::kWriteBufferManager,
false /* entered_write_thread */,
WriteThreadJoinMode::kNonBlocking, &made_progress);
}
if (!s.ok()) {
ROCKS_LOG_WARN(
immutable_db_options_.info_log,
"Flush on behalf of a shared WriteBufferManager failed: %s",
s.ToString().c_str());
}
s.PermitUncheckedError();
}
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCallWBMFlush:MadeProgress",
&made_progress);
InstrumentedMutexLock l(&mutex_);
if (cfd_to_flush != nullptr) {
cfd_to_flush->UnrefAndTryDelete();
}
RefreshFlushableMemAccounting();
--bg_wbm_flush_scheduled_;
write_buffer_manager_->NotifyFlushInitiatorFlushCompleted(
wbm_flush_initiator_.get(), made_progress);
TEST_SYNC_POINT_CALLBACK("DBImpl::BGWorkWBMFlush:done", this);
bg_cv_.SignalAll();
}
Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
LogBuffer* log_buffer, FlushReason* reason,
bool* flush_rescheduled_to_retain_udt,
@@ -4547,9 +4194,6 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
assert(num_running_flushes_ > 0);
num_running_flushes_--;
bg_flush_scheduled_--;
if (write_buffer_manager_->ShouldTrackFlushInitiator()) {
RefreshFlushableMemAccounting();
}
// See if there's more work to be done
MaybeScheduleFlushOrCompaction();
@@ -5208,7 +4852,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
false /* marked_for_compact */,
c->GetOutputTemperature() /* temperature */,
in_file->oldest_blob_file_number,
in_file->oldest_ancestor_time,
in_file->oldest_ancester_time,
out_file_creation_time,
c->MinInputFileEpochNumber(),
dest_writer->GetFileChecksum(),
@@ -6014,47 +5658,15 @@ Status DBImpl::WaitForCompact(
const auto deadline = immutable_db_options_.clock->NowMicros() +
wait_for_compact_options.timeout.count();
for (;;) {
// The coordinator reserves this DB before invoking its non-blocking
// scheduling callback. Drain that DB-specific reservation without holding
// mutex_; successful callbacks publish bg_wbm_flush_scheduled_ before
// releasing the reservation.
if (write_buffer_manager_ != nullptr &&
write_buffer_manager_->ShouldTrackFlushInitiator() &&
wbm_flush_initiator_ != nullptr) {
// Fence all coordinator requests visible before this local quiescence
// check. Candidate retrieval either observes this generation and skips
// the DB, or atomically publishes a handoff reservation first.
write_buffer_manager_->FenceFlushInitiator(wbm_flush_initiator_.get());
}
if (wbm_flush_initiator_ != nullptr &&
wbm_flush_initiator_->HasPendingFlushHandoff()) {
std::chrono::microseconds handoff_timeout{0};
if (wait_for_compact_options.timeout.count() != 0) {
const uint64_t now = immutable_db_options_.clock->NowMicros();
if (now >= deadline) {
return Status::TimedOut();
}
handoff_timeout = std::chrono::microseconds(deadline - now);
}
mutex_.Unlock();
const bool handoff_finished =
wbm_flush_initiator_->WaitForPendingFlushHandoff(handoff_timeout);
mutex_.Lock();
if (!handoff_finished) {
return Status::TimedOut();
}
continue;
}
if (shutting_down_.load(std::memory_order_acquire)) {
return Status::ShutdownInProgress();
}
if (bg_work_paused_ && wait_for_compact_options.abort_on_pause) {
return Status::Aborted();
}
// A WBM job can enqueue a regular flush, so wait for both to quiesce.
if ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
bg_flush_scheduled_ || bg_wbm_flush_scheduled_ ||
unscheduled_compactions_ || !parked_compaction_cfds_.empty() ||
bg_flush_scheduled_ || unscheduled_compactions_ ||
!parked_compaction_cfds_.empty() ||
(wait_for_compact_options.wait_for_purge && bg_purge_scheduled_) ||
unscheduled_flushes_ || error_handler_.IsRecoveryInProgress()) &&
(error_handler_.GetBGError().ok())) {
+3 -37
View File
@@ -144,18 +144,6 @@ Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
return s;
}
size_t DBImpl::TEST_GetWBMFlushableMemUsage() {
InstrumentedMutexLock l(&mutex_);
assert(wbm_flush_initiator_ != nullptr);
RefreshFlushableMemAccounting();
return wbm_flush_initiator_->GetFlushableMemUsage();
}
bool DBImpl::TEST_IsWBMFlushInitiatorFlushable() const {
assert(wbm_flush_initiator_ != nullptr);
return wbm_flush_initiator_->IsFlushable();
}
Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
ColumnFamilyHandle* cfh) {
FlushOptions fo;
@@ -194,31 +182,9 @@ Status DBImpl::TEST_FlushMemTableWithListenerWait(bool allow_write_stall,
Status DBImpl::TEST_AtomicFlushMemTables(
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
const FlushOptions& flush_opts, bool non_blocking_write_thread) {
const auto join_mode = non_blocking_write_thread
? WriteThreadJoinMode::kNonBlocking
: WriteThreadJoinMode::kBlocking;
return AtomicFlushMemTablesImpl(
flush_opts, FlushReason::kTest, provided_candidate_cfds,
false /* entered_write_thread */, join_mode, nullptr /* made_progress */);
}
void DBImpl::TEST_BeginWriteStall() {
InstrumentedMutexLock l(&mutex_);
write_thread_.BeginWriteStall();
if (wbm_flush_initiator_ != nullptr &&
write_buffer_manager_->ShouldTrackFlushInitiator()) {
wbm_flush_initiator_->SetFlushable(false);
}
}
void DBImpl::TEST_EndWriteStall() {
InstrumentedMutexLock l(&mutex_);
write_thread_.EndWriteStall();
if (wbm_flush_initiator_ != nullptr &&
write_buffer_manager_->ShouldTrackFlushInitiator()) {
wbm_flush_initiator_->SetFlushable(true);
}
const FlushOptions& flush_opts) {
return AtomicFlushMemTables(flush_opts, FlushReason::kTest,
provided_candidate_cfds);
}
Status DBImpl::TEST_WaitForBackgroundWork() {
+1 -1
View File
@@ -140,7 +140,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
f->fd.GetFileSize(), f->smallest, f->largest,
f->fd.smallest_seqno, f->fd.largest_seqno,
f->marked_for_compaction, f->temperature,
f->oldest_blob_file_number, f->oldest_ancestor_time,
f->oldest_blob_file_number, f->oldest_ancester_time,
f->file_creation_time, f->epoch_number, f->file_checksum,
f->file_checksum_func_name, f->unique_id,
f->compensated_range_deletion_size, f->tail_size,
+1 -2
View File
@@ -298,8 +298,7 @@ Status DB::OpenAsFollower(
dbname, &impl->immutable_db_options_, impl->mutable_db_options_,
impl->file_options_, impl->table_cache_.get(),
impl->write_buffer_manager_, &impl->write_controller_, impl->io_tracer_,
impl->db_id_, impl->db_session_id_,
/*defer_sst_file_opening=*/false));
impl->db_id_, impl->db_session_id_));
impl->column_family_memtables_.reset(
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
+13 -32
View File
@@ -683,7 +683,7 @@ Status DBImpl::Recover(
f->marked_for_compaction,
f->temperature, // this can be different from
// `last_level_temperature`
f->oldest_blob_file_number, f->oldest_ancestor_time,
f->oldest_blob_file_number, f->oldest_ancester_time,
f->file_creation_time, f->epoch_number,
f->file_checksum, f->file_checksum_func_name,
f->unique_id, f->compensated_range_deletion_size,
@@ -2143,7 +2143,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
immutable_db_options_.clock->GetCurrentTime(&_current_time)
.PermitUncheckedError(); // ignore error
const uint64_t current_time = static_cast<uint64_t>(_current_time);
meta.oldest_ancestor_time = current_time;
meta.oldest_ancester_time = current_time;
meta.epoch_number = cfd->NewEpochNumber();
{
auto write_hint = cfd->current()->storage_info()->CalculateSSTWriteHint(
@@ -2277,7 +2277,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
meta.fd.GetFileSize(), meta.smallest, meta.largest,
meta.fd.smallest_seqno, meta.fd.largest_seqno,
meta.marked_for_compaction, meta.temperature,
meta.oldest_blob_file_number, meta.oldest_ancestor_time,
meta.oldest_blob_file_number, meta.oldest_ancester_time,
meta.file_creation_time, meta.epoch_number,
meta.file_checksum, meta.file_checksum_func_name,
meta.unique_id, meta.compensated_range_deletion_size,
@@ -2791,9 +2791,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
}
if (s.ok()) {
s = impl->immutable_db_options_.use_fsync
? log_writer->file()->Fsync(opts)
: log_writer->file()->Sync(opts);
s = log_writer->file()->Sync(opts,
impl->immutable_db_options_.use_fsync);
}
}
}
@@ -2980,9 +2979,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
IOOptions opts;
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = impl->immutable_db_options_.use_fsync
? log_writer->file()->Fsync(opts)
: log_writer->file()->Sync(opts);
s = log_writer->file()->Sync(opts,
impl->immutable_db_options_.use_fsync);
}
}
}
@@ -3015,8 +3013,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
}
}
if (s.ok()) {
// Register only after open succeeds and without holding the DB mutex.
impl->MaybeRegisterFlushInitiator();
*dbptr = std::move(impl);
} else {
for (auto* h : *handles) {
@@ -3074,18 +3070,8 @@ void DBImpl::ScheduleAsyncFileOpening() {
bg_async_file_open_state_ = AsyncFileOpenState::kScheduled;
// since this is a one time job, best to schedule it with high priority
auto unschedule_callback = [](void* arg) {
auto* unscheduled_ctx = static_cast<AsyncFileOpenContext*>(arg);
DBImpl* db = unscheduled_ctx->db;
db->mutex_.AssertHeld();
delete unscheduled_ctx;
db->bg_async_file_open_state_ = AsyncFileOpenState::kComplete;
db->bg_cv_.SignalAll();
TEST_SYNC_POINT("DBImpl::UnscheduleAsyncFileOpenCallback");
TEST_SYNC_POINT("DBImpl::BGWorkAsyncFileOpen:Done");
};
env_->Schedule(&DBImpl::BGWorkAsyncFileOpen, ctx, Env::Priority::HIGH,
&bg_async_file_open_state_, unschedule_callback);
nullptr);
}
void DBImpl::MarkAsyncFileOpenNotNeeded() {
@@ -3115,9 +3101,8 @@ void DBImpl::BGWorkAsyncFileOpen(void* arg) {
auto* version = ctx->versions[i];
ColumnFamilyData* cfd = version->cfd();
// Retained secondary handles remain readable after the primary drops the
// column family, so their captured Versions still need pinned readers.
if (cfd->IsDropped() && !db->read_only_) {
// Skip column families that were dropped after scheduling
if (cfd->IsDropped()) {
continue;
}
@@ -3147,15 +3132,11 @@ void DBImpl::BGWorkAsyncFileOpen(void* arg) {
if (!s.ok()) {
ROCKS_LOG_ERROR(
db->immutable_db_options_.info_log,
"BGWorkAsyncFileOpen: file validation/opening failed for CF %s: %s",
"BGWorkAsyncFileOpen: LoadTableHandlers failed for CF %s: "
"%s",
cfd->GetName().c_str(), s.ToString().c_str());
InstrumentedMutexLock l(&db->mutex_);
if (db->read_only_) {
db->error_handler_.SetBGErrorNoAutoRecovery(
s, BackgroundErrorReason::kAsyncFileOpen);
} else {
db->error_handler_.SetBGError(s, BackgroundErrorReason::kAsyncFileOpen);
}
db->error_handler_.SetBGError(s, BackgroundErrorReason::kAsyncFileOpen);
break;
}
}
+16 -56
View File
@@ -100,11 +100,7 @@ void DBImplSecondary::RetireSecondaryReadView(
std::lock_guard<std::mutex> lock(retired_secondary_read_views_mutex_);
retired_secondary_read_views_.emplace_back(std::move(read_view));
}
// Publish and close synchronously reclaim after retiring a view. Do not wake
// the background reclaimer here: it could take a reader-free view first and
// let catch-up scan for obsolete files before releasing the view's
// SuperVersions. A reader that makes a retained view reclaimable later wakes
// the reclaimer in ReleaseSecondaryReadView().
retired_secondary_read_views_cv_.notify_all();
}
bool DBImplSecondary::HasReclaimableSecondaryReadView() const {
@@ -184,9 +180,6 @@ void DBImplSecondary::ReclaimRetiredSecondaryReadViewsLoop() {
void DBImplSecondary::CleanupRetiredSecondaryReadViews() {
ReclaimRetiredSecondaryReadViews();
std::unique_lock<std::mutex> lock(retired_secondary_read_views_mutex_);
retired_secondary_read_views_cv_.wait(
lock, [this]() { return secondary_read_view_reclaims_in_flight_ == 0; });
}
void DBImplSecondary::DrainRetiredSecondaryReadViews() {
@@ -1183,24 +1176,6 @@ Status DBImplSecondary::OpenAsSecondaryImpl(
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
bool recover_wal, bool trust_manifest_recovery) {
*dbptr = nullptr;
// Only normal DB::OpenAsSecondary recovery can defer SST opening.
// `recover_wal` excludes OpenAndCompact even when no snapshot floor is
// supplied, `open_files_async` is the explicit opt-in, and trust-MANIFEST
// recovery must only open its protected compaction inputs.
const bool defer_sst_file_opening =
recover_wal && db_options.open_files_async && !trust_manifest_recovery;
if (defer_sst_file_opening && !db_options.skip_stats_update_on_db_open) {
return Status::InvalidArgument(
"open_files_async requires skip_stats_update_on_db_open = true.");
}
if (defer_sst_file_opening) {
for (const ColumnFamilyDescriptor& column_family : column_families) {
if (column_family.options.compaction_style == kCompactionStyleFIFO) {
return Status::NotSupported(
"FIFO compaction is not supported with open_files_async = true.");
}
}
}
DBOptions tmp_opts(db_options);
Status s;
@@ -1213,7 +1188,7 @@ Status DBImplSecondary::OpenAsSecondaryImpl(
}
assert(tmp_opts.info_log != nullptr);
if (db_options.max_open_files != -1 || defer_sst_file_opening) {
if (db_options.max_open_files != -1) {
std::ostringstream oss;
oss << "The primary instance may delete all types of files after they "
"become obsolete. The application can coordinate the primary and "
@@ -1221,24 +1196,15 @@ Status DBImplSecondary::OpenAsSecondaryImpl(
"currently being used by the secondary. Alternatively, a custom "
"Env/FS can be provided such that files become inaccessible only "
"after all primary and secondaries indicate that they are obsolete "
"and deleted. ";
if (defer_sst_file_opening) {
oss << "With `open_files_async=true`, `max_open_files==-1` does not pin "
"all table files before OpenAsSecondary returns. ";
} else {
oss << "If the above two are not possible, you can open the secondary "
"instance with `max_open_files==-1` so that secondary will "
"eagerly keep all table files open. Even if a file is deleted, "
"its content can still be accessed via a prior open file "
"descriptor. This is a hacky workaround for only table files. ";
}
oss << "If none of the above is done, then point lookup or "
"and deleted. If the above two are not possible, you can open the "
"secondary instance with `max_open_files==-1` so that secondary "
"will eagerly keep all table files open. Even if a file is deleted, "
"its content can still be accessed via a prior open file "
"descriptor. This is a hacky workaround for only table files. If "
"none of the above is done, then point lookup or "
"range scan via the secondary instance can result in IOError: file "
"not found. TryCatchUpWithPrimary() can resolve a transient, "
"non-latched error if a newer primary version no longer references "
"the missing file. A latched asynchronous-open background error "
"requires closing and reopening the secondary after fixing the "
"underlying problem.";
"not found. This can be resolved by retrying "
"TryCatchUpWithPrimary().";
ROCKS_LOG_WARN(tmp_opts.info_log, "%s", oss.str().c_str());
}
@@ -1248,10 +1214,9 @@ Status DBImplSecondary::OpenAsSecondaryImpl(
dbname, &impl->immutable_db_options_, impl->mutable_db_options_,
impl->file_options_, impl->table_cache_.get(),
impl->write_buffer_manager_, &impl->write_controller_, impl->io_tracer_,
impl->db_id_, impl->db_session_id_, defer_sst_file_opening));
ReactiveVersionSet* reactive_versions =
static_cast_with_check<ReactiveVersionSet>(impl->versions_.get());
reactive_versions->SetTrustManifestRecovery(trust_manifest_recovery);
impl->db_id_, impl->db_session_id_));
static_cast_with_check<ReactiveVersionSet>(impl->versions_.get())
->SetTrustManifestRecovery(trust_manifest_recovery);
impl->column_family_memtables_.reset(
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
@@ -1290,11 +1255,7 @@ Status DBImplSecondary::OpenAsSecondaryImpl(
cfd->InstallSuperVersion(&sv_context, &impl->mutex_);
}
impl->PublishSecondaryReadView();
if (defer_sst_file_opening) {
impl->ScheduleAsyncFileOpening();
} else {
impl->MarkAsyncFileOpenNotNeeded();
}
impl->MarkAsyncFileOpenNotNeeded();
}
impl->mutex_.Unlock();
impl->CleanupRetiredSecondaryReadViews();
@@ -2266,9 +2227,8 @@ Status DBImplSecondary::PersistInitialCompactionProgress(
return s;
}
s = immutable_db_options_.use_fsync
? compaction_progress_writer->file()->Fsync(opts)
: compaction_progress_writer->file()->Sync(opts);
s = compaction_progress_writer->file()->Sync(opts,
immutable_db_options_.use_fsync);
return s;
}
+63 -244
View File
@@ -380,26 +380,15 @@ Status DBImpl::IngestWBWIAsMemtable(
// Switch memtable and add WBWIMemTables
Status s;
const bool defer_wbm_accounting_refresh =
immutable_db_options_.atomic_flush && cfds.size() > 1;
bool switch_attempted = false;
for (size_t i = 0; i < cfds.size(); ++i) {
WriteContext write_context;
switch_attempted = true;
// TODO: not switch on empty memtable, may need to update metadata
// like NextLogNumber(), earliest_seqno and memtable id.
if (i < memtables.size()) {
s = defer_wbm_accounting_refresh
? SwitchMemtableImpl(cfds[i], &write_context, memtables[i],
last_seqno_after_ingest,
false /* refresh_wbm_accounting */)
: SwitchMemtable(cfds[i], &write_context, memtables[i],
last_seqno_after_ingest);
s = SwitchMemtable(cfds[i], &write_context, memtables[i],
last_seqno_after_ingest);
} else {
s = defer_wbm_accounting_refresh
? SwitchMemtableImpl(cfds[i], &write_context, nullptr, 0,
false /* refresh_wbm_accounting */)
: SwitchMemtable(cfds[i], &write_context);
s = SwitchMemtable(cfds[i], &write_context);
}
if (!s.ok()) {
// SwitchMemtable() can only fail if a new WAL is to be created, this
@@ -431,9 +420,6 @@ Status DBImpl::IngestWBWIAsMemtable(
break;
}
}
if (defer_wbm_accounting_refresh && switch_attempted) {
UpdateWBMAccountingAfterMemtableSwitch(true /* refresh_now */);
}
for (size_t i = 0; i < cfds.size(); ++i) {
if (cfds[i]->UnrefAndTryDelete()) {
cfds[i] = nullptr;
@@ -1215,7 +1201,6 @@ Status DBImpl::WriteImpl(
IOStatus io_s;
Status pre_release_cb_status;
size_t seq_inc = 0;
uint64_t wal_write_number = 0;
if (status.ok()) {
if (deferred_put_entities != nullptr) {
my_batch->Clear();
@@ -1377,7 +1362,6 @@ Status DBImpl::WriteImpl(
if (!two_write_queues_) {
if (status.ok() && !write_options.disableWAL) {
assert(wal_context.wal_file_number_size);
wal_write_number = wal_context.wal_file_number_size->number;
wal_context.prev_size = wal_context.writer->file()->GetFileSize();
PERF_TIMER_GUARD(write_wal_time);
io_s = WriteGroupToWAL(write_group, wal_context.writer, wal_used,
@@ -1502,9 +1486,7 @@ Status DBImpl::WriteImpl(
if (!io_s.ok()) {
// Check WriteToWAL status
TEST_SYNC_POINT_CALLBACK("DBImpl::WriteImpl:BeforeWALIOStatusCheck", &io_s);
WALIOStatusCheck(io_s, wal_write_number, last_sequence,
last_sequence + 1 - seq_inc);
WALIOStatusCheck(io_s);
}
if (!w.CallbackFailed()) {
if (!io_s.ok()) {
@@ -2087,10 +2069,7 @@ void DBImpl::WriteStatusCheck(const Status& status) {
}
}
void DBImpl::WALIOStatusCheck(const IOStatus& io_status,
uint64_t failed_wal_number,
SequenceNumber failed_wal_sequence,
SequenceNumber failed_wal_sequence_start) {
void DBImpl::WALIOStatusCheck(const IOStatus& io_status) {
// Is setting bg_error_ enough here? This will at least stop
// compaction and fail any further writes.
if ((immutable_db_options_.paranoid_checks && !io_status.ok() &&
@@ -2098,33 +2077,8 @@ void DBImpl::WALIOStatusCheck(const IOStatus& io_status,
io_status.IsIOFenced()) {
mutex_.Lock();
// Maybe change the return status to void?
if (io_status.IsIOError() && !io_status.IsIOFenced()) {
DBRecoverContext context;
context.failed_wal_sequence = failed_wal_sequence;
context.failed_wal_sequence_start = failed_wal_sequence_start;
const uint64_t resolved_failed_wal_number =
failed_wal_number != 0 ? failed_wal_number : cur_wal_number_;
if (io_status.GetScope() == IOStatus::IOErrorScope::kIOErrorScopeFile) {
context.failed_wal_number = resolved_failed_wal_number;
if (failed_wal_sequence_start != 0 &&
failed_wal_sequence_start <= failed_wal_sequence) {
// Successful live resumption deliberately omits this write from the
// database even if the physical WAL record is complete. Keep a
// DB-lifetime fence so TransactionLogIterator does not expose it.
wal_manager_.RecordIndeterminateWriteSequence(
resolved_failed_wal_number, failed_wal_sequence_start,
failed_wal_sequence);
}
ExtendWALRecoveryCutoff(context.failed_wal_number);
}
error_handler_.SetBGError(io_status,
BackgroundErrorReason::kWriteCallback,
/*wal_related=*/true, context);
} else {
error_handler_.SetBGError(io_status,
BackgroundErrorReason::kWriteCallback,
/*wal_related=*/true);
}
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback,
/*wal_related=*/true);
mutex_.Unlock();
} else {
// Force writable file to be continue writable.
@@ -2132,16 +2086,6 @@ void DBImpl::WALIOStatusCheck(const IOStatus& io_status,
}
}
void DBImpl::ExtendWALRecoveryCutoff(uint64_t failed_wal_number) {
assert(failed_wal_number != 0);
uint64_t current = wal_recovery_cutoff_.load(std::memory_order_relaxed);
while (current < failed_wal_number &&
!wal_recovery_cutoff_.compare_exchange_weak(
current, failed_wal_number, std::memory_order_release,
std::memory_order_relaxed)) {
}
}
void DBImpl::HandleMemTableInsertFailure(const Status& status) {
assert(!status.ok());
// A non-OK status on memtable insert indicates that the state implied by the
@@ -2183,11 +2127,14 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
}
if (UNLIKELY(status.ok() && write_buffer_manager_->ShouldFlush())) {
if (write_buffer_manager_->TryAcquireLocalFlush()) {
InstrumentedMutexLock l(&mutex_);
WaitForPendingWrites();
status = HandleWriteBufferManagerFlush(write_context);
}
// Before a new memtable is added in SwitchMemtable(),
// write_buffer_manager_->ShouldFlush() will keep returning true. If another
// thread is writing to another DB with the same write buffer, they may also
// be flushed. We may end up with flushing much more DBs than needed. It's
// suboptimal but still correct.
InstrumentedMutexLock l(&mutex_);
WaitForPendingWrites();
status = HandleWriteBufferManagerFlush(write_context);
}
if (UNLIKELY(status.ok() && !trim_history_scheduler_.Empty())) {
@@ -2231,7 +2178,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
status = Status::Incomplete("Write stall");
} else {
InstrumentedMutexLock l(&mutex_);
status = WriteBufferManagerStallWrites(write_context);
WriteBufferManagerStallWrites();
}
}
InstrumentedMutexLock l(&wal_write_mutex_);
@@ -2340,13 +2287,10 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
}
IOStatus io_s = log_writer->MaybeAddUserDefinedTimestampSizeRecord(
write_options, versions_->GetColumnFamiliesTimestampSizeForRecord());
TEST_SYNC_POINT_CALLBACK(
"DBImpl::WriteToWAL:AfterMaybeAddUserDefinedTimestampSizeRecord", &io_s);
if (!io_s.ok()) {
return io_s;
}
io_s = log_writer->AddRecord(write_options, log_entry, sequence);
TEST_SYNC_POINT_CALLBACK("DBImpl::WriteToWAL:AfterAddRecord", &io_s);
if (UNLIKELY(needs_locking)) {
wal_write_mutex_.Unlock();
@@ -2435,8 +2379,7 @@ IOStatus DBImpl::WriteGroupToWAL(const WriteThread::WriteGroup& write_group,
// and closed WAL that just needs to be recorded as synced in the
// manifest.
if (auto* f = log.writer->file()) {
io_s =
immutable_db_options_.use_fsync ? f->Fsync(opts) : f->Sync(opts);
io_s = f->Sync(opts, immutable_db_options_.use_fsync);
if (!io_s.ok()) {
break;
}
@@ -2728,24 +2671,14 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
const bool defer_wbm_accounting_refresh =
immutable_db_options_.atomic_flush && cfds.size() > 1;
bool switch_attempted = false;
for (const auto cfd : cfds) {
cfd->Ref();
switch_attempted = true;
status = defer_wbm_accounting_refresh
? SwitchMemtableImpl(cfd, write_context, nullptr, 0,
false /* refresh_wbm_accounting */)
: SwitchMemtable(cfd, write_context);
status = SwitchMemtable(cfd, write_context);
cfd->UnrefAndTryDelete();
if (!status.ok()) {
break;
}
}
if (defer_wbm_accounting_refresh && switch_attempted) {
UpdateWBMAccountingAfterMemtableSwitch(true /* refresh_now */);
}
if (two_write_queues_) {
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
}
@@ -2789,17 +2722,25 @@ Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
if (immutable_db_options_.atomic_flush) {
SelectColumnFamiliesForAtomicFlush(&cfds);
} else {
const WriteBufferFlushPolicy policy = write_buffer_manager_->flush_policy();
// Cross-DB selection still flushes the largest CF within the chosen DB.
const bool flush_largest =
policy == WriteBufferFlushPolicy::kFlushLargest ||
policy == WriteBufferFlushPolicy::kFlushLargestAcrossDBs;
const bool include_waiting_immutable =
policy == WriteBufferFlushPolicy::kFlushLargestAcrossDBs;
const FlushableCFs flushable =
CollectFlushableCFs(include_waiting_immutable);
ColumnFamilyData* cfd_picked =
flush_largest ? flushable.largest : flushable.oldest;
ColumnFamilyData* cfd_picked = nullptr;
SequenceNumber seq_num_for_cf_picked = kMaxSequenceNumber;
for (auto cfd : *versions_->GetColumnFamilySet()) {
if (cfd->IsDropped()) {
continue;
}
if (!cfd->mem()->IsEmpty() && !cfd->imm()->IsFlushPendingOrRunning()) {
// We only consider flush on CFs with bytes in the mutable memtable,
// and no immutable memtables for which flush has yet to finish. If
// we triggered flush on CFs already trying to flush, we would risk
// creating too many immutable memtables leading to write stalls.
uint64_t seq = cfd->mem()->GetCreationSeq();
if (cfd_picked == nullptr || seq < seq_num_for_cf_picked) {
cfd_picked = cfd;
seq_num_for_cf_picked = seq;
}
}
}
if (cfd_picked != nullptr) {
cfds.push_back(cfd_picked);
}
@@ -2819,17 +2760,12 @@ Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
const bool defer_wbm_accounting_refresh =
immutable_db_options_.atomic_flush && cfds.size() > 1;
for (const auto cfd : cfds) {
if (cfd->mem()->IsEmpty()) {
continue;
}
cfd->Ref();
status = defer_wbm_accounting_refresh
? SwitchMemtableImpl(cfd, write_context, nullptr, 0,
false /* refresh_wbm_accounting */)
: SwitchMemtable(cfd, write_context);
status = SwitchMemtable(cfd, write_context);
cfd->UnrefAndTryDelete();
if (!status.ok()) {
break;
@@ -2858,11 +2794,6 @@ Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
GenerateFlushRequest(cfds, FlushReason::kWriteBufferManager, &flush_req);
EnqueuePendingFlush(flush_req);
}
}
if (write_buffer_manager_->ShouldTrackFlushInitiator()) {
RefreshFlushableMemAccounting();
}
if (status.ok()) {
MaybeScheduleFlushOrCompaction();
}
return status;
@@ -2983,63 +2914,25 @@ Status DBImpl::DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
// REQUIRES: mutex_ is held
// REQUIRES: this thread is currently at the front of the writer queue
Status DBImpl::WriteBufferManagerStallWrites(WriteContext* write_context) {
void DBImpl::WriteBufferManagerStallWrites() {
mutex_.AssertHeld();
for (;;) {
// First block future writer threads who want to add themselves to the
// queue of WriteThread.
write_thread_.BeginWriteStall();
const bool hid_flush_initiator =
wbm_flush_initiator_ != nullptr &&
write_buffer_manager_->ShouldTrackFlushInitiator();
if (hid_flush_initiator) {
wbm_flush_initiator_->SetFlushable(false);
}
mutex_.Unlock();
// First block future writer threads who want to add themselves to the queue
// of WriteThread.
write_thread_.BeginWriteStall();
mutex_.Unlock();
WBMStallInterface* const stall =
static_cast<WBMStallInterface*>(wbm_stall_.get());
stall->SetState(WBMStallInterface::State::BLOCKED);
write_buffer_manager_->BeginWriteStall(wbm_stall_.get());
bool retry_local_flush = false;
if (write_buffer_manager_->ShouldTrackFlushInitiator()) {
for (;;) {
if (stall->BlockFor(
write_buffer_manager_->GetLocalFlushRetryMicros())) {
break;
}
if (write_buffer_manager_->TryAcquireLocalFlush(wbm_stall_.get())) {
write_buffer_manager_->RemoveDBFromQueue(wbm_stall_.get());
retry_local_flush = true;
break;
}
}
} else {
stall->Block();
}
// Change the state to State::Blocked.
static_cast<WBMStallInterface*>(wbm_stall_.get())
->SetState(WBMStallInterface::State::BLOCKED);
// Then WriteBufferManager will add DB instance to its queue
// and block this thread by calling WBMStallInterface::Block().
write_buffer_manager_->BeginWriteStall(wbm_stall_.get());
wbm_stall_->Block();
mutex_.Lock();
// Allow this front writer to retry the manager-wide emergency lease. It
// must not let its write through while the hard limit is still exceeded.
write_thread_.EndWriteStall();
// Restore the state hidden by this stall even if the runtime policy
// changed while the writer was blocked. Otherwise switching back to the
// across-DB policy can leave this DB permanently ineligible.
if (hid_flush_initiator) {
wbm_flush_initiator_->SetFlushable(true);
if (write_buffer_manager_->ShouldTrackFlushInitiator()) {
write_buffer_manager_->NotifyFlushInitiatorChanged();
}
}
if (!retry_local_flush || !write_buffer_manager_->ShouldStall()) {
return Status::OK();
}
WaitForPendingWrites();
Status status = HandleWriteBufferManagerFlush(write_context);
if (!status.ok()) {
return status;
}
}
mutex_.Lock();
// Stall has ended. Signal writer threads so that they can add
// themselves to the WriteThread queue for writes.
write_thread_.EndWriteStall();
}
Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
@@ -3170,27 +3063,17 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
nullptr);
autovector<FlushReason> flush_reasons;
flush_reasons.reserve(cfds.size());
const bool defer_wbm_accounting_refresh =
immutable_db_options_.atomic_flush && cfds.size() > 1;
bool switch_attempted = false;
for (auto& cfd : cfds) {
FlushReason flush_reason = FlushReason::kWriteBufferFull;
if (status.ok() && !cfd->mem()->IsEmpty()) {
flush_reason = cfd->mem()->GetFlushReason();
switch_attempted = true;
status = defer_wbm_accounting_refresh
? SwitchMemtableImpl(cfd, context, nullptr, 0,
false /* refresh_wbm_accounting */)
: SwitchMemtable(cfd, context);
status = SwitchMemtable(cfd, context);
}
flush_reasons.push_back(flush_reason);
if (cfd->UnrefAndTryDelete()) {
cfd = nullptr;
}
}
if (defer_wbm_accounting_refresh && switch_attempted) {
UpdateWBMAccountingAfterMemtableSwitch(true /* refresh_now */);
}
if (two_write_queues_) {
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
@@ -3235,31 +3118,6 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
ReadOnlyMemTable* new_imm,
SequenceNumber last_seqno) {
return SwitchMemtableImpl(cfd, context, new_imm, last_seqno,
true /* refresh_wbm_accounting */);
}
void DBImpl::UpdateWBMAccountingAfterMemtableSwitch(bool refresh_now) {
mutex_.AssertHeld();
if (wbm_flush_initiator_ == nullptr ||
!write_buffer_manager_->ShouldTrackFlushInitiator()) {
return;
}
if (refresh_now) {
TEST_SYNC_POINT("DBImpl::UpdateWBMAccountingAfterMemtableSwitch:Refresh");
RefreshFlushableMemAccounting();
} else {
// Do not let the sorter consume an intermediate snapshot while a caller is
// switching a batch of memtables. The caller refreshes once after the
// complete batch, including a partial-error exit.
wbm_flush_initiator_->InvalidateLargestFlushableCFMem();
}
}
Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
ReadOnlyMemTable* new_imm,
SequenceNumber last_seqno,
bool refresh_wbm_accounting) {
mutex_.AssertHeld();
assert(lock_wal_owner_thread_id_counts_.empty());
@@ -3270,7 +3128,6 @@ Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
UnpublishedWAL prepared_wal;
MemTable* new_mem = nullptr;
IOStatus io_s;
bool current_wal_write_buffer_failed = false;
// Recoverable state is persisted in WAL. After memtable switch, WAL might
// be deleted, so we write the state to memtable to be persisted as well.
@@ -3285,9 +3142,7 @@ Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
if (two_write_queues_) {
wal_write_mutex_.Lock();
}
bool creating_new_log =
!wal_empty_ || (recovering_from_wal_write_error_ &&
cur_wal_number_ <= recover_wal_through_number_);
bool creating_new_log = !wal_empty_;
if (two_write_queues_) {
wal_write_mutex_.Unlock();
}
@@ -3418,28 +3273,13 @@ Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
InstrumentedMutexLock l(&wal_write_mutex_);
assert(new_log != nullptr);
if (!logs_.empty()) {
// Always flush the buffer of the last log before switching to a new one,
// except when recovery is replacing a file-scoped failed WAL. Its
// accepted contents are persisted by the atomic recovery flush.
// Alway flush the buffer of the last log before switching to a new one
log::Writer* cur_log_writer = logs_.back().writer;
const bool abandon_current_wal =
recovering_from_wal_write_error_ &&
cur_log_writer->get_log_number() <= recover_wal_through_number_;
if (abandon_current_wal) {
TEST_SYNC_POINT("DBImpl::SwitchMemtable:AbandonWAL");
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"[WAL recovery] [%s] Replacing failed WAL #%" PRIu64
" with WAL #%" PRIu64,
cfd->GetName().c_str(), cur_log_writer->get_log_number(),
new_log_number);
} else if (error_handler_.IsRecoveryInProgress()) {
if (error_handler_.IsRecoveryInProgress()) {
// In recovery path, we force another try of writing WAL buffer.
cur_log_writer->file()->reset_seen_error();
}
if (!abandon_current_wal) {
io_s = cur_log_writer->WriteBuffer(write_options);
current_wal_write_buffer_failed = !io_s.ok();
}
io_s = cur_log_writer->WriteBuffer(write_options);
if (s.ok()) {
s = io_s;
}
@@ -3466,35 +3306,16 @@ Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
delete new_mem;
delete new_log;
context->superversion_context.new_superversion.reset();
// Only a WriteBuffer() failure belongs to the current WAL. CreateWAL() and
// StartWALFile() operate on an unpublished replacement, so a failure there
// must not quarantine an otherwise healthy current WAL.
// We may have lost data from the WritableFileBuffer in-memory buffer for
// the current log, so treat it as a fatal error and set bg_error
if (!io_s.ok()) {
if (!current_wal_write_buffer_failed) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Failed to create or start replacement WAL #%" PRIu64
"; current WAL #%" PRIu64 " remains active: %s",
new_log_number, cur_wal_number_,
io_s.ToString().c_str());
}
if (current_wal_write_buffer_failed && io_s.IsIOError() &&
!io_s.IsIOFenced() &&
io_s.GetScope() == IOStatus::IOErrorScope::kIOErrorScopeFile) {
DBRecoverContext recovery_context;
recovery_context.failed_wal_number = cur_wal_number_;
ExtendWALRecoveryCutoff(recovery_context.failed_wal_number);
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable,
/*wal_related=*/true, recovery_context);
} else {
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable,
/*wal_related=*/true);
}
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable,
/*wal_related=*/true);
} else {
error_handler_.SetBGError(s, BackgroundErrorReason::kMemTable);
}
// Read back bg_error in order to get the right severity
s = error_handler_.GetBGError();
UpdateWBMAccountingAfterMemtableSwitch(refresh_wbm_accounting);
return s;
}
@@ -3536,7 +3357,6 @@ Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
BackgroundErrorReason::kManifestWrite);
}
if (!s.ok()) {
UpdateWBMAccountingAfterMemtableSwitch(refresh_wbm_accounting);
return s;
}
@@ -3589,7 +3409,6 @@ Status DBImpl::SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
}
new_mem->Ref();
cfd->SetMemtable(new_mem);
UpdateWBMAccountingAfterMemtableSwitch(refresh_wbm_accounting);
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context);
MaybeScheduleAsyncWALPrecreate(preallocate_block_size);
+4 -8
View File
@@ -140,8 +140,7 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
ImmutableOptions ioptions(options);
WriteBufferManager wb(options.db_write_buffer_size);
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
// Write some keys and make sure it returns false on duplicates
ASSERT_OK(
@@ -189,8 +188,7 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
new TestPrefixExtractor()); // which uses _ to extract the prefix
ioptions = ImmutableOptions(options);
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
// Insert a duplicate key with _ in it
ASSERT_OK(
mem->Add(seq, kTypeValue, "key_1", "value", nullptr /* kv_prot_info */));
@@ -203,8 +201,7 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
options.allow_concurrent_memtable_write = true;
ioptions = ImmutableOptions(options);
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
MemTablePostProcessInfo post_process_info;
ASSERT_OK(mem->Add(seq, kTypeValue, "key", "value",
nullptr /* kv_prot_info */, true, &post_process_info));
@@ -237,8 +234,7 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
ImmutableOptions ioptions(options);
WriteBufferManager wb(options.db_write_buffer_size);
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
// Put 0 as the base
PutFixed64(&value, static_cast<uint64_t>(0));
+2 -384
View File
@@ -7,7 +7,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
@@ -878,336 +877,6 @@ TEST_F(DBSecondaryTest, OpenAsSecondary) {
verify_db_func("new_foo_value", "new_bar_value");
}
TEST_F(DBSecondaryTest, OpenFilesAsync) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
for (int i = 0; i < 3; ++i) {
ASSERT_OK(Put("key", "value" + std::to_string(i)));
ASSERT_OK(Flush());
}
Options secondary_options = options;
secondary_options.max_open_files = -1;
secondary_options.open_files_async = true;
secondary_options.skip_stats_update_on_db_open = true;
secondary_options.statistics = CreateDBStatistics();
std::atomic<uint32_t> file_opens = 0;
SyncPoint::GetInstance()->SetCallBack(
"VersionBuilder::Rep::LoadTableHandlers::BeforeFindTable",
[&](void* /*arg*/) { ++file_opens; });
SyncPoint::GetInstance()->LoadDependency(
{{"DBSecondaryTest::OpenFilesAsync:Release",
"DBImpl::BGWorkAsyncFileOpen::Start"},
{"DBImpl::BGWorkAsyncFileOpen:Done",
"DBSecondaryTest::OpenFilesAsync:Done"}});
SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_point([] {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
});
OpenSecondary(secondary_options);
ASSERT_EQ(0, file_opens.load());
ASSERT_EQ(0, TestGetTickerCount(secondary_options, NO_FILE_OPENS));
TEST_SYNC_POINT("DBSecondaryTest::OpenFilesAsync:Release");
TEST_SYNC_POINT("DBSecondaryTest::OpenFilesAsync:Done");
ASSERT_EQ(3, file_opens.load());
ASSERT_EQ(3, TestGetTickerCount(secondary_options, NO_FILE_OPENS));
VerifySecondaryValue("key", "value2");
const uint32_t initial_file_opens = file_opens.load();
const uint64_t initial_no_file_opens =
TestGetTickerCount(secondary_options, NO_FILE_OPENS);
ASSERT_OK(Put("key", "value3"));
ASSERT_OK(Flush());
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
ASSERT_GT(file_opens.load(), initial_file_opens);
ASSERT_GT(TestGetTickerCount(secondary_options, NO_FILE_OPENS),
initial_no_file_opens);
VerifySecondaryValue("key", "value3");
}
TEST_F(DBSecondaryTest, OpenFilesAsyncRequiresSkipStatsUpdate) {
Options options = CurrentOptions();
options.open_files_async = true;
options.skip_stats_update_on_db_open = false;
ASSERT_TRUE(TryOpenSecondary(options).IsInvalidArgument());
}
TEST_F(DBSecondaryTest, OpenFilesAsyncRejectsFIFOCompaction) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleFIFO;
options.open_files_async = true;
options.skip_stats_update_on_db_open = true;
const Status s = TryOpenSecondary(options);
ASSERT_TRUE(s.IsNotSupported());
ASSERT_NE(std::string::npos, s.ToString().find("FIFO compaction"));
}
TEST_F(DBSecondaryTest, OpenFilesAsyncQueuedJobCanceledDuringShutdown) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
ASSERT_OK(Put("key", "value"));
ASSERT_OK(Flush());
Options secondary_options = options;
secondary_options.max_open_files = -1;
secondary_options.open_files_async = true;
secondary_options.skip_stats_update_on_db_open = true;
const int original_high_threads =
env_->GetBackgroundThreads(Env::Priority::HIGH);
Defer restore_high_threads([&] {
env_->SetBackgroundThreads(original_high_threads, Env::Priority::HIGH);
});
std::atomic<uint32_t> async_open_cancellations = 0;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::UnscheduleAsyncFileOpenCallback",
[&](void* /*arg*/) { ++async_open_cancellations; });
SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_point([] {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
});
for (int mode = 0; mode < 2; ++mode) {
const bool cancel_directly = mode == 1;
SCOPED_TRACE(cancel_directly ? "CancelAllBackgroundWork" : "Close");
env_->SetBackgroundThreads(1, Env::Priority::HIGH);
test::SleepingBackgroundTask sleeping_task;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
Env::Priority::HIGH);
sleeping_task.WaitUntilSleeping();
OpenSecondary(secondary_options);
ASSERT_EQ(1U, env_->GetThreadPoolQueueLen(Env::Priority::HIGH));
env_->SetBackgroundThreads(0, Env::Priority::HIGH);
sleeping_task.WakeUp();
sleeping_task.WaitUntilDone();
std::mutex close_mutex;
std::condition_variable close_cv;
bool close_done = false;
Status close_status;
port::Thread close_thread([&] {
if (cancel_directly) {
CancelAllBackgroundWork(db_secondary_.get(), /*wait=*/true);
} else {
close_status = db_secondary_->Close();
}
{
std::lock_guard<std::mutex> lock(close_mutex);
close_done = true;
}
close_cv.notify_all();
});
bool closed_without_worker;
{
std::unique_lock<std::mutex> lock(close_mutex);
closed_without_worker = close_cv.wait_for(lock, std::chrono::seconds(10),
[&] { return close_done; });
}
if (!closed_without_worker) {
// Let an uncanceled opener drain so a regression fails without hanging
// the test process.
env_->SetBackgroundThreads(1, Env::Priority::HIGH);
}
close_thread.join();
ASSERT_TRUE(closed_without_worker);
ASSERT_OK(close_status);
ASSERT_EQ(static_cast<uint32_t>(mode + 1), async_open_cancellations.load());
if (cancel_directly) {
CloseSecondary();
} else {
db_secondary_.reset();
}
}
}
TEST_F(DBSecondaryTest, OpenFilesAsyncDetectsFileSizeMismatch) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
ASSERT_OK(Put("key", "value"));
ASSERT_OK(Flush());
const std::string table_path =
GetNewestTableFilePath(kDefaultColumnFamilyName);
uint64_t table_size = 0;
ASSERT_OK(env_->GetFileSize(table_path, &table_size));
Close();
Options secondary_options = options;
secondary_options.max_open_files = -1;
secondary_options.open_files_async = true;
secondary_options.skip_stats_update_on_db_open = true;
SyncPoint::GetInstance()->LoadDependency(
{{"DBSecondaryTest::OpenFilesAsyncDetectsFileSizeMismatch:Release",
"DBImpl::BGWorkAsyncFileOpen::Start"},
{"DBImpl::BGWorkAsyncFileOpen:Done",
"DBSecondaryTest::OpenFilesAsyncDetectsFileSizeMismatch:Done"}});
SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_point([] {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
});
OpenSecondary(secondary_options);
std::unique_ptr<WritableFile> table_file;
ASSERT_OK(env_->ReopenWritableFile(table_path, &table_file, EnvOptions()));
ASSERT_OK(table_file->Truncate(table_size + 1));
ASSERT_OK(table_file->Close());
TEST_SYNC_POINT(
"DBSecondaryTest::OpenFilesAsyncDetectsFileSizeMismatch:Release");
TEST_SYNC_POINT(
"DBSecondaryTest::OpenFilesAsyncDetectsFileSizeMismatch:Done");
const Status bg_error = db_secondary_full()->TEST_GetBGError();
ASSERT_TRUE(bg_error.IsCorruption());
ASSERT_NE(std::string::npos,
bg_error.ToString().find("Sst file size mismatch"));
}
TEST_F(DBSecondaryTest, OpenFilesAsyncStopsOpeningDuringShutdown) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
for (int i = 0; i < 3; ++i) {
ASSERT_OK(Put("key", "value" + std::to_string(i)));
ASSERT_OK(Flush());
}
Options secondary_options = options;
secondary_options.max_open_files = -1;
secondary_options.max_file_opening_threads = 1;
secondary_options.open_files_async = true;
secondary_options.skip_stats_update_on_db_open = true;
std::mutex state_mutex;
std::condition_variable state_cv;
uint32_t file_opens = 0;
bool allow_async_open = false;
bool close_waiting = false;
SyncPoint::GetInstance()->SetCallBack(
"VersionBuilder::Rep::LoadTableHandlers::BeforeFindTable",
[&](void* /*arg*/) {
std::unique_lock<std::mutex> lock(state_mutex);
++file_opens;
if (file_opens == 1) {
state_cv.notify_all();
state_cv.wait(lock, [&] { return allow_async_open; });
}
});
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::~DBImpl:WaitJob", [&](void* /*arg*/) {
std::lock_guard<std::mutex> lock(state_mutex);
close_waiting = true;
state_cv.notify_all();
});
SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_point([] {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
});
OpenSecondary(secondary_options);
{
std::unique_lock<std::mutex> lock(state_mutex);
state_cv.wait(lock, [&] { return file_opens == 1; });
}
Status close_status;
port::Thread close_thread([&] { close_status = db_secondary_->Close(); });
{
std::unique_lock<std::mutex> lock(state_mutex);
state_cv.wait(lock, [&] { return close_waiting; });
allow_async_open = true;
state_cv.notify_all();
}
close_thread.join();
ASSERT_OK(close_status);
ASSERT_EQ(1, file_opens);
db_secondary_.reset();
}
TEST_F(DBSecondaryTest, OpenFilesAsyncErrorDoesNotStartRecovery) {
class CountingListener final : public EventListener {
public:
void OnBackgroundError(BackgroundErrorReason /*reason*/,
Status* bg_error) override {
*bg_error = Status::IOError("listener-adjusted file-open error");
++background_errors;
}
void OnErrorRecoveryBegin(BackgroundErrorReason /*reason*/, Status bg_error,
bool* /*auto_recovery*/) override {
bg_error.PermitUncheckedError();
++recovery_begins;
}
std::atomic<uint32_t> background_errors{0};
std::atomic<uint32_t> recovery_begins{0};
};
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
ASSERT_OK(Put("key", "value"));
ASSERT_OK(Flush());
auto fault_fs = std::make_shared<FaultInjectionTestFS>(env_->GetFileSystem());
std::unique_ptr<Env> fault_env(new CompositeEnvWrapper(env_, fault_fs));
Options secondary_options = options;
secondary_options.env = fault_env.get();
secondary_options.max_open_files = -1;
secondary_options.open_files_async = true;
secondary_options.skip_stats_update_on_db_open = true;
auto listener = std::make_shared<CountingListener>();
secondary_options.listeners.emplace_back(listener);
SyncPoint::GetInstance()->LoadDependency(
{{"DBSecondaryTest::OpenFilesAsyncErrorDoesNotStartRecovery:Release",
"DBImpl::BGWorkAsyncFileOpen::Start"},
{"DBImpl::BGWorkAsyncFileOpen:Done",
"DBSecondaryTest::OpenFilesAsyncErrorDoesNotStartRecovery:Done"}});
SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup([&] {
fault_fs->SetFilesystemActive(true);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
});
OpenSecondary(secondary_options);
IOStatus error = IOStatus::IOError("injected retryable file-open error");
error.SetRetryable(true);
fault_fs->SetFilesystemActive(false, error);
TEST_SYNC_POINT(
"DBSecondaryTest::OpenFilesAsyncErrorDoesNotStartRecovery:Release");
TEST_SYNC_POINT(
"DBSecondaryTest::OpenFilesAsyncErrorDoesNotStartRecovery:Done");
const Status bg_error = db_secondary_full()->TEST_GetBGError();
ASSERT_OK(bg_error);
ASSERT_FALSE(db_secondary_full()->TEST_IsRecoveryInProgress());
ASSERT_EQ(1, listener->background_errors.load());
ASSERT_EQ(0, listener->recovery_begins.load());
std::string value;
ASSERT_TRUE(db_secondary_->Get(ReadOptions(), "key", &value).IsIOError());
ASSERT_OK(db_secondary_->Resume());
fault_fs->SetFilesystemActive(true);
VerifySecondaryValue("key", "value");
CloseSecondary();
}
TEST_F(DBSecondaryTest, OptionsOverrideTest) {
Options options;
options.env = env_;
@@ -1344,15 +1013,7 @@ TEST_F(DBSecondaryTest, SecondaryCloseFiles) {
options1.env = traced_env.get();
OpenSecondary(options1);
// `db_secondary_` holds `traced_env` as its Env and touches it during close
// (CloseHelper unschedules background work through env_). traced_env is a
// local here, but db_secondary_ is a fixture member torn down later in
// ~DBSecondaryTestBase; an early return from a failed ASSERT below would free
// traced_env first and leave that later close calling into a destroyed Env.
// Close the secondary now, before traced_env is destroyed, on every path.
Defer close_secondary([this]() { CloseSecondary(); });
const auto verify_db = [&]() {
static const auto verify_db = [&]() {
std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
std::unique_ptr<Iterator> iter2(db_secondary_->NewIterator(ReadOptions()));
for (iter1->SeekToFirst(), iter2->SeekToFirst();
@@ -1384,6 +1045,7 @@ TEST_F(DBSecondaryTest, SecondaryCloseFiles) {
Status s = db_secondary_->SetDBOptions({{"max_open_files", "-1"}});
ASSERT_TRUE(s.IsNotSupported());
CloseSecondary();
}
TEST_F(DBSecondaryTest, OpenAsSecondaryWALTailing) {
@@ -2554,50 +2216,6 @@ TEST_F(DBSecondaryTest, PrimaryDropColumnFamily) {
ASSERT_EQ("foo_val_1", iterators[1]->value().ToString());
}
TEST_F(DBSecondaryTest, OpenFilesAsyncPrimaryDropColumnFamily) {
Options options;
options.env = env_;
options.disable_auto_compactions = true;
const std::string kCfName = "pikachu";
CreateAndReopenWithCF({kCfName}, options);
ASSERT_OK(Put(1 /*cf*/, "foo", "foo_val"));
ASSERT_OK(Flush(1 /*cf*/));
const std::string table_path = GetNewestTableFilePath(kCfName);
Options secondary_options = options;
secondary_options.max_open_files = -1;
secondary_options.open_files_async = true;
secondary_options.skip_stats_update_on_db_open = true;
SyncPoint::GetInstance()->LoadDependency(
{{"DBSecondaryTest::OpenFilesAsyncPrimaryDropColumnFamily:Release",
"DBImpl::BGWorkAsyncFileOpen::Start"},
{"DBImpl::BGWorkAsyncFileOpen:Done",
"DBSecondaryTest::OpenFilesAsyncPrimaryDropColumnFamily:Done"}});
SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_point([] {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
});
OpenSecondaryWithColumnFamilies({kCfName}, secondary_options);
ASSERT_EQ(2, handles_secondary_.size());
ASSERT_OK(db_->DisableFileDeletions());
ASSERT_OK(db_->DropColumnFamily(handles_[1]));
ASSERT_OK(db_->DestroyColumnFamilyHandle(handles_[1]));
handles_[1] = nullptr;
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
TEST_SYNC_POINT(
"DBSecondaryTest::OpenFilesAsyncPrimaryDropColumnFamily:Release");
TEST_SYNC_POINT(
"DBSecondaryTest::OpenFilesAsyncPrimaryDropColumnFamily:Done");
ASSERT_OK(db_->EnableFileDeletions());
dbfull()->TEST_DeleteObsoleteFiles();
ASSERT_OK(dbfull()->TEST_WaitForPurge());
ASSERT_TRUE(env_->FileExists(table_path).IsNotFound());
VerifySecondaryValue(handles_secondary_[1], "foo", "foo_val");
}
TEST_F(DBSecondaryTest, ReclaimsRetiredReadViewAfterLastReader) {
Options options;
options.env = env_;
+5 -81
View File
@@ -1183,43 +1183,10 @@ TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
}
TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
Destroy(last_options_);
int bg_delete_file = 0;
int preexisting_trash_files_scheduled = 0;
uint64_t preexisting_trash_penalty = 0;
std::string file_being_deleted;
std::string preexisting_trash_file;
std::vector<uint64_t> penalties;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteTrashFile:DeleteFile",
[&](void* /*arg*/) { bg_delete_file++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteTrashFile::cb",
[&](void* arg) { file_being_deleted = *static_cast<std::string*>(arg); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::BackgroundEmptyTrash:Wait", [&](void* arg) {
uint64_t penalty = *(static_cast<uint64_t*>(arg));
penalties.push_back(penalty);
if (file_being_deleted == preexisting_trash_file) {
preexisting_trash_penalty = penalty;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"SstFileManagerImpl::ScheduleExistingTrashFileDeletion", [&](void* arg) {
const std::string& file_path = *static_cast<std::string*>(arg);
if (EndsWith(file_path, ".trash")) {
preexisting_trash_files_scheduled++;
}
});
std::atomic<bool> fail_preexisting_trash_delete{true};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteTrashFile:BeforeDeleteFile", [&](void* arg) {
if (file_being_deleted == preexisting_trash_file &&
fail_preexisting_trash_delete.exchange(false)) {
*static_cast<Status*>(arg) = Status::IOError("injected delete error");
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Status s;
@@ -1229,7 +1196,7 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
options.sst_file_manager.reset(
NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
ASSERT_OK(s);
Reopen(options);
DestroyAndReopen(options);
// Create 4 files in L0
for (int i = 0; i < 4; i++) {
@@ -1244,70 +1211,27 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
int num_sst_files = 0;
int num_wal_files = 0;
std::string sst_file_to_mark_trash;
std::vector<std::string> db_files;
ASSERT_OK(env_->GetChildren(dbname_, &db_files));
for (const std::string& f : db_files) {
if (f.substr(f.find_last_of('.') + 1) == "sst") {
num_sst_files++;
if (sst_file_to_mark_trash.empty()) {
sst_file_to_mark_trash = dbname_;
sst_file_to_mark_trash.append("/").append(f);
}
} else if (f.substr(f.find_last_of('.') + 1) == "log") {
num_wal_files++;
} else {
continue;
}
}
ASSERT_GT(num_sst_files, 0);
ASSERT_GT(num_wal_files, 0);
ASSERT_FALSE(sst_file_to_mark_trash.empty());
uint64_t preexisting_trash_file_size = 0;
ASSERT_OK(
env_->GetFileSize(sst_file_to_mark_trash, &preexisting_trash_file_size));
preexisting_trash_file = sst_file_to_mark_trash;
preexisting_trash_file.append(".trash");
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
const int64_t rate_bytes_per_sec = 1024 * 1024;
sfm->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
sfm->SetDeleteRateBytesPerSecond(1024 * 1024);
// Set an extra high trash ratio to prevent immediate/non-rate limited
// deletions
sfm->delete_scheduler()->SetMaxTrashDBRatio(1000.0);
ASSERT_OK(
sfm->OnAddFile(sst_file_to_mark_trash, preexisting_trash_file_size));
ASSERT_OK(sfm->ScheduleFileDeletion(sst_file_to_mark_trash, dbname_,
/*force_bg=*/true));
sfm->WaitForEmptyTrashInDirectory(dbname_);
ASSERT_FALSE(fail_preexisting_trash_delete);
ASSERT_OK(env_->FileExists(preexisting_trash_file));
ASSERT_EQ(sfm->GetTotalSize(), preexisting_trash_file_size);
ASSERT_EQ(sfm->GetTotalTrashSize(), preexisting_trash_file_size);
const std::string unaccounted_trash_file = dbname_ + "/unaccounted.sst.trash";
ASSERT_OK(WriteStringToFile(env_, "trash", unaccounted_trash_file,
/*should_sync=*/false));
std::string dbname_alias = dbname_;
dbname_alias.insert(dbname_alias.find_last_of('/'), "/");
ASSERT_OK(DestroyDB(dbname_alias, options));
ASSERT_OK(DestroyDB(dbname_, options));
sfm->WaitForEmptyTrash();
ASSERT_EQ(bg_delete_file, num_sst_files + num_wal_files + 2);
ASSERT_EQ(preexisting_trash_files_scheduled, 2);
ASSERT_GE(preexisting_trash_penalty,
preexisting_trash_file_size * 1000000 / rate_bytes_per_sec);
ASSERT_EQ(penalties.size(), num_sst_files + num_wal_files + 2);
ASSERT_TRUE(sfm->GetTrackedFiles().empty());
ASSERT_EQ(sfm->GetTotalSize(), 0);
ASSERT_EQ(sfm->GetTotalTrashSize(), 0);
auto bg_errors = sfm->delete_scheduler()->GetBackgroundErrors();
ASSERT_EQ(bg_errors.size(), 1);
for (auto& error : bg_errors) {
ASSERT_NOK(error.second);
}
ASSERT_TRUE(env_->FileExists(dbname_).IsNotFound());
ASSERT_EQ(bg_delete_file, num_sst_files + num_wal_files);
}
TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) {
@@ -1748,7 +1672,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
// it. Should remove the workaround after we propagate the property on
// compaction.
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"FlushJob::WriteLevel0Table:oldest_ancestor_time", [&](void* arg) {
"FlushJob::WriteLevel0Table:oldest_ancester_time", [&](void* arg) {
uint64_t* current_time = static_cast<uint64_t*>(arg);
*current_time = 0;
});
-17
View File
@@ -897,23 +897,6 @@ TEST_F(DBTablePropertiesTest, LsmInfoAtCreationPacking) {
EXPECT_TRUE(decoded.is_bottommost);
}
TEST_F(DBTablePropertiesTest, CreationTimeAliasesOldestAncestorTime) {
TableProperties props;
ASSERT_EQ(&props.creation_time, &props.oldest_ancestor_time);
props.creation_time = 12345;
ASSERT_EQ(props.oldest_ancestor_time, 12345U);
ASSERT_EQ(&TablePropertiesNames::kCreationTime,
&TablePropertiesNames::kOldestAncestorTime);
}
TEST_F(DBTablePropertiesTest, SstFileMetaDataOldestAncesterTimeAlias) {
// Intentionally uses the deprecated misspelled alias
LiveFileMetaData meta;
ASSERT_EQ(&meta.oldest_ancester_time, &meta.oldest_ancestor_time);
meta.oldest_ancester_time = 12345;
ASSERT_EQ(meta.oldest_ancestor_time, 12345U);
}
TEST_F(DBTablePropertiesTest, UncompressedDataSizeAndLsmInfoPersisted) {
Options options = CurrentOptions();
options.compression = kNoCompression;
+4 -4
View File
@@ -1334,16 +1334,16 @@ void CheckColumnFamilyMeta(
file_meta_from_files.largest.user_key().ToString());
ASSERT_EQ(file_meta_from_cf.oldest_blob_file_number,
file_meta_from_files.oldest_blob_file_number);
ASSERT_EQ(file_meta_from_cf.oldest_ancestor_time,
file_meta_from_files.oldest_ancestor_time);
ASSERT_EQ(file_meta_from_cf.oldest_ancester_time,
file_meta_from_files.oldest_ancester_time);
ASSERT_EQ(file_meta_from_cf.file_creation_time,
file_meta_from_files.file_creation_time);
ASSERT_GE(file_meta_from_cf.file_creation_time, start_time);
ASSERT_LE(file_meta_from_cf.file_creation_time, end_time);
ASSERT_EQ(file_meta_from_cf.epoch_number,
file_meta_from_files.epoch_number);
ASSERT_GE(file_meta_from_cf.oldest_ancestor_time, start_time);
ASSERT_LE(file_meta_from_cf.oldest_ancestor_time, end_time);
ASSERT_GE(file_meta_from_cf.oldest_ancester_time, start_time);
ASSERT_LE(file_meta_from_cf.oldest_ancester_time, end_time);
// More from FileStorageInfo
ASSERT_EQ(file_meta_from_cf.file_type, kTableFile);
ASSERT_EQ(file_meta_from_cf.name,
+28 -41
View File
@@ -991,12 +991,35 @@ std::vector<std::string> DBTestBase::MultiGet(const std::vector<std::string>& k,
bool async,
bool optimize_multiget_for_io,
bool use_coroutine) {
std::vector<Status> statuses;
std::vector<PinnableSlice> pin_values;
MultiGet(k, snapshot, async, optimize_multiget_for_io, use_coroutine,
pin_values, statuses);
ReadOptions options;
options.verify_checksums = true;
options.snapshot = snapshot;
options.async_io = async;
options.optimize_multiget_for_io = optimize_multiget_for_io;
std::vector<Slice> keys;
std::vector<std::string> result(k.size());
std::vector<Status> statuses(k.size());
std::vector<PinnableSlice> pin_values(k.size());
for (size_t i = 0; i < k.size(); ++i) {
keys.emplace_back(k[i]);
}
#if USE_COROUTINES
if (use_coroutine) {
std::vector<ColumnFamilyHandle*> cfs(keys.size(),
dbfull()->DefaultColumnFamily());
folly::coro::blockingWait(CoroDB::CoMultiGet(
dbfull(), options, keys.size(), cfs.data(), keys.data(),
pin_values.data(), /*timestamps=*/nullptr, statuses.data(),
/*sorted_input=*/false));
} else
#else
(void)use_coroutine;
#endif // USE_COROUTINES
{
db_->MultiGet(options, dbfull()->DefaultColumnFamily(), keys.size(),
keys.data(), pin_values.data(), statuses.data());
}
for (size_t i = 0; i < statuses.size(); ++i) {
if (statuses[i].IsNotFound()) {
result[i] = "NOT_FOUND";
@@ -1012,42 +1035,6 @@ std::vector<std::string> DBTestBase::MultiGet(const std::vector<std::string>& k,
return result;
}
void DBTestBase::MultiGet(const std::vector<std::string>& k,
const Snapshot* snapshot, bool async,
bool optimize_multiget_for_io, bool use_coroutine,
std::vector<PinnableSlice>& values,
std::vector<Status>& statuses) {
ReadOptions options;
options.verify_checksums = true;
options.snapshot = snapshot;
options.async_io = async;
options.optimize_multiget_for_io = optimize_multiget_for_io;
std::vector<Slice> keys;
values.clear();
statuses.resize(k.size());
values.resize(k.size());
for (size_t i = 0; i < k.size(); ++i) {
keys.emplace_back(k[i]);
}
#if USE_COROUTINES
if (use_coroutine) {
std::vector<ColumnFamilyHandle*> cfs(keys.size(),
dbfull()->DefaultColumnFamily());
folly::coro::blockingWait(CoroDB::CoMultiGet(
dbfull(), options, keys.size(), cfs.data(), keys.data(), values.data(),
/*timestamps=*/nullptr, statuses.data(),
/*sorted_input=*/false));
} else
#else
(void)use_coroutine;
#endif // USE_COROUTINES
{
db_->MultiGet(options, dbfull()->DefaultColumnFamily(), keys.size(),
keys.data(), values.data(), statuses.data());
}
}
Status DBTestBase::Get(const std::string& k, PinnableSlice* v,
bool use_coroutine) {
ReadOptions options;
-5
View File
@@ -1253,11 +1253,6 @@ class DBTestBase : public testing::Test {
bool optimize_multiget_for_io = true,
bool use_coroutine = false);
void MultiGet(const std::vector<std::string>& k, const Snapshot* snapshot,
bool async, bool optimize_multiget_for_io, bool use_coroutine,
std::vector<PinnableSlice>& values,
std::vector<Status>& statuses);
Status CompactRange(const CompactRangeOptions& options,
std::optional<Slice> begin, std::optional<Slice> end);
File diff suppressed because it is too large. Load diff
-155
View File
@@ -1075,161 +1075,6 @@ INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
DBTestBase::kConcurrentWALWrites,
DBTestBase::kPipelinedWrite));
// A non-blocking unbatched join must refuse an active write stall.
TEST(WriteThreadTest, EnterUnbatchedNonBlockingRefusesDuringStall) {
Options options;
ImmutableDBOptions db_options(options);
WriteThread write_thread(db_options);
InstrumentedMutex mu;
// No stall: joining succeeds and must be paired with ExitUnbatched().
{
WriteThread::Writer w;
InstrumentedMutexLock l(&mu);
ASSERT_TRUE(write_thread.EnterUnbatchedNonBlocking(&w, &mu));
ASSERT_OK(w.status);
EXPECT_FALSE(w.no_slowdown);
EXPECT_FALSE(w.non_blocking_join);
write_thread.ExitUnbatched(&w);
}
// Stalled: refuses rather than parking on stall_cv_. Reaching the assertion
// at all is the point -- EnterUnbatched() would never have returned.
{
InstrumentedMutexLock l(&mu);
write_thread.BeginWriteStall();
ASSERT_NE(0, write_thread.GetBegunCountOfOutstandingStall());
WriteThread::Writer w;
ASSERT_FALSE(write_thread.EnterUnbatchedNonBlocking(&w, &mu));
ASSERT_TRUE(w.status.IsIncomplete());
EXPECT_FALSE(w.no_slowdown);
EXPECT_FALSE(w.non_blocking_join);
// Not linked, so ExitUnbatched() must not be called; ending the stall is
// enough to leave the queue clean.
write_thread.EndWriteStall();
ASSERT_EQ(0, write_thread.GetBegunCountOfOutstandingStall());
}
{
WriteThread::Writer w;
InstrumentedMutexLock l(&mu);
ASSERT_TRUE(write_thread.EnterUnbatchedNonBlocking(&w, &mu));
write_thread.ExitUnbatched(&w);
}
}
TEST(WriteThreadTest, EnterUnbatchedNonBlockingFinishesAfterLeadershipStall) {
Options options;
ImmutableDBOptions db_options(options);
WriteThread write_thread(db_options);
InstrumentedMutex mu;
InstrumentedMutex signal_mu;
InstrumentedCondVar signal_cv(&signal_mu);
bool before_lock = false;
bool release_joiner = false;
bool entered = true;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"WriteThread::EnterUnbatchedNonBlocking:BeforeLock", [&](void*) {
InstrumentedMutexLock l(&signal_mu);
before_lock = true;
signal_cv.SignalAll();
while (!release_joiner) {
signal_cv.Wait();
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_points([] {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
});
port::Thread joiner([&] {
WriteThread::Writer w;
InstrumentedMutexLock l(&mu);
entered = write_thread.EnterUnbatchedNonBlocking(&w, &mu);
if (entered) {
write_thread.ExitUnbatched(&w);
}
});
{
InstrumentedMutexLock l(&signal_mu);
while (!before_lock) {
signal_cv.Wait();
}
}
{
InstrumentedMutexLock l(&mu);
write_thread.BeginWriteStall();
}
{
InstrumentedMutexLock l(&signal_mu);
release_joiner = true;
signal_cv.SignalAll();
}
joiner.join();
EXPECT_TRUE(entered);
{
InstrumentedMutexLock l(&mu);
write_thread.EndWriteStall();
}
}
// A WBM join queues behind an ordinary leader so sustained write traffic
// cannot starve the memtable switch.
TEST(WriteThreadTest, EnterUnbatchedNonBlockingQueuesBehindOrdinaryLeader) {
Options options;
ImmutableDBOptions db_options(options);
WriteThread write_thread(db_options);
InstrumentedMutex mu;
InstrumentedMutex signal_mu;
InstrumentedCondVar signal_cv(&signal_mu);
bool queued = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"WriteThread::EnterUnbatched:Wait", [&](void*) {
InstrumentedMutexLock l(&signal_mu);
queued = true;
signal_cv.SignalAll();
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Defer cleanup_sync_points([] {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
});
WriteThread::Writer leader;
mu.Lock();
write_thread.EnterUnbatched(&leader, &mu);
mu.Unlock();
bool entered = false;
port::Thread joiner([&] {
WriteThread::Writer follower;
InstrumentedMutexLock l(&mu);
entered = write_thread.EnterUnbatchedNonBlocking(&follower, &mu);
if (entered) {
write_thread.ExitUnbatched(&follower);
}
});
{
InstrumentedMutexLock l(&signal_mu);
while (!queued) {
signal_cv.Wait();
}
}
mu.Lock();
write_thread.ExitUnbatched(&leader);
mu.Unlock();
joiner.join();
EXPECT_TRUE(entered);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+15 -113
View File
@@ -5,8 +5,6 @@
//
#include "db/error_handler.h"
#include <cinttypes>
#include "db/db_impl/db_impl.h"
#include "db/event_helpers.h"
#include "file/sst_file_manager_impl.h"
@@ -295,9 +293,7 @@ void ErrorHandler::CancelErrorRecoveryForShutDown() {
// also track the error separately in recovery_error_ so we can tell in the
// end whether recovery succeeded or not
void ErrorHandler::HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason,
const DBRecoverContext& context,
bool allow_auto_recovery) {
BackgroundErrorReason reason) {
db_mutex_->AssertHeld();
if (bg_err.ok()) {
return;
@@ -306,6 +302,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
bool paranoid = db_options_.paranoid_checks;
Status::Severity sev = Status::Severity::kFatalError;
Status new_bg_err;
DBRecoverContext context;
bool found = false;
{
@@ -341,7 +338,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
recovery_error_ = status_to_io_status(Status(new_bg_err));
}
bool auto_recovery = allow_auto_recovery && auto_recovery_;
bool auto_recovery = auto_recovery_;
if (new_bg_err.severity() >= Status::Severity::kFatalError && auto_recovery) {
auto_recovery = false;
}
@@ -356,13 +353,9 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
Status s = new_bg_err;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason, &s,
db_mutex_, &auto_recovery);
if (!s.ok() && s.severity() > bg_error_.severity()) {
if (!s.ok() && (s.severity() > bg_error_.severity())) {
bg_error_ = s;
UpdateRecoveryContext(context, /*replace_existing_context=*/true);
} else {
if (!s.ok()) {
UpdateRecoveryContext(context, /*replace_existing_context=*/false);
}
ROCKS_LOG_INFO(db_options_.info_log,
"ErrorHandler: Hit less severe background error\n");
@@ -378,6 +371,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
"ErrorHandler: Set regular background error, auto_recovery=%d, stop=%d\n",
int{auto_recovery}, int{stop});
recover_context_ = context;
if (auto_recovery) {
recovery_in_prog_ = true;
@@ -418,27 +412,6 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
// such as delegating to SstFileManager to handle no space error.
void ErrorHandler::SetBGError(const Status& bg_status,
BackgroundErrorReason reason, bool wal_related) {
SetBGErrorImpl(bg_status, reason, wal_related, DBRecoverContext(),
/*allow_auto_recovery=*/true);
}
void ErrorHandler::SetBGError(const Status& bg_status,
BackgroundErrorReason reason, bool wal_related,
DBRecoverContext context) {
SetBGErrorImpl(bg_status, reason, wal_related, std::move(context),
/*allow_auto_recovery=*/true);
}
void ErrorHandler::SetBGErrorNoAutoRecovery(const Status& bg_status,
BackgroundErrorReason reason) {
SetBGErrorImpl(bg_status, reason, /*wal_related=*/false, DBRecoverContext(),
/*allow_auto_recovery=*/false);
}
void ErrorHandler::SetBGErrorImpl(const Status& bg_status,
BackgroundErrorReason reason,
bool wal_related, DBRecoverContext context,
bool allow_auto_recovery) {
db_mutex_->AssertHeld();
Status tmp_status = bg_status;
IOStatus bg_io_err = status_to_io_status(std::move(tmp_status));
@@ -448,34 +421,18 @@ void ErrorHandler::SetBGErrorImpl(const Status& bg_status,
}
ROCKS_LOG_WARN(db_options_.info_log, "Background IO error %s, reason %d",
bg_io_err.ToString().c_str(), static_cast<int>(reason));
if (context.failed_wal_number != 0) {
ROCKS_LOG_WARN(
db_options_.info_log,
"[WAL recovery] Quarantined WALs through #%" PRIu64
" after a file-scoped error; indeterminate sequence range=[%" PRIu64
", %" PRIu64 "], status=%s",
context.failed_wal_number, context.failed_wal_sequence_start,
context.failed_wal_sequence, bg_io_err.ToString().c_str());
}
RecordStats({ERROR_HANDLER_BG_ERROR_COUNT, ERROR_HANDLER_BG_IO_ERROR_COUNT},
{} /* int_histograms */);
Status new_bg_io_err = bg_io_err;
DBRecoverContext context;
if (bg_io_err.GetScope() != IOStatus::IOErrorScope::kIOErrorScopeFile &&
bg_io_err.GetDataLoss()) {
// First, data loss (non file scope) is treated as unrecoverable error. So
// it can directly overwrite any existing bg_error_.
bool auto_recovery = false;
Status bg_err(new_bg_io_err, Status::Severity::kUnrecoverableError);
if (!allow_auto_recovery) {
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&bg_err, db_mutex_, &auto_recovery);
if (!bg_err.ok()) {
CheckAndSetRecoveryAndBGError(bg_err, context);
}
return;
}
CheckAndSetRecoveryAndBGError(bg_err, context);
ROCKS_LOG_INFO(
db_options_.info_log,
@@ -491,11 +448,12 @@ void ErrorHandler::SetBGErrorImpl(const Status& bg_status,
}
if (db_options_.manual_wal_flush && wal_related && bg_io_err.IsIOError()) {
// With manual_wal_flush, a WAL write failure can drop buffered WAL writes.
// Memtables and WAL can then become inconsistent. Full error recovery now
// flushes all CFs atomically, but automatically recovering this case still
// needs dedicated analysis and testing. Keep the error fatal until then.
// TODO: Re-evaluate `wal_related` and automatic recovery for
// manual_wal_flush WAL failures.
// Memtables and WAL then become inconsistent. A successful memtable flush
// on one CF can cause CFs to be inconsistent upon restart. Before we fix
// the bug in auto recovery from WAL write failures that can flush one CF
// at a time, we set the error severity to fatal to disallow auto recovery.
// TODO: remove parameter `wal_related` once we can automatically recover
// from WAL write failures.
bool auto_recovery = false;
Status bg_err(new_bg_io_err, Status::Severity::kFatalError);
CheckAndSetRecoveryAndBGError(bg_err, context);
@@ -559,15 +517,6 @@ void ErrorHandler::SetBGErrorImpl(const Status& bg_status,
severity = Status::Severity::kHardError;
}
Status bg_err(new_bg_io_err, severity);
if (!allow_auto_recovery) {
bool auto_recovery = false;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&bg_err, db_mutex_, &auto_recovery);
if (!bg_err.ok()) {
CheckAndSetRecoveryAndBGError(bg_err, context);
}
return;
}
CheckAndSetRecoveryAndBGError(bg_err, context);
bool auto_recovery = db_options_.max_bgerror_resume_count > 0;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
@@ -576,7 +525,7 @@ void ErrorHandler::SetBGErrorImpl(const Status& bg_status,
StartRecoverFromRetryableBGIOError(bg_io_err);
return;
}
HandleKnownErrors(new_bg_io_err, reason, context, allow_auto_recovery);
HandleKnownErrors(new_bg_io_err, reason);
}
void ErrorHandler::AddFilesToQuarantine(
@@ -661,10 +610,6 @@ Status ErrorHandler::ClearBGError() {
is_db_stopped_.store(false, std::memory_order_release);
bg_error_ = Status::OK();
recovery_error_ = IOStatus::OK();
// Recovery facts belong to one background-error episode. Keeping a
// retired WAL cutoff or reserved sequence here would attach it to a later,
// unrelated error when UpdateRecoveryContext() merges the next context.
recover_context_ = DBRecoverContext();
bg_error_.PermitUncheckedError();
recovery_error_.PermitUncheckedError();
recovery_in_prog_ = false;
@@ -712,15 +657,6 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
recovery_error_.PermitUncheckedError();
DBRecoverContext context = recover_context_;
context.flush_after_recovery = true;
TEST_SYNC_POINT_CALLBACK("ErrorHandler::RecoverFromBGError:Context",
&context);
if (context.failed_wal_number != 0 || context.failed_wal_sequence != 0) {
ROCKS_LOG_INFO(db_options_.info_log,
"[WAL recovery] Starting requested recovery: cutoff=%" PRIu64
", indeterminate sequence range=[%" PRIu64 ", %" PRIu64 "]",
context.failed_wal_number, context.failed_wal_sequence_start,
context.failed_wal_sequence);
}
Status s = db_->ResumeImpl(context, Env::IOActivity::kFlush);
if (s.ok()) {
soft_error_no_bg_work_ = false;
@@ -818,15 +754,6 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
DBRecoverContext context = recover_context_;
context.flush_after_recovery = true;
retry_count++;
if (context.failed_wal_number != 0 || context.failed_wal_sequence != 0) {
ROCKS_LOG_INFO(
db_options_.info_log,
"[WAL recovery] Starting automatic recovery attempt #%" PRIu64
": cutoff=%" PRIu64 ", indeterminate sequence range=[%" PRIu64
", %" PRIu64 "]",
retry_count, context.failed_wal_number,
context.failed_wal_sequence_start, context.failed_wal_sequence);
}
Status s = db_->ResumeImpl(context, Env::IOActivity::kFlush);
RecordStats({ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT},
{} /* int_histograms */);
@@ -892,40 +819,15 @@ void ErrorHandler::CheckAndSetRecoveryAndBGError(
if (recovery_in_prog_ && recovery_error_.ok()) {
recovery_error_ = status_to_io_status(Status(bg_err));
}
if (!bg_err.ok() && bg_err.severity() > bg_error_.severity()) {
if (bg_err.severity() > bg_error_.severity()) {
bg_error_ = bg_err;
UpdateRecoveryContext(context, /*replace_existing_context=*/true);
} else {
UpdateRecoveryContext(context, /*replace_existing_context=*/false);
recover_context_ = context;
}
if (bg_error_.severity() >= Status::Severity::kHardError) {
is_db_stopped_.store(true, std::memory_order_release);
}
}
void ErrorHandler::UpdateRecoveryContext(const DBRecoverContext& context,
bool replace_existing_context) {
db_mutex_->AssertHeld();
const uint64_t failed_wal_number =
std::max(recover_context_.failed_wal_number, context.failed_wal_number);
uint64_t failed_wal_sequence = recover_context_.failed_wal_sequence;
uint64_t failed_wal_sequence_start =
recover_context_.failed_wal_sequence_start;
if (context.failed_wal_sequence > failed_wal_sequence) {
failed_wal_sequence = context.failed_wal_sequence;
failed_wal_sequence_start = context.failed_wal_sequence_start;
}
if (replace_existing_context) {
recover_context_ = context;
}
// WAL failures can race or be classified through different severity paths.
// Preserve the broadest failed-file cutoff and highest sequence assigned to
// an indeterminate write group independently of the winning Status.
recover_context_.failed_wal_number = failed_wal_number;
recover_context_.failed_wal_sequence = failed_wal_sequence;
recover_context_.failed_wal_sequence_start = failed_wal_sequence_start;
}
void ErrorHandler::EndAutoRecovery() {
db_mutex_->AssertHeld();
if (!end_recovery_) {
+4 -39
View File
@@ -23,33 +23,12 @@ class DBImpl;
struct DBRecoverContext {
FlushReason flush_reason;
bool flush_after_recovery;
// A non-zero value is an inclusive upper bound on WAL file handles that
// recovery must not reuse. Recovery must not declare success until every
// column family has atomically advanced beyond it in the MANIFEST.
uint64_t failed_wal_number;
// Highest sequence assigned to the write group whose WAL outcome is
// indeterminate. Reserving it prevents a later acknowledged write from
// reusing a sequence that might exist in the abandoned WAL.
uint64_t failed_wal_sequence;
// First sequence in the same indeterminate write group. Generic errors keep
// this evidence without reserving it, in case a retry later establishes that
// the WAL handle is file-scoped and activates the stronger recovery path.
uint64_t failed_wal_sequence_start;
DBRecoverContext()
: flush_reason(FlushReason::kErrorRecovery),
flush_after_recovery(false),
failed_wal_number(0),
failed_wal_sequence(0),
failed_wal_sequence_start(0) {}
flush_after_recovery(false) {}
DBRecoverContext(FlushReason reason)
: flush_reason(reason),
flush_after_recovery(false),
failed_wal_number(0),
failed_wal_sequence(0),
failed_wal_sequence_start(0) {}
bool IsWALWriteErrorRecovery() const { return failed_wal_number != 0; }
: flush_reason(reason), flush_after_recovery(false) {}
};
class ErrorHandler {
@@ -80,11 +59,6 @@ class ErrorHandler {
void SetBGError(const Status& bg_err, BackgroundErrorReason reason,
bool wal_related = false);
void SetBGError(const Status& bg_err, BackgroundErrorReason reason,
bool wal_related, DBRecoverContext context);
void SetBGErrorNoAutoRecovery(const Status& bg_err,
BackgroundErrorReason reason);
Status GetBGError() const { return bg_error_; }
@@ -177,22 +151,13 @@ class ErrorHandler {
// unsorted.
autovector<uint64_t> files_to_quarantine_;
void SetBGErrorImpl(const Status& bg_err, BackgroundErrorReason reason,
bool wal_related, DBRecoverContext context,
bool allow_auto_recovery);
void HandleKnownErrors(const Status& bg_err, BackgroundErrorReason reason,
const DBRecoverContext& context,
bool allow_auto_recovery);
// REQUIRES: db_mutex_ held. When replacing the general recovery context,
// retain WAL facts accumulated from concurrent errors independently.
void UpdateRecoveryContext(const DBRecoverContext& context,
bool replace_existing_context);
void HandleKnownErrors(const Status& bg_err, BackgroundErrorReason reason);
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
void RecoverFromNoSpace();
void StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
void RecoverFromRetryableBGIOError();
// If recovery is in progress, record the first new error for the current
// attempt. A more severe error replaces the current background error.
// attempt. If this error is more severe, retain it and its recovery context.
void CheckAndSetRecoveryAndBGError(const Status& bg_err,
const DBRecoverContext& context);
};
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -136,9 +136,9 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
<< table_properties.property_collectors_names << "compression"
<< table_properties.compression_name << "compression_options"
<< table_properties.compression_options << "creation_time"
<< table_properties.oldest_ancestor_time << "oldest_key_time"
<< table_properties.oldest_key_time << "newest_key_time"
<< table_properties.newest_key_time << "file_creation_time"
<< table_properties.creation_time << "oldest_key_time"
<< table_properties.newest_key_time << "newest_key_time"
<< table_properties.oldest_key_time << "file_creation_time"
<< table_properties.file_creation_time
<< "slow_compression_estimated_data_size"
<< table_properties.slow_compression_estimated_data_size
+1 -1
View File
@@ -163,7 +163,7 @@ Status UpdateManifestForFilesState(
lf->fd.GetFileSize(), lf->smallest, lf->largest,
lf->fd.smallest_seqno, lf->fd.largest_seqno,
lf->marked_for_compaction, temp, lf->oldest_blob_file_number,
lf->oldest_ancestor_time, lf->file_creation_time,
lf->oldest_ancester_time, lf->file_creation_time,
lf->epoch_number, lf->file_checksum,
lf->file_checksum_func_name, lf->unique_id,
lf->compensated_range_deletion_size, lf->tail_size,
File diff suppressed because it is too large. Load diff
+148 -21
View File
@@ -114,8 +114,11 @@ Status ExternalSstFileIngestionJob::PrepareAtomicReplaceRangeTombstone(
super_version->mutable_cf_options.default_write_temperature;
tombstone.prefetch_lmax_index_and_filter_blocks =
ingestion_options_.prefetch_lmax_index_and_filter_blocks;
status = GetIngestedFileInfo(file_path, file_number,
external_file_info.prepared_file_info.get(),
const PreparedFileInfo* prepared_file_info =
ingestion_options_.write_global_seqno
? nullptr
: external_file_info.prepared_file_info.get();
status = GetIngestedFileInfo(file_path, file_number, prepared_file_info,
&tombstone, super_version);
if (!status.ok()) {
fs_->DeleteFile(file_path, IOOptions(), nullptr).PermitUncheckedError();
@@ -389,23 +392,28 @@ Status ExternalSstFileIngestionJob::Prepare(
}
TEST_SYNC_POINT("ExternalSstFileIngestionJob::AfterSyncDir");
// Generate and check the SST file checksum.
// Generate and check the sst file checksum. Note that, if
// IngestExternalFileOptions::write_global_seqno is true, we will not update
// the checksum information in the files_to_ingests_ here, since the file is
// updated with the new global_seqno. After global_seqno is updated, DB will
// generate the new checksum and store it in the Manifest. In all other cases
// if ingestion_options_.write_global_seqno == true and
// verify_file_checksum is false, we only check the checksum function name.
if (status.ok() && db_options_.file_checksum_gen_factory != nullptr) {
bool need_generate_file_checksum;
if (ingestion_options_.verify_file_checksum == false &&
files_checksums.size() == files_to_ingest_.size() &&
files_checksum_func_names.size() == files_to_ingest_.size()) {
// Only when verify_file_checksum == false and the checksum for ingested
// files are provided, DB will use the provided checksum and does not
// generate the checksum for ingested files.
need_generate_file_checksum = false;
need_generate_file_checksum_ = false;
} else {
need_generate_file_checksum = true;
need_generate_file_checksum_ = true;
}
std::vector<std::string> generated_checksums;
std::vector<std::string> generated_checksum_func_names;
// Step 1: generate the checksum for ingested sst file.
if (need_generate_file_checksum) {
if (need_generate_file_checksum_) {
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
std::string generated_checksum;
std::string generated_checksum_func_name;
@@ -445,9 +453,11 @@ Status ExternalSstFileIngestionJob::Prepare(
status.ToString().c_str());
break;
}
files_to_ingest_[i].file_checksum = generated_checksum;
files_to_ingest_[i].file_checksum_func_name =
generated_checksum_func_name;
if (ingestion_options_.write_global_seqno == false) {
files_to_ingest_[i].file_checksum = generated_checksum;
files_to_ingest_[i].file_checksum_func_name =
generated_checksum_func_name;
}
generated_checksums.push_back(generated_checksum);
generated_checksum_func_names.push_back(generated_checksum_func_name);
}
@@ -854,13 +864,21 @@ Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
}
max_assigned_seqno_ = std::max(max_assigned_seqno_, assigned_seqno);
// We use the import time as the ancestor time. This is the time the data
status = GenerateChecksumForIngestedFile(file);
if (!status.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
"Failed to generate checksum for ingested file: %s",
status.ToString().c_str());
return status;
}
// We use the import time as the ancester time. This is the time the data
// is written to the database.
int64_t temp_current_time = 0;
uint64_t current_time = kUnknownFileCreationTime;
uint64_t oldest_ancestor_time = kUnknownOldestAncestorTime;
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
if (clock_->GetCurrentTime(&temp_current_time).ok()) {
current_time = oldest_ancestor_time =
current_time = oldest_ancester_time =
static_cast<uint64_t>(temp_current_time);
}
uint64_t tail_size = FileMetaData::CalculateTailSize(
@@ -884,7 +902,7 @@ Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
file->fd.GetNumber(), file->fd.GetPathId(), file->fd.GetFileSize(),
file->smallest_internal_key, file->largest_internal_key, smallest_seqno,
largest_seqno, false, file->file_temperature, kInvalidBlobFileNumber,
oldest_ancestor_time, current_time,
oldest_ancester_time, current_time,
ingestion_options_.ingest_behind
? kReservedEpochNumberForFileIngestedBehind
: cfd_->NewEpochNumber(), // orders files ingested to L0
@@ -1211,6 +1229,17 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
// Set the global sequence number
file_to_ingest->original_seqno = DecodeFixed64(seqno_iter->second.c_str());
file_to_ingest->global_seqno_offset =
static_cast<size_t>(props.external_sst_file_global_seqno_offset);
// The on-disk offset is only needed if we will write the global seqno back
// into the file (write_global_seqno). The metadata fast-path does not open
// the file, and its in-memory table properties do not carry the offset
// (it is only computed while reading the file back); that is fine as long
// as write_global_seqno is not requested.
if (ingestion_options_.write_global_seqno &&
file_to_ingest->global_seqno_offset == 0) {
return Status::Corruption("Was not able to find file global seqno field");
}
} else if (file_to_ingest->version == 1) {
// SST file V1 should not have global seqno field
assert(seqno_iter == uprops.end());
@@ -1224,6 +1253,7 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
// 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 " +
std::to_string(file_to_ingest->version) +
@@ -1789,13 +1819,64 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
assert(seqno == 0);
assert(file_to_ingest->original_seqno == 0);
}
if (file_to_ingest->original_seqno != seqno) {
if (!ingestion_options_.allow_global_seqno) {
return Status::InvalidArgument("Global seqno is required, but disabled");
}
if (file_to_ingest->original_seqno != 0) {
return Status::InvalidArgument(
"External file global seqno does not match assigned seqno");
if (file_to_ingest->original_seqno == seqno) {
// This file already has the correct global seqno.
return Status::OK();
} else if (!ingestion_options_.allow_global_seqno) {
return Status::InvalidArgument("Global seqno is required, but disabled");
} else if (ingestion_options_.write_global_seqno &&
file_to_ingest->global_seqno_offset == 0) {
return Status::InvalidArgument(
"Trying to set global seqno for a file that don't have a global seqno "
"field");
}
if (ingestion_options_.write_global_seqno) {
// Determine if we can write global_seqno to a given offset of file.
// If the file system does not support random write, then we should not.
// Otherwise we should.
std::unique_ptr<FSRandomRWFile> rwfile;
Status status = fs_->NewRandomRWFile(file_to_ingest->internal_file_path,
env_options_, &rwfile, nullptr);
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::NewRandomRWFile",
&status);
if (status.ok()) {
FSRandomRWFilePtr fsptr(std::move(rwfile), io_tracer_,
file_to_ingest->internal_file_path);
std::string seqno_val;
PutFixed64(&seqno_val, seqno);
status = fsptr->Write(file_to_ingest->global_seqno_offset, seqno_val,
IOOptions(), nullptr);
if (!status.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
"Failed to write global seqno to %s: %s",
file_to_ingest->internal_file_path.c_str(),
status.ToString().c_str());
return status;
}
if (status.ok()) {
TEST_SYNC_POINT("ExternalSstFileIngestionJob::BeforeSyncGlobalSeqno");
status = SyncIngestedFile(fsptr.get());
TEST_SYNC_POINT("ExternalSstFileIngestionJob::AfterSyncGlobalSeqno");
if (!status.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
"Failed to sync ingested file %s after writing global "
"sequence number: %s",
file_to_ingest->internal_file_path.c_str(),
status.ToString().c_str());
}
}
if (!status.ok()) {
return status;
}
} else if (!status.IsNotSupported()) {
ROCKS_LOG_WARN(
db_options_.info_log,
"Failed to open ingested file %s for random read/write: %s",
file_to_ingest->internal_file_path.c_str(),
status.ToString().c_str());
return status;
}
}
@@ -1803,6 +1884,42 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
return Status::OK();
}
IOStatus ExternalSstFileIngestionJob::GenerateChecksumForIngestedFile(
IngestedFileInfo* file_to_ingest) {
if (db_options_.file_checksum_gen_factory == nullptr ||
need_generate_file_checksum_ == false ||
ingestion_options_.write_global_seqno == false) {
// If file_checksum_gen_factory is not set, we are not able to generate
// the checksum. if write_global_seqno is false, it means we will use
// file checksum generated during Prepare(). This step will be skipped.
return IOStatus::OK();
}
std::string file_checksum;
std::string file_checksum_func_name;
std::string requested_checksum_func_name;
// TODO: rate limit file reads for checksum calculation during file ingestion.
// TODO: plumb Env::IOActivity
ReadOptions ro;
FileOptions gen_fopts;
gen_fopts.file_checksum_func_name = kNoFileChecksumFuncName;
IOStatus io_s = GenerateOneFileChecksum(
fs_.get(), file_to_ingest->internal_file_path,
db_options_.file_checksum_gen_factory.get(), requested_checksum_func_name,
&file_checksum, &file_checksum_func_name,
ingestion_options_.verify_checksums_readahead_size,
db_options_.allow_mmap_reads, io_tracer_, db_options_.rate_limiter.get(),
ro, db_options_.stats, db_options_.clock, gen_fopts);
if (!io_s.ok()) {
ROCKS_LOG_WARN(
db_options_.info_log, "Failed to generate checksum for %s: %s",
file_to_ingest->internal_file_path.c_str(), io_s.ToString().c_str());
return io_s;
}
file_to_ingest->file_checksum = std::move(file_checksum);
file_to_ingest->file_checksum_func_name = std::move(file_checksum_func_name);
return IOStatus::OK();
}
bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
const IngestedFileInfo* file_to_ingest, int level) {
if (level == 0) {
@@ -1825,6 +1942,16 @@ bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
return true;
}
template <typename TWritableFile>
Status ExternalSstFileIngestionJob::SyncIngestedFile(TWritableFile* file) {
assert(file != nullptr);
if (db_options_.use_fsync) {
return file->Fsync(IOOptions(), nullptr);
} else {
return file->Sync(IOOptions(), nullptr);
}
}
Status ExternalSstFileIngestionJob::GetSeqnoBoundaryForFile(
TableReader* table_reader, SuperVersion* sv,
IngestedFileInfo* file_to_ingest, bool allow_data_in_errors) {
+13 -1
View File
@@ -133,6 +133,9 @@ struct IngestedFileInfo : public KeyRangeInfo {
std::string limit_ukey;
// Sequence number for keys in external file
SequenceNumber original_seqno;
// Offset of the global sequence number field in the file, will
// be zero if version is 1 (global seqno is not supported)
size_t global_seqno_offset;
// External file size
uint64_t file_size;
// total number of keys in external file
@@ -427,15 +430,21 @@ class ExternalSstFileIngestionJob {
// REQUIRES: Mutex held
Status CheckLevelForIngestedBehindFile(IngestedFileInfo* file_to_ingest);
// Set the file's assigned global sequence number to `seqno`.
// Set the file global sequence number to `seqno`
Status AssignGlobalSeqnoForIngestedFile(IngestedFileInfo* file_to_ingest,
SequenceNumber seqno);
// Generate the file checksum and store in the IngestedFileInfo
IOStatus GenerateChecksumForIngestedFile(IngestedFileInfo* file_to_ingest);
// Check if `file_to_ingest` can fit in level `level`
// REQUIRES: Mutex held
bool IngestedFileFitInLevel(const IngestedFileInfo* file_to_ingest,
int level);
// Helper method to sync given file.
template <typename TWritableFile>
Status SyncIngestedFile(TWritableFile* file);
// Helper function to obtain the smallest and largest sequence number from a
// file. When OK is returned, file_to_ingest->smallest_seqno and
// file_to_ingest->largest_seqno will be updated.
@@ -475,6 +484,9 @@ class ExternalSstFileIngestionJob {
// Set in ExternalSstFileIngestionJob::Prepare(), if true all files are
// ingested in L0
bool files_overlap_{false};
// Set in ExternalSstFileIngestionJob::Prepare(), if true and DB
// file_checksum_gen_factory is set, DB will generate checksum each file.
bool need_generate_file_checksum_{true};
std::shared_ptr<IOTracer> io_tracer_;
// Flag indicating whether the column family is flushed after `Prepare` and
+160 -164
View File
@@ -96,14 +96,14 @@ class ExternSSTFileLinkFailFallbackTest
class ExternalSSTFileTest
: public ExternalSSTFileTestBase,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
public:
ExternalSSTFileTest() = default;
void SetUp() override {
const auto* info = ::testing::UnitTest::GetInstance()->current_test_info();
if (info != nullptr && info->value_param() != nullptr) {
two_phase_ingest_ = std::get<1>(GetParam());
two_phase_ingest_ = std::get<2>(GetParam());
}
}
@@ -159,7 +159,7 @@ class ExternalSSTFileTest
Status GenerateAndAddExternalFile(
const Options options,
std::vector<std::pair<std::string, std::string>> data, int file_id = -1,
bool allow_global_seqno = false,
bool allow_global_seqno = false, bool write_global_seqno = false,
bool verify_checksums_before_ingest = true, bool ingest_behind = false,
bool sort_data = false,
std::map<std::string, std::string>* true_data = nullptr,
@@ -210,6 +210,7 @@ class ExternalSSTFileTest
if (s.ok()) {
IngestExternalFileOptions ifo;
ifo.allow_global_seqno = allow_global_seqno;
ifo.write_global_seqno = allow_global_seqno ? write_global_seqno : false;
ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
ifo.ingest_behind = ingest_behind;
ifo.fill_cache = fill_cache;
@@ -287,6 +288,7 @@ class ExternalSSTFileTest
Status GenerateAndAddExternalFile(
const Options options, std::vector<std::pair<int, std::string>> data,
int file_id = -1, bool allow_global_seqno = false,
bool write_global_seqno = false,
bool verify_checksums_before_ingest = true, bool ingest_behind = false,
bool sort_data = false,
std::map<std::string, std::string>* true_data = nullptr,
@@ -296,14 +298,14 @@ class ExternalSSTFileTest
file_data.emplace_back(Key(entry.first), entry.second);
}
return GenerateAndAddExternalFile(options, file_data, file_id,
allow_global_seqno,
allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest,
ingest_behind, sort_data, true_data, cfh);
}
Status GenerateAndAddExternalFile(
const Options options, std::vector<int> keys, int file_id = -1,
bool allow_global_seqno = false,
bool allow_global_seqno = false, bool write_global_seqno = false,
bool verify_checksums_before_ingest = true, bool ingest_behind = false,
bool sort_data = false,
std::map<std::string, std::string>* true_data = nullptr,
@@ -313,19 +315,21 @@ class ExternalSSTFileTest
file_data.emplace_back(Key(k), Key(k) + std::to_string(file_id));
}
return GenerateAndAddExternalFile(
options, file_data, file_id, allow_global_seqno,
options, file_data, file_id, allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest, ingest_behind, sort_data, true_data,
cfh, fill_cache);
}
Status DeprecatedAddFile(const std::vector<std::string>& files,
bool move_files = false,
bool skip_snapshot_check = false) {
bool skip_snapshot_check = false,
bool skip_write_global_seqno = false) {
IngestExternalFileOptions opts;
opts.move_files = move_files;
opts.snapshot_consistency = !skip_snapshot_check;
opts.allow_global_seqno = false;
opts.allow_blocking_flush = false;
opts.write_global_seqno = !skip_write_global_seqno;
return db_->IngestExternalFile(files, opts);
}
@@ -370,8 +374,8 @@ TEST_F(ExternalSSTFileTest, IngestionTimingHistogram) {
ASSERT_OK(GenerateAndAddExternalFile(
options, {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}, /*file_id=*/-1,
/*allow_global_seqno=*/true, /*verify_checksums_before_ingest=*/true,
/*ingest_behind=*/false,
/*allow_global_seqno=*/true, /*write_global_seqno=*/false,
/*verify_checksums_before_ingest=*/true, /*ingest_behind=*/false,
/*sort_data=*/true));
auto hist = [&](Histograms h) {
@@ -391,8 +395,8 @@ TEST_F(ExternalSSTFileTest, IngestionTimingHistogram) {
// A second call adds exactly one more sample to each histogram.
ASSERT_OK(GenerateAndAddExternalFile(
options, {{"k4", "v4"}, {"k5", "v5"}}, /*file_id=*/-1,
/*allow_global_seqno=*/true, /*verify_checksums_before_ingest=*/true,
/*ingest_behind=*/false,
/*allow_global_seqno=*/true, /*write_global_seqno=*/false,
/*verify_checksums_before_ingest=*/true, /*ingest_behind=*/false,
/*sort_data=*/true));
ASSERT_EQ(2, hist(INGEST_EXTERNAL_FILE_PREPARE_TIME).count);
ASSERT_EQ(2, hist(INGEST_EXTERNAL_FILE_RUN_TIME).count);
@@ -752,8 +756,8 @@ TEST_F(ExternalSSTFileTest, IngestWithFileInfo) {
auto ingest_fi = [&](std::vector<std::pair<std::string, std::string>> data) {
return GenerateAndAddExternalFile(
options, std::move(data), /*file_id=*/-1, /*allow_global_seqno=*/true,
/*verify_checksums_before_ingest=*/true, /*ingest_behind=*/false,
/*sort_data=*/true, &true_data,
/*write_global_seqno=*/false, /*verify_checksums_before_ingest=*/true,
/*ingest_behind=*/false, /*sort_data=*/true, &true_data,
/*cfh=*/nullptr, /*fill_cache=*/false, /*ingest_with_file_info=*/true);
};
@@ -773,8 +777,8 @@ TEST_F(ExternalSSTFileTest, IngestWithFileInfo) {
// Control: ingesting the same way but WITHOUT file_infos does enter the scan.
ASSERT_OK(GenerateAndAddExternalFile(
options, {{Key(8), "c8"}}, /*file_id=*/-1, /*allow_global_seqno=*/true,
/*verify_checksums_before_ingest=*/true, /*ingest_behind=*/false,
/*sort_data=*/true, &true_data));
/*write_global_seqno=*/false, /*verify_checksums_before_ingest=*/true,
/*ingest_behind=*/false, /*sort_data=*/true, &true_data));
ASSERT_GT(read_path_count.load(), 0);
SyncPoint::GetInstance()->DisableProcessing();
@@ -841,57 +845,25 @@ TEST_F(ExternalSSTFileTest, IngestWithFileInfoVerifiesChecksum) {
ASSERT_TRUE(db_->IngestExternalFiles({arg}).IsCorruption());
}
TEST_F(ExternalSSTFileTest, IngestWithSerializedFileInfo) {
// write_global_seqno is incompatible with file_infos: the file is not opened,
// so the seqno cannot be written back into it.
TEST_F(ExternalSSTFileTest, IngestWithFileInfoRejectsWriteGlobalSeqno) {
Options options = CurrentOptions();
DestroyAndReopen(options);
const std::string file_path = sst_files_dir_ + "serialized_file_info.sst";
SstFileWriter writer(EnvOptions(), options);
ASSERT_OK(writer.Open(file_path));
ASSERT_OK(writer.Put(Key(10), "v10"));
const std::string f = sst_files_dir_ + "wgs.sst";
SstFileWriter w(EnvOptions(), options);
ASSERT_OK(w.Open(f));
ASSERT_OK(w.Put(Key(1), "v1"));
ExternalSstFileInfo file_info;
ASSERT_OK(writer.Finish(&file_info));
std::string serialized;
ASSERT_OK(
SerializePreparedFileInfo(*file_info.prepared_file_info, &serialized));
std::shared_ptr<const PreparedFileInfo> deserialized;
ASSERT_OK(DeserializePreparedFileInfo(serialized, &deserialized));
ASSERT_OK(w.Finish(&file_info));
IngestExternalFileArg arg;
arg.column_family = db_->DefaultColumnFamily();
arg.external_files = {file_path};
arg.file_infos = {deserialized.get()};
ASSERT_OK(db_->IngestExternalFiles({arg}));
ASSERT_EQ(Get(Key(10)), "v10");
}
TEST_F(ExternalSSTFileTest, PreparedFileInfoRejectsInvalidSerialization) {
std::shared_ptr<const PreparedFileInfo> prepared_file_info;
ASSERT_TRUE(DeserializePreparedFileInfo("invalid", &prepared_file_info)
.IsCorruption());
ASSERT_EQ(prepared_file_info, nullptr);
}
TEST_F(ExternalSSTFileTest, PreparedFileInfoDetectsSerializationCorruption) {
Options options = CurrentOptions();
const std::string file_path = sst_files_dir_ + "file_info_corruption.sst";
SstFileWriter writer(EnvOptions(), options);
ASSERT_OK(writer.Open(file_path));
ASSERT_OK(writer.Put(Key(1), "v1"));
ExternalSstFileInfo file_info;
ASSERT_OK(writer.Finish(&file_info));
std::string serialized;
ASSERT_OK(
SerializePreparedFileInfo(*file_info.prepared_file_info, &serialized));
ASSERT_FALSE(serialized.empty());
serialized.back() ^= 1;
std::shared_ptr<const PreparedFileInfo> deserialized;
ASSERT_TRUE(
DeserializePreparedFileInfo(serialized, &deserialized).IsCorruption());
ASSERT_EQ(deserialized, nullptr);
arg.external_files = {f};
arg.file_infos = {file_info.prepared_file_info.get()};
arg.options.write_global_seqno = true;
ASSERT_TRUE(db_->IngestExternalFiles({arg}).IsInvalidArgument());
}
TEST_F(ExternalSSTFileTest, GetPreparedFileInfoForExternalSstIngestion) {
@@ -934,15 +906,10 @@ TEST_F(ExternalSSTFileTest, GetPreparedFileInfoForExternalSstIngestion) {
ASSERT_EQ(prepared_file_info->table_properties.key_largest_seqno,
live_meta[0].largest_seqno);
std::string serialized;
ASSERT_OK(SerializePreparedFileInfo(*prepared_file_info, &serialized));
std::shared_ptr<const PreparedFileInfo> deserialized;
ASSERT_OK(DeserializePreparedFileInfo(serialized, &deserialized));
IngestExternalFileArg arg;
arg.column_family = db_->DefaultColumnFamily();
arg.external_files = {file_path};
arg.file_infos = {deserialized.get()};
arg.file_infos = {prepared_file_info.get()};
arg.options.allow_db_generated_files = true;
arg.options.snapshot_consistency = false;
@@ -1054,8 +1021,8 @@ TEST_F(ExternalSSTFileTest, NoBlockCache) {
size_t usage_before_ingestion = cache->GetUsage();
std::map<std::string, std::string> true_data;
// Ingest with fill_cache = true
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, false, true, false,
false, &true_data, nullptr,
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, false, false, true,
false, false, &true_data, nullptr,
/*fill_cache=*/true));
ASSERT_EQ(FilesPerLevel(), "0,0,0,0,0,0,1");
EXPECT_GT(cache->GetUsage(), usage_before_ingestion);
@@ -1069,8 +1036,8 @@ TEST_F(ExternalSSTFileTest, NoBlockCache) {
usage_before_ingestion = cache->GetUsage();
// Ingest with fill_cache = false
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 4}, -1, false, true, false,
false, &true_data, nullptr,
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 4}, -1, false, false, true,
false, false, &true_data, nullptr,
/*fill_cache=*/false));
EXPECT_EQ(usage_before_ingestion, cache->GetUsage());
@@ -2106,13 +2073,13 @@ TEST_P(ExternalSSTFileTest, PickedLevel) {
std::map<std::string, std::string> true_data;
// File 0 will go to last level (L3)
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 10}, -1, false, true, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 10}, -1, false, false, true,
false, false, &true_data));
EXPECT_EQ(FilesPerLevel(), "0,0,0,1");
// File 1 will go to level L2 (since it overlap with file 0 in L3)
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 9}, -1, false, true, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 9}, -1, false, false, true,
false, false, &true_data));
EXPECT_EQ(FilesPerLevel(), "0,0,1,1");
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
@@ -2142,13 +2109,13 @@ TEST_P(ExternalSSTFileTest, PickedLevel) {
// This file overlaps with file 0 (L3), file 1 (L2) and the
// output of compaction going to L1
ASSERT_OK(GenerateAndAddExternalFile(options, {4, 7}, -1,
true /* allow_global_seqno */, true,
false, false, &true_data));
true /* allow_global_seqno */, false,
true, false, false, &true_data));
EXPECT_EQ(FilesPerLevel(), "5,0,1,1");
// This file does not overlap with any file or with the running compaction
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, false,
false, false, &true_data));
false, false, false, &true_data));
EXPECT_EQ(FilesPerLevel(), "5,0,1,2");
// Hold compaction from finishing
@@ -2298,13 +2265,13 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
// This file overlaps with the output of the compaction (going to L3)
// so the file will be added to L0 since L3 is the base level
ASSERT_OK(GenerateAndAddExternalFile(options, {31, 32, 33, 34}, -1,
true /* allow_global_seqno */, true,
false, false, &true_data));
true /* allow_global_seqno */, false,
true, false, false, &true_data));
EXPECT_EQ(FilesPerLevel(), "5");
// This file does not overlap with the current running compactiong
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, true,
false, false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, false,
true, false, false, &true_data));
EXPECT_EQ(FilesPerLevel(), "5,0,0,1");
// Hold compaction from finishing
@@ -2318,26 +2285,26 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
options.disable_auto_compactions = true;
Reopen(options);
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 15, 19}, -1, false, true,
false, false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 15, 19}, -1, false, false,
true, false, false, &true_data));
ASSERT_EQ(FilesPerLevel(), "1,0,0,3");
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1001, 1002}, -1, false,
true, false, false, &true_data));
false, true, false, false, &true_data));
ASSERT_EQ(FilesPerLevel(), "1,0,0,4");
ASSERT_OK(GenerateAndAddExternalFile(options, {500, 600, 700}, -1, false,
true, false, false, &true_data));
false, true, false, false, &true_data));
ASSERT_EQ(FilesPerLevel(), "1,0,0,5");
// File 5 overlaps with file 2 (L3 / base level)
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 10}, -1, false, true, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 10}, -1, false, false, true,
false, false, &true_data));
ASSERT_EQ(FilesPerLevel(), "2,0,0,5");
// File 6 overlaps with file 2 (L3 / base level) and file 5 (L0)
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 9}, -1, false, true, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 9}, -1, false, false, true,
false, false, &true_data));
ASSERT_EQ(FilesPerLevel(), "3,0,0,5");
// Verify data in files
@@ -2356,7 +2323,7 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
// File 7 overlaps with file 4 (L3)
ASSERT_OK(GenerateAndAddExternalFile(options, {650, 651, 652}, -1, false,
true, false, false, &true_data));
false, true, false, false, &true_data));
ASSERT_EQ(FilesPerLevel(), "5,0,0,5");
VerifyDBFromMap(true_data, &kcnt, false);
@@ -2774,7 +2741,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
options.level0_slowdown_writes_trigger = 256;
options.level0_stop_writes_trigger = 256;
bool verify_checksums_before_ingest = std::get<0>(GetParam());
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
for (int iter = 0; iter < 2; iter++) {
bool write_to_memtable = (iter == 0);
DestroyAndReopen(options);
@@ -2797,9 +2765,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
true_data[entry.first] = entry.second;
}
} else {
ASSERT_OK(GenerateAndAddExternalFile(options, random_data, -1, true,
verify_checksums_before_ingest,
false, true, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, random_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, true, &true_data));
}
}
size_t kcnt = 0;
@@ -2829,10 +2797,11 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
for (int i = 0; i <= 20; i++) {
file_data.emplace_back(Key(i), "L4");
}
bool verify_checksums_before_ingest = std::get<0>(GetParam());
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file don't overlap with anything in the DB, will go to L4
ASSERT_EQ("0,0,0,0,1", FilesPerLevel());
@@ -2842,9 +2811,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
for (int i = 80; i <= 130; i++) {
file_data.emplace_back(Key(i), "L0");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file overlap with the memtable, so it will flush it and add
// it self to L0
@@ -2855,9 +2824,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
for (int i = 30; i <= 50; i++) {
file_data.emplace_back(Key(i), "L4");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file don't overlap with anything in the DB and fit in L4 as well
ASSERT_EQ("2,0,0,0,2", FilesPerLevel());
@@ -2867,9 +2836,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
for (int i = 10; i <= 40; i++) {
file_data.emplace_back(Key(i), "L3");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file overlap with files in L4, we will ingest it in L3
ASSERT_EQ("2,0,0,1,2", FilesPerLevel());
@@ -2879,7 +2848,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
}
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
bool verify_checksums_before_ingest = std::get<0>(GetParam());
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
Options options = CurrentOptions();
options.num_levels = 5;
options.compaction_style = kCompactionStyleUniversal;
@@ -2913,9 +2883,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
file_data.emplace_back(Key(i), "L4");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file don't overlap with anything in the DB, will go to L4
ASSERT_EQ("0,0,0,0,2", FilesPerLevel());
@@ -2925,9 +2895,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
for (int i = 80; i <= 130; i++) {
file_data.emplace_back(Key(i), "L0");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file overlap with the memtable, so it will flush it and add
// it self to L0
@@ -2938,9 +2908,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
for (int i = 30; i <= 50; i++) {
file_data.emplace_back(Key(i), "L4");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file don't overlap with anything in the DB and fit in L4 as well
ASSERT_EQ("2,0,0,0,3", FilesPerLevel());
@@ -2950,9 +2920,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
for (int i = 10; i <= 40; i++) {
file_data.emplace_back(Key(i), "L3");
}
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file overlap with files in L4, we will ingest it into the closest
// non-overlapping level, in this case, it's L3.
@@ -2977,19 +2947,20 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
&entries_in_memtable));
ASSERT_GE(entries_in_memtable, 1);
bool verify_checksums_before_ingest = std::get<0>(GetParam());
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
// No need for flush
ASSERT_OK(GenerateAndAddExternalFile(options, {90, 100, 110}, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, {90, 100, 110}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
&entries_in_memtable));
ASSERT_GE(entries_in_memtable, 1);
// This file will flush the memtable
ASSERT_OK(GenerateAndAddExternalFile(options, {19, 20, 21}, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, {19, 20, 21}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
&entries_in_memtable));
ASSERT_EQ(entries_in_memtable, 0);
@@ -3003,17 +2974,17 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
ASSERT_GE(entries_in_memtable, 1);
// No need for flush, this file keys fit between the memtable keys
ASSERT_OK(GenerateAndAddExternalFile(options, {202, 203, 204}, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, {202, 203, 204}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
&entries_in_memtable));
ASSERT_GE(entries_in_memtable, 1);
// This file will flush the memtable
ASSERT_OK(GenerateAndAddExternalFile(options, {206, 207}, -1, true,
verify_checksums_before_ingest, false,
false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(
options, {206, 207}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
&entries_in_memtable));
ASSERT_EQ(entries_in_memtable, 0);
@@ -3031,13 +3002,16 @@ TEST_P(ExternalSSTFileTest, L0SortingIssue) {
ASSERT_OK(Put(Key(1), "memtable"));
ASSERT_OK(Put(Key(10), "memtable"));
bool verify_checksums_before_ingest = std::get<0>(GetParam());
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
// No Flush needed, No global seqno needed, Ingest in L1
ASSERT_OK(GenerateAndAddExternalFile(
options, {7, 8}, -1, true, verify_checksums_before_ingest, false, false));
ASSERT_OK(
GenerateAndAddExternalFile(options, {7, 8}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false));
// No Flush needed, but need a global seqno, Ingest in L0
ASSERT_OK(GenerateAndAddExternalFile(
options, {7, 8}, -1, true, verify_checksums_before_ingest, false, false));
ASSERT_OK(
GenerateAndAddExternalFile(options, {7, 8}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false));
printf("%s\n", FilesPerLevel().c_str());
// Overwrite what we added using external files
@@ -3308,11 +3282,12 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
options.listeners.emplace_back(listener);
CreateAndReopenWithCF({"koko", "toto"}, options);
bool verify_checksums_before_ingest = std::get<0>(GetParam());
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
// Ingest into default cf
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
verify_checksums_before_ingest, false,
true, nullptr, handles_[0]));
ASSERT_OK(GenerateAndAddExternalFile(
options, {1, 2}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, true, nullptr, handles_[0]));
ASSERT_EQ(listener->ingested_files.size(), 1);
ASSERT_EQ(listener->ingested_files.back().cf_name, "default");
ASSERT_EQ(listener->ingested_files.back().global_seqno, 0);
@@ -3322,9 +3297,9 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
"default");
// Ingest into cf1
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
verify_checksums_before_ingest, false,
true, nullptr, handles_[1]));
ASSERT_OK(GenerateAndAddExternalFile(
options, {1, 2}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, true, nullptr, handles_[1]));
ASSERT_EQ(listener->ingested_files.size(), 2);
ASSERT_EQ(listener->ingested_files.back().cf_name, "koko");
ASSERT_EQ(listener->ingested_files.back().global_seqno, 0);
@@ -3334,9 +3309,9 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
"koko");
// Ingest into cf2
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
verify_checksums_before_ingest, false,
true, nullptr, handles_[2]));
ASSERT_OK(GenerateAndAddExternalFile(
options, {1, 2}, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, true, nullptr, handles_[2]));
ASSERT_EQ(listener->ingested_files.size(), 3);
ASSERT_EQ(listener->ingested_files.back().cf_name, "toto");
ASSERT_EQ(listener->ingested_files.back().global_seqno, 0);
@@ -3403,11 +3378,12 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
bool allow_global_seqno = true;
bool ingest_behind = true;
bool verify_checksums_before_ingest = std::get<0>(GetParam());
bool write_global_seqno = std::get<0>(GetParam());
bool verify_checksums_before_ingest = std::get<1>(GetParam());
// Can't ingest behind since allow_ingest_behind isn't set to true
ASSERT_NOK(GenerateAndAddExternalFile(
options, file_data, -1, allow_global_seqno,
options, file_data, -1, allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest, ingest_behind, false /*sort_data*/,
&true_data));
@@ -3433,13 +3409,13 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
// Universal picker should go at second from the bottom level
ASSERT_EQ("0,1", FilesPerLevel());
ASSERT_OK(GenerateAndAddExternalFile(
options, file_data, -1, allow_global_seqno,
options, file_data, -1, allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest, true /*ingest_behind*/,
false /*sort_data*/, &true_data));
ASSERT_EQ("0,1,1", FilesPerLevel());
// this time ingest should fail as the file doesn't fit to the bottom level
ASSERT_NOK(GenerateAndAddExternalFile(
options, file_data, -1, allow_global_seqno,
options, file_data, -1, allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest, true /*ingest_behind*/,
false /*sort_data*/, &true_data));
ASSERT_EQ("0,1,1", FilesPerLevel());
@@ -3483,8 +3459,9 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
ASSERT_OK(db_->CreateColumnFamily(new_cf_option, "new_cf", &new_cfh));
ASSERT_TRUE(GenerateAndAddExternalFile(
new_cf_option, file_data, -1, allow_global_seqno,
verify_checksums_before_ingest, true /*ingest_behind*/,
false /*sort_data*/, nullptr, /*cfh=*/new_cfh)
write_global_seqno, verify_checksums_before_ingest,
true /*ingest_behind*/, false /*sort_data*/, nullptr,
/*cfh=*/new_cfh)
.IsInvalidArgument());
ASSERT_OK(db_->DropColumnFamily(new_cfh));
ASSERT_OK(db_->DestroyColumnFamilyHandle(new_cfh));
@@ -3647,8 +3624,10 @@ TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_Success) {
std::vector<IngestExternalFileOptions> ifos(column_families.size());
for (auto& ifo : ifos) {
ifo.allow_global_seqno = true; // Always allow global_seqno
// May or may not write global_seqno
ifo.write_global_seqno = std::get<0>(GetParam());
// Whether to verify checksums before ingestion
ifo.verify_checksums_before_ingest = std::get<0>(GetParam());
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
}
std::vector<std::vector<std::pair<std::string, std::string>>> data;
data.push_back(
@@ -3719,8 +3698,10 @@ TEST_P(ExternalSSTFileTest,
std::vector<IngestExternalFileOptions> ifos(column_families.size());
for (auto& ifo : ifos) {
ifo.allow_global_seqno = true; // Always allow global_seqno
// May or may not write global_seqno
ifo.write_global_seqno = std::get<0>(GetParam());
// Whether to verify checksums before ingestion
ifo.verify_checksums_before_ingest = std::get<0>(GetParam());
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
}
std::vector<std::vector<std::pair<std::string, std::string>>> data;
data.push_back(
@@ -3825,8 +3806,10 @@ TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_PrepareFail) {
std::vector<IngestExternalFileOptions> ifos(column_families.size());
for (auto& ifo : ifos) {
ifo.allow_global_seqno = true; // Always allow global_seqno
// May or may not write global_seqno
ifo.write_global_seqno = std::get<0>(GetParam());
// Whether to verify block checksums before ingest
ifo.verify_checksums_before_ingest = std::get<0>(GetParam());
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
}
std::vector<std::vector<std::pair<std::string, std::string>>> data;
data.push_back(
@@ -3893,8 +3876,10 @@ TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_CommitFail) {
std::vector<IngestExternalFileOptions> ifos(column_families.size());
for (auto& ifo : ifos) {
ifo.allow_global_seqno = true; // Always allow global_seqno
// May or may not write global_seqno
ifo.write_global_seqno = std::get<0>(GetParam());
// Whether to verify block checksums before ingestion
ifo.verify_checksums_before_ingest = std::get<0>(GetParam());
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
}
std::vector<std::vector<std::pair<std::string, std::string>>> data;
data.push_back(
@@ -3965,8 +3950,10 @@ TEST_P(ExternalSSTFileTest,
std::vector<IngestExternalFileOptions> ifos(column_families.size());
for (auto& ifo : ifos) {
ifo.allow_global_seqno = true; // Always allow global_seqno
// May or may not write global_seqno
ifo.write_global_seqno = std::get<0>(GetParam());
// Whether to verify block checksums before ingestion
ifo.verify_checksums_before_ingest = std::get<0>(GetParam());
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
}
std::vector<std::vector<std::pair<std::string, std::string>>> data;
data.push_back(
@@ -4146,7 +4133,7 @@ TEST_F(ExternalSSTFileTest, FIFOCompaction) {
}
// Overlaps with memtable, will trigger flush
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/true, false,
/*allow_global_seqno=*/true, true, false,
false, false, &true_data));
ASSERT_EQ("2", FilesPerLevel());
@@ -4156,10 +4143,10 @@ TEST_F(ExternalSSTFileTest, FIFOCompaction) {
}
// global sequence number is always assigned, so this will fail
ASSERT_NOK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/false, false,
false, false, &true_data));
/*allow_global_seqno=*/false, true,
false, false, false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/true, false,
/*allow_global_seqno=*/true, true, false,
false, false, &true_data));
// Compact to data to lower level to test multi-level FIFO later
@@ -4180,7 +4167,7 @@ TEST_F(ExternalSSTFileTest, FIFOCompaction) {
}
// Files are ingested into L0 for multi-level FIFO
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/true, false,
/*allow_global_seqno=*/true, true, false,
false, false, &true_data));
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
@@ -4848,7 +4835,8 @@ TEST_F(ExternalSSTFileWithTimestampTest,
}
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
testing::Combine(testing::Bool(), testing::Bool()));
testing::Combine(testing::Bool(), testing::Bool(),
testing::Bool()));
class IngestDBGeneratedFileTest
: public ExternalSSTFileTestBase,
@@ -4928,6 +4916,14 @@ TEST_P(IngestDBGeneratedFileTest, FailureCase) {
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));
+10 -14
View File
@@ -502,13 +502,9 @@ Status FlushJob::MemPurge() {
}
}
// MemPurge output is installed directly into the immutable list, so it
// must not advertise memory that can be reclaimed by switching a mutable
// memtable.
new_mem = new MemTable(cfd_->internal_comparator(), cfd_->ioptions(),
mutable_cf_options_, cfd_->write_buffer_mgr(),
earliest_seqno, cfd_->GetID(),
nullptr /* flush_initiator */);
earliest_seqno, cfd_->GetID());
assert(new_mem != nullptr);
Env* env = db_options_.env;
@@ -1006,11 +1002,11 @@ Status FlushJob::WriteLevel0Table() {
auto status = clock_->GetCurrentTime(&_current_time);
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
if (!status.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
"Failed to get current time to populate "
"oldest_ancestor_time property. "
"Status: %s",
status.ToString().c_str());
ROCKS_LOG_WARN(
db_options_.info_log,
"Failed to get current time to populate creation_time property. "
"Status: %s",
status.ToString().c_str());
}
const uint64_t current_time = static_cast<uint64_t>(_current_time);
@@ -1018,12 +1014,12 @@ Status FlushJob::WriteLevel0Table() {
// It's not clear whether oldest_key_time is always available. In case
// it is not available, use current_time.
uint64_t oldest_ancestor_time = std::min(current_time, oldest_key_time);
uint64_t oldest_ancester_time = std::min(current_time, oldest_key_time);
TEST_SYNC_POINT_CALLBACK(
"FlushJob::WriteLevel0Table:oldest_ancestor_time",
&oldest_ancestor_time);
meta_.oldest_ancestor_time = oldest_ancestor_time;
"FlushJob::WriteLevel0Table:oldest_ancester_time",
&oldest_ancester_time);
meta_.oldest_ancester_time = oldest_ancester_time;
meta_.file_creation_time = current_time;
uint64_t memtable_payload_bytes = 0;
+5 -5
View File
@@ -164,13 +164,13 @@ Status ImportColumnFamilyJob::Prepare(uint64_t next_file_number,
// REQUIRES: we have become the only writer by entering both write_thread_ and
// nonmem_write_thread_
Status ImportColumnFamilyJob::Run() {
// We use the import time as the ancestor time. This is the time the data
// We use the import time as the ancester time. This is the time the data
// is written to the database.
int64_t temp_current_time = 0;
uint64_t oldest_ancestor_time = kUnknownOldestAncestorTime;
uint64_t current_time = kUnknownOldestAncestorTime;
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
uint64_t current_time = kUnknownOldestAncesterTime;
if (clock_->GetCurrentTime(&temp_current_time).ok()) {
current_time = oldest_ancestor_time =
current_time = oldest_ancester_time =
static_cast<uint64_t>(temp_current_time);
}
@@ -211,7 +211,7 @@ Status ImportColumnFamilyJob::Run() {
f.fd.GetFileSize(), f.smallest_internal_key, f.largest_internal_key,
file_metadata.smallest_seqno, file_metadata.largest_seqno, false,
file_metadata.temperature, kInvalidBlobFileNumber,
oldest_ancestor_time, current_time, file_metadata.epoch_number,
oldest_ancester_time, current_time, file_metadata.epoch_number,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, f.unique_id, 0,
tail_size,
static_cast<bool>(
+2 -2
View File
@@ -897,8 +897,8 @@ class RetriableLogTest : public ::testing::TestWithParam<int> {
}
void Write(const Slice& data) {
ASSERT_OK(writer_->Append(data, IOOptions()));
ASSERT_OK(writer_->Fsync(IOOptions()));
ASSERT_OK(writer_->Append(IOOptions(), data));
ASSERT_OK(writer_->Sync(IOOptions(), true));
}
bool TryRead(std::string* result) {
+6 -5
View File
@@ -116,9 +116,10 @@ IOStatus Writer::AddRecord(const WriteOptions& write_options,
// Fill the trailer (literal below relies on kHeaderSize and
// kRecyclableHeaderSize being <= 11)
assert(header_size_ <= 11);
s = dest_->Append(Slice("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
s = dest_->Append(opts,
Slice("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
static_cast<size_t>(leftover)),
opts);
0 /* crc32c_checksum */);
if (!s.ok()) {
break;
}
@@ -349,10 +350,10 @@ IOStatus Writer::EmitPhysicalRecord(const WriteOptions& write_options,
IOOptions opts;
IOStatus s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = dest_->Append(Slice(buf, header_size), opts);
s = dest_->Append(opts, Slice(buf, header_size), 0 /* crc32c_checksum */);
}
if (s.ok()) {
s = dest_->Append(Slice(ptr, n), opts, Crc32cChecksum(payload_crc));
s = dest_->Append(opts, Slice(ptr, n), payload_crc);
}
block_offset_ += header_size + n;
return s;
@@ -386,7 +387,7 @@ IOStatus Writer::MaybeSwitchToNewBlock(const WriteOptions& write_options,
}
std::vector<char> trailer(leftover, '\x00');
s = dest_->Append(Slice(trailer.data(), trailer.size()), opts);
s = dest_->Append(opts, Slice(trailer.data(), trailer.size()));
if (!s.ok()) {
return s;
}
+4 -8
View File
@@ -189,12 +189,11 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options,
WriteBufferManager* write_buffer_manager,
SequenceNumber latest_seq, uint32_t column_family_id,
FlushInitiator* flush_initiator)
SequenceNumber latest_seq, uint32_t column_family_id)
: comparator_(cmp),
moptions_(ioptions, mutable_cf_options),
kArenaBlockSize(Arena::OptimizeBlockSize(moptions_.arena_block_size)),
mem_tracker_(write_buffer_manager, flush_initiator),
mem_tracker_(write_buffer_manager),
arena_(moptions_.arena_block_size,
(write_buffer_manager != nullptr &&
(write_buffer_manager->enabled() ||
@@ -1166,7 +1165,6 @@ Status MemTable::Add(SequenceNumber s, ValueType type,
}
Slice key_without_ts = StripTimestampFromUserKey(key, ts_sz_);
bool activate_flush_initiator = false;
if (!allow_concurrent) {
// Extract prefix for insert with hint. Hints are for point key table
@@ -1189,7 +1187,7 @@ Status MemTable::Add(SequenceNumber s, ValueType type,
// (non-concurrent), these counters may be concurrently modified by
// BatchPostProcess() (e.g., from AddLogicallyRedundantRangeTombstone
// called on a read path).
activate_flush_initiator = num_entries_.FetchAddRelaxed(1) == 0;
num_entries_.FetchAddRelaxed(1);
data_size_.FetchAddRelaxed(encoded_len);
if (type == kTypeDeletion || type == kTypeSingleDeletion ||
type == kTypeDeletionWithTimestamp) {
@@ -1246,9 +1244,6 @@ Status MemTable::Add(SequenceNumber s, ValueType type,
while ((cur_earliest_seqno == kMaxSequenceNumber || s < cur_earliest_seqno) &&
!earliest_seqno_.compare_exchange_weak(cur_earliest_seqno, s)) {
}
if (activate_flush_initiator) {
mem_tracker_.ActivateFlushInitiator();
}
MaybeUpdateNewestUDT(key_slice);
if (type == kTypeRangeDeletion) {
auto new_cache = std::make_shared<FragmentedRangeTombstoneListCache>();
@@ -1279,6 +1274,7 @@ Status MemTable::Add(SequenceNumber s, ValueType type,
is_range_del_table_empty_.StoreRelaxed(false);
}
UpdateOldestKeyTime();
TEST_SYNC_POINT_CALLBACK("MemTable::Add:BeforeReturn:Encoded", &encoded);
return Status::OK();
}
+2 -29
View File
@@ -126,10 +126,6 @@ class ReadOnlyMemTable {
// used by MemTableListVersion::MemoryAllocatedBytesExcludingLast
virtual size_t MemoryAllocatedBytes() const = 0;
// Returns memory charged to this memtable's WriteBufferManager. Memtable
// implementations that do not allocate through a WBM return zero.
virtual size_t WBMTrackedMemoryUsage() const = 0;
// Returns a vector of unique random memtable entries of size 'sample_size'.
//
// Note: the entries are stored in the unordered_set as length-prefixed keys,
@@ -601,8 +597,7 @@ class MemTable final : public ReadOnlyMemTable {
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options,
WriteBufferManager* write_buffer_manager,
SequenceNumber earliest_seq, uint32_t column_family_id,
FlushInitiator* flush_initiator);
SequenceNumber earliest_seq, uint32_t column_family_id);
// No copying allowed
MemTable(const MemTable&) = delete;
MemTable& operator=(const MemTable&) = delete;
@@ -625,24 +620,6 @@ class MemTable final : public ReadOnlyMemTable {
arena_.MemoryAllocatedBytes();
}
size_t WBMTrackedMemoryUsage() const override {
return mem_tracker_.allocated_bytes();
}
bool IsWBMTrackingActive() const {
return mem_tracker_.IsFlushInitiatorActive();
}
void RefreshWBMTracking() {
ReadLock rl(&immutable_mutex_);
mem_tracker_.RefreshFlushInitiator();
}
void StopWBMTracking() {
WriteLock wl(&immutable_mutex_);
mem_tracker_.DeactivateFlushInitiator();
}
void UniqueRandomSample(const uint64_t& target_sample_size,
std::unordered_set<const char*>* entries) override {
// TODO(bjlemaire): at the moment, only supported by skiplistrep.
@@ -762,11 +739,7 @@ class MemTable final : public ReadOnlyMemTable {
// Used in concurrent memtable inserts.
void BatchPostProcess(const MemTablePostProcessInfo& update_counters) {
table_->BatchPostProcess();
const uint64_t old_num_entries =
num_entries_.FetchAddRelaxed(update_counters.num_entries);
if (old_num_entries == 0 && update_counters.num_entries != 0) {
mem_tracker_.ActivateFlushInitiator();
}
num_entries_.FetchAddRelaxed(update_counters.num_entries);
data_size_.FetchAddRelaxed(update_counters.data_size);
if (update_counters.num_deletes != 0) {
num_deletes_.FetchAddRelaxed(update_counters.num_deletes);
-18
View File
@@ -794,24 +794,6 @@ size_t MemTableList::ApproximateUnflushedMemTablesMemoryUsage() {
return total_size;
}
size_t MemTableList::WBMTrackedUnflushedMemTablesMemoryUsage() const {
size_t total_size = 0;
for (const ReadOnlyMemTable* memtable : current_->memlist_) {
total_size += memtable->WBMTrackedMemoryUsage();
}
return total_size;
}
size_t MemTableList::WBMTrackedUnstartedMemTablesMemoryUsage() const {
size_t total_size = 0;
for (const ReadOnlyMemTable* memtable : current_->memlist_) {
if (!memtable->flush_in_progress_) {
total_size += memtable->WBMTrackedMemoryUsage();
}
}
return total_size;
}
size_t MemTableList::ApproximateMemoryUsage() { return current_memory_usage_; }
size_t MemTableList::MemoryAllocatedBytesExcludingLast() const {
-8
View File
@@ -399,14 +399,6 @@ class MemTableList {
// the unflushed mem-tables.
size_t ApproximateUnflushedMemTablesMemoryUsage();
// Returns the memory charged to the WriteBufferManager by unflushed
// memtables.
size_t WBMTrackedUnflushedMemTablesMemoryUsage() const;
// Returns WBM-tracked memory for immutable memtables whose flush has not
// started. Requires the DB mutex, like the other MemTableList accessors.
size_t WBMTrackedUnstartedMemTablesMemoryUsage() const;
// Returns an estimate of the timestamp of the earliest key.
uint64_t ApproximateOldestKeyTime() const;
+18 -28
View File
@@ -267,8 +267,7 @@ TEST_F(MemTableListTest, GetTest) {
WriteBufferManager wb(options.db_write_buffer_size);
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
mem->Ref();
// Write some keys to this memtable.
@@ -336,8 +335,7 @@ TEST_F(MemTableListTest, GetTest) {
// Create another memtable and write some keys to it
WriteBufferManager wb2(options.db_write_buffer_size);
MemTable* mem2 = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb2,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
mem2->SetID(2);
mem2->Ref();
@@ -434,8 +432,7 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
WriteBufferManager wb(options.db_write_buffer_size);
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
mem->Ref();
// Write some keys to this memtable.
@@ -539,8 +536,7 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
// Create another memtable and write some keys to it
WriteBufferManager wb2(options.db_write_buffer_size);
MemTable* mem2 = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb2,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
mem2->Ref();
ASSERT_OK(
@@ -570,8 +566,7 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
// Add a third memtable to push the first memtable out of the history
WriteBufferManager wb3(options.db_write_buffer_size);
MemTable* mem3 = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb3,
kMaxSequenceNumber, 0 /* column_family_id */,
nullptr /* flush_initiator */);
kMaxSequenceNumber, 0 /* column_family_id */);
mem3->Ref();
// This is to make assert(memtable->IsFragmentedRangeTombstonesConstructed())
// in MemTableListVersion::GetFromList work.
@@ -665,9 +660,8 @@ TEST_F(MemTableListTest, FlushPendingTest) {
std::vector<MemTable*> tables;
MutableCFOptions mutable_cf_options(options);
for (int i = 0; i < num_tables; i++) {
MemTable* mem =
new MemTable(cmp, ioptions, mutable_cf_options, &wb, kMaxSequenceNumber,
0 /* column_family_id */, nullptr /* flush_initiator */);
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
kMaxSequenceNumber, 0 /* column_family_id */);
mem->SetID(memtable_id++);
mem->Ref();
@@ -941,9 +935,8 @@ TEST_F(MemTableListTest, FlushRequestPersistsAfterPartialPick) {
0 /* max_write_buffer_size_to_maintain */);
for (uint64_t memtable_id = 0; memtable_id < 2; ++memtable_id) {
MemTable* mem =
new MemTable(cmp, ioptions, mutable_cf_options, &wb, kMaxSequenceNumber,
0 /* column_family_id */, nullptr /* flush_initiator */);
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
kMaxSequenceNumber, 0 /* column_family_id */);
mem->SetID(memtable_id);
mem->Ref();
ASSERT_OK(mem->Add(++seq, kTypeValue, "key" + std::to_string(memtable_id),
@@ -998,9 +991,8 @@ TEST_F(MemTableListTest, RemoveOldMemTablesTest) {
// WAL 20.
SequenceNumber seq = 1;
for (uint64_t next_log_number : {10, 20}) {
MemTable* mem =
new MemTable(cmp, ioptions, mutable_cf_options, &wb, kMaxSequenceNumber,
0 /* column_family_id */, nullptr /* flush_initiator */);
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
kMaxSequenceNumber, 0 /* column_family_id */);
mem->Ref();
ASSERT_OK(mem->Add(++seq, kTypeValue, "key", "value",
nullptr /* kv_prot_info */));
@@ -1073,9 +1065,9 @@ TEST_F(MemTableListTest, AtomicFlushTest) {
mutable_cf_options_list.emplace_back(new MutableCFOptions(options));
uint64_t memtable_id = 0;
for (int i = 0; i != num_tables_per_cf; ++i) {
MemTable* mem = new MemTable(
cmp, ioptions, *(mutable_cf_options_list.back()), &wb,
kMaxSequenceNumber, cf_id, nullptr /* flush_initiator */);
MemTable* mem =
new MemTable(cmp, ioptions, *(mutable_cf_options_list.back()), &wb,
kMaxSequenceNumber, cf_id);
mem->SetID(memtable_id++);
mem->Ref();
@@ -1226,9 +1218,8 @@ TEST_F(MemTableListWithTimestampTest, GetTableNewestUDT) {
std::string key;
std::string write_ts;
for (int i = 0; i < num_tables; i++) {
MemTable* mem =
new MemTable(cmp, ioptions, mutable_cf_options, &wb, kMaxSequenceNumber,
0 /* column_family_id */, nullptr /* flush_initiator */);
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
kMaxSequenceNumber, 0 /* column_family_id */);
mem->SetID(memtable_id++);
mem->Ref();
@@ -1280,9 +1271,8 @@ TEST_F(MemTableListWithTimestampTest, ConcurrentGetTableNewestUDT) {
WriteBufferManager wb(options.db_write_buffer_size);
MutableCFOptions mutable_cf_options(options);
MemTable* mem =
new MemTable(cmp, ioptions, mutable_cf_options, &wb, kMaxSequenceNumber,
0 /* column_family_id */, nullptr /* flush_initiator */);
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
kMaxSequenceNumber, 0 /* column_family_id */);
mem->Ref();
std::atomic<uint64_t> next_seq{1};
+2 -2
View File
@@ -575,7 +575,7 @@ class Repairer {
status =
AddColumnFamily(props->column_family_name, t->column_family_id);
}
t->meta.oldest_ancestor_time = props->oldest_ancestor_time;
t->meta.oldest_ancester_time = props->creation_time;
t->meta.user_defined_timestamps_persisted =
static_cast<bool>(props->user_defined_timestamps_persisted);
}
@@ -712,7 +712,7 @@ class Repairer {
0, meta.fd.GetNumber(), meta.fd.GetPathId(), meta.fd.GetFileSize(),
meta.smallest, meta.largest, meta.fd.smallest_seqno,
meta.fd.largest_seqno, meta.marked_for_compaction, meta.temperature,
meta.oldest_blob_file_number, meta.oldest_ancestor_time,
meta.oldest_blob_file_number, meta.oldest_ancester_time,
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
meta.file_checksum_func_name, meta.unique_id,
meta.compensated_range_deletion_size, meta.tail_size,
+8 -11
View File
@@ -10,7 +10,6 @@
#include "db/table_cache.h"
#include <algorithm>
#include <utility>
#include "db/dbformat.h"
#include "db/range_tombstone_fragmenter.h"
@@ -73,8 +72,7 @@ TableCache::TableCache(const ImmutableOptions& ioptions,
const FileOptions* file_options, Cache* const cache,
BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_session_id, bool fast_sst_open,
std::string column_family_name)
const std::string& db_session_id, bool fast_sst_open)
: ioptions_(ioptions),
file_options_(*file_options),
cache_(cache),
@@ -85,8 +83,7 @@ TableCache::TableCache(const ImmutableOptions& ioptions,
block_cache_tracer_(block_cache_tracer),
loader_mutex_(kLoadConcurency),
io_tracer_(io_tracer),
db_session_id_(db_session_id),
column_family_name_(std::move(column_family_name)) {
db_session_id_(db_session_id) {
if (ioptions_.row_cache) {
// If the same cache is shared by multiple instances, we need to
// disambiguate its entries.
@@ -174,11 +171,11 @@ Status TableCache::GetTableReader(
}
StopWatch sw(ioptions_.clock, ioptions_.stats, TABLE_OPEN_IO_MICROS);
std::unique_ptr<RandomAccessFileReader> file_reader(
new RandomAccessFileReader(
std::move(file), fname, ioptions_.clock, io_tracer_,
ioptions_.stats, SST_READ_MICROS, file_read_hist,
ioptions_.rate_limiter.get(), ioptions_.listeners, file_temperature,
level == ioptions_.num_levels - 1, ioptions_.fs.get()));
new RandomAccessFileReader(std::move(file), fname, ioptions_.clock,
io_tracer_, ioptions_.stats, SST_READ_MICROS,
file_read_hist, ioptions_.rate_limiter.get(),
ioptions_.listeners, file_temperature,
level == ioptions_.num_levels - 1));
UniqueId64x2 expected_unique_id;
if (ioptions_.verify_sst_unique_id_in_manifest) {
expected_unique_id = file_meta.unique_id;
@@ -194,7 +191,7 @@ Status TableCache::GetTableReader(
db_session_id_, file_meta.fd.GetNumber(), expected_unique_id,
file_meta.fd.largest_seqno, file_meta.tail_size,
file_meta.user_defined_timestamps_persisted,
avoid_shared_metadata_cache, column_family_name_);
avoid_shared_metadata_cache);
// Route same-file ("embedded") blob reads through the CFD's BlobSource for
// caching + stats. nullptr in non-DB contexts (e.g. repair).
table_reader_options.blob_source = blob_source_;
+1 -3
View File
@@ -67,8 +67,7 @@ class TableCache {
const FileOptions* storage_options, Cache* cache,
BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_session_id, bool fast_sst_open = false,
std::string column_family_name = "");
const std::string& db_session_id, bool fast_sst_open = false);
~TableCache();
// Sets the BlobSource used to route same-file ("embedded") blob reads of SSTs
@@ -365,7 +364,6 @@ class TableCache {
Striped<CacheAlignedWrapper<port::Mutex>> loader_mutex_;
std::shared_ptr<IOTracer> io_tracer_;
std::string db_session_id_;
std::string column_family_name_;
// Owned by the same ColumnFamilyData; see SetBlobSource(). nullptr in non-DB
// contexts (e.g. repair).
BlobSource* blob_source_ = nullptr;
+3 -5
View File
@@ -1718,11 +1718,9 @@ class VersionBuilder::Rep : public MutableScalars {
missing_blob_files_high_ != kInvalidBlobFileNumber;
}
std::vector<std::string> GetAndClearIntermediateFiles() {
std::vector<std::string>& GetAndClearIntermediateFiles() {
assert(track_found_and_missing_files_);
std::vector<std::string> result;
result.swap(intermediate_files_);
return result;
return intermediate_files_;
}
void ClearFoundFiles() {
@@ -2075,7 +2073,7 @@ bool VersionBuilder::ValidVersionAvailable() {
bool VersionBuilder::HasMissingFiles() const { return rep_->HasMissingFiles(); }
std::vector<std::string> VersionBuilder::GetAndClearIntermediateFiles() {
std::vector<std::string>& VersionBuilder::GetAndClearIntermediateFiles() {
return rep_->GetAndClearIntermediateFiles();
}
+5 -4
View File
@@ -77,10 +77,11 @@ class VersionBuilder {
bool HasMissingFiles() const;
// When applying a sequence of VersionEdits, intermediate files are the ones
// that are added and then deleted. Returns the tracked files and clears them
// so that subsequent VersionEdits start with a clean state.
std::vector<std::string> GetAndClearIntermediateFiles();
// When applying a sequence of VersionEdit, intermediate files are the ones
// that are added and then deleted. The caller should clear this intermediate
// files tracking after calling this API. So that the tracking for subsequent
// VersionEdits can start over with a clean state.
std::vector<std::string>& GetAndClearIntermediateFiles();
// Clearing all the found files in this Version.
void ClearFoundFiles();
+34 -34
View File
@@ -80,7 +80,7 @@ class VersionBuilderTest : public testing::Test {
file_number, path_id, file_size, GetInternalKey(smallest, smallest_seq),
GetInternalKey(largest, largest_seq), smallest_seqno, largest_seqno,
/* marked_for_compact */ false, Temperature::kUnknown,
oldest_blob_file_number, kUnknownOldestAncestorTime,
oldest_blob_file_number, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, epoch_number, kUnknownFileChecksum,
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
/* user_defined_timestamps_persisted */ true, /* min timestamp */ "",
@@ -145,7 +145,7 @@ class VersionBuilderTest : public testing::Test {
level, table_file_number, path_id, file_size, GetInternalKey(smallest),
GetInternalKey(largest), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, blob_file_number,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, epoch_number,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, epoch_number,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
0, 0, /* user_defined_timestamps_persisted */ true);
}
@@ -314,7 +314,7 @@ TEST_F(VersionBuilderTest, ApplyAndSaveTo) {
version_edit.AddFile(
2, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.DeleteFile(3, 27U);
@@ -365,7 +365,7 @@ TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic) {
version_edit.AddFile(
3, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -420,7 +420,7 @@ TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic2) {
version_edit.AddFile(
4, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.DeleteFile(0, 1U);
@@ -460,31 +460,31 @@ TEST_F(VersionBuilderTest, ApplyMultipleAndSaveTo) {
version_edit.AddFile(
2, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 676, 0, 100U, GetInternalKey("401"), GetInternalKey("450"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 636, 0, 100U, GetInternalKey("601"), GetInternalKey("650"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 616, 0, 100U, GetInternalKey("501"), GetInternalKey("550"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 606, 0, 100U, GetInternalKey("701"), GetInternalKey("750"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -530,31 +530,31 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
version_edit.AddFile(
2, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 676, 0, 100U, GetInternalKey("401"), GetInternalKey("450"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 636, 0, 100U, GetInternalKey("601"), GetInternalKey("650"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 616, 0, 100U, GetInternalKey("501"), GetInternalKey("550"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit.AddFile(
2, 606, 0, 100U, GetInternalKey("701"), GetInternalKey("750"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
ASSERT_OK(version_builder.Apply(&version_edit));
@@ -563,7 +563,7 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
version_edit.AddFile(
2, 808, 0, 100U, GetInternalKey("901"), GetInternalKey("950"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
version_edit2.DeleteFile(2, 616);
@@ -571,7 +571,7 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
version_edit.AddFile(
2, 806, 0, 100U, GetInternalKey("801"), GetInternalKey("850"), 200, 200,
false, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -683,7 +683,7 @@ TEST_F(VersionBuilderTest, ApplyFileDeletionAndAddition) {
GetInternalKey(smallest, smallest_seq),
GetInternalKey(largest, largest_seq), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -735,7 +735,7 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyInBase) {
new_level, file_number, path_id, file_size, GetInternalKey(smallest),
GetInternalKey(largest), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -772,7 +772,7 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyApplied) {
level, file_number, path_id, file_size, GetInternalKey(smallest),
GetInternalKey(largest), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -786,7 +786,7 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyApplied) {
new_level, file_number, path_id, file_size, GetInternalKey(smallest),
GetInternalKey(largest), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -823,7 +823,7 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAndDeletion) {
level, file_number, path_id, file_size, GetInternalKey(smallest),
GetInternalKey(largest), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
0, /* user_defined_timestamps_persisted */ true);
@@ -1433,7 +1433,7 @@ TEST_F(VersionBuilderTest, SaveBlobFilesToConcurrentJobs) {
level, table_file_number, path_id, file_size, GetInternalKey(smallest),
GetInternalKey(largest), smallest_seqno, largest_seqno,
marked_for_compaction, Temperature::kUnknown, blob_file_number,
kUnknownOldestAncestorTime, kUnknownFileCreationTime, 2 /*epoch_number*/,
kUnknownOldestAncesterTime, kUnknownFileCreationTime, 2 /*epoch_number*/,
checksum_value, checksum_method, kNullUniqueId64x2, 0, 0,
/* user_defined_timestamps_persisted */ true);
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
@@ -1522,7 +1522,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
/* largest */ GetInternalKey("750"), /* smallest_seqno */ 200,
/* largest_seqno */ 200, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ 16, kUnknownOldestAncestorTime,
/* oldest_blob_file_number */ 16, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0,
@@ -1533,7 +1533,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
/* largest */ GetInternalKey("850"), /* smallest_seqno */ 200,
/* largest_seqno */ 200, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ 1000, kUnknownOldestAncestorTime,
/* oldest_blob_file_number */ 1000, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0,
@@ -1764,7 +1764,7 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
/* largest */ GetInternalKey("21", 2100), /* smallest_seqno */ 2100,
/* largest_seqno */ 2100, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ 1, kUnknownOldestAncestorTime,
/* oldest_blob_file_number */ 1, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, kUnknownEpochNumber, kUnknownFileChecksum,
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
/* user_defined_timestamps_persisted */ true);
@@ -1775,7 +1775,7 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
/* file_size */ 100, /* smallest */ GetInternalKey("22", 2200),
/* largest */ GetInternalKey("22", 2200), /* smallest_seqno */ 2200,
/* largest_seqno */ 2200, /* marked_for_compaction */ false,
Temperature::kUnknown, kInvalidBlobFileNumber, kUnknownOldestAncestorTime,
Temperature::kUnknown, kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, kUnknownEpochNumber, kUnknownFileChecksum,
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
/* user_defined_timestamps_persisted */ true);
@@ -1798,7 +1798,7 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
/* smallest_seqno */ 300,
/* largest_seqno */ 300, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ 3, kUnknownOldestAncestorTime,
/* oldest_blob_file_number */ 3, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0,
@@ -1812,7 +1812,7 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
/* smallest_seqno */ 1300,
/* largest_seqno */ 1300, /* marked_for_compaction */ false,
Temperature::kUnknown, kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime,
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
kUnknownEpochNumber, kUnknownFileChecksum,
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
/* user_defined_timestamps_persisted */ true);
@@ -1826,7 +1826,7 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
/* smallest_seqno */ 2300,
/* largest_seqno */ 2300, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ 5, kUnknownOldestAncestorTime,
/* oldest_blob_file_number */ 5, kUnknownOldestAncesterTime,
kUnknownFileCreationTime, kUnknownEpochNumber,
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0,
@@ -1937,7 +1937,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
/* largest_seqno */ 3, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime,
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
1 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
version_edit_1.AddFile(
@@ -1947,7 +1947,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
/* largest_seqno */ 4, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime,
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
1 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
@@ -1977,7 +1977,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
/* largest_seqno */ 1, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime,
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
1 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
version_edit_2.AddFile(
@@ -1987,7 +1987,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
/* largest_seqno */ 2, /* marked_for_compaction */ false,
Temperature::kUnknown,
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
kUnknownOldestAncestorTime, kUnknownFileCreationTime,
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
2 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
Loaded 100 of 254 files, more files were not shown because too many files have changed in this diff. Show more