Compare commits

..
26 Commits
Author SHA1 Message Date
anand76 bb4497729c Update HISTORY and version for 10.7.6 2025-10-27 16:42:03 -07:00
Xingbo Wang 75623f6a7a Follow up on MultiScan change in #14040 (#14055)
Summary:
* Address feedback from https://github.com/facebook/rocksdb/issues/14040
* Add additional test for MultiScan
* Fix a bug when del range and data are in same file for multi-scan
* Rewrite the cases need to be handled in SeekMultiScan

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

Test Plan: Unit test

Reviewed By: cbi42, anand1976

Differential Revision: D84851788

Pulled By: xingbowang

fbshipit-source-id: 0f69632733afb99685f6341badbf239681010c38
2025-10-27 16:19:57 -07:00
anand76 812b12bc78 Update HISTORY and version for 10.7.5 2025-10-20 11:17:17 -07:00
Xingbo Wang fba76b2ab8 Fix a nullptr access bug in MultiScan (#14062)
Summary:
Fixing a nullptr access in multiscan, under following situation.

```
Block Based Table: blk1:[k1,k2], blk2:[k3,                k8], blk3:[k9]
Scan ranges:            [k1,             k4), [k5,k6), [k7,            k10)
Prepared block ranges:  [0,2],                [2,2],   [1,3]
```

1. Seek key k1 on the first range, read key k1, k2.
2. Seek key k4 on the 2nd range, blocks 0,1 would be unpinned.
3. Seek key k9, block 1 would be accessed, but it is unpinned, which trigger assert failure in debug mode and nullptr access on release build.

This fix changes how blocks are unpinned. It is now only unpinning the block, when the cur_data_block_idx has passed it.

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

Test Plan:
Unit Test
rand_seed 304010984 on UserDefinedIndexStressTest

Reviewed By: cbi42

Differential Revision: D84976410

Pulled By: xingbowang

fbshipit-source-id: 6b99bf85fc9d4108c5267ae77be77ccfe08923cd
2025-10-20 11:13:02 -07:00
Xingbo Wang 24efab2f04 Release RocksDB 10.7.4
Summary:

Release RocksDB 10.7.4

Test Plan:

Unit test

Reviewers:

Subscribers:

Tasks:

Tags:
2025-10-14 15:36:55 -07:00
anand76 c8746289ba Allow empty MultiScan result in BlockBasedTableIterator Prepare (#14046)
Summary:
Currently in BlockBasedTableIterator's Prepare(), the index lookup for a MultiScan range is expected to return atleast 1 data block (unless UDI is in use). This is because there's an implicit assumption that only ranges intersecting with the keys in the file will be prepared. This assumption, however, doesn't hold if there are range deletions and the smallest and/or largest keys in the file extend beyond the keys in the file. The LevelIterator prunes the MultiScan ranges based on the smallest/largest key, so its possible for a range to only overlap the range deletion portion of the file and not overlap any of the data blocks. Furthermore, the BlockBasedTableIterator is now much more forgiving of Seek to targets outside of prepared ranges after https://github.com/facebook/rocksdb/issues/14040 .

Keeping the above in mind, this PR removes the check in BlockBasedTableIterator for non-empty index result. It adds assertions in LevelIterator to verify that ranges are being properly pruned. Another side effect is we can no longer rely solely on a scan range having 0 data blocks (i.e cur_scan_start_idx >= cur_scan_end_idx) to decide if the iterator is out of bound. We can only do so for all but the last range prepared range.

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

Test Plan:
1. Add unit test in db_iterator_test
2. Run crash test

Reviewed By: xingbowang

Differential Revision: D84623871

Pulled By: anand1976

fbshipit-source-id: 2418e629f92b1c46c555ddea3761140f700819e4
2025-10-14 14:29:16 -07:00
anand76 6507b9ced8 Use wget for folly dependency download (#14030)
Summary:
Fix the binutils truncated download issue by switching to wget in the folly build scripts for downloading dependencies.

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

Test Plan: make build_folly

Reviewed By: jaykorean

Differential Revision: D84033126

Pulled By: anand1976

fbshipit-source-id: bc6706d7e57c97d6edff149a965aa12c7959825f
2025-10-13 16:10:01 -07:00
Xingbo Wang d3713c0fe3 Move the MultiScan seek key check to upper layer (#14040)
Summary:
The current seek key validation is too strict. This change relaxes it at block iterator level, and add additional check at DB iterator level. The new contract is that when MultiScan is used, after prepared is called, each following seek must seek the start key of the prepared scan range in order. Otherwise, the iterator is set with error status.

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

Test Plan: Unit test

Reviewed By: anand1976

Differential Revision: D84292297

Pulled By: xingbowang

fbshipit-source-id: 7b31f727e67e7c0bfc53c2f9a6552e0c3d324869
2025-10-13 15:42:01 -07:00
anand76 d18c2906fa Pass the correct comparator to MultiScanArgs (#14033)
Summary:
Fix assertion failure in crash tests with timestamp due to the wrong comparator passed to MultiScanArgs

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

Reviewed By: xingbowang

Differential Revision: D84036954

Pulled By: anand1976

fbshipit-source-id: 526be21c0754dcccf8e4d2b9fba33716fe35860a
2025-10-13 15:36:08 -07:00
Xingbo Wang f972d77133 Allow passing comparator in UDI (#14001)
Summary:
Pass the comparator to UDI interface for both reader and builder.

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

Test Plan: Unit test

Reviewed By: anand1976

Differential Revision: D83339943

Pulled By: xingbowang

fbshipit-source-id: 7f6541776b0995260e28224329f0cca37f13b3d4
2025-10-13 15:35:57 -07:00
Xingbo Wang 9775409298 Update version and HISTORY for 10.7.3 patch release 2025-10-06 16:54:02 -07:00
Xingbo Wang 4e7fe99741 Fix range delete file caused MultiScan issue (#14028)
Summary:
When there is an ingested SST file that only contains delete range operations, MultiScan may return error "Scan does not intersect with file". This is due to file selection during Prepare uses the file smallest and largest key without considering whether there is any key in the file. This is only a temporary fix.

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

Test Plan: Unit test

Reviewed By: anand1976

Differential Revision: D83986964

Pulled By: xingbowang

fbshipit-source-id: e0961ca854e2062c2457be4324817ba073ae785d
2025-10-06 16:50:59 -07:00
Changyu Bi 4b0558b948 Fix handling of out-of-range scan option (#13995)
Summary:
currently BlockBasedTableIterator::Prepare() fails the iterator with non-ok status if an out-of-range scan option is detected. This is due to the interaction between LevelIterator and BlockBasedTableIterator, see added comment above BlockBasedTableIterator::Prepare(). This can fail stress test for L0 files since it doesn't use LevelIterator and scan options are not pruned. This PR fixes this by adding an internal option to MultiScanArgs that enables this check.

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

Test Plan:
- new unit test
- stress test that fails before this pr: `python3 -u ./tools/db_crashtest.py whitebox --iterpercent=60 --prefix_size=-1 --prefixpercent=0 --readpercent=0 --test_batches_snapshots=0 --use_multiscan=1 --read_fault_one_in=0 --kill_random_test=88888 --interval=60 --multiscan_use_async_io=0 --mmap_read=0 --level0_file_num_compaction_trigger=20`

Reviewed By: anand1976

Differential Revision: D83166088

Pulled By: cbi42

fbshipit-source-id: 241a7d43c8c00d9a98eea0cabb03d2174d51aae5
2025-10-06 16:50:59 -07:00
Peter Dillinger a74934f0f5 Update version and HISTORY for 10.7.2 patch release 2025-10-02 10:27:59 -07:00
anand76 b4a3612256 Fix incorrect MultiScan handling of range limit between files (#14011)
Summary:
This PR fixes a bug in how MultiScan handled a scan range limit falling in the key range between files. The bug was in LevelIterator, where Prepare() relied on FindFile to determine the lower bound file for the range limit. FindFile returns the smallest file index with `range.limit < file.largest_key`. However, that doesn't guarantee that the range overlaps the file, as the `range.limit` could be smaller than `file.smallest_key`.

This also fixes a bug in BlockBasedTableIterator of Valid() returning true even if status() returned error. This was exposed by the previous bug.

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

Test Plan: Add unit tests in db_iterator_test and table_test

Reviewed By: cbi42

Differential Revision: D83496439

Pulled By: anand1976

fbshipit-source-id: a9d2d138d69d0c816d9f4160a984b273d00d683f
2025-10-02 10:01:22 -07:00
Peter Dillinger c44a283847 Fix regression in LZ4 compression performance since 10.6 (#14017)
Summary:
In RocksDB 10.6 with https://github.com/facebook/rocksdb/issues/13805, due to inaccurate testing of an async system, it went undetected at the time that LZ4 compression was using more CPU despite making a change to reuse stream objects which dramatically improved LZ4HC compression efficiency.

This change switches to using a basic LZ4 compress API which appears to be faster than all of these:
* Legacy behavior of creating LZ4_stream_t for each compression
* 10.6-10.7 behavior of re-using streams between compressions for the same file (with stream-as-WorkingArea)
* using LZ4's extState APIs without streams (with extState-as-WorkingArea) (data not shown in below results)

Also in this PR: more improvements to sst_dump --recompress, which is arguably the best SST construction benchmark right now since db_bench seems to be so noisy due to backgroun flush+compaction, even with no compaction (FIFO). Streamlined some output and added a SST read time test, mostly for decompression performance.

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

Test Plan:
Performance test using sst_dump --recompress with newer sst_dump back-ported to 10.5:
```
./sst_dump --command=recompress --compression_types=kLZ4Compression
test5.sst --compression_level_from=-6 --compression_level_to=-1
```
and with default compression level.

10.5:
```
Cx level: -6    Cx size:   61608137 Write usec:     880404
Cx level: -5    Cx size:   60793749 Write usec:     840903
Cx level: -4    Cx size:   58134030 Write usec:     836365
Cx level: -3    Cx size:   55193773 Write usec:     857113
Cx level: -2    Cx size:   54013891 Write usec:     855642
Cx level: -1    Cx size:   50400393 Write usec:     865194
Cx level: 32767 Cx size:   50400393 Write usec:     886310
```

Before this change (showing the regression, more time, from 10.6:
```
Cx level: -6    Cx size:   61608137 Write usec:     933448
Cx level: -5    Cx size:   60793749 Write usec:     893826
Cx level: -4    Cx size:   58134030 Write usec:     891138
Cx level: -3    Cx size:   55193773 Write usec:     898461
Cx level: -2    Cx size:   54013891 Write usec:     897485
Cx level: -1    Cx size:   50400393 Write usec:     936970
Cx level: 32767 Cx size:   50400393 Write usec:     958764
```

After this change (faster than both the above):
```
Cx level: -6    Cx size:   63641883 Write usec:     874190
Cx level: -5    Cx size:   58860032 Write usec:     834662
Cx level: -4    Cx size:   57150188 Write usec:     832707
Cx level: -3    Cx size:   58791894 Write usec:     850305
Cx level: -2    Cx size:   53145885 Write usec:     839574
Cx level: -1    Cx size:   49809139 Write usec:     845639
Cx level: 32767 Cx size:   49809139 Write usec:     875199
```

Similar tests with dictionary compression show essentially no difference (need to use stream APIs and reuse doesn't seem to matter). LZ4HC also unaffected (still improved vs. 10.5)

Reviewed By: hx235

Differential Revision: D83722880

Pulled By: pdillinger

fbshipit-source-id: 30149dd187686d5dd98321e6aa7d74bd7653a905
2025-10-02 09:16:59 -07:00
Peter Dillinger 12b6696e7f Resolve missing/inconsistent tickers in Java (#14012)
Summary:
Pretty self-explanatory from the changes, including re-arranging the "COOL" entries for easier tracking of which values are used.

I'm not touching the TICKER_ENUM_MAX issue because IIRC we've gotten in trouble in the past for changing any Java ticker values.

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

Test Plan: CI, sufficient prompts to get AI to discover the known issues relayed by hx235, to help ensure we found any other outstanding issues.

Reviewed By: hx235

Differential Revision: D83497503

Pulled By: pdillinger

fbshipit-source-id: ec0bd7e28188e0430fb03fc5bd79c2ed7b28f3ad
2025-09-30 11:20:13 -07:00
Peter Dillinger 10985ceb00 Add kCool Temperature (#14000)
Summary:
also requested by internal user, like kIce in https://github.com/facebook/rocksdb/issues/13927

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

Test Plan: unit tests updated

Reviewed By: archang19

Differential Revision: D83200479

Pulled By: pdillinger

fbshipit-source-id: 31f2842d87bcad40227aeee9687ff5772393689c
2025-09-30 11:19:52 -07:00
Andrew Chang cd6491d0a4 Update version.h for 10.7.1 2025-09-29 09:14:57 -07:00
Andrew Chang c411586f1b Add shared mutex field to IODebugContext (#13993)
Summary:
There can be concurrent reads/writes to fields in `IODebugContext`. One example we have seen is for the `cost_info` field which is of type `std::any`. In fact, in RocksDB's async MultiRead implementation, the same `IODebugContext` is re-used across separate async read requests.

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

Test Plan: Update code which reads/writes to `cost_data` to first acquire shared/exclusive lock on the `mutex` field. There should not be any race conditions when async MultiRead is used.

Reviewed By: pdillinger

Differential Revision: D83091423

Pulled By: archang19

fbshipit-source-id: 4db86d33cf162ed39114b1cd115fcd8964c8ff9b
2025-09-29 09:03:51 -07:00
Changyu Bi 09074d7fb7 update HISTORY 2025-09-24 16:11:38 -07:00
anand76 f9ef31a08e Allow UDIs with non BytewiseComparator (#13999)
Summary:
Remove the restriction of only using BytewiseComparator(). In a follow on PR, the UDI interface will be updated to take the Comparator as a parameter.

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

Test Plan: Add a unit test in table_test.cc

Reviewed By: cbi42

Differential Revision: D83179747

Pulled By: anand1976

fbshipit-source-id: 60222533c71022aa0701ac61c39268d36ca86338
2025-09-24 16:10:19 -07:00
Peter Dillinger 17bcdd9bce Speed up AutoHCC check in dtor (#13998)
Summary:
In https://github.com/facebook/rocksdb/issues/13964 I changed an expensive DEBUG check in ~AutoHyperClockTable to only run in ASAN builds. It's still expensive so I'm modifying it to scan only about one page beyond what we expect to have written to the anonymous mmap, rather than scanning the whole thing.

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

Test Plan: manually checked that lru_cache_test running time went from 5.0s to 4.0s after the change. Verified that existing unit test ClockCacheTest.Limits uses the full anonymous mmap to be sure it is sized as expected, by temporarily breaking AutoHyperClockTable::Grow() to allow slightly exceeding the anonymous mmap size.

Reviewed By: cbi42

Differential Revision: D83178493

Pulled By: pdillinger

fbshipit-source-id: a2bf093e98bf68b540c073800be7e193021f2692
2025-09-24 15:25:28 -07:00
anand76 28d9cc6484 Fail multi scan upon Prepare failure or bad scan options (#13974)
Summary:
Return a failure status for multi scan if Prepare fails, or if the scan options are unsupported, instead of falling back on a regular scan. This PR also fixes a bug in LevelIterator that caused max_prefetch_size to be ignored.

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

Test Plan: Add new test in db_iterator_test and table_test

Reviewed By: xingbowang

Differential Revision: D82843944

Pulled By: anand1976

fbshipit-source-id: f12756c40ebd38d8d4e4425e97438b6e766a4663
2025-09-23 09:55:58 -07:00
Changyu Bi 66236860f3 Revert "Create a new API FileSystem::SyncFile for file sync (#13762)" (#13987)
Summary:
This is causing some internal failure, we decide to revert this for now until we have a proper fix.

This reverts commit 961880b458.

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

Reviewed By: anand1976

Differential Revision: D82990294

Pulled By: cbi42

fbshipit-source-id: 5f5b4d18d0afe47599738d27e11e3eb2d08d88a0
2025-09-23 09:55:50 -07:00
Changyu Bi 6302542b9c Update HISTORY.md for 10.7.0 2025-09-19 18:21:09 -07:00
146 changed files with 1430 additions and 8404 deletions

No files matched your search

-10
View File
@@ -1,17 +1,7 @@
name: build-folly
description: Build folly and dependencies (skipped if cache hit)
inputs:
cache-hit:
description: Whether the folly cache was hit
required: true
runs:
using: composite
steps:
- name: Build folly and dependencies
if: ${{ inputs.cache-hit != 'true' }}
run: make build_folly
shell: bash
- name: Skip folly build (using cached version)
if: ${{ inputs.cache-hit == 'true' }}
run: echo "Folly build skipped - using cached version"
shell: bash
-26
View File
@@ -1,26 +0,0 @@
name: cache-folly
description: Cache folly build to speed up CI
outputs:
cache-hit:
description: Whether the cache was hit
value: ${{ steps.cache-folly-build.outputs.cache-hit }}
runs:
using: composite
steps:
- name: Extract FOLLY_COMMIT_HASH from Makefile
id: extract-folly-hash
shell: bash
run: |
FOLLY_COMMIT_HASH=$(grep '^FOLLY_COMMIT_HASH' Makefile | awk '{print $3}')
echo "hash=$FOLLY_COMMIT_HASH" >> $GITHUB_OUTPUT
- name: Cache folly build
id: cache-folly-build
uses: actions/cache@v4
with:
# Cache the folly build directory
path: /tmp/fbcode_builder_getdeps-Z__wZrocksdbZrocksdbZthird-partyZfollyZbuildZfbcode_builder-root/installed
# Key is based on:
# - OS and architecture
# - The specific folly commit hash from Makefile
# - The container image version to account for different compiler/library versions
key: folly-build-${{ runner.os }}-${{ runner.arch }}-${{ steps.extract-folly-hash.outputs.hash }}-ubuntu22.1-v1
+2 -17
View File
@@ -4,16 +4,6 @@ runs:
steps:
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.3.1
- name: Cache ccache directory
id: ccache-cache
uses: actions/cache@v4
with:
path: C:\a\rocksdb\rocksdb\.ccache
key: rocksdb-build-${{ runner.os }}-${{ runner.arch }}-ccache-${{ hashFiles('CMakeLists.txt', 'cmake/**/*.cmake') }}-v1
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
max-size: "10GB"
- name: Custom steps
env:
THIRDPARTY_HOME: ${{ github.workspace }}/thirdparty
@@ -48,12 +38,11 @@ runs:
$env:Path = $env:JAVA_HOME + ";" + $env:Path
mkdir build
cd build
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DWIN_CI=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
if(!$?) { Exit $LASTEXITCODE }
cd ..
echo "Building with VS version: $Env:CMAKE_GENERATOR"
# use more parallel processes than the number of processes available, as most of the compile command would be cache hit
msbuild build/rocksdb.sln /m:32 /p:LinkIncremental=false -property:Configuration=Debug -property:Platform=x64
msbuild build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
if(!$?) { Exit $LASTEXITCODE }
echo ========================= Test RocksDB =========================
build_tools\run_ci_db_test.ps1 -SuiteRun arena_test,db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
@@ -63,7 +52,3 @@ runs:
& ctest -C Debug -j 16
if(!$?) { Exit $LASTEXITCODE }
shell: pwsh
- name: Show ccache stats
shell: pwsh
run: |
ccache --show-stats -v
+20 -38
View File
@@ -103,11 +103,7 @@ jobs:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- uses: "./.github/actions/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
build-linux-make-with-folly:
@@ -121,11 +117,7 @@ jobs:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- uses: "./.github/actions/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j32 check
- uses: "./.github/actions/post-steps"
build-linux-make-with-folly-lite-no-test:
@@ -152,11 +144,7 @@ jobs:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- uses: "./.github/actions/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: "(mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-benchmark:
@@ -340,57 +328,51 @@ jobs:
# ========================= MacOS build only ======================== #
build-macos:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
runs-on: macos-13
env:
ROCKSDB_DISABLE_JEMALLOC: 1
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
xcode-version: 14.3.1
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: Build
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j8 all
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
- uses: "./.github/actions/post-steps"
# ========================= MacOS with Tests ======================== #
build-macos-cmake:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
runs-on: macos-13
strategy:
matrix:
run_sharded_tests: [0, 1, 2, 3]
run_even_tests: [true, false]
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
xcode-version: 14.3.1
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: cmake generate project file
run: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
- name: Build tests
run: cd build && make V=1 -j8
- name: Run shard 0 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 0,,4
if: ${{ matrix.run_sharded_tests == 0 }}
- name: Run shard 1 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 1,,4
if: ${{ matrix.run_sharded_tests == 1 }}
- name: Run shard 2 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 2,,4
if: ${{ matrix.run_sharded_tests == 2 }}
- name: Run shard 3 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 3,,4
if: ${{ matrix.run_sharded_tests == 3 }}
run: cd build && make V=1 -j16
- name: Run even tests
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 0,,2
if: ${{ matrix.run_even_tests }}
- name: Run odd tests
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 1,,2
if: ${{ ! matrix.run_even_tests }}
- uses: "./.github/actions/post-steps"
# ======================== Windows with Tests ======================= #
# NOTE: some windows jobs are in "nightly" to save resources
build-windows-vs2022:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: windows-8-core
runs-on: windows-2022
env:
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
@@ -462,7 +444,7 @@ jobs:
# post-steps skipped because of compatibility issues with docker image
build-macos-java:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
runs-on: macos-13
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
ROCKSDB_DISABLE_JEMALLOC: 1
@@ -470,7 +452,7 @@ jobs:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
xcode-version: 14.3.1
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/install-jdk8-on-macos"
@@ -485,14 +467,14 @@ jobs:
- uses: "./.github/actions/post-steps"
build-macos-java-static:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
runs-on: macos-13
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
xcode-version: 14.3.1
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/install-jdk8-on-macos"
@@ -507,14 +489,14 @@ jobs:
- uses: "./.github/actions/post-steps"
build-macos-java-static-universal:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: macos-15-xlarge
runs-on: macos-13
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
xcode-version: 14.3.1
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/install-jdk8-on-macos"
-13
View File
@@ -425,7 +425,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
"db_stress_tool/batched_ops_stress.cc",
"db_stress_tool/cf_consistency_stress.cc",
"db_stress_tool/db_stress_common.cc",
"db_stress_tool/db_stress_compaction_service.cc",
"db_stress_tool/db_stress_compression_manager.cc",
"db_stress_tool/db_stress_driver.cc",
"db_stress_tool/db_stress_filters.cc",
@@ -4844,12 +4843,6 @@ cpp_unittest_wrapper(name="db_encryption_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_etc3_test",
srcs=["db/db_etc3_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_flush_test",
srcs=["db/db_flush_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5200,12 +5193,6 @@ cpp_unittest_wrapper(name="import_column_family_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="index_builder_test",
srcs=["table/block_based/index_builder_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="inlineskiplist_test",
srcs=["memtable/inlineskiplist_test.cc"],
deps=[":rocksdb_test_lib"],
+14 -30
View File
@@ -203,16 +203,9 @@ if(WIN32 AND MSVC)
endif()
endif()
option(WIN_CI "Accelerate build speed and reduce build artifect size for github CI with MSVC" OFF)
if(MSVC)
if(WIN_CI)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /nologo /EHsc /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /W4 /wd4127 /wd4996 /wd4100 /wd4324 /wd4702")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4996 /wd4100 /wd4324")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4996 /wd4100 /wd4324")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing -Wno-invalid-offsetof")
@@ -320,7 +313,7 @@ if(NOT MSVC)
endif()
# Check if -latomic is required or not
if (NOT MSVC AND NOT APPLE)
if (NOT MSVC)
CHECK_CXX_SOURCE_COMPILES("
#include <atomic>
std::atomic<uint64_t> x(0);
@@ -457,33 +450,24 @@ else()
endif()
endif()
# Used to run optimized debug build and tests so we can run faster
# Used to run CI build and tests so we can run faster
option(OPTDBG "Build optimized debug build with MSVC" OFF)
option(WITH_RUNTIME_DEBUG "build with debug version of runtime library" ON)
if(MSVC)
if (WIN_CI)
if(OPTDBG)
message(STATUS "Debug optimization is enabled")
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG:FASTLINK")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG:FASTLINK")
else()
if(OPTDBG)
message(STATUS "Debug optimization is enabled")
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1")
# Minimal Build is deprecated after MSVC 2015
if( MSVC_VERSION GREATER 1900 )
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm-")
else()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1")
# Minimal Build is deprecated after MSVC 2015
if( MSVC_VERSION GREATER 1900 )
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm-")
else()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm")
endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm")
endif()
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
endif()
endif()
if(WITH_RUNTIME_DEBUG)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /${RUNTIME_LIBRARY}d")
else()
@@ -491,6 +475,8 @@ if(MSVC)
endif()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /${RUNTIME_LIBRARY}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
@@ -1359,7 +1345,6 @@ if(WITH_TESTS)
db/db_clip_test.cc
db/db_dynamic_level_test.cc
db/db_encryption_test.cc
db/db_etc3_test.cc
db/db_flush_test.cc
db/db_inplace_update_test.cc
db/db_io_failure_test.cc
@@ -1453,7 +1438,6 @@ if(WITH_TESTS)
table/block_based/block_based_table_reader_test.cc
table/block_based/block_test.cc
table/block_based/data_block_hash_index_test.cc
table/block_based/index_builder_test.cc
table/block_based/full_filter_block_test.cc
table/block_based/partitioned_filter_block_test.cc
table/cleanable_test.cc
-9
View File
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<CLToolExe>ccache_msvc_compiler.bat</CLToolExe>
<CLToolPath>$(MSBuildThisFileDirectory)</CLToolPath>
<UseMultiToolTask>true</UseMultiToolTask>
<EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>
</PropertyGroup>
</Project>
+20 -31
View File
@@ -1,44 +1,32 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 10.8.3 (11/10/2025)
### New Features
* Add a GetColumnFamilyMetaData API variant in DB to get the SST files intersecting a given key range.
## 10.7.6 (10/23/2025)
* Fix a bug when del range and data are in same file for multi-scan
## 10.8.2 (11/07/2025)
### New Features
* Added an auto-tuning feature for DB manifest file size that also (by default) improves the safety of existing configurations in case `max_manifest_file_size` is repeatedly exceeded. The new recommendation is to set `max_manifest_file_size` to something small like 1MB and tune `max_manifest_space_amp_pct` as needed to balance write amp and space amp in the manifest. Refer to comments on those options in `DBOptions` for details. Both options are (now) mutable.
* Add a new option allow_trivial_move in CompactionOptions to allow CompactFiles to perform trivial move if possible. By default the flag of allow_trivial_move is false, so it preserve the original behavior.
## 10.8.1 (10/29/2025)
### Behavior Changes
* PosixWritableFile now repositions the seek pointer to the new end of file after a call to Truncate.
## 10.8.0 (10/21/2025)
### New Features
* Add kFSPrefetch to FSSupportedOps enum to allow file systems to indicate prefetch support capability, avoiding unnecessary prefetch system calls on file systems that don't support them.
* Added experimental support `OpenAndCompactOptions::allow_resumption` for resumable compaction that persists progress during `OpenAndCompact()`, allowing interrupted compactions to resume from the last progress persitence. The default behavior is to not persist progress.
## 10.7.5 (10/20/2025)
### Bug Fixes
* Fix a bug in Page unpinning in MultiScan
## 10.7.4 (10/14/2025)
### Public API Changes
* Allow specifying output temperature in CompactionOptions
* Added `DB::FlushWAL(const FlushWALOptions&)` as an alternative to `DB::FlushWAL(bool sync)`, where `FlushWALOptions` includes a new `rate_limiter_priority` field (default `Env::IO_TOTAL`) that allows rate limiting and priority passing of manual WAL flush's IO operations.
* The MultiScan API contract is updated. After a multi scan range got prepared with Prepare API call, the following seeks must seek the start of each prepared scan range in order. In addition, when limit is set, upper bound must be set to the same value of limit before each seek
### Behavior Changes
* `kChangeTemperature` FIFO compaction will now honor `compaction_target_temp` to all levels regardless of `cf_options::last_level_temperature`
* Allow UDIs with a non BytewiseComparator
## 10.7.3 (10/06/2025)
### Bug Fixes
* Fix a few bugs in MultiScan
## 10.7.2 (09/30/2025)
### Bug Fixes
* Fix incorrect MultiScan seek error status due to bugs in handling range limit falling between adjacent SST files key range.
* Fix a bug in Page unpinning in MultiScan
### Performance Improvements
* Fixed a performance regression in LZ4 compression that started in version 10.6.0
## 10.7.0 (09/19/2025)
## 10.7.0 (09/24/2025)
### New Features
* Add the fail_if_no_udi_on_open flag in BlockBasedTableOption to control whether a missing user defined index block in a SST is a hard error or not.
* A new flag memtable_verify_per_key_checksum_on_seek is added to AdvancedColumnFamilyOptions. When it is enabled, it will validate key checksum along the binary search path on skiplist based memtable during seek operation.
* A new flag memtable_veirfy_per_key_checksum_on_seek is added to AdvancedColumnFamilyOptions. When it is enabled, it will validate key checksum along the binary search path on skiplist based memtable during seek operation.
* Introduce option MultiScanArgs::use_async_io to enable asynchronous I/O during MultiScan, instead of waiting for I/O to be done in Prepare().
* Add new option `MultiScanArgs::max_prefetch_size` that limits the memory usage of per file pinning of prefetched blocks.
* Improved `sst_dump` by allowing standalone file and directory arguments without `--file=`. Also added new options and better output for `sst_dump --command=recompress`. See `sst_dump --help`
@@ -50,6 +38,7 @@
### Behavior Changes
* The default provided block cache implementation is now HyperClockCache instead of LRUCache, when `block_cache` is nullptr (default) and `no_block_cache==false` (default). We recommend explicitly creating a HyperClockCache block cache based on memory budget and sharing it across all column families and even DB instances. This change could expose previously hidden memory or resource leaks.
* Allow UDIs with a non BytewiseComparator
### Bug Fixes
* Reported numbers for compaction and flush CPU usage now include time spent by parallel compression worker threads. This now means compaction/flush CPU usage could exceed the wall clock time.
@@ -96,7 +85,7 @@ Together, a new configuration TransactionOptions::deadlock_timeout_us is added,
* DB option skip_checking_sst_file_sizes_on_db_open is deprecated, in favor of validating file size in parallel in a thread pool, when db is opened. When DB is opened, with paranoid check enabled, a file with the wrong size would fail the DB open. With paranoid check disabled, the DB open would succeed, the column family with the corrupted file would not be read or write, while the other healthy column families could be read and write normally. When max_open_files option is not set to -1, only a subset of the files will be opened and checked. The rest of the files will be opened and checked when they are accessed.
### Behavior Changes
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occurred.
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occured.
* A new API GetFileSize is added to FSRandomAccessFile interface class. It uses fstat vs stat on the posix implementation which is more efficient. Caller could use it to get file size faster. This function might be required in the future for FileSystem implementation outside of the RocksDB code base.
* RocksDB now triggers eligible compactions every 12 hours when periodic compaction is configured. This solves a limitation of the compaction trigger mechanism, which would only trigger compaction after specific events like flush, compaction, or SetOptions.
@@ -153,7 +142,7 @@ system's prefetch) on SST file during compaction read
* Deprecated API `DB::MaxMemCompactionLevel()`.
* Deprecated `ReadOptions::ignore_range_deletions`.
* Deprecated API `experimental::PromoteL0()`.
* Added arbitrary string map for additional options to be overridden for remote compactions
* Added arbitrary string map for additional options to be overriden for remote compactions
* The fail_if_options_file_error option in DBOptions has been removed. The behavior now is to always return failure in any API that fails to persist the OPTIONS file.
### Behavior Changes
@@ -303,7 +292,7 @@ system's prefetch) on SST file during compaction read
* In FIFO compaction, compactions for changing file temperature (configured by option `file_temperature_age_thresholds`) will compact one file at a time, instead of merging multiple eligible file together (#13018).
* Support ingesting db generated files using hard link, i.e. IngestExternalFileOptions::move_files/link_files and IngestExternalFileOptions::allow_db_generated_files.
* Add a new file ingestion option `IngestExternalFileOptions::link_files` to hard link input files and preserve original files links after ingestion.
* DB::Close now untracks files in SstFileManager, making available any space used
* DB::Close now untracks files in SstFileManager, making avaialble any space used
by them. Prior to this change they would be orphaned until the DB is re-opened.
### Bug Fixes
@@ -499,7 +488,7 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
* Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
* Removed tickers with typos "rocksdb.error.handler.bg.error.count", "rocksdb.error.handler.bg.io.error.count", "rocksdb.error.handler.bg.retryable.io.error.count".
* Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
@@ -526,7 +515,7 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
* Exposed options ttl via c api.
### Behavior Changes
* `rocksdb.blobdb.blob.file.write.micros` expands to also measure time writing the header and footer. Therefore the COUNT may be higher and values may be smaller than before. For stacked BlobDB, it no longer measures the time of explicitly flushing blob file.
* `rocksdb.blobdb.blob.file.write.micros` expands to also measure time writing the header and footer. Therefore the COUNT may be higher and values may be smaller than before. For stacked BlobDB, it no longer measures the time of explictly flushing blob file.
* Files will be compacted to the next level if the data age exceeds periodic_compaction_seconds except for the last level.
* Reduced the compaction debt ratio trigger for scheduling parallel compactions
* For leveled compaction with default compaction pri (kMinOverlappingRatio), files marked for compaction will be prioritized over files not marked when picking a file from a level for compaction.
@@ -591,7 +580,7 @@ want to continue to use force enabling, they need to explicitly pass a `true` to
### Behavior Changes
* During off-peak hours defined by `daily_offpeak_time_utc`, the compaction picker will select a larger number of files for periodic compaction. This selection will include files that are projected to expire by the next off-peak start time, ensuring that these files are not chosen for periodic compaction outside of off-peak hours.
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occurred previously in its status message.
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occured previously in its status message.
* Deleting stale files upon recovery are delegated to SstFileManger if available so they can be rate limited.
* Make RocksDB only call `TablePropertiesCollector::Finish()` once.
* When `WAL_ttl_seconds > 0`, we now process archived WALs for deletion at least every `WAL_ttl_seconds / 2` seconds. Previously it could be less frequent in case of small `WAL_ttl_seconds` values when size-based expiration (`WAL_size_limit_MB > 0 `) was simultaneously enabled.
@@ -1379,7 +1368,7 @@ Note: The next release will be major release 7.0. See https://github.com/faceboo
### Public API change
* Extend WriteBatch::AssignTimestamp and AssignTimestamps API so that both functions can accept an optional `checker` argument that performs additional checking on timestamp sizes.
* Introduce a new EventListener callback that will be called upon the end of automatic error recovery.
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low separately.
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low seperately.
* Add GetFullHistoryTsLow API so users can query current full_history_low value of specified column family.
### Performance Improvements
+1 -9
View File
@@ -1496,9 +1496,6 @@ db_test: $(OBJ_DIR)/db/db_test.o $(TEST_LIBRARY) $(LIBRARY)
db_test2: $(OBJ_DIR)/db/db_test2.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_etc3_test: $(OBJ_DIR)/db/db_etc3_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
compression_test: $(OBJ_DIR)/util/compression_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -1742,9 +1739,6 @@ block_test: $(OBJ_DIR)/table/block_based/block_test.o $(TEST_LIBRARY) $(LIBRARY)
data_block_hash_index_test: $(OBJ_DIR)/table/block_based/data_block_hash_index_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
index_builder_test: $(OBJ_DIR)/table/block_based/index_builder_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
inlineskiplist_test: $(OBJ_DIR)/memtable/inlineskiplist_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -2498,8 +2492,6 @@ commit_prereq:
false # J=$(J) build_tools/precommit_checker.py unit clang_unit release clang_release tsan asan ubsan lite unit_non_shm
# $(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
FOLLY_COMMIT_HASH = e95383b7c8b5b1e46cf47acf2f317d54f93c8268
# For public CI runs, checkout folly in a way that can build with RocksDB.
# This is mostly intended as a test-only simulation of Meta-internal folly
# integration.
@@ -2511,7 +2503,7 @@ checkout_folly:
fi
@# Pin to a particular version for public CI, so that PR authors don't
@# need to worry about folly breaking our integration. Update periodically
cd third-party/folly && git reset --hard $(FOLLY_COMMIT_HASH)
cd third-party/folly && git reset --hard e95383b7c8b5b1e46cf47acf2f317d54f93c8268
@# Apparently missing include
perl -pi -e 's/(#include <atomic>)/$$1\n#include <cstring>/' third-party/folly/folly/lang/Exception.h
@# Warning-as-error on memcpy
+4 -4
View File
@@ -33,7 +33,7 @@ const char* kTieredCacheName = "TieredCache";
// proportionally across the primary/secondary caches.
//
// The primary block cache is initially sized to the sum of the primary cache
// budget + the secondary cache budget, as follows -
// budget + teh secondary cache budget, as follows -
// |--------- Primary Cache Configured Capacity -----------|
// |---Secondary Cache Budget----|----Primary Cache Budget-----|
//
@@ -51,7 +51,7 @@ const char* kTieredCacheName = "TieredCache";
// placeholder is counted against the primary cache. To compensate and count
// a portion of it against the secondary cache, the secondary cache Deflate()
// method is called to shrink it. Since the Deflate() causes the secondary
// actual usage to shrink, it is reflected here by releasing an equal amount
// actual usage to shrink, it is refelcted here by releasing an equal amount
// from the pri_cache_res_ reservation. The Deflate() in the secondary cache
// can be, but is not required to be, implemented using its own cache
// reservation manager.
@@ -72,7 +72,7 @@ const char* kTieredCacheName = "TieredCache";
// reservation is increased by an equal amount.
//
// Another way of implementing this would have been to simply split the user
// reservation into primary and secondary components. However, this would
// reservation into primary and seconary components. However, this would
// require allocating a structure to track the associated secondary cache
// reservation, which adds some complexity and overhead.
//
@@ -621,7 +621,7 @@ Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
} else {
// We're shrinking the ratio. Try to avoid unnecessary evictions -
// 1. Lower the secondary cache capacity
// 2. Decrease pri_cache_res_ reservation to reflect lower secondary
// 2. Decrease pri_cache_res_ reservation to relect lower secondary
// cache utilization (decrease in capacity - decrease in share of cache
// reservations)
// 3. Inflate the secondary cache to give it back the reduction in its
-1
View File
@@ -1 +0,0 @@
ccache.exe cl.exe %*
-226
View File
@@ -534,232 +534,6 @@ TEST_F(CompactFilesTest, GetCompactionJobInfo) {
delete db;
}
// Helper function to generate zero-padded keys
// e.g., MakeKey("a", 5) -> "a05", MakeKey("b", 42) -> "b42"
static std::string MakeKey(const std::string& prefix, int index) {
return prefix + (index < 10 ? "0" : "") + std::to_string(index);
}
TEST_F(CompactFilesTest, TrivialMoveNonOverlappingFiles) {
Options options;
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.compression = kNoCompression;
options.level_compaction_dynamic_level_bytes = false;
DB* db = nullptr;
ASSERT_OK(DestroyDB(db_name_, options));
Status s = DB::Open(options, db_name_, &db);
ASSERT_OK(s);
ASSERT_NE(db, nullptr);
// Create 3 non-overlapping files in L0
// File 1: keys [a00-a99]
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("a", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// File 2: keys [b00-b99]
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("b", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// File 3: keys [c00-c99]
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("c", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// Verify files are in L0
ColumnFamilyMetaData meta;
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[0].files.size(), 3);
ASSERT_EQ(meta.levels[1].files.size(), 0);
// Get L0 files
std::vector<std::string> l0_files;
for (const auto& file : meta.levels[0].files) {
l0_files.push_back(file.db_path + "/" + file.name);
}
CompactionOptions compact_option;
compact_option.allow_trivial_move = true;
// Compact all L0 files to L1 (non-overlapping in L1)
ASSERT_OK(db->CompactFiles(compact_option, l0_files, 1));
// Verify files are now in L1
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[0].files.size(), 0);
ASSERT_EQ(meta.levels[1].files.size(), 3);
// Get the first file from L1 (should be the one with keys a00-a99)
std::string l1_file_to_move;
std::vector<std::string> l1_files_to_move_later;
uint64_t l1_file_number = 0;
for (const auto& file : meta.levels[1].files) {
if (file.smallestkey[0] == 'a') {
l1_file_to_move = file.db_path + "/" + file.name;
l1_file_number = file.file_number;
} else {
l1_files_to_move_later.push_back(file.db_path + "/" + file.name);
}
}
ASSERT_FALSE(l1_file_to_move.empty());
// Set up sync point to verify trivial move path is taken
bool trivial_move_executed = false;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::CompactFilesImpl:TrivialMove",
[&](void* /*arg*/) { trivial_move_executed = true; });
SyncPoint::GetInstance()->EnableProcessing();
// Move the file from L1 to L6 - this should be a trivial move
// because the file doesn't overlap with anything in L6
std::vector<std::string> files_to_move = {l1_file_to_move};
ASSERT_OK(db->CompactFiles(compact_option, files_to_move, 6));
// Verify trivial move was executed
ASSERT_TRUE(trivial_move_executed);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Verify the file is now in L6
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 2); // Two files remain in L1
ASSERT_EQ(meta.levels[6].files.size(), 1); // One file in L6
// Verify it's the correct file in L6
bool found_file_in_l6 = false;
for (const auto& file : meta.levels[6].files) {
if (file.file_number == l1_file_number) {
found_file_in_l6 = true;
// Verify key range hasn't changed
ASSERT_EQ(file.smallestkey[0], 'a');
ASSERT_EQ(file.largestkey[0], 'a');
break;
}
}
ASSERT_TRUE(found_file_in_l6);
// Move the other 2 files from L1 to L6, with allow_trivial_move set to false.
// This will trigger a normal compaction, so the 2 files will be compacted
// into a single file in L6.
ASSERT_OK(db->CompactFiles(CompactionOptions(), l1_files_to_move_later, 6));
// Verify files in L6
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 0); // Zero files remain in L1
ASSERT_EQ(meta.levels[6].files.size(), 2); // Two file in L6
// Verify data integrity - all keys should still be readable
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("a", i);
std::string value;
ASSERT_OK(db->Get(ReadOptions(), key, &value));
ASSERT_EQ(value, "value_" + key);
}
delete db;
}
TEST_F(CompactFilesTest, TrivialMoveBlockedByOverlap) {
Options options;
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.compression = kNoCompression;
options.level_compaction_dynamic_level_bytes = false;
options.num_levels = 7;
DB* db = nullptr;
ASSERT_OK(DestroyDB(db_name_, options));
Status s = DB::Open(options, db_name_, &db);
ASSERT_OK(s);
ASSERT_NE(db, nullptr);
// Create a file in L6 with keys [m00-m99] (wide range)
for (int i = 0; i < 100; i++) {
std::string key = MakeKey("m", i);
ASSERT_OK(db->Put(WriteOptions(), key, "value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// Get L0 file
ColumnFamilyMetaData meta;
db->GetColumnFamilyMetaData(&meta);
std::vector<std::string> l0_files;
for (const auto& file : meta.levels[0].files) {
l0_files.push_back(file.db_path + "/" + file.name);
}
CompactionOptions compact_option;
compact_option.allow_trivial_move = true;
// Move to L6
ASSERT_OK(db->CompactFiles(compact_option, l0_files, 6));
// Now create a file in L1 with overlapping keys [m50-m60]
for (int i = 50; i <= 60; i++) {
std::string key = "m" + std::to_string(i);
ASSERT_OK(db->Put(WriteOptions(), key, "updated_value_" + key));
}
ASSERT_OK(db->Flush(FlushOptions()));
// Get the L0 file
db->GetColumnFamilyMetaData(&meta);
std::vector<std::string> l0_files_2;
for (const auto& file : meta.levels[0].files) {
l0_files_2.push_back(file.db_path + "/" + file.name);
}
// Move to L1
ASSERT_OK(db->CompactFiles(compact_option, l0_files_2, 1));
// Get the L1 file
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 1);
std::string l1_file =
meta.levels[1].files[0].db_path + "/" + meta.levels[1].files[0].name;
// Set up sync point to verify full compaction path is taken
bool trivial_move_executed = false;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::CompactFilesImpl:TrivialMove",
[&](void* /*arg*/) { trivial_move_executed = true; });
SyncPoint::GetInstance()->EnableProcessing();
// Try to move from L1 to L6 - this should NOT be a trivial move
// because the file overlaps with the existing file in L6
ASSERT_OK(db->CompactFiles(compact_option, {l1_file}, 6));
// Verify trivial move was NOT executed (full compaction happened)
ASSERT_FALSE(trivial_move_executed);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Verify the result - should have merged data in L6
db->GetColumnFamilyMetaData(&meta);
ASSERT_EQ(meta.levels[1].files.size(), 0); // L1 should be empty
// L6 should have the merged file (may be 1 file if merged, or 2 if not)
ASSERT_GE(meta.levels[6].files.size(), 1);
// Verify updated values are present
for (int i = 50; i <= 60; i++) {
std::string key = "m" + std::to_string(i);
std::string value;
ASSERT_OK(db->Get(ReadOptions(), key, &value));
ASSERT_EQ(value, "updated_value_" + key);
}
delete db;
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+3 -17
View File
@@ -281,9 +281,8 @@ Compaction::Compaction(
std::vector<CompactionInputFiles> _inputs, int _output_level,
uint64_t _target_file_size, uint64_t _max_compaction_bytes,
uint32_t _output_path_id, CompressionType _compression,
CompressionOptions _compression_opts,
Temperature _output_temperature_override, uint32_t _max_subcompactions,
std::vector<FileMetaData*> _grandparents,
CompressionOptions _compression_opts, Temperature _output_temperature,
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
std::optional<SequenceNumber> _earliest_snapshot,
const SnapshotChecker* _snapshot_checker,
CompactionReason _compaction_reason, const std::string& _trim_ts,
@@ -304,7 +303,7 @@ Compaction::Compaction(
output_path_id_(_output_path_id),
output_compression_(_compression),
output_compression_opts_(_compression_opts),
output_temperature_override_(_output_temperature_override),
output_temperature_(_output_temperature),
deletion_compaction_(_compaction_reason == CompactionReason::kFIFOTtl ||
_compaction_reason ==
CompactionReason::kFIFOMaxSize),
@@ -1129,17 +1128,4 @@ void Compaction::FilterInputsForCompactionIterator() {
}
}
Temperature Compaction::GetOutputTemperature(bool is_proximal_level) const {
if (output_temperature_override_ != Temperature::kUnknown) {
return output_temperature_override_;
}
if (is_last_level() && !is_proximal_level &&
mutable_cf_options_.last_level_temperature != Temperature::kUnknown) {
return mutable_cf_options_.last_level_temperature;
}
return mutable_cf_options_.default_write_temperature;
}
} // namespace ROCKSDB_NAMESPACE
+3 -8
View File
@@ -90,8 +90,7 @@ class Compaction {
uint64_t target_file_size, uint64_t max_compaction_bytes,
uint32_t output_path_id, CompressionType compression,
CompressionOptions compression_opts,
Temperature output_temperature_override,
uint32_t max_subcompactions,
Temperature output_temperature, uint32_t max_subcompactions,
std::vector<FileMetaData*> grandparents,
std::optional<SequenceNumber> earliest_snapshot,
const SnapshotChecker* snapshot_checker,
@@ -410,11 +409,7 @@ class Compaction {
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
// Order of precedence for temperature:
// 1. Override temp if not kUnknown
// 2. Temperature of the last level files if applicable
// 3. Default write temperature
Temperature GetOutputTemperature(bool is_proximal_level = false) const;
Temperature output_temperature() const { return output_temperature_; }
uint32_t max_subcompactions() const { return max_subcompactions_; }
@@ -546,7 +541,7 @@ class Compaction {
const uint32_t output_path_id_;
CompressionType output_compression_;
CompressionOptions output_compression_opts_;
Temperature output_temperature_override_;
Temperature output_temperature_;
// If true, then the compaction can be done by simply deleting input files.
const bool deletion_compaction_;
// should it split the output file using the compact cursor?
-10
View File
@@ -270,16 +270,6 @@ class CompactionIterator {
// true, unless `must_count_input_entries=true` was specified during iterator
// creation (which ensures the count is always accurate).
uint64_t NumInputEntryScanned() const { return input_.NumItered(); }
// Returns true if the current valid key was already scanned/counted during
// a lookahead operation in a previous iteration.
//
// REQUIRED: Valid() must be true
bool IsCurrentKeyAlreadyScanned() const {
assert(Valid());
return at_next_ || merge_out_iter_.Valid();
}
Status InputStatus() const { return input_.status(); }
bool IsDeleteRangeSentinelKey() const { return is_range_del_; }
+92 -584
View File
@@ -51,9 +51,7 @@
#include "rocksdb/status.h"
#include "rocksdb/table.h"
#include "rocksdb/utilities/options_type.h"
#include "table/format.h"
#include "table/merging_iterator.h"
#include "table/meta_blocks.h"
#include "table/table_builder.h"
#include "table/unique_id_impl.h"
#include "test_util/sync_point.h"
@@ -255,9 +253,7 @@ void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
void CompactionJob::Prepare(
std::optional<std::pair<std::optional<Slice>, std::optional<Slice>>>
known_single_subcompact,
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer) {
known_single_subcompact) {
db_mutex_->AssertHeld();
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_PREPARE);
@@ -307,9 +303,6 @@ void CompactionJob::Prepare(
/*sub_job_id*/ 0);
}
MaybeAssignCompactionProgressAndWriter(compaction_progress,
compaction_progress_writer);
// collect all seqno->time information from the input files which will be used
// to encode seqno->time to the output files.
SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber;
@@ -408,25 +401,6 @@ void CompactionJob::Prepare(
options_file_number_ = versions_->options_file_number();
}
void CompactionJob::MaybeAssignCompactionProgressAndWriter(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer) {
// LIMITATION: Only supports resuming single subcompaction for now
if (compact_->sub_compact_states.size() != 1) {
return;
}
if (!compaction_progress.empty()) {
assert(compaction_progress.size() == 1);
SubcompactionState* sub_compact = &compact_->sub_compact_states[0];
const SubcompactionProgress& subcompaction_progress =
compaction_progress[0];
sub_compact->SetSubcompactionProgress(subcompaction_progress);
}
compaction_progress_writer_ = compaction_progress_writer;
}
uint64_t CompactionJob::GetSubcompactionsLimit() {
return extra_num_subcompaction_threads_reserved_ +
std::max(
@@ -812,19 +786,20 @@ Status CompactionJob::SyncOutputDirectories() {
Status CompactionJob::VerifyOutputFiles() {
Status status;
std::vector<port::Thread> thread_pool;
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
VerifyOutputFlags verify_output_flags =
compact_->compaction->mutable_cf_options().verify_output_flags;
// For backward compatibility
if (paranoid_file_checks_) {
verify_output_flags |= VerifyOutputFlags::kVerifyIteration;
verify_output_flags |= VerifyOutputFlags::kEnableForLocalCompaction;
verify_output_flags |= VerifyOutputFlags::kEnableForRemoteCompaction;
std::vector<const CompactionOutputs::Output*> files_output;
for (const auto& state : compact_->sub_compact_states) {
for (const auto& output : state.GetOutputs()) {
files_output.emplace_back(&output);
}
}
auto verify_table = [&](SubcompactionState& subcompaction_state) {
for (const auto& output_file : subcompaction_state.GetOutputs()) {
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
std::atomic<size_t> next_file_idx(0);
auto verify_table = [&](Status& output_status) {
while (true) {
size_t file_idx = next_file_idx.fetch_add(1);
if (file_idx >= files_output.size()) {
break;
}
// Verify that the table is usable
// We set for_compaction to false and don't
// OptimizeForCompactionTableRead here because this is a special case
@@ -833,19 +808,13 @@ Status CompactionJob::VerifyOutputFiles() {
// verification as user reads since the goal is to cache it here for
// further user reads
ReadOptions verify_table_read_options(Env::IOActivity::kCompaction);
verify_table_read_options.verify_checksums = true;
verify_table_read_options.readahead_size =
file_options_for_read_.compaction_readahead_size;
std::unique_ptr<TableReader> table_reader_guard;
TableReader* table_reader_ptr = table_reader_guard.get();
verify_table_read_options.rate_limiter_priority =
GetRateLimiterPriority();
InternalIterator* iter = cfd->table_cache()->NewIterator(
verify_table_read_options, file_options_, cfd->internal_comparator(),
output_file.meta,
files_output[file_idx]->meta,
/*range_del_agg=*/nullptr, compact_->compaction->mutable_cf_options(),
/*table_reader_ptr=*/&table_reader_ptr,
/*table_reader_ptr=*/nullptr,
cfd->internal_stats()->GetFileReadHist(
compact_->compaction->output_level()),
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
@@ -855,63 +824,38 @@ Status CompactionJob::VerifyOutputFiles() {
/*largest_compaction_key=*/nullptr,
/*allow_unprepared_value=*/false);
auto s = iter->status();
if (s.ok()) {
// Check for remote/local compaction and verify_output_flags flags
const bool should_verify =
(subcompaction_state.compaction_job_stats.is_remote_compaction &&
!!(verify_output_flags &
VerifyOutputFlags::kEnableForRemoteCompaction)) ||
(!subcompaction_state.compaction_job_stats.is_remote_compaction &&
!!(verify_output_flags &
VerifyOutputFlags::kEnableForLocalCompaction));
if (should_verify) {
const bool should_verify_block_checksum =
!!(verify_output_flags & VerifyOutputFlags::kVerifyBlockChecksum);
const bool should_verify_iteration =
!!(verify_output_flags & VerifyOutputFlags::kVerifyIteration);
if (should_verify_block_checksum) {
assert(table_reader_ptr != nullptr);
// If verifying iteration as well, verify meta blocks here only to
// avoid redundant checks on data blocks
s = table_reader_ptr->VerifyChecksum(
verify_table_read_options, TableReaderCaller::kCompaction,
/*meta_blocks_only=*/should_verify_iteration);
}
if (s.ok() && should_verify_iteration) {
OutputValidator validator(cfd->internal_comparator(),
/*_enable_hash=*/true);
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
s = validator.Add(iter->key(), iter->value());
if (!s.ok()) {
break;
}
}
if (s.ok()) {
s = iter->status();
}
if (s.ok() && !validator.CompareValidator(output_file.validator)) {
s = Status::Corruption(
"Key-value checksum of compaction output doesn't match what "
"was computed when written");
}
if (s.ok() && paranoid_file_checks_) {
OutputValidator validator(cfd->internal_comparator(),
/*_enable_hash=*/true);
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
s = validator.Add(iter->key(), iter->value());
if (!s.ok()) {
break;
}
}
if (s.ok()) {
s = iter->status();
}
if (s.ok() &&
!validator.CompareValidator(files_output[file_idx]->validator)) {
s = Status::Corruption("Paranoid checksums do not match");
}
}
delete iter;
if (!s.ok()) {
subcompaction_state.status = s;
output_status = s;
break;
}
}
};
for (size_t i = 1; i < compact_->sub_compact_states.size(); i++) {
thread_pool.emplace_back(verify_table,
std::ref(compact_->sub_compact_states[i]));
std::ref(compact_->sub_compact_states[i].status));
}
verify_table(compact_->sub_compact_states[0]);
verify_table(compact_->sub_compact_states[0].status);
for (auto& thread : thread_pool) {
thread.join();
}
@@ -980,8 +924,7 @@ void CompactionJob::FinalizeCompactionRun(
UpdateCompactionJobInputStatsFromInternalStats(internal_stats_,
num_input_range_del);
}
UpdateCompactionJobOutputStatsFromInternalStats(input_status,
internal_stats_);
UpdateCompactionJobOutputStatsFromInternalStats(internal_stats_);
RecordCompactionIOStats();
LogFlush(db_options_.info_log);
@@ -1306,8 +1249,8 @@ Status CompactionJob::SetupAndValidateCompactionFilter(
return Status::OK();
}
void CompactionJob::InitializeReadOptionsAndBoundaries(
const size_t ts_sz, ReadOptions& read_options,
void CompactionJob::InitializeReadOptions(
ColumnFamilyData* cfd, ReadOptions& read_options,
SubcompactionKeyBoundaries& boundaries) {
read_options.verify_checksums = true;
read_options.fill_cache = false;
@@ -1321,6 +1264,8 @@ void CompactionJob::InitializeReadOptionsAndBoundaries(
// Remove the timestamps from boundaries because boundaries created in
// GenSubcompactionBoundaries doesn't strip away the timestamp.
const size_t ts_sz = cfd->user_comparator()->timestamp_size();
if (boundaries.start.has_value()) {
read_options.iterate_lower_bound = &(*boundaries.start);
if (ts_sz > 0) {
@@ -1337,7 +1282,30 @@ void CompactionJob::InitializeReadOptionsAndBoundaries(
read_options.iterate_upper_bound = &(*boundaries.end_without_ts);
}
}
}
InternalIterator* CompactionJob::CreateInputIterator(
SubcompactionState* sub_compact, ColumnFamilyData* cfd,
SubcompactionInternalIterators& iterators,
SubcompactionKeyBoundaries& boundaries, ReadOptions& read_options) {
// This is assigned after creation of SubcompactionState to simplify that
// creation across both CompactionJob and CompactionServiceCompactionJob
sub_compact->AssignRangeDelAggregator(
std::make_unique<CompactionRangeDelAggregator>(
&cfd->internal_comparator(), job_context_->snapshot_seqs,
&full_history_ts_low_, &trim_ts_));
InitializeReadOptions(cfd, read_options, boundaries);
// Although the v2 aggregator is what the level iterator(s) know about,
// the AddTombstones calls will be propagated down to the v1 aggregator.
iterators.raw_input =
std::unique_ptr<InternalIterator>(versions_->MakeInputIterator(
read_options, sub_compact->compaction, sub_compact->RangeDelAgg(),
file_options_for_read_, boundaries.start, boundaries.end));
InternalIterator* input = iterators.raw_input.get();
const size_t ts_sz = cfd->user_comparator()->timestamp_size();
if (ts_sz > 0) {
if (ts_sz <= strlen(boundaries.kMaxTs)) {
boundaries.ts_slice = Slice(boundaries.kMaxTs, ts_sz);
@@ -1346,6 +1314,7 @@ void CompactionJob::InitializeReadOptionsAndBoundaries(
boundaries.ts_slice = Slice(boundaries.max_ts);
}
}
if (boundaries.start.has_value()) {
boundaries.start_ikey.SetInternalKey(*boundaries.start, kMaxSequenceNumber,
kValueTypeForSeek);
@@ -1366,29 +1335,6 @@ void CompactionJob::InitializeReadOptionsAndBoundaries(
boundaries.end_internal_key = boundaries.end_ikey.GetInternalKey();
boundaries.end_user_key = boundaries.end_ikey.GetUserKey();
}
}
InternalIterator* CompactionJob::CreateInputIterator(
SubcompactionState* sub_compact, ColumnFamilyData* cfd,
SubcompactionInternalIterators& iterators,
SubcompactionKeyBoundaries& boundaries, ReadOptions& read_options) {
const size_t ts_sz = cfd->user_comparator()->timestamp_size();
InitializeReadOptionsAndBoundaries(ts_sz, read_options, boundaries);
// This is assigned after creation of SubcompactionState to simplify that
// creation across both CompactionJob and CompactionServiceCompactionJob
sub_compact->AssignRangeDelAggregator(
std::make_unique<CompactionRangeDelAggregator>(
&cfd->internal_comparator(), job_context_->snapshot_seqs,
&full_history_ts_low_, &trim_ts_));
// Although the v2 aggregator is what the level iterator(s) know about,
// the AddTombstones calls will be propagated down to the v1 aggregator.
iterators.raw_input =
std::unique_ptr<InternalIterator>(versions_->MakeInputIterator(
read_options, sub_compact->compaction, sub_compact->RangeDelAgg(),
file_options_for_read_, boundaries.start, boundaries.end));
InternalIterator* input = iterators.raw_input.get();
if (boundaries.start.has_value() || boundaries.end.has_value()) {
iterators.clip = std::make_unique<ClippingIterator>(
@@ -1458,8 +1404,7 @@ std::unique_ptr<CompactionIterator> CompactionJob::CreateCompactionIterator(
env_, ShouldReportDetailedTime(env_, stats_), sub_compact->RangeDelAgg(),
blob_resources.blob_file_builder.get(), db_options_.allow_data_in_errors,
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
sub_compact->compaction
->DoesInputReferenceBlobFiles() /* must_count_input_entries */,
sub_compact->compaction->DoesInputReferenceBlobFiles(),
sub_compact->compaction, compaction_filter, shutting_down_,
db_options_.info_log, full_history_ts_low, preserve_seqno_after_);
}
@@ -1479,13 +1424,11 @@ CompactionJob::CreateFileHandlers(SubcompactionState* sub_compact,
const CompactionFileCloseFunc close_file_func =
[this, sub_compact, start_user_key, end_user_key](
const Status& status,
const ParsedInternalKey& prev_table_last_internal_key,
const Slice& next_table_min_key, const CompactionIterator* c_iter,
CompactionOutputs& outputs) {
return this->FinishCompactionOutputFile(
status, prev_table_last_internal_key, next_table_min_key,
start_user_key, end_user_key, c_iter, sub_compact, outputs);
CompactionOutputs& outputs, const Status& status,
const Slice& next_table_min_key) {
return this->FinishCompactionOutputFile(status, sub_compact, outputs,
next_table_min_key,
start_user_key, end_user_key);
};
return {open_file_func, close_file_func};
@@ -1499,9 +1442,6 @@ Status CompactionJob::ProcessKeyValue(
const uint64_t kRecordStatsEvery = 1000;
[[maybe_unused]] const std::optional<const Slice> end = sub_compact->end;
IterKey last_output_key;
ParsedInternalKey last_output_ikey;
TEST_SYNC_POINT_CALLBACK(
"CompactionJob::ProcessKeyValueCompaction()::Processing",
static_cast<void*>(const_cast<Compaction*>(sub_compact->compaction)));
@@ -1551,9 +1491,8 @@ Status CompactionJob::ProcessKeyValue(
// and `close_file_func`.
// TODO: it would be better to have the compaction file open/close moved
// into `CompactionOutputs` which has the output file information.
status =
sub_compact->AddToOutput(*c_iter, use_proximal_output, open_file_func,
close_file_func, last_output_ikey);
status = sub_compact->AddToOutput(*c_iter, use_proximal_output,
open_file_func, close_file_func);
if (!status.ok()) {
break;
}
@@ -1561,10 +1500,6 @@ Status CompactionJob::ProcessKeyValue(
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:2",
static_cast<void*>(const_cast<std::atomic<bool>*>(
&manual_compaction_canceled_)));
last_output_key.SetInternalKey(c_iter->key(), &last_output_ikey);
last_output_ikey.sequence = ikey.sequence;
last_output_ikey.type = ikey.type;
c_iter->Next();
#ifndef NDEBUG
@@ -1749,22 +1684,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
ReadOptions read_options;
const WriteOptions write_options(Env::IOPriority::IO_LOW,
Env::IOActivity::kCompaction);
InternalIterator* input_iter = CreateInputIterator(
sub_compact, cfd, iterators, boundaries, read_options);
assert(input_iter);
Status status =
MaybeResumeSubcompactionProgressOnInputIterator(sub_compact, input_iter);
if (status.IsNotFound()) {
input_iter->SeekToFirst();
} else if (!status.ok()) {
sub_compact->status = status;
return;
}
MergeHelper merge(
env_, cfd->user_comparator(), cfd->ioptions().merge_operator.get(),
compaction_filter, db_options_.info_log.get(),
@@ -1773,6 +1692,11 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
compact_->compaction->level(), db_options_.stats);
BlobFileResources blob_resources;
InternalIterator* input_iter = CreateInputIterator(
sub_compact, cfd, iterators, boundaries, read_options);
assert(input_iter);
input_iter->SeekToFirst();
auto c_iter =
CreateCompactionIterator(sub_compact, cfd, input_iter, compaction_filter,
merge, blob_resources, write_options);
@@ -1787,8 +1711,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
auto [open_file_func, close_file_func] =
CreateFileHandlers(sub_compact, boundaries);
status = ProcessKeyValue(sub_compact, cfd, c_iter.get(), open_file_func,
close_file_func, prev_cpu_micros);
Status status =
ProcessKeyValue(sub_compact, cfd, c_iter.get(), open_file_func,
close_file_func, prev_cpu_micros);
status = FinalizeProcessKeyValueStatus(cfd, input_iter, c_iter.get(), status);
@@ -1870,11 +1795,9 @@ void CompactionJob::RecordDroppedKeys(
}
Status CompactionJob::FinishCompactionOutputFile(
const Status& input_status,
const ParsedInternalKey& prev_table_last_internal_key,
const Slice& next_table_min_key, const Slice* comp_start_user_key,
const Slice* comp_end_user_key, const CompactionIterator* c_iter,
SubcompactionState* sub_compact, CompactionOutputs& outputs) {
const Status& input_status, SubcompactionState* sub_compact,
CompactionOutputs& outputs, const Slice& next_table_min_key,
const Slice* comp_start_user_key, const Slice* comp_end_user_key) {
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_SYNC_FILE);
assert(sub_compact != nullptr);
@@ -2048,94 +1971,10 @@ Status CompactionJob::FinishCompactionOutputFile(
}
}
if (s.ok() && ShouldUpdateSubcompactionProgress(sub_compact, c_iter,
prev_table_last_internal_key,
next_table_min_key, meta)) {
UpdateSubcompactionProgress(c_iter, next_table_min_key, sub_compact);
s = PersistSubcompactionProgress(sub_compact);
}
outputs.ResetBuilder();
return s;
}
bool CompactionJob::ShouldUpdateSubcompactionProgress(
const SubcompactionState* sub_compact, const CompactionIterator* c_iter,
const ParsedInternalKey& prev_table_last_internal_key,
const Slice& next_table_min_internal_key, const FileMetaData* meta) const {
const auto* cfd = sub_compact->compaction->column_family_data();
// No need to update when the output will not get persisted
if (compaction_progress_writer_ == nullptr) {
return false;
}
// No need to update for a new empty output
if (meta == nullptr) {
return false;
}
// TODO(hx235): save progress even on the last output file
if (next_table_min_internal_key.empty()) {
return false;
}
// LIMITATION: Persisting compaction progress with timestamp
// is not supported since the feature of persisting timestamp of the key in
// SST files itself is still experimental
size_t ts_sz = cfd->user_comparator()->timestamp_size();
if (ts_sz > 0) {
return false;
}
// LIMITATION: Compaction progress persistence disabled for file boundaries
// contaning range deletions. Range deletions can span file boundaries, making
// it difficult (but possible) to ensure adjacent output tables have different
// user keys. See the last check for why different users keys of adjacent
// output tables are needed
const ValueType next_table_min_internal_key_type =
ExtractValueType(next_table_min_internal_key);
const ValueType prev_table_last_internal_key_type =
prev_table_last_internal_key.user_key.empty()
? ValueType::kTypeValue
: prev_table_last_internal_key.type;
if (next_table_min_internal_key_type == ValueType::kTypeRangeDeletion ||
prev_table_last_internal_key_type == ValueType::kTypeRangeDeletion) {
return false;
}
// LIMITATION: Compaction progress persistence disabled when adjacent output
// tables share the same user key at boundaries. This ensures a simple Seek()
// of the next key when resuming can process all versions of a user key
const Slice next_table_min_user_key =
ExtractUserKey(next_table_min_internal_key);
const Slice prev_table_last_user_key =
prev_table_last_internal_key.user_key.empty()
? Slice()
: prev_table_last_internal_key.user_key;
if (cfd->user_comparator()->EqualWithoutTimestamp(next_table_min_user_key,
prev_table_last_user_key)) {
return false;
}
// LIMITATION: Don't save progress if the current key has already been scanned
// (looked ahead) in the input but not yet output. This can happen with merge
// operations, single deletes, and deletes at the bottommost level where
// CompactionIterator needs to look ahead to process multiple entries for the
// same user key before outputting a result. If we saved progress and resumed
// at this boundary, the resumed session would see and process the same input
// key again through Seek(), leading to incorrect double-counting in
// number of processed input entries and input count verification failure
//
// TODO(hx235): Offset num_processed_input_records to avoid double counting
// instead of disabling progress persistence.
if (c_iter->IsCurrentKeyAlreadyScanned()) {
return false;
}
return true;
}
Status CompactionJob::InstallCompactionResults(bool* compaction_released) {
assert(compact_);
@@ -2281,8 +2120,15 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
// Pass temperature of the last level files to FileSystem.
FileOptions fo_copy = file_options_;
auto temperature =
sub_compact->compaction->GetOutputTemperature(outputs.IsProximalLevel());
Temperature temperature = sub_compact->compaction->output_temperature();
Temperature last_level_temp =
sub_compact->compaction->mutable_cf_options().last_level_temperature;
// Here last_level_temperature supersedes default_write_temperature, when
// enabled and applicable
if (last_level_temp != Temperature::kUnknown &&
sub_compact->compaction->is_last_level() && !outputs.IsProximalLevel()) {
temperature = last_level_temp;
}
fo_copy.temperature = temperature;
fo_copy.write_hint = write_hint_;
@@ -2558,7 +2404,6 @@ void CompactionJob::UpdateCompactionJobInputStatsFromInternalStats(
}
void CompactionJob::UpdateCompactionJobOutputStatsFromInternalStats(
const Status& status,
const InternalStats::CompactionStatsFull& internal_stats) const {
assert(job_stats_);
job_stats_->elapsed_micros = internal_stats.output_level_stats.micros;
@@ -2589,7 +2434,7 @@ void CompactionJob::UpdateCompactionJobOutputStatsFromInternalStats(
internal_stats.proximal_level_stats.num_output_files_blob;
}
if (status.ok() && job_stats_->num_output_files > 0) {
if (job_stats_->num_output_files > 0) {
CopyPrefix(compact_->SmallestUserKey(),
CompactionJobStats::kMaxPrefixLength,
&job_stats_->smallest_output_key_prefix);
@@ -2670,344 +2515,6 @@ Env::IOPriority CompactionJob::GetRateLimiterPriority() {
return Env::IO_LOW;
}
Status CompactionJob::ReadTablePropertiesDirectly(
const ImmutableOptions& ioptions, const MutableCFOptions& moptions,
const FileMetaData* file_meta, const ReadOptions& read_options,
std::shared_ptr<const TableProperties>* tp) {
std::unique_ptr<FSRandomAccessFile> file;
std::string file_name = GetTableFileName(file_meta->fd.GetNumber());
Status s = ioptions.fs->NewRandomAccessFile(file_name, file_options_, &file,
nullptr /* dbg */);
if (!s.ok()) {
return s;
}
std::unique_ptr<RandomAccessFileReader> file_reader(
new RandomAccessFileReader(
std::move(file), file_name, ioptions.clock, io_tracer_,
ioptions.stats, Histograms::SST_READ_MICROS /* hist_type */,
nullptr /* file_read_hist */, ioptions.rate_limiter.get(),
ioptions.listeners));
std::unique_ptr<TableProperties> props;
uint64_t magic_number = kBlockBasedTableMagicNumber;
const auto* table_factory = moptions.table_factory.get();
if (table_factory == nullptr) {
return Status::Incomplete("Table factory is not set");
} else {
const auto& table_factory_name = table_factory->Name();
if (table_factory_name == TableFactory::kPlainTableName()) {
magic_number = kPlainTableMagicNumber;
} else if (table_factory_name == TableFactory::kCuckooTableName()) {
magic_number = kCuckooTableMagicNumber;
}
}
s = ReadTableProperties(file_reader.get(), file_meta->fd.GetFileSize(),
magic_number, ioptions, read_options, &props);
if (!s.ok()) {
return s;
}
*tp = std::move(props);
return s;
}
Status CompactionJob::ReadOutputFilesTableProperties(
const autovector<FileMetaData>& output_files,
const ReadOptions& read_options,
std::vector<std::shared_ptr<const TableProperties>>&
output_files_table_properties,
bool is_proximal_level) {
assert(!output_files.empty());
static const char* level_type =
is_proximal_level ? "proximal output" : "output";
output_files_table_properties.reserve(output_files.size());
Status s;
for (const FileMetaData& metadata : output_files) {
std::shared_ptr<const TableProperties> tp;
s = ReadTablePropertiesDirectly(compact_->compaction->immutable_options(),
compact_->compaction->mutable_cf_options(),
&metadata, read_options, &tp);
if (!s.ok()) {
ROCKS_LOG_ERROR(
db_options_.info_log,
"Failed to read table properties for %s level output file #%" PRIu64
": %s",
level_type, metadata.fd.GetNumber(), s.ToString().c_str());
return s;
}
if (tp == nullptr) {
ROCKS_LOG_ERROR(db_options_.info_log,
"Empty table property for %s level output file #%" PRIu64
"",
level_type, metadata.fd.GetNumber());
s = Status::Corruption("Empty table property for " +
std::string(level_type) +
" level output files during resuming");
return s;
}
output_files_table_properties.push_back(tp);
}
return s;
}
void CompactionJob::RestoreCompactionOutputs(
const ColumnFamilyData* cfd,
const std::vector<std::shared_ptr<const TableProperties>>&
output_files_table_properties,
SubcompactionProgressPerLevel& subcompaction_progress_per_level,
CompactionOutputs* outputs_to_restore) {
assert(outputs_to_restore->GetOutputs().size() == 0);
const auto& output_files = subcompaction_progress_per_level.GetOutputFiles();
for (size_t i = 0; i < output_files.size(); i++) {
FileMetaData file_copy = output_files[i];
outputs_to_restore->AddOutput(std::move(file_copy),
cfd->internal_comparator(),
paranoid_file_checks_, true /* finished */);
outputs_to_restore->UpdateTableProperties(
*output_files_table_properties[i]);
}
outputs_to_restore->SetNumOutputRecords(
subcompaction_progress_per_level.GetNumProcessedOutputRecords());
}
// Attempt to resume compaction from a previously persisted compaction progress.
//
// RETURNS:
// - Status::OK():
// * Input iterator positioned at next unprocessed key
// * CompactionOutputs objects fully restored for both output and proximal
// output levels in SubcompactionState
// * Compaction job statistics accurately reflect input and output records
// processed for record count verification
// * File number generation advanced to prevent conflicts with existing outputs
// - Status::NotFound(): No valid progress to resume from
// - Status::Corruption(): Resume key is invalid, beyond input range, or output
// restoration failed
// - Other non-OK status: Iterator errors or file system issues during
// restoration
//
// The caller must check for Status::IsIncomplete() to distinguish between
// "no resume needed" (proceed with `InternalIterator::SeekToFirst()`) vs
// "resume failed" scenarios.
Status CompactionJob::MaybeResumeSubcompactionProgressOnInputIterator(
SubcompactionState* sub_compact, InternalIterator* input_iter) {
const ReadOptions read_options(Env::IOActivity::kCompaction);
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
SubcompactionProgress& subcompaction_progress =
sub_compact->GetSubcompactionProgressRef();
if (subcompaction_progress.output_level_progress
.GetNumProcessedOutputRecords() == 0 &&
subcompaction_progress.proximal_output_level_progress
.GetNumProcessedOutputRecords() == 0) {
return Status::NotFound("No subcompaction progress to resume");
}
ROCKS_LOG_INFO(db_options_.info_log, "[%s] [JOB %d] Resuming compaction : %s",
cfd->GetName().c_str(), job_id_,
subcompaction_progress.ToString().c_str());
input_iter->Seek(subcompaction_progress.next_internal_key_to_compact);
if (!input_iter->Valid()) {
ROCKS_LOG_ERROR(db_options_.info_log,
"[%s] [JOB %d] Iterator is invalid after "
"seeking to the key to resume. This indicates the key is "
"incorrectly beyond the input data range.",
cfd->GetName().c_str(), job_id_);
return Status::Corruption(
"The key to resume is beyond the input data range");
} else if (!input_iter->status().ok()) {
ROCKS_LOG_ERROR(db_options_.info_log,
"[%s] [JOB %d] Iterator has error after seeking to "
"the key to resume: %s",
cfd->GetName().c_str(), job_id_,
input_iter->status().ToString().c_str());
return Status::Corruption(
"Iterator has error status after seeking to the key: " +
input_iter->status().ToString());
}
sub_compact->compaction_job_stats.has_accurate_num_input_records =
subcompaction_progress.num_processed_input_records != 0;
sub_compact->compaction_job_stats.num_input_records =
subcompaction_progress.num_processed_input_records;
for (const bool& is_proximal_level : {false, true}) {
if (is_proximal_level &&
!sub_compact->compaction->SupportsPerKeyPlacement()) {
continue;
}
Status s;
SubcompactionProgressPerLevel& subcompaction_progress_per_level =
is_proximal_level
? subcompaction_progress.proximal_output_level_progress
: subcompaction_progress.output_level_progress;
const auto& output_files =
subcompaction_progress_per_level.GetOutputFiles();
std::vector<std::shared_ptr<const TableProperties>>
output_files_table_properties;
// TODO(hx235): investigate if we can skip reading properties to save read
// IO
s = ReadOutputFilesTableProperties(output_files, read_options,
output_files_table_properties);
if (!s.ok()) {
ROCKS_LOG_ERROR(
db_options_.info_log,
"[%s] [JOB %d] Failed to read table properties for %s output level"
"files "
"during resume: %s.",
cfd->GetName().c_str(), job_id_, is_proximal_level ? "proximal" : "",
s.ToString().c_str());
return Status::Corruption(
"Not able to resume due to table property reading error " +
s.ToString());
}
RestoreCompactionOutputs(cfd, output_files_table_properties,
subcompaction_progress_per_level,
sub_compact->Outputs(is_proximal_level));
// Skip past all the used file numbers to avoid creating new output files
// after resumption that conflict with the existing output files
for (const auto& file_meta : output_files) {
uint64_t file_number = file_meta.fd.GetNumber();
while (versions_->NewFileNumber() <= file_number) {
versions_->FetchAddFileNumber(1);
}
}
}
return Status::OK();
}
void CompactionJob::UpdateSubcompactionProgress(
const CompactionIterator* c_iter, const Slice next_table_min_key,
SubcompactionState* sub_compact) {
assert(c_iter);
SubcompactionProgress& subcompaction_progress =
sub_compact->GetSubcompactionProgressRef();
IterKey next_ikey_to_compact;
next_ikey_to_compact.SetInternalKey(ExtractUserKey(next_table_min_key),
kMaxSequenceNumber, kValueTypeForSeek);
subcompaction_progress.next_internal_key_to_compact =
next_ikey_to_compact.GetInternalKey().ToString();
// Track total processed input records for progress reporting by combining:
// - Resumed count: records already processed before compaction was
// interrupted
// - Current count: records scanned in the current compaction session
// Only update when both tracking mechanisms provide accurate counts to ensure
// reliability.
subcompaction_progress.num_processed_input_records =
c_iter->HasNumInputEntryScanned() &&
sub_compact->compaction_job_stats.has_accurate_num_input_records
? c_iter->NumInputEntryScanned() +
sub_compact->compaction_job_stats.num_input_records
: 0;
UpdateSubcompactionProgressPerLevel(
sub_compact, false /* is_proximal_level */, subcompaction_progress);
if (sub_compact->compaction->SupportsPerKeyPlacement()) {
UpdateSubcompactionProgressPerLevel(
sub_compact, true /* is_proximal_level */, subcompaction_progress);
}
}
void CompactionJob::UpdateSubcompactionProgressPerLevel(
SubcompactionState* sub_compact, bool is_proximal_level,
SubcompactionProgress& subcompaction_progress) {
SubcompactionProgressPerLevel& subcompaction_progress_per_level =
is_proximal_level ? subcompaction_progress.proximal_output_level_progress
: subcompaction_progress.output_level_progress;
subcompaction_progress_per_level.SetNumProcessedOutputRecords(
sub_compact->OutputStats(is_proximal_level)->num_output_records);
const auto& prev_output_files =
subcompaction_progress_per_level.GetOutputFiles();
const auto& current_output_files =
sub_compact->Outputs(is_proximal_level)->GetOutputs();
for (size_t i = prev_output_files.size(); i < current_output_files.size();
i++) {
subcompaction_progress_per_level.AddToOutputFiles(
current_output_files[i].meta);
}
}
Status CompactionJob::PersistSubcompactionProgress(
SubcompactionState* sub_compact) {
SubcompactionProgress& subcompaction_progress =
sub_compact->GetSubcompactionProgressRef();
assert(compaction_progress_writer_);
VersionEdit edit;
edit.SetSubcompactionProgress(subcompaction_progress);
std::string record;
if (!edit.EncodeTo(&record)) {
ROCKS_LOG_ERROR(
db_options_.info_log,
"[%s] [JOB %d] Failed to encode subcompaction "
"progress",
compact_->compaction->column_family_data()->GetName().c_str(), job_id_);
return Status::Corruption("Failed to encode subcompaction progress");
}
WriteOptions write_options(Env::IOActivity::kCompaction);
Status s = compaction_progress_writer_->AddRecord(write_options, record);
IOOptions opts;
if (s.ok()) {
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
}
if (s.ok()) {
s = compaction_progress_writer_->file()->Sync(opts, db_options_.use_fsync);
}
if (!s.ok()) {
ROCKS_LOG_ERROR(
db_options_.info_log,
"[%s] [JOB %d] Failed to persist subcompaction "
"progress: %s",
compact_->compaction->column_family_data()->GetName().c_str(), job_id_,
s.ToString().c_str());
return s;
}
subcompaction_progress.output_level_progress
.UpdateLastPersistedOutputFilesCount();
subcompaction_progress.proximal_output_level_progress
.UpdateLastPersistedOutputFilesCount();
return Status::OK();
}
Status CompactionJob::VerifyInputRecordCount(
uint64_t num_input_range_del) const {
size_t ts_sz = compact_->compaction->column_family_data()
@@ -3078,4 +2585,5 @@ Status CompactionJob::VerifyOutputRecordCount() const {
}
return Status::OK();
}
} // namespace ROCKSDB_NAMESPACE
+10 -69
View File
@@ -176,20 +176,9 @@ class CompactionJob {
// and organizing seqno <-> time info. `known_single_subcompact` is non-null
// if we already have a known single subcompaction, with optional key bounds
// (currently for executing a remote compaction).
//
// @param compaction_progress Previously saved compaction progress
// to resume from. If empty, compaction starts fresh from the
// beginning.
//
// @param compaction_progress_writer Writer for persisting
// subcompaction progress periodically during compaction
// execution. If nullptr, progress tracking is disabled and compaction
// cannot be resumed later.
void Prepare(
std::optional<std::pair<std::optional<Slice>, std::optional<Slice>>>
known_single_subcompact,
const CompactionProgress& compaction_progress = CompactionProgress{},
log::Writer* compaction_progress_writer = nullptr);
known_single_subcompact);
// REQUIRED mutex not held
// Launch threads for each subcompaction and wait for them to finish. After
@@ -208,7 +197,6 @@ class CompactionJob {
protected:
void UpdateCompactionJobOutputStatsFromInternalStats(
const Status& status,
const InternalStats::CompactionStatsFull& internal_stats) const;
void LogCompaction();
@@ -271,10 +259,6 @@ class CompactionJob {
// consecutive groups such that each group has a similar size.
void GenSubcompactionBoundaries();
void MaybeAssignCompactionProgressAndWriter(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer);
// Get the number of planned subcompactions based on max_subcompactions and
// extra reserved resources
uint64_t GetSubcompactionsLimit();
@@ -375,9 +359,8 @@ class CompactionJob {
const CompactionFilter* configured_compaction_filter,
const CompactionFilter*& compaction_filter,
std::unique_ptr<CompactionFilter>& compaction_filter_from_factory);
void InitializeReadOptionsAndBoundaries(
size_t ts_sz, ReadOptions& read_options,
SubcompactionKeyBoundaries& boundaries);
void InitializeReadOptions(ColumnFamilyData* cfd, ReadOptions& read_options,
SubcompactionKeyBoundaries& boundaries);
InternalIterator* CreateInputIterator(
SubcompactionState* sub_compact, ColumnFamilyData* cfd,
SubcompactionInternalIterators& iterators,
@@ -428,12 +411,12 @@ class CompactionJob {
// update the thread status for starting a compaction.
void ReportStartedCompaction(Compaction* compaction);
Status FinishCompactionOutputFile(
const Status& input_status,
const ParsedInternalKey& prev_table_last_internal_key,
const Slice& next_table_min_key, const Slice* comp_start_user_key,
const Slice* comp_end_user_key, const CompactionIterator* c_iter,
SubcompactionState* sub_compact, CompactionOutputs& outputs);
Status FinishCompactionOutputFile(const Status& input_status,
SubcompactionState* sub_compact,
CompactionOutputs& outputs,
const Slice& next_table_min_key,
const Slice* comp_start_user_key,
const Slice* comp_end_user_key);
Status InstallCompactionResults(bool* compaction_released);
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
CompactionOutputs& outputs);
@@ -510,9 +493,6 @@ class CompactionJob {
// Setting this requires DBMutex.
uint64_t options_file_number_ = 0;
// Writer for persisting compaction progress during compaction
log::Writer* compaction_progress_writer_ = nullptr;
// Get table file name in where it's outputting to, which should also be in
// `output_directory_`.
virtual std::string GetTableFileName(uint64_t file_number);
@@ -520,43 +500,6 @@ class CompactionJob {
// The Compaction Read and Write priorities are the same for different
// scenarios, such as write stalled.
Env::IOPriority GetRateLimiterPriority();
Status MaybeResumeSubcompactionProgressOnInputIterator(
SubcompactionState* sub_compact, InternalIterator* input_iter);
Status ReadOutputFilesTableProperties(
const autovector<FileMetaData>& temporary_output_file_allocation,
const ReadOptions& read_options,
std::vector<std::shared_ptr<const TableProperties>>&
output_files_table_properties,
bool is_proximal_level = false);
Status ReadTablePropertiesDirectly(
const ImmutableOptions& ioptions, const MutableCFOptions& moptions,
const FileMetaData* file_meta, const ReadOptions& read_options,
std::shared_ptr<const TableProperties>* tp);
void RestoreCompactionOutputs(
const ColumnFamilyData* cfd,
const std::vector<std::shared_ptr<const TableProperties>>&
output_files_table_properties,
SubcompactionProgressPerLevel& subcompaction_progress_per_level,
CompactionOutputs* outputs_to_restore);
bool ShouldUpdateSubcompactionProgress(
const SubcompactionState* sub_compact, const CompactionIterator* c_iter,
const ParsedInternalKey& prev_table_last_internal_key,
const Slice& next_table_min_internal_key, const FileMetaData* meta) const;
void UpdateSubcompactionProgress(const CompactionIterator* c_iter,
const Slice next_table_min_key,
SubcompactionState* sub_compact);
Status PersistSubcompactionProgress(SubcompactionState* sub_compact);
void UpdateSubcompactionProgressPerLevel(
SubcompactionState* sub_compact, bool is_proximal_level,
SubcompactionProgress& subcompaction_progress);
};
// CompactionServiceInput is used the pass compaction information between two
@@ -706,9 +649,7 @@ class CompactionServiceCompactionJob : private CompactionJob {
// REQUIRED: mutex held
// Like CompactionJob::Prepare()
void Prepare(
const CompactionProgress& compaction_progress = CompactionProgress{},
log::Writer* compaction_progress_writer = nullptr);
void Prepare();
// Run the compaction in current thread and return the result
Status Run();
+10 -455
View File
@@ -17,7 +17,6 @@
#include "db/db_impl/db_impl.h"
#include "db/error_handler.h"
#include "db/version_set.h"
#include "file/filename.h"
#include "file/random_access_file_reader.h"
#include "file/writable_file_writer.h"
#include "options/options_helper.h"
@@ -211,12 +210,12 @@ class CompactionJobTestBase : public testing::Test {
table_cache_(NewLRUCache(50000, 16)),
write_buffer_manager_(db_options_.db_write_buffer_size),
versions_(new VersionSet(
dbname_, &db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr,
/*io_tracer=*/nullptr, /*db_id=*/"", /*db_session_id=*/"",
/*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false)),
/*error_handler=*/nullptr, /*read_only=*/false)),
shutting_down_(false),
mock_table_factory_(new mock::MockTableFactory()),
error_handler_(nullptr, db_options_, &mutex_),
@@ -546,13 +545,13 @@ class CompactionJobTestBase : public testing::Test {
ASSERT_OK(s);
db_options_.info_log = info_log;
versions_.reset(new VersionSet(
dbname_, &db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
test::kUnitTestDbId, /*db_session_id=*/"",
/*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
versions_.reset(
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
test::kUnitTestDbId, /*db_session_id=*/"",
/*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
compaction_job_stats_.Reset();
VersionEdit new_db;
@@ -2410,450 +2409,6 @@ TEST_F(CompactionJobIOPriorityTest, GetRateLimiterPriority) {
Env::IO_LOW, Env::IO_LOW);
}
class ResumableCompactionJobTest : public CompactionJobTestBase {
public:
ResumableCompactionJobTest()
: CompactionJobTestBase(
test::PerThreadDBPath("allow_resumption_job_test"),
BytewiseComparator(), [](uint64_t /*ts*/) { return ""; },
/*test_io_priority=*/false, TableTypeForTest::kBlockBasedTable) {}
protected:
static constexpr const char* kCancelBeforeThisKey = "cancel_before_this_key";
std::string progress_dir_;
bool enable_cancel_ = false;
std::atomic<int> stop_count_{0};
std::atomic<bool> cancel_{false};
void SetUp() override {
CompactionJobTestBase::SetUp();
SyncPoint::GetInstance()->SetCallBack(
"CompactionOutputs::ShouldStopBefore::manual_decision",
[this](void* p) {
auto* pair = static_cast<std::pair<bool*, const Slice>*>(p);
*(pair->first) = true;
// Cancel after outputting a specific key
if (enable_cancel_) {
ParsedInternalKey parsed_key;
if (ParseInternalKey(pair->second, &parsed_key, true).ok()) {
if (parsed_key.user_key == kCancelBeforeThisKey) {
cancel_.store(true);
}
}
}
});
SyncPoint::GetInstance()->EnableProcessing();
}
void TearDown() override {
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
if (env_->FileExists(progress_dir_).ok()) {
std::vector<std::string> files;
EXPECT_OK(env_->GetChildren(progress_dir_, &files));
for (const auto& file : files) {
if (file != "." && file != "..") {
EXPECT_OK(env_->DeleteFile(progress_dir_ + "/" + file));
}
}
EXPECT_OK(env_->DeleteDir(progress_dir_));
}
CompactionJobTestBase::TearDown();
}
void NewDB() {
if (env_->FileExists(progress_dir_).ok()) {
std::vector<std::string> files;
EXPECT_OK(env_->GetChildren(progress_dir_, &files));
for (const auto& file : files) {
if (file != "." && file != "..") {
EXPECT_OK(env_->DeleteFile(progress_dir_ + "/" + file));
}
}
EXPECT_OK(env_->DeleteDir(progress_dir_));
}
CompactionJobTestBase::NewDB();
progress_dir_ = test::PerThreadDBPath("compaction_progress");
ASSERT_OK(env_->CreateDirIfMissing(progress_dir_));
}
void EnableCompactionCancel() { enable_cancel_ = true; }
void DisableCompactionCancel() {
enable_cancel_ = false;
cancel_.store(false);
}
std::unique_ptr<log::Writer> CreateCompactionProgressWriter(
const std::string& compaction_progress_file) {
std::unique_ptr<FSWritableFile> file;
EXPECT_OK(fs_->NewWritableFile(compaction_progress_file, FileOptions(),
&file, nullptr));
auto file_writer = std::make_unique<WritableFileWriter>(
std::move(file), compaction_progress_file, FileOptions());
auto compaction_progress_writer =
std::make_unique<log::Writer>(std::move(file_writer), 0, false);
return compaction_progress_writer;
}
Status RunCompactionWithProgressTracking(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer,
std::vector<SequenceNumber> snapshots = {},
std::shared_ptr<Statistics> stats = nullptr) {
mutex_.Lock();
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
auto files = cfd->current()->storage_info()->LevelFiles(0);
db_options_.statistics = stats;
db_options_.stats = db_options_.statistics.get();
std::vector<CompactionInputFiles> compaction_input_files;
CompactionInputFiles level;
level.level = 0;
level.files = files;
compaction_input_files.push_back(level);
Compaction compaction(
cfd->current()->storage_info(), cfd->ioptions(),
cfd->GetLatestMutableCFOptions(), mutable_db_options_,
compaction_input_files, 1, mutable_cf_options_.target_file_size_base,
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
cfd->GetLatestMutableCFOptions().compression_opts,
Temperature::kUnknown, 0, {}, std::nullopt, nullptr,
CompactionReason::kManualCompaction);
compaction.FinalizeInputInfo(cfd->current());
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
EventLogger event_logger(db_options_.info_log.get());
JobContext job_context(1, false);
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber,
std::move(snapshots));
CompactionJobStats job_stats;
CompactionJob compaction_job(
0, &compaction, db_options_, mutable_db_options_, env_options_,
versions_.get(), &shutting_down_, &log_buffer, nullptr, nullptr,
nullptr, stats.get(), &mutex_, &error_handler_, &job_context,
table_cache_, &event_logger, false, false, dbname_, &job_stats,
Env::Priority::USER, nullptr, cancel_, env_->GenerateUniqueId(),
DBImpl::GenerateDbSessionId(nullptr), "");
compaction_job.Prepare(std::nullopt, compaction_progress,
compaction_progress_writer);
mutex_.Unlock();
compaction_job.Run().PermitUncheckedError();
EXPECT_OK(compaction_job.io_status());
mutex_.Lock();
bool compaction_released = false;
Status s = compaction_job.Install(&compaction_released);
mutex_.Unlock();
if (!compaction_released) {
compaction.ReleaseCompactionFiles(s);
}
return s;
}
SubcompactionProgress ReadAndParseProgress(
const std::string& compaction_progress_file) {
std::unique_ptr<FSSequentialFile> seq_file;
EXPECT_OK(fs_->NewSequentialFile(compaction_progress_file, FileOptions(),
&seq_file, nullptr));
auto file_reader = std::make_unique<SequentialFileReader>(
std::move(seq_file), compaction_progress_file, 0, nullptr);
log::Reader reader(nullptr, std::move(file_reader), nullptr, true, 0);
SubcompactionProgressBuilder builder;
std::string record;
Slice slice;
while (reader.ReadRecord(&slice, &record)) {
VersionEdit edit;
if (!edit.DecodeFrom(slice).ok()) {
continue;
}
builder.ProcessVersionEdit(edit);
}
EXPECT_TRUE(builder.HasAccumulatedSubcompactionProgress());
return builder.GetAccumulatedSubcompactionProgress();
}
// Test utility function to verify that compaction progress was correctly
// persisted to the progress file after compaction interruption.
//
// VERIFIES:
// - Progress file exists and has expected size (empty if no progress
// expected)
// - Next internal key to compact matches expected user key with proper format
// - Number of processed input records matches position in ordered input keys
// - Number of processed output records equals number of processed input
// records (by test design to simplify verification)
// - Each output file contains exactly one user key (by test design to
// simplify verification)
void VerifyCompactionProgressPersisted(
const std::string& compaction_progress_file,
const std::string& next_user_key_to_compact,
const std::vector<std::string>& ordered_intput_keys) {
ASSERT_OK(env_->FileExists(compaction_progress_file));
uint64_t file_size;
ASSERT_OK(env_->GetFileSize(compaction_progress_file, &file_size));
if (next_user_key_to_compact.empty()) {
ASSERT_EQ(file_size, 0);
return;
}
const auto& subcompaction_progress =
ReadAndParseProgress(compaction_progress_file);
ASSERT_FALSE(subcompaction_progress.next_internal_key_to_compact.empty());
ParsedInternalKey parsed_next_key;
ASSERT_OK(
ParseInternalKey(subcompaction_progress.next_internal_key_to_compact,
&parsed_next_key, true /* log_err_key */));
ASSERT_EQ(parsed_next_key.user_key, next_user_key_to_compact);
ASSERT_EQ(parsed_next_key.sequence, kMaxSequenceNumber);
ASSERT_EQ(parsed_next_key.type, kValueTypeForSeek);
auto it = std::find(ordered_intput_keys.begin(), ordered_intput_keys.end(),
next_user_key_to_compact);
ASSERT_TRUE(it != ordered_intput_keys.end());
auto next_key_index = std::distance(ordered_intput_keys.begin(), it);
ASSERT_EQ(subcompaction_progress.num_processed_input_records,
next_key_index);
ASSERT_EQ(subcompaction_progress.output_level_progress
.GetNumProcessedOutputRecords(),
next_key_index);
ASSERT_EQ(
subcompaction_progress.output_level_progress.GetOutputFiles().size(),
next_key_index);
for (size_t i = 0;
i <
subcompaction_progress.output_level_progress.GetOutputFiles().size();
++i) {
const auto& output_file =
subcompaction_progress.output_level_progress.GetOutputFiles()[i];
ASSERT_EQ(output_file.smallest.user_key().ToString(),
output_file.largest.user_key().ToString());
ASSERT_EQ(output_file.largest.user_key().ToString(),
ordered_intput_keys[i]);
}
}
void RunCancelAndResumeTest(
const std::initializer_list<mock::KVPair>& input_file_1,
const std::initializer_list<mock::KVPair>& input_file_2,
uint64_t last_sequence, const std::vector<uint64_t>& snapshots,
const std::string& expected_next_key_to_compact,
const std::vector<std::string>& expected_input_keys, bool exists_progress,
bool cancelled_past_mid_point = false) {
std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
auto file1 = mock::MakeMockFile(input_file_1);
AddMockFile(file1);
auto file2 = mock::MakeMockFile(input_file_2);
AddMockFile(file2);
SetLastSequence(last_sequence);
// First compaction (will be cancelled)
std::string compaction_progress_file =
CompactionProgressFileName(progress_dir_, 123);
std::unique_ptr<log::Writer> compaction_progress_writer =
CreateCompactionProgressWriter(compaction_progress_file);
ASSERT_OK(stats->Reset());
EnableCompactionCancel();
Status status = RunCompactionWithProgressTracking(
CompactionProgress{}, compaction_progress_writer.get(), snapshots,
stats);
ASSERT_TRUE(status.IsManualCompactionPaused());
DisableCompactionCancel();
HistogramData cancelled_compaction_stats;
stats->histogramData(FILE_WRITE_COMPACTION_MICROS,
&cancelled_compaction_stats);
VerifyCompactionProgressPersisted(compaction_progress_file,
expected_next_key_to_compact,
expected_input_keys);
// Resume compaction
CompactionProgress compaction_progress;
if (exists_progress) {
compaction_progress.push_back(
ReadAndParseProgress(compaction_progress_file));
}
std::string compaction_progress_file_2 =
CompactionProgressFileName(progress_dir_, 234);
std::unique_ptr<log::Writer> compaction_progress_writer_2 =
CreateCompactionProgressWriter(compaction_progress_file_2);
ASSERT_OK(stats->Reset());
status = RunCompactionWithProgressTracking(
compaction_progress, compaction_progress_writer_2.get(),
{} /* snapshots */, stats);
ASSERT_OK(status);
if (cancelled_past_mid_point) {
HistogramData resumed_compaction_stats;
stats->histogramData(FILE_WRITE_COMPACTION_MICROS,
&resumed_compaction_stats);
ASSERT_GT(cancelled_compaction_stats.count,
resumed_compaction_stats.count);
}
}
};
TEST_F(ResumableCompactionJobTest, BasicProgressPersistence) {
NewDB();
auto file1 = mock::MakeMockFile({
{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
});
AddMockFile(file1);
auto file2 = mock::MakeMockFile({
{KeyStr("c", 3U, kTypeValue), "val3"},
{KeyStr("d", 4U, kTypeValue), "val4"},
});
AddMockFile(file2);
SetLastSequence(4U);
std::string compaction_progress_file =
CompactionProgressFileName(progress_dir_, 123);
std::unique_ptr<log::Writer> compaction_progress_writer =
CreateCompactionProgressWriter(compaction_progress_file);
Status status = RunCompactionWithProgressTracking(
CompactionProgress(), compaction_progress_writer.get());
ASSERT_OK(status);
VerifyCompactionProgressPersisted(
compaction_progress_file, "d" /* next_user_key_to_compact */,
{"a", "b", "c", "d"} /* ordered_intput_keys */);
}
TEST_F(ResumableCompactionJobTest, BasicProgressResume) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"}} /* input_file_1 */,
{{KeyStr("bb", 3U, kTypeValue), "val3"},
{KeyStr(kCancelBeforeThisKey, 4U, kTypeValue),
"val4"}} /* input_file_2 */,
4U /* last_sequence */, {} /* snapshots */,
kCancelBeforeThisKey /* expected_next_key_to_compact */,
{"a", "b", "bb", kCancelBeforeThisKey} /* expected_input_keys */,
true /* exists_progress */, true /* cancelled_past_mid_point*/);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnSameKey) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr(kCancelBeforeThisKey, 1U, kTypeValue),
"val1"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 2U, kTypeValue),
"val2"}} /* input_file_2 */,
2U /* last_sequence */, {1U} /* snapshots */,
"" /* expected_next_key_to_compact */,
{kCancelBeforeThisKey, kCancelBeforeThisKey} /* expected_input_keys */,
false /* exists_progress */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnDeleteRange) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr(kCancelBeforeThisKey, 1U, kTypeValue),
"val1"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 2U, kTypeRangeDeletion),
"val2"}} /* input_file_2 */,
2U /* last_sequence */, {1U} /* snapshots */,
"" /* expected_next_key_to_compact */,
{kCancelBeforeThisKey, kCancelBeforeThisKey} /* expected_input_keys */,
false /* exists_progress */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnMerge) {
merge_op_ = MergeOperators::CreateStringAppendOperator();
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"}} /* input_file_1 */,
{{KeyStr("bb", 3U, kTypeValue), "val3"},
{KeyStr(kCancelBeforeThisKey, 4U, kTypeMerge),
"val4"}} /* input_file_2 */,
4U /* last_sequence */, {} /* snapshots */,
"bb" /* expected_next_key_to_compact */,
{"a", "b", "bb", kCancelBeforeThisKey} /* expected_input_keys */,
true /* exists_progress */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnSingleDelete) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
{KeyStr(kCancelBeforeThisKey, 3U, kTypeValue),
"val3"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 4U, kTypeSingleDeletion), ""},
{KeyStr("d", 5U, kTypeValue), "val4"}} /* input_file_2 */,
5U /* last_sequence */, {3U} /* snapshots */,
"b" /* expected_next_key_to_compact */,
{"a", "b", kCancelBeforeThisKey, kCancelBeforeThisKey,
"d"} /* expected_input_keys */,
true /* exists_progress */);
}
TEST_F(ResumableCompactionJobTest, NoProgressResumeOnDeletionAtBottom) {
NewDB();
RunCancelAndResumeTest(
{{KeyStr("a", 1U, kTypeValue), "val1"},
{KeyStr("b", 2U, kTypeValue), "val2"},
{KeyStr(kCancelBeforeThisKey, 3U, kTypeValue),
"val3"}} /* input_file_1 */,
{{KeyStr(kCancelBeforeThisKey, 4U, kTypeDeletion), ""},
{KeyStr("d", 5U, kTypeValue), "val4"}} /* input_file_2 */,
5U /* last_sequence */, {3U} /* snapshots */,
"b" /* expected_next_key_to_compact */,
{"a", "b", kCancelBeforeThisKey, kCancelBeforeThisKey,
"d"} /* expected_input_keys */,
true /* exists_progress */);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+2 -4
View File
@@ -359,8 +359,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
Status CompactionOutputs::AddToOutput(
const CompactionIterator& c_iter,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
const ParsedInternalKey& prev_table_last_internal_key) {
const CompactionFileCloseFunc& close_file_func) {
Status s;
bool is_range_del = c_iter.IsDeleteRangeSentinelKey();
if (is_range_del && compaction_->bottommost_level()) {
@@ -371,8 +370,7 @@ Status CompactionOutputs::AddToOutput(
}
const Slice& key = c_iter.key();
if (ShouldStopBefore(c_iter) && HasBuilder()) {
s = close_file_func(c_iter.InputStatus(), prev_table_last_internal_key, key,
&c_iter, *this);
s = close_file_func(*this, c_iter.InputStatus(), key);
if (!s.ok()) {
return s;
}
+3 -14
View File
@@ -21,8 +21,7 @@ namespace ROCKSDB_NAMESPACE {
class CompactionOutputs;
using CompactionFileOpenFunc = std::function<Status(CompactionOutputs&)>;
using CompactionFileCloseFunc =
std::function<Status(const Status&, const ParsedInternalKey&, const Slice&,
const CompactionIterator*, CompactionOutputs&)>;
std::function<Status(CompactionOutputs&, const Status&, const Slice&)>;
// Files produced by subcompaction, most of the functions are used by
// compaction_job Open/Close compaction file functions.
@@ -59,8 +58,6 @@ class CompactionOutputs {
precalculated_hash, is_proximal_level_);
}
const std::vector<Output>& GetOutputs() const { return outputs_; }
// Set new table builder for the current output
void NewBuilder(const TableBuilderOptions& tboptions);
@@ -198,10 +195,6 @@ class CompactionOutputs {
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
const Slice& next_table_min_key, const std::string& full_history_ts_low);
void SetNumOutputRecords(uint64_t num_output_records) {
stats_.num_output_records = num_output_records;
}
private:
friend class SubcompactionState;
@@ -261,8 +254,7 @@ class CompactionOutputs {
// close and open new compaction output with the functions provided.
Status AddToOutput(const CompactionIterator& c_iter,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
const ParsedInternalKey& prev_table_last_internal_key);
const CompactionFileCloseFunc& close_file_func);
// Close the current output. `open_file_func` is needed for creating new file
// for range-dels only output file.
@@ -278,12 +270,9 @@ class CompactionOutputs {
!range_del_agg->IsEmpty()) {
status = open_file_func(*this);
}
if (HasBuilder()) {
const ParsedInternalKey empty_internal_key{};
const Slice empty_key{};
Status s = close_file_func(status, empty_internal_key, empty_key,
nullptr /* c_iter */, *this);
Status s = close_file_func(*this, status, empty_key);
if (!s.ok() && status.ok()) {
status = s;
}
+5 -5
View File
@@ -375,13 +375,12 @@ Compaction* CompactionPicker::PickCompactionForCompactFiles(
// without configurable `CompressionOptions`, which is inconsistent.
compression_type = compact_options.compression;
}
auto c = new Compaction(
vstorage, ioptions_, mutable_cf_options, mutable_db_options, input_files,
output_level, compact_options.output_file_size_limit,
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
compact_options.output_temperature_override,
mutable_cf_options.default_write_temperature,
compact_options.max_subcompactions,
/* grandparents */ {}, earliest_snapshot, snapshot_checker,
CompactionReason::kManualCompaction);
@@ -680,7 +679,8 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
compact_range_options.target_path_id,
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
Temperature::kUnknown, compact_range_options.max_subcompactions,
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions,
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kManualCompaction,
trim_ts, /* score */ -1,
@@ -871,8 +871,8 @@ Compaction* CompactionPicker::PickCompactionForCompactRange(
GetCompressionType(vstorage, mutable_cf_options, output_level,
vstorage->base_level()),
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
Temperature::kUnknown, compact_range_options.max_subcompactions,
std::move(grandparents),
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions, std::move(grandparents),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
CompactionReason::kManualCompaction, trim_ts, /* score */ -1,
/* l0_files_might_overlap */ true,
+6 -3
View File
@@ -124,7 +124,8 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
Compaction* c = new Compaction(
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
std::move(inputs), 0, 0, 0, 0, kNoCompression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kFIFOTtl,
/* trim_ts */ "", vstorage->CompactionScore(0),
@@ -193,7 +194,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
{comp_inputs}, 0, 16 * 1024 * 1024 /* output file size limit */,
0 /* max compaction bytes, not applicable */,
0 /* output path ID */, mutable_cf_options.compression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompactions */, {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
@@ -292,7 +294,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
/* target_file_size */ 0,
/* max_compaction_bytes */ 0,
/* output_path_id */ 0, kNoCompression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, CompactionReason::kFIFOMaxSize,
/* trim_ts */ "", vstorage->CompactionScore(0),
+1 -1
View File
@@ -557,7 +557,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
GetCompressionType(vstorage_, mutable_cf_options_, output_level_,
vstorage_->base_level()),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
Temperature::kUnknown,
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, std::move(grandparents_),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
compaction_reason_,
+5 -28
View File
@@ -1178,7 +1178,7 @@ TEST_F(CompactionPickerTest, FIFOToCold1) {
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(compaction->compaction_reason(),
CompactionReason::kChangeTemperature);
ASSERT_EQ(compaction->GetOutputTemperature(), Temperature::kCold);
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
ASSERT_EQ(1U, compaction->num_input_files(0));
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
}
@@ -1248,7 +1248,7 @@ TEST_F(CompactionPickerTest, FIFOToColdMaxCompactionSize) {
ASSERT_EQ(compaction->compaction_reason(),
CompactionReason::kChangeTemperature);
// Compaction picker picks older files first and picks one file at a time.
ASSERT_EQ(compaction->GetOutputTemperature(), Temperature::kCold);
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
ASSERT_EQ(1U, compaction->num_input_files(0));
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
}
@@ -1316,7 +1316,7 @@ TEST_F(CompactionPickerTest, FIFOToColdWithExistingCold) {
ASSERT_EQ(compaction->compaction_reason(),
CompactionReason::kChangeTemperature);
// Compaction picker picks older files first and picks one file at a time.
ASSERT_EQ(compaction->GetOutputTemperature(), Temperature::kCold);
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
ASSERT_EQ(1U, compaction->num_input_files(0));
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
}
@@ -1383,7 +1383,7 @@ TEST_F(CompactionPickerTest, FIFOToColdWithHotBetweenCold) {
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(compaction->compaction_reason(),
CompactionReason::kChangeTemperature);
ASSERT_EQ(compaction->GetOutputTemperature(), Temperature::kCold);
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
ASSERT_EQ(1U, compaction->num_input_files(0));
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
}
@@ -1464,35 +1464,12 @@ TEST_F(CompactionPickerTest, FIFOToHotAndWarm) {
ASSERT_EQ(compaction->compaction_reason(),
CompactionReason::kChangeTemperature);
// Compaction picker picks older files first and picks one file at a time.
ASSERT_EQ(compaction->GetOutputTemperature(), Temperature::kWarm);
ASSERT_EQ(compaction->output_temperature(), Temperature::kWarm);
ASSERT_EQ(1U, compaction->num_input_files(0));
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
}
}
TEST_F(CompactionPickerTest, CompactFilesOutputTemperature) {
NewVersionStorage(6, kCompactionStyleLevel);
auto file_number = 66U;
Add(0, file_number, "150", "200", 1000000000U);
UpdateVersionStorageInfo();
std::unordered_set<uint64_t> input{file_number};
std::vector<CompactionInputFiles> input_files;
ASSERT_OK(level_compaction_picker.GetCompactionInputsFromFileNumbers(
&input_files, &input, vstorage_.get(), CompactionOptions()));
auto compaction_options = CompactionOptions();
compaction_options.output_temperature_override = Temperature::kCold;
std::unique_ptr<Compaction> compaction(
level_compaction_picker.PickCompactionForCompactFiles(
compaction_options, input_files, 1, vstorage_.get(),
mutable_cf_options_, mutable_db_options_, /*output_path_id=*/0));
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(compaction->GetOutputTemperature(), Temperature::kCold);
}
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
NewVersionStorage(6, kCompactionStyleLevel);
ioptions_.compaction_pri = kMinOverlappingRatio;
+4 -4
View File
@@ -1093,7 +1093,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
output_level, 1, enable_compression),
GetCompressionOptions(mutable_cf_options_, vstorage_,
output_level, enable_compression),
Temperature::kUnknown,
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents,
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, compaction_reason,
@@ -1441,7 +1441,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
true /* enable_compression */),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
true /* enable_compression */),
Temperature::kUnknown,
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
@@ -1594,7 +1594,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
/* max_grandparent_overlap_bytes */ GetMaxOverlappingBytes(), path_id,
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
Temperature::kUnknown,
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
snapshot_checker_, CompactionReason::kFilesMarkedForCompaction,
/* trim_ts */ "", score_,
@@ -1690,7 +1690,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
true /* enable_compression */),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
true /* enable_compression */),
Temperature::kUnknown,
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, compaction_reason,
+3 -6
View File
@@ -326,9 +326,7 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
compaction_input_(compaction_service_input),
compaction_result_(compaction_service_result) {}
void CompactionServiceCompactionJob::Prepare(
const CompactionProgress& compaction_progress,
log::Writer* compaction_progress_writer) {
void CompactionServiceCompactionJob::Prepare() {
std::optional<Slice> begin;
if (compaction_input_.has_begin) {
begin = compaction_input_.begin;
@@ -337,8 +335,7 @@ void CompactionServiceCompactionJob::Prepare(
if (compaction_input_.has_end) {
end = compaction_input_.end;
}
CompactionJob::Prepare(std::make_pair(begin, end), compaction_progress,
compaction_progress_writer);
CompactionJob::Prepare(std::make_pair(begin, end));
}
Status CompactionServiceCompactionJob::Run() {
@@ -411,7 +408,7 @@ Status CompactionServiceCompactionJob::Run() {
// 2. Update job-level output stats with the aggregated internal_stats_
// Please note that input stats will be updated by primary host when all
// subcompactions are finished
UpdateCompactionJobOutputStatsFromInternalStats(status, internal_stats_);
UpdateCompactionJobOutputStatsFromInternalStats(internal_stats_);
// and set fields that are not propagated as part of the update
compaction_result_->stats.is_manual_compaction = c->is_manual_compaction();
compaction_result_->stats.is_full_compaction = c->is_full_compaction();
+102 -541
View File
@@ -4,7 +4,6 @@
// (found in the LICENSE.Apache file in the root directory).
#include "db/db_test_util.h"
#include "file/file_util.h"
#include "port/stack_trace.h"
#include "rocksdb/utilities/options_util.h"
#include "table/unique_id_impl.h"
@@ -17,17 +16,17 @@ class MyTestCompactionService : public CompactionService {
MyTestCompactionService(
std::string db_path, Options& options,
std::shared_ptr<Statistics>& statistics,
std::vector<std::shared_ptr<EventListener>> listeners,
std::vector<std::shared_ptr<EventListener>>& listeners,
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
table_properties_collector_factories)
: db_path_(std::move(db_path)),
statistics_(statistics),
options_(options),
statistics_(statistics),
start_info_("na", "na", "na", 0, "na", 0, Env::TOTAL,
CompactionReason::kUnknown, false, false, false, -1, -1),
wait_info_("na", "na", "na", 0, "na", 0, Env::TOTAL,
CompactionReason::kUnknown, false, false, false, -1, -1),
listeners_(std::move(listeners)),
listeners_(listeners),
table_properties_collector_factories_(
std::move(table_properties_collector_factories)) {}
@@ -73,31 +72,6 @@ class MyTestCompactionService : public CompactionService {
if (is_override_wait_status_) {
return override_wait_status_;
}
CompactionServiceOptionsOverride options_override = GetOptionsOverride();
OpenAndCompactOptions options;
options.canceled = &canceled_;
Status s =
DB::OpenAndCompact(options, db_path_, GetOutputPath(scheduled_job_id),
compaction_input, result, options_override);
{
InstrumentedMutexLock l(&mutex_);
if (is_override_wait_result_) {
*result = override_wait_result_;
}
result_ = *result;
}
compaction_num_.fetch_add(1);
if (s.ok()) {
return CompactionServiceJobStatus::kSuccess;
} else {
return CompactionServiceJobStatus::kFailure;
}
}
CompactionServiceOptionsOverride GetOptionsOverride() {
CompactionServiceOptionsOverride options_override;
options_override.env = options_.env;
options_override.file_checksum_gen_factory =
@@ -120,7 +94,26 @@ class MyTestCompactionService : public CompactionService {
options_override.table_properties_collector_factories =
table_properties_collector_factories_;
}
return options_override;
OpenAndCompactOptions options;
options.canceled = &canceled_;
Status s =
DB::OpenAndCompact(options, db_path_, db_path_ + "/" + scheduled_job_id,
compaction_input, result, options_override);
{
InstrumentedMutexLock l(&mutex_);
if (is_override_wait_result_) {
*result = override_wait_result_;
}
result_ = *result;
}
compaction_num_.fetch_add(1);
if (s.ok()) {
return CompactionServiceJobStatus::kSuccess;
} else {
return CompactionServiceJobStatus::kFailure;
}
}
void CancelAwaitingJobs() override { canceled_ = true; }
@@ -167,21 +160,14 @@ class MyTestCompactionService : public CompactionService {
return final_updated_status_.load();
}
protected:
private:
InstrumentedMutex mutex_;
const std::string db_path_;
std::shared_ptr<Statistics> statistics_;
std::atomic_int compaction_num_{0};
std::map<std::string, std::string> jobs_;
std::map<std::string, CompactionServiceJobInfo> infos_;
std::string result_;
std::string GetOutputPath(const std::string& scheduled_job_id) {
return db_path_ + "/" + scheduled_job_id;
}
private:
std::atomic_int compaction_num_{0};
const std::string db_path_;
Options options_;
std::shared_ptr<Statistics> statistics_;
CompactionServiceJobInfo start_info_;
CompactionServiceJobInfo wait_info_;
bool is_override_start_status_ = false;
@@ -191,6 +177,7 @@ class MyTestCompactionService : public CompactionService {
CompactionServiceJobStatus override_wait_status_ =
CompactionServiceJobStatus::kFailure;
bool is_override_wait_result_ = false;
std::string result_;
std::string override_wait_result_;
std::vector<std::shared_ptr<EventListener>> listeners_;
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
@@ -476,6 +463,8 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
TEST_F(CompactionServiceTest, StandaloneDeleteRangeTombstoneOptimization) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
ReopenWithCompactionService(&options);
size_t num_files_after_filtered = 0;
SyncPoint::GetInstance()->SetCallBack(
@@ -483,97 +472,83 @@ TEST_F(CompactionServiceTest, StandaloneDeleteRangeTombstoneOptimization) {
[&](void* arg) {
num_files_after_filtered = *static_cast<size_t*>(arg);
});
SyncPoint::GetInstance()->EnableProcessing();
for (auto compaction_style : {CompactionStyle::kCompactionStyleLevel,
CompactionStyle::kCompactionStyleUniversal}) {
SCOPED_TRACE("Style: " + std::to_string(compaction_style));
options.compaction_style = compaction_style;
ReopenWithCompactionService(&options);
std::vector<std::string> files;
{
// Writes first version of data in range partitioned files.
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = dbname_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a1"));
ASSERT_OK(sst_file_writer.Put("b", "b1"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
num_files_after_filtered = 0;
std::vector<std::string> files;
{
// Writes first version of data in range partitioned files.
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = dbname_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a1"));
ASSERT_OK(sst_file_writer.Put("b", "b1"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
std::string file2 = dbname_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("x", "x1"));
ASSERT_OK(sst_file_writer.Put("y", "y1"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a1");
ASSERT_EQ(Get("b"), "b1");
ASSERT_EQ(Get("x"), "x1");
ASSERT_EQ(Get("y"), "y1");
ASSERT_EQ(2, NumTableFilesAtLevel(6));
auto my_cs = GetCompactionService();
uint64_t comp_num = my_cs->GetCompactionNum();
{
// Atomically delete old version of data with one range delete file.
// And a new batch of range partitioned files with new version of data.
files.clear();
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file2 = dbname_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.DeleteRange("a", "z"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
std::string file3 = dbname_ + "file3.sst";
ASSERT_OK(sst_file_writer.Open(file3));
ASSERT_OK(sst_file_writer.Put("a", "a2"));
ASSERT_OK(sst_file_writer.Put("b", "b2"));
ExternalSstFileInfo file3_info;
ASSERT_OK(sst_file_writer.Finish(&file3_info));
files.push_back(std::move(file3));
std::string file4 = dbname_ + "file4.sst";
ASSERT_OK(sst_file_writer.Open(file4));
ASSERT_OK(sst_file_writer.Put("x", "x2"));
ASSERT_OK(sst_file_writer.Put("y", "y2"));
ExternalSstFileInfo file4_info;
ASSERT_OK(sst_file_writer.Finish(&file4_info));
files.push_back(std::move(file4));
}
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_OK(db_->WaitForCompact(WaitForCompactOptions()));
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
if (compaction_style == kCompactionStyleUniversal) {
ASSERT_EQ(num_files_after_filtered, 1);
} else {
// Not filtered
ASSERT_EQ(num_files_after_filtered, 3);
}
Close();
std::string file2 = dbname_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("x", "x1"));
ASSERT_OK(sst_file_writer.Put("y", "y1"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a1");
ASSERT_EQ(Get("b"), "b1");
ASSERT_EQ(Get("x"), "x1");
ASSERT_EQ(Get("y"), "y1");
ASSERT_EQ(2, NumTableFilesAtLevel(6));
auto my_cs = GetCompactionService();
uint64_t comp_num = my_cs->GetCompactionNum();
{
// Atomically delete old version of data with one range delete file.
// And a new batch of range partitioned files with new version of data.
files.clear();
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file2 = dbname_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.DeleteRange("a", "z"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
std::string file3 = dbname_ + "file3.sst";
ASSERT_OK(sst_file_writer.Open(file3));
ASSERT_OK(sst_file_writer.Put("a", "a2"));
ASSERT_OK(sst_file_writer.Put("b", "b2"));
ExternalSstFileInfo file3_info;
ASSERT_OK(sst_file_writer.Finish(&file3_info));
files.push_back(std::move(file3));
std::string file4 = dbname_ + "file4.sst";
ASSERT_OK(sst_file_writer.Open(file4));
ASSERT_OK(sst_file_writer.Put("x", "x2"));
ASSERT_OK(sst_file_writer.Put("y", "y2"));
ExternalSstFileInfo file4_info;
ASSERT_OK(sst_file_writer.Finish(&file4_info));
files.push_back(std::move(file4));
}
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_OK(db_->WaitForCompact(WaitForCompactOptions()));
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
ASSERT_EQ(num_files_after_filtered, 1);
Close();
SyncPoint::GetInstance()->DisableProcessing();
}
@@ -1047,7 +1022,6 @@ TEST_F(CompactionServiceTest, CorruptedOutputParanoidFileCheck) {
Destroy(options);
options.disable_auto_compactions = true;
options.paranoid_file_checks = paranoid_file_check_enabled;
options.verify_output_flags = VerifyOutputFlags::kVerifyNone;
ReopenWithCompactionService(&options);
GenerateTestData();
@@ -1102,87 +1076,6 @@ TEST_F(CompactionServiceTest, CorruptedOutputParanoidFileCheck) {
}
}
TEST_F(CompactionServiceTest, CorruptedOutputVerifyOutputFlags) {
for (VerifyOutputFlags verify_output_flags :
{VerifyOutputFlags::kVerifyNone,
VerifyOutputFlags::kEnableForLocalCompaction |
VerifyOutputFlags::kVerifyBlockChecksum,
VerifyOutputFlags::kEnableForRemoteCompaction |
VerifyOutputFlags::kVerifyBlockChecksum,
VerifyOutputFlags::kEnableForRemoteCompaction |
VerifyOutputFlags::kVerifyIteration,
VerifyOutputFlags::kVerifyAll}) {
SCOPED_TRACE(
"verify_output_flags=" +
std::to_string(static_cast<std::underlying_type_t<VerifyOutputFlags>>(
verify_output_flags)));
Options options = CurrentOptions();
Destroy(options);
options.disable_auto_compactions = true;
options.paranoid_file_checks = false;
options.verify_output_flags = verify_output_flags;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
CompactionServiceResult* compaction_result =
*(static_cast<CompactionServiceResult**>(arg));
ASSERT_TRUE(compaction_result != nullptr &&
!compaction_result->output_files.empty());
// Corrupt files here
for (const auto& output_file : compaction_result->output_files) {
std::string file_name =
compaction_result->output_path + "/" + output_file.file_name;
// Corrupt very small range of bytes. This corruption is so small
// that this isn't caught by default light-weight check
ASSERT_OK(test::CorruptFile(env_, file_name, 0, 1,
false /* verifyChecksum */));
}
});
SyncPoint::GetInstance()->EnableProcessing();
const bool is_enabled_for_remote_compaction =
!!(verify_output_flags & VerifyOutputFlags::kEnableForRemoteCompaction);
const bool should_verify_block_checksum =
!!(verify_output_flags & VerifyOutputFlags::kVerifyBlockChecksum);
const bool should_verify_iteration =
!!(verify_output_flags & VerifyOutputFlags::kVerifyIteration);
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
if (is_enabled_for_remote_compaction &&
(should_verify_block_checksum || should_verify_iteration)) {
ASSERT_NOK(s);
ASSERT_TRUE(s.IsCorruption());
} else {
// CompactRange() goes through if block checksum wasn't verified
ASSERT_OK(s);
}
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// On the worker side, the compaction is considered success
// Verification is done on the primary side
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
}
TEST_F(CompactionServiceTest, TruncatedOutput) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
@@ -2100,338 +1993,6 @@ TEST_F(CompactionServiceTest, TablePropertiesCollector) {
ASSERT_TRUE(has_user_property);
}
class ResumableCompactionService : public MyTestCompactionService {
public:
enum class TestScenario {
// Test scenario 1: Two-phase compaction with resumption
// - Phase 1: Cancel the compaction running with resumption enabled (saves
// progress)
// - Phase 2: Resume from saved progress and complete
// Validates: Resumption reduces redundant work
kCancelThenResume,
// Test scenario 2: Two-phase compaction without resumption
// - Phase 1: Cancel the compaction running with resumption enabled (saves
// progress)
// - Phase 2: Start fresh without resumption (ignores saved progress) and
// complete
// Validates: Disabling resumption causes full reprocessing
kCancelThenFreshStart,
// Test scenario 3: Three-phase compaction toggling resumption on/off/on
// - Phase 1: Cancel the compaction running with resumption enabled (saves
// progress)
// - Phase 2: Start fresh wtihout resumption (ignores saved progress) and
// cancel agains
// - Phase 3: Resume with resumption support (loads Phase 1's progress) and
// complete
// Validates: Resumption state can be toggled;
kMultipleCancelToggleResumption
};
ResumableCompactionService(const std::string& db_path, Options& options,
std::shared_ptr<Statistics> statistics,
TestScenario scenario)
: MyTestCompactionService(db_path, options, statistics,
{} /* listeners */,
{} /* table_properties_collector_factories */),
scenario_(scenario) {}
CompactionServiceJobStatus Wait(const std::string& scheduled_job_id,
std::string* result) override {
std::string compaction_input = ExtractCompactionInput(scheduled_job_id);
EXPECT_FALSE(compaction_input.empty());
OpenAndCompactOptions open_and_compaction_options;
auto override_options = GetOptionsOverride();
// Force creation of one key per output file for test simplicity.
// ASSUMPTION: This makes stats.count directly proportional to keys
// processed.
SyncPoint::GetInstance()->SetCallBack(
"CompactionOutputs::ShouldStopBefore::manual_decision", [](void* p) {
auto* pair = static_cast<std::pair<bool*, const Slice>*>(p);
*(pair->first) = true;
});
// Simulate cancelled compaction by overriding status at completion. So
// compaction processes all keys before this point to make stats.count
// comparison straightforward.
SyncPoint::GetInstance()->SetCallBack(
"DBImplSecondary::CompactWithoutInstallation::End", [&](void* status) {
auto s = static_cast<Status*>(status);
*s = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
});
SyncPoint::GetInstance()->EnableProcessing();
// Phase 1: Run compaction with resumption enabled and cancel it
// - Processes all input keys
// - Creates output files and saves progress
// - Status overridden to "paused"
open_and_compaction_options.allow_resumption = true;
auto phase1_stats =
RunCancelledCompaction(open_and_compaction_options, scheduled_job_id,
compaction_input, override_options);
HistogramData phase2_stats;
if (scenario_ == TestScenario::kMultipleCancelToggleResumption) {
// Phase 2: Run compaction WITHOUT resumption (fresh start) and cancel it
// - Delete all files left behind Phase 1 before calling OpenAndCompact()
// - Processes all input keys again from scratch
// - Creates output files but does NOT save progress
// - Status overridden to "paused"
open_and_compaction_options.allow_resumption = false;
// Clean up output folder for fresh start
std::string output_dir = GetOutputPath(scheduled_job_id);
Status cleanup_status = DestroyDir(override_options.env, output_dir);
EXPECT_TRUE(cleanup_status.ok());
EXPECT_OK(override_options.env->CreateDir(output_dir));
phase2_stats =
RunCancelledCompaction(open_and_compaction_options, scheduled_job_id,
compaction_input, override_options);
// Validation: Phase 2 starts from scratch, so it processes the same
// input keys as Phase 1.
// ASSUMPTION: With fixed input (10 keys) and deterministic cancellation
// (after processing), both phases create the same number of output files.
EXPECT_EQ(phase2_stats.count, phase1_stats.count);
}
SyncPoint::GetInstance()->ClearCallBack(
"DBImplSecondary::CompactWithoutInstallation::End");
// Final phase: Run compaction to completion (no cancellation)
if (scenario_ == TestScenario::kMultipleCancelToggleResumption) {
// Attempt to resume but it ends up starting fresh
open_and_compaction_options.allow_resumption = true;
} else if (scenario_ == TestScenario::kCancelThenResume) {
// Resume from Phase 1's saved progress
open_and_compaction_options.allow_resumption = true;
} else { // kCancelThenFreshStart
// Start fresh without resumption
open_and_compaction_options.allow_resumption = false;
// Clean up output folder for fresh start
std::string output_dir = GetOutputPath(scheduled_job_id);
Status cleanup_status = DestroyDir(override_options.env, output_dir);
EXPECT_TRUE(cleanup_status.ok());
EXPECT_OK(override_options.env->CreateDir(output_dir));
}
auto final_phase_stats =
RunCompaction(open_and_compaction_options, scheduled_job_id,
compaction_input, override_options, result);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Validate statistics based on scenario
if (scenario_ == TestScenario::kMultipleCancelToggleResumption) {
// ASSUMPTION: Phase 1 processes all keys before cancellation
EXPECT_GT(phase1_stats.count, 0);
// ASSUMPTION: Phase 2 runs with allow_resumption=false and an empty
// folder. Phase 2 then creates its own output files (but doesn't save
// progress). When Phase 3 starts with allow_resumption=true, it finds no
// progress file exists, so it cannot resume and must start from scratch,
// processing all input keys again.
// Result: Phase 3 does the same amount of work as Phase 1.
EXPECT_EQ(final_phase_stats.count, phase1_stats.count);
} else if (scenario_ == TestScenario::kCancelThenResume) {
// ASSUMPTION: Phase 1 processes all keys before cancellation
EXPECT_GT(phase1_stats.count, 0);
// ASSUMPTION: Phase 1 processes all keys and saves progress before
// cancellation. Final phase resumes from Phase 1's saved progress.
// Since Phase 1 completed all processing before being cancelled, the
// final phase should do less work than Phase 1.
EXPECT_LT(final_phase_stats.count, phase1_stats.count);
} else { // kCancelThenFreshStart
// ASSUMPTION: Phase 1 processes all keys before cancellation
EXPECT_GT(phase1_stats.count, 0);
// ASSUMPTION: Final phase starts fresh without resumption, so it
// processes all input keys again and creates the same number of files
EXPECT_EQ(final_phase_stats.count, phase1_stats.count);
}
StoreResult(*result);
return CompactionServiceJobStatus::kSuccess;
}
private:
std::string ExtractCompactionInput(const std::string& scheduled_job_id) {
InstrumentedMutexLock l(&mutex_);
auto job_index = jobs_.find(scheduled_job_id);
if (job_index == jobs_.end()) {
return "";
}
std::string compaction_input = std::move(job_index->second);
jobs_.erase(job_index);
auto info_index = infos_.find(scheduled_job_id);
if (info_index == infos_.end()) {
return "";
}
infos_.erase(info_index);
return compaction_input;
}
HistogramData RunCancelledCompaction(
const OpenAndCompactOptions& options, const std::string& scheduled_job_id,
const std::string& compaction_input,
const CompactionServiceOptionsOverride& override_options) {
std::string temp_result;
EXPECT_OK(statistics_->Reset());
Status s =
DB::OpenAndCompact(options, db_path_, GetOutputPath(scheduled_job_id),
compaction_input, &temp_result, override_options);
EXPECT_TRUE(s.IsManualCompactionPaused());
HistogramData stats;
statistics_->histogramData(FILE_WRITE_COMPACTION_MICROS, &stats);
return stats;
}
HistogramData RunCompaction(
const OpenAndCompactOptions& options, const std::string& scheduled_job_id,
const std::string& compaction_input,
const CompactionServiceOptionsOverride& override_options,
std::string* result) {
EXPECT_OK(statistics_->Reset());
Status s =
DB::OpenAndCompact(options, db_path_, GetOutputPath(scheduled_job_id),
compaction_input, result, override_options);
EXPECT_TRUE(s.ok());
HistogramData stats;
statistics_->histogramData(FILE_WRITE_COMPACTION_MICROS, &stats);
return stats;
}
void StoreResult(const std::string& result) {
InstrumentedMutexLock l(&mutex_);
result_ = result;
}
TestScenario scenario_;
};
class ResumableCompactionServiceTest : public CompactionServiceTest {
public:
explicit ResumableCompactionServiceTest() : CompactionServiceTest() {}
void RunCompactionCancelTest(
ResumableCompactionService::TestScenario scenario) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
std::shared_ptr<Statistics> statistics = CreateDBStatistics();
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
BlockBasedTableOptions table_options;
table_options.verify_compression = true;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
auto resume_cs = std::make_shared<ResumableCompactionService>(
dbname_, options, statistics, scenario);
options.compaction_service = resume_cs;
DestroyAndReopen(options);
GenerateTestData();
ASSERT_OK(statistics->Reset());
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
Status s = db_->CompactRange(cro, nullptr, nullptr);
ASSERT_OK(s);
VerifyTestData();
s = db_->VerifyChecksum();
ASSERT_OK(s);
s = db_->VerifyFileChecksums(ReadOptions());
ASSERT_OK(s);
CompactionServiceResult result;
resume_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
ASSERT_GT(result.output_files.size(), 0);
uint64_t resumed_bytes =
statistics->getTickerCount(REMOTE_COMPACT_RESUMED_BYTES);
if (scenario ==
ResumableCompactionService::TestScenario::kCancelThenResume) {
// When resuming compaction, some bytes should be resumed from previous
// progress
ASSERT_GT(resumed_bytes, 0);
} else if (scenario == ResumableCompactionService::TestScenario::
kCancelThenFreshStart) {
// When starting fresh (ignoring existing progress), no bytes should be
// resumed
ASSERT_EQ(resumed_bytes, 0);
} else { // kMultipleCancelToggleResumption
// Phase 2 ran without resumption (fresh start), so Phase 3 has no
// progress to resume from. It processes all keys again from scratch.
ASSERT_EQ(resumed_bytes, 0);
}
}
void GenerateTestData() {
for (int i = 0; i < kNumKeys; ++i) {
ASSERT_OK(Put(Key(i), "value"));
ASSERT_OK(Flush());
if (i % 2 == 0) {
ASSERT_OK(Delete(Key(i)));
ASSERT_OK(Flush());
}
}
}
void VerifyTestData() {
for (int i = 0; i < kNumKeys; ++i) {
if (i % 2 == 0) {
ASSERT_EQ("NOT_FOUND", Get((Key(i))));
} else {
ASSERT_EQ("value", Get((Key(i))));
}
}
}
private:
static constexpr int kNumKeys = 10;
};
TEST_F(ResumableCompactionServiceTest, CompactionCancelThenResume) {
RunCompactionCancelTest(
ResumableCompactionService::TestScenario::kCancelThenResume);
}
TEST_F(ResumableCompactionServiceTest, CompactionCancelThenFreshStart) {
RunCompactionCancelTest(
ResumableCompactionService::TestScenario::kCancelThenFreshStart);
}
TEST_F(ResumableCompactionServiceTest,
CompactionMultipleCancelToggleResumption) {
RunCompactionCancelTest(ResumableCompactionService::TestScenario::
kMultipleCancelToggleResumption);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+2 -4
View File
@@ -108,13 +108,11 @@ Slice SubcompactionState::LargestUserKey() const {
Status SubcompactionState::AddToOutput(
const CompactionIterator& iter, bool use_proximal_output,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
const ParsedInternalKey& prev_table_last_internal_key) {
const CompactionFileCloseFunc& close_file_func) {
// update target output
current_outputs_ =
use_proximal_output ? &proximal_level_outputs_ : &compaction_outputs_;
return current_outputs_->AddToOutput(iter, open_file_func, close_file_func,
prev_table_last_internal_key);
return current_outputs_->AddToOutput(iter, open_file_func, close_file_func);
}
} // namespace ROCKSDB_NAMESPACE
+1 -13
View File
@@ -208,20 +208,10 @@ class SubcompactionState {
return range_del_agg_ && !range_del_agg_->IsEmpty();
}
void SetSubcompactionProgress(
const SubcompactionProgress& subcompaction_progress) {
subcompaction_progress_ = subcompaction_progress;
}
SubcompactionProgress& GetSubcompactionProgressRef() {
return subcompaction_progress_;
}
// Add compaction_iterator key/value to the `Current` output group.
Status AddToOutput(const CompactionIterator& iter, bool use_proximal_output,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func,
const ParsedInternalKey& prev_table_last_internal_key);
const CompactionFileCloseFunc& close_file_func);
// Close all compaction output files, both output_to_proximal_level outputs
// and normal outputs.
@@ -251,8 +241,6 @@ class SubcompactionState {
CompactionOutputs proximal_level_outputs_;
CompactionOutputs* current_outputs_ = &compaction_outputs_;
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
SubcompactionProgress subcompaction_progress_;
};
} // namespace ROCKSDB_NAMESPACE
+24
View File
@@ -675,6 +675,30 @@ TEST_F(DBBasicTest, Flush) {
} while (ChangeCompactOptions());
}
TEST_F(DBBasicTest, ManifestRollOver) {
do {
Options options;
options.max_manifest_file_size = 10; // 10 bytes
options = CurrentOptions(options);
CreateAndReopenWithCF({"pikachu"}, options);
{
ASSERT_OK(Put(1, "manifest_key1", std::string(1000, '1')));
ASSERT_OK(Put(1, "manifest_key2", std::string(1000, '2')));
ASSERT_OK(Put(1, "manifest_key3", std::string(1000, '3')));
uint64_t manifest_before_flush = dbfull()->TEST_Current_Manifest_FileNo();
ASSERT_OK(Flush(1)); // This should trigger LogAndApply.
uint64_t manifest_after_flush = dbfull()->TEST_Current_Manifest_FileNo();
ASSERT_GT(manifest_after_flush, manifest_before_flush);
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_GT(dbfull()->TEST_Current_Manifest_FileNo(), manifest_after_flush);
// check if a new manifest file got inserted or not.
ASSERT_EQ(std::string(1000, '1'), Get(1, "manifest_key1"));
ASSERT_EQ(std::string(1000, '2'), Get(1, "manifest_key2"));
ASSERT_EQ(std::string(1000, '3'), Get(1, "manifest_key3"));
}
} while (ChangeCompactOptions());
}
TEST_F(DBBasicTest, IdentityAcrossRestarts) {
constexpr size_t kMinIdSize = 10;
do {
-161
View File
@@ -1,161 +0,0 @@
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include "db/db_test_util.h"
namespace ROCKSDB_NAMESPACE {
class DBEtc3Test : public DBTestBase {
public:
DBEtc3Test() : DBTestBase("db_etc3_test", /*env_do_fsync=*/true) {}
};
TEST_F(DBEtc3Test, ManifestRollOver) {
do {
Options options;
// Force new manifest on each manifest write
options.max_manifest_file_size = 0;
options.max_manifest_space_amp_pct = 0;
options = CurrentOptions(options);
CreateAndReopenWithCF({"pikachu"}, options);
{
ASSERT_OK(Put(1, "key1", std::string(1000, '1')));
ASSERT_OK(Put(1, "key2", std::string(1000, '2')));
ASSERT_OK(Put(1, "key3", std::string(1000, '3')));
uint64_t manifest_before_flush = dbfull()->TEST_Current_Manifest_FileNo();
ASSERT_OK(Flush(1)); // This should trigger LogAndApply.
uint64_t manifest_after_flush = dbfull()->TEST_Current_Manifest_FileNo();
ASSERT_GT(manifest_after_flush, manifest_before_flush);
// Re-open should always re-create manifest file
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_GT(dbfull()->TEST_Current_Manifest_FileNo(), manifest_after_flush);
ASSERT_EQ(std::string(1000, '1'), Get(1, "key1"));
ASSERT_EQ(std::string(1000, '2'), Get(1, "key2"));
ASSERT_EQ(std::string(1000, '3'), Get(1, "key3"));
}
} while (ChangeCompactOptions());
}
TEST_F(DBEtc3Test, AutoTuneManifestSize) {
// Ensure we have auto-tuning beyond max_manifest_file_size by default
ASSERT_EQ(DBOptions{}.max_manifest_space_amp_pct, 500);
Options options = CurrentOptions();
ASSERT_OK(db_->SetOptions({{"level0_file_num_compaction_trigger", "20"}}));
// Use large column family names to essentially control the amount of payload
// data needed for the manifest file. Drop manifest entries don't include the
// CF name so are small.
uint64_t prev_manifest_num = 0, cur_manifest_num = 0;
std::deque<ColumnFamilyHandle*> handles;
int counter = 5;
auto AddCfFn = [&]() {
std::string name = "cf" + std::to_string(counter++);
name.resize(1000, 'a');
ASSERT_OK(db_->CreateColumnFamily(options, name, &handles.emplace_back()));
prev_manifest_num = cur_manifest_num;
cur_manifest_num = dbfull()->TEST_Current_Manifest_FileNo();
};
auto DropCfFn = [&]() {
ASSERT_OK(db_->DropColumnFamily(handles.front()));
ASSERT_OK(db_->DestroyColumnFamilyHandle(handles.front()));
handles.pop_front();
prev_manifest_num = cur_manifest_num;
cur_manifest_num = dbfull()->TEST_Current_Manifest_FileNo();
};
auto TrivialManifestWriteFn = [&]() {
ASSERT_OK(Put("x", std::to_string(counter++)));
ASSERT_OK(Flush());
prev_manifest_num = cur_manifest_num;
cur_manifest_num = dbfull()->TEST_Current_Manifest_FileNo();
};
options.max_manifest_file_size = 1000000;
options.max_manifest_space_amp_pct = 0; // no auto-tuning yet
DestroyAndReopen(options);
// With the generous (minimum) maximum manifest size, should not be rotated
AddCfFn();
AddCfFn();
AddCfFn();
ASSERT_EQ(prev_manifest_num, cur_manifest_num);
// Change options for small max and (still) no auto-tuning
ASSERT_OK(db_->SetDBOptions({{"max_manifest_file_size", "3000"}}));
// Takes effect on the next manifest write
TrivialManifestWriteFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
// Now we have to rewrite the whole manifest on each write because the
// compacted size exceeds the "max" size.
AddCfFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
DropCfFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
AddCfFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
TrivialManifestWriteFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
// Enabling auto-tuning should fix this, immediately for next manifest writes.
// This will allow up to double-ish the size of the compacted manifest,
// which last should have been 4000 + some bytes.
ASSERT_EQ(handles.size(), 4U);
ASSERT_OK(db_->SetDBOptions({{"max_manifest_space_amp_pct", "105"}}));
// After 9 CF names should be enough to rotate the manifest
for (int i = 1; i <= 5; ++i) {
if ((i % 2) == 1) {
DropCfFn();
}
AddCfFn();
ASSERT_EQ(prev_manifest_num, cur_manifest_num);
}
TrivialManifestWriteFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
// We now have a different last compacted manifest size, should be
// able to go beyond 9 CFs named in manifest this time.
ASSERT_EQ(handles.size(), 6U);
DropCfFn();
DropCfFn();
for (int i = 1; i <= 4; ++i) {
DropCfFn();
AddCfFn();
ASSERT_EQ(prev_manifest_num, cur_manifest_num);
}
// We've written 10 named CFs to the manifest. We should be able to
// dynamically change the auto-tuning still based on the last "compacted"
// manifest size of 7000 + some bytes.
ASSERT_OK(db_->SetDBOptions({{"max_manifest_space_amp_pct", "51"}}));
TrivialManifestWriteFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
// And the "compacted" manifest size has reset again, so should be changed
// again sooner.
ASSERT_EQ(handles.size(), 4U);
for (int i = 1; i <= 2; ++i) {
AddCfFn();
ASSERT_EQ(prev_manifest_num, cur_manifest_num);
}
// Enough for manifest change
AddCfFn();
ASSERT_LT(prev_manifest_num, cur_manifest_num);
// Wrap up
while (!handles.empty()) {
DropCfFn();
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
RegisterCustomObjects(argc, argv);
return RUN_ALL_TESTS();
}
-146
View File
@@ -3561,152 +3561,6 @@ TEST_F(DBFlushTest, VerifyOutputRecordCount) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
class DBFlushSuperBlockTest
: public DBFlushTest,
public ::testing::WithParamInterface<std::tuple<bool, size_t, size_t>> {
public:
DBFlushSuperBlockTest() : DBFlushTest() {}
std::string formatKey(int i) {
int desired_length = 10;
char buffer[64];
snprintf(buffer, 64, "%0*d", desired_length, i);
return buffer;
}
void VerifyReadWithGet(int key_count) {
for (int i = 0; i < key_count; ++i) {
PinnableSlice value;
ASSERT_OK(Get(formatKey(i), &value));
ASSERT_EQ(value.ToString(), added_data[formatKey(i)]);
}
}
void VerifyReadWithIterator(int key_count) {
{
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
int i = 0;
for (it->SeekToFirst(); it->Valid(); it->Next()) {
ASSERT_OK(it->status());
ASSERT_EQ((it->key()).ToString(), formatKey(i));
ASSERT_EQ((it->value()).ToString(), added_data[formatKey(i)]);
i++;
}
ASSERT_OK(it->status());
ASSERT_EQ(i, key_count);
}
}
protected:
Random rnd{123};
std::unordered_map<std::string, std::string> added_data;
};
constexpr size_t kLowSpaceOverheadRatio = 256;
TEST_P(DBFlushSuperBlockTest, SuperBlock) {
constexpr int key_count = 12345;
Options options;
options.env = env_;
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
options.paranoid_file_checks = true;
options.write_buffer_size = 1024 * 1024;
BlockBasedTableOptions block_options;
block_options.block_align = get<0>(GetParam());
block_options.index_block_restart_interval = 3;
block_options.super_block_alignment_size = get<1>(GetParam());
block_options.super_block_alignment_space_overhead_ratio = get<2>(GetParam());
options.table_factory.reset(NewBlockBasedTableFactory(block_options));
if (block_options.block_align) {
// When block align is enabled, disable compression
options.compression = kNoCompression;
}
ASSERT_OK(options.table_factory->ValidateOptions(
DBOptions(options), ColumnFamilyOptions(options)));
Reopen(options);
int super_block_pad_count = 0;
int super_block_pad_exceed_limit_count = 0;
SyncPoint::GetInstance()->SetCallBack(
"BlockBasedTableBuilder::WriteMaybeCompressedBlock:"
"SuperBlockAlignment",
[&super_block_pad_count](void* /*arg*/) { super_block_pad_count++; });
SyncPoint::GetInstance()->SetCallBack(
"BlockBasedTableBuilder::WriteMaybeCompressedBlock:"
"SuperBlockAlignmentPaddingBytesExceedLimit",
[&super_block_pad_exceed_limit_count](void* /*arg*/) {
super_block_pad_exceed_limit_count++;
});
SyncPoint::GetInstance()->EnableProcessing();
// Add lots of keys
for (int i = 0; i < key_count; ++i) {
added_data[formatKey(i)] = std::string(rnd.RandomString(rnd.Next() % 1000));
ASSERT_OK(Put(formatKey(i), added_data[formatKey(i)]));
}
// flush the data in memory to disk to verify with super block alignment, the
// data could be read back properly
Reopen(options);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// When block_align is enabled, super block is always aligned, so there should
// be 0 padding for super block alignment
if (block_options.super_block_alignment_size != 0 &&
!block_options.block_align) {
ASSERT_GT(super_block_pad_count, 0);
} else {
ASSERT_EQ(super_block_pad_count, 0);
}
if (!block_options.block_align &&
block_options.super_block_alignment_size != 0 &&
block_options.super_block_alignment_space_overhead_ratio ==
kLowSpaceOverheadRatio) {
ASSERT_GT(super_block_pad_exceed_limit_count, 0);
}
// verify the values are correct
VerifyReadWithGet(key_count);
Reopen(options);
VerifyReadWithIterator(key_count);
// verify checksum
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
// Reopen options and flip the option of super block configuration, read still
// works. This verifies the forward/backward compatibility
if (block_options.super_block_alignment_size == 0) {
block_options.super_block_alignment_size = 16 * 1024;
} else {
block_options.super_block_alignment_size = 0;
}
options.table_factory.reset(NewBlockBasedTableFactory(block_options));
Reopen(options);
// verify the values are correct
VerifyReadWithGet(key_count);
Reopen(options);
VerifyReadWithIterator(key_count);
// verify checksum
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
}
INSTANTIATE_TEST_CASE_P(
SuperBlockTests, DBFlushSuperBlockTest,
testing::Combine(testing::Bool(), testing::Values(0, 32 * 1024, 16 * 1024),
// Use very low space overhead ratio to test
// the case where required padded bytes is
// larger than the max allowed padding size
testing::Values(4, kLowSpaceOverheadRatio)));
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+2 -2
View File
@@ -370,10 +370,10 @@ TEST_F(DBFollowerTest, RetryCatchupManifestRollover) {
// This test creates 4 L0 files and compacts them. The follower, during catchup,
// successfully instantiates 4 Versions corresponding to the 4 files (but
// doesn't install them yet), followed by deleting those 4 and adding a new
// donesn't install them yet), followed by deleting those 4 and adding a new
// file from compaction. The test verifies that the 4 L0 files are deleted
// correctly by the follower.
// We use the Barrier* functions to ensure that the follower first sees the 4
// We use teh Barrier* functions to ensure that the follower first sees the 4
// L0 files and is able to link them, and then sees the compaction that
// obsoletes those L0 files (so those L0 files are intermediates that it has
// to explicitly delete). Suppose we don't have any barriers, its possible
+5 -24
View File
@@ -258,10 +258,10 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
[this]() { this->TriggerPeriodicCompaction(); });
versions_.reset(new VersionSet(
dbname_, &immutable_db_options_, mutable_db_options_, file_options_,
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));
dbname_, &immutable_db_options_, file_options_, 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));
column_family_memtables_.reset(
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
@@ -1412,7 +1412,7 @@ Status DBImpl::SetDBOptions(
file_options_for_compaction_ = FileOptions(new_db_options);
file_options_for_compaction_ = fs_->OptimizeForCompactionTableWrite(
file_options_for_compaction_, immutable_db_options_);
versions_->UpdatedMutableDbOptions(mutable_db_options_, &mutex_);
versions_->ChangeFileOptions(mutable_db_options_);
// TODO(xiez): clarify why apply optimize for read to write options
file_options_for_compaction_ = fs_->OptimizeForCompactionTableRead(
file_options_for_compaction_, immutable_db_options_);
@@ -1480,12 +1480,6 @@ int DBImpl::FindMinimumEmptyLevelFitting(
return minimum_level;
}
Status DBImpl::FlushWAL(const FlushWALOptions& options) {
WriteOptions write_options;
write_options.rate_limiter_priority = options.rate_limiter_priority;
return FlushWAL(write_options, options.sync);
}
Status DBImpl::FlushWAL(const WriteOptions& write_options, bool sync) {
if (manual_wal_flush_) {
IOStatus io_s;
@@ -5047,19 +5041,6 @@ void DBImpl::GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
}
}
void DBImpl::GetColumnFamilyMetaData(
ColumnFamilyHandle* column_family,
const GetColumnFamilyMetaDataOptions& options,
ColumnFamilyMetaData* metadata) {
assert(column_family);
auto* cfd =
static_cast_with_check<ColumnFamilyHandleImpl>(column_family)->cfd();
{
InstrumentedMutexLock l(&mutex_);
cfd->current()->GetColumnFamilyMetaData(options, metadata);
}
}
void DBImpl::GetAllColumnFamilyMetaData(
std::vector<ColumnFamilyMetaData>* metadata) {
InstrumentedMutexLock l(&mutex_);
+2 -18
View File
@@ -484,13 +484,10 @@ class DBImpl : public DB {
const FlushOptions& options,
const std::vector<ColumnFamilyHandle*>& column_families) override;
Status FlushWAL(bool sync) override {
FlushWALOptions options;
options.sync = sync;
return FlushWAL(options);
// TODO: plumb Env::IOActivity, Env::IOPriority
return FlushWAL(WriteOptions(), sync);
}
Status FlushWAL(const FlushWALOptions& options) override;
virtual Status FlushWAL(const WriteOptions& write_options, bool sync);
bool WALBufferIsEmpty();
Status SyncWAL() override;
@@ -573,11 +570,6 @@ class DBImpl : public DB {
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
ColumnFamilyMetaData* metadata) override;
// Get column family metadata with filtering based on key range and level
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
const GetColumnFamilyMetaDataOptions& options,
ColumnFamilyMetaData* metadata) override;
void GetAllColumnFamilyMetaData(
std::vector<ColumnFamilyMetaData>* metadata) override;
@@ -2401,14 +2393,6 @@ class DBImpl : public DB {
JobContext* job_context, LogBuffer* log_buffer,
CompactionJobInfo* compaction_job_info);
// Helper function to perform trivial move by updating manifest metadata
// without rewriting data files. This is called when IsTrivialMove() is true.
// REQUIRES: mutex held
// Returns: Status of the trivial move operation
Status PerformTrivialMove(Compaction& c, LogBuffer* log_buffer,
bool& compaction_released, size_t& moved_files,
size_t& moved_bytes);
// REQUIRES: mutex unlocked
void TrackOrUntrackFiles(const std::vector<std::string>& existing_data_files,
bool track);
+58 -132
View File
@@ -1424,56 +1424,6 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
return s;
}
Status DBImpl::PerformTrivialMove(Compaction& c, LogBuffer* log_buffer,
bool& compaction_released,
size_t& moved_files, size_t& moved_bytes) {
mutex_.AssertHeld();
ROCKS_LOG_BUFFER(log_buffer, "[%s] Moving %d files to level-%d\n",
c.column_family_data()->GetName().c_str(),
static_cast<int>(c.num_input_files(0)), c.output_level());
// Move files to the output level by editing the manifest
for (unsigned int l = 0; l < c.num_input_levels(); l++) {
if (c.level(l) == c.output_level()) {
continue;
}
for (size_t i = 0; i < c.num_input_files(l); i++) {
FileMetaData* f = c.input(l, i);
c.edit()->DeleteFile(c.level(l), f->fd.GetNumber());
c.edit()->AddFile(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_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);
moved_bytes += static_cast<size_t>(c.input(l, i)->fd.GetFileSize());
ROCKS_LOG_BUFFER(
log_buffer, "[%s] Moved #%" PRIu64 " to level-%d %" PRIu64 " bytes\n",
c.column_family_data()->GetName().c_str(), f->fd.GetNumber(),
c.output_level(), f->fd.GetFileSize());
}
moved_files += c.num_input_files(l);
}
// Install the new version
const ReadOptions read_options(Env::IOActivity::kCompaction);
const WriteOptions write_options(Env::IOActivity::kCompaction);
Status status = versions_->LogAndApply(
c.column_family_data(), read_options, write_options, c.edit(), &mutex_,
directories_.GetDbDir(), /*new_descriptor_log=*/false,
/*column_family_options=*/nullptr,
[&c, &compaction_released](const Status& s) {
c.ReleaseCompactionFiles(s);
compaction_released = true;
});
return status;
}
Status DBImpl::CompactFilesImpl(
const CompactionOptions& compact_options, ColumnFamilyData* cfd,
Version* version, const std::vector<std::string>& input_file_names,
@@ -1561,63 +1511,6 @@ Status DBImpl::CompactFilesImpl(
// deletion compaction currently not allowed in CompactFiles.
assert(!c->deletion_compaction());
// Check if this can be a trivial move (metadata-only update)
// Similar to the logic in DBImpl::BackgroundCompaction
// Note: We disable trivial move when compaction_service is present because
// the service expects all compactions to go through CompactionJob for
// tracking
bool is_trivial_move = compact_options.allow_trivial_move &&
c->IsTrivialMove() &&
immutable_db_options().compaction_service == nullptr;
if (is_trivial_move) {
// Perform trivial move: just update manifest without rewriting data
TEST_SYNC_POINT("DBImpl::CompactFilesImpl:TrivialMove");
bool compaction_released = false;
size_t moved_files = 0;
size_t moved_bytes = 0;
Status status = PerformTrivialMove(
*c.get(), log_buffer, compaction_released, moved_files, moved_bytes);
if (status.ok()) {
InstallSuperVersionAndScheduleWork(
c->column_family_data(), job_context->superversion_contexts.data());
// Populate output file names for trivial move
if (output_file_names != nullptr) {
for (const auto& newf : c->edit()->GetNewFiles()) {
output_file_names->push_back(TableFileName(
c->immutable_options().cf_paths, newf.second.fd.GetNumber(),
newf.second.fd.GetPathId()));
}
}
ROCKS_LOG_BUFFER(
log_buffer,
"[%s] Trivial move succeeded for %zu files, %zu bytes total\n",
c->column_family_data()->GetName().c_str(), moved_files, moved_bytes);
} else {
if (!compaction_released) {
c->ReleaseCompactionFiles(status);
}
ROCKS_LOG_BUFFER(log_buffer, "[%s] Trivial move failed: %s\n",
c->column_family_data()->GetName().c_str(),
status.ToString().c_str());
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
}
c.reset();
bg_compaction_scheduled_--;
if (bg_compaction_scheduled_ == 0) {
bg_cv_.SignalAll();
}
MaybeScheduleFlushOrCompaction();
return status;
}
// Not a trivial move, proceed with full compaction
InitSnapshotContext(job_context);
std::unique_ptr<std::list<uint64_t>::iterator> pending_outputs_inserted_elem(
@@ -1950,7 +1843,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
,
LLONG_MAX /* max compaction bytes, not applicable */,
0 /* output path ID, not applicable */, mutable_cf_options.compression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompactions, not applicable */,
{} /* grandparents, not applicable */,
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
@@ -3967,7 +3861,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
uint64_t out_file_creation_time = static_cast<uint64_t>(tmp_current_time);
FileOptions copied_file_options = file_options_;
copied_file_options.temperature = c->GetOutputTemperature();
copied_file_options.temperature = c->output_temperature();
std::unique_ptr<WritableFileWriter> dest_writer;
{
std::unique_ptr<FSWritableFile> dest_file;
@@ -3985,7 +3879,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
"NewWritableFile %s\n"
" out_fname=%s, temperature=%s, io_status=%s",
c->column_family_data()->GetName().c_str(), out_fname.c_str(),
temperature_to_string[c->GetOutputTemperature()].c_str(),
temperature_to_string[c->output_temperature()].c_str(),
io_s.ToString().c_str());
break;
}
@@ -4007,7 +3901,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
temperature_to_string[in_file->temperature].c_str(),
out_fname.c_str(),
temperature_to_string[c->GetOutputTemperature()].c_str(),
temperature_to_string[c->output_temperature()].c_str(),
c->mutable_cf_options()
.compaction_options_fifo.trivial_copy_buffer_size);
// Add IO_LOW HINT for compaction
@@ -4047,7 +3941,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
temperature_to_string[in_file->temperature].c_str(),
out_fname.c_str(),
temperature_to_string[c->GetOutputTemperature()].c_str(),
temperature_to_string[c->output_temperature()].c_str(),
io_s.ToString().c_str());
break;
}
@@ -4056,15 +3950,15 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
io_s = copy_file_io_status;
if (!io_s.ok()) {
ROCKS_LOG_BUFFER(
log_buffer,
"[%s] Failed to copy from: %s\n"
" temperature=%s, to=%s, temperature=%s, io_status=%s",
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
temperature_to_string[in_file->temperature].c_str(),
out_fname.c_str(),
temperature_to_string[c->GetOutputTemperature()].c_str(),
io_s.ToString().c_str());
ROCKS_LOG_BUFFER(log_buffer,
"[%s] Failed to copy from: %s\n"
" temperature=%s, to=%s, temperature=%s, io_status=%s",
c->column_family_data()->GetName().c_str(),
in_fname.c_str(),
temperature_to_string[in_file->temperature].c_str(),
out_fname.c_str(),
temperature_to_string[c->output_temperature()].c_str(),
io_s.ToString().c_str());
break;
}
ROCKS_LOG_BUFFER(log_buffer,
@@ -4074,7 +3968,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
in_fname.c_str(),
temperature_to_string[in_file->temperature].c_str(),
out_fname.c_str(),
temperature_to_string[c->GetOutputTemperature()].c_str(),
temperature_to_string[c->output_temperature()].c_str(),
io_s.ToString().c_str());
FileMetaData out_file_metadata{
@@ -4086,7 +3980,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
in_file->fd.smallest_seqno,
in_file->fd.largest_seqno,
false /* marked_for_compact */,
c->GetOutputTemperature() /* temperature */,
c->output_temperature() /* temperature */,
in_file->oldest_blob_file_number,
in_file->oldest_ancester_time,
out_file_creation_time,
@@ -4155,7 +4049,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
" temperature=%s, to temperature=%s, status=%s, io_status=%s",
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
temperature_to_string[in_file->temperature].c_str(),
temperature_to_string[c->GetOutputTemperature()].c_str(),
temperature_to_string[c->output_temperature()].c_str(),
status.ToString().c_str(), io_s.ToString().c_str());
}
}
@@ -4181,6 +4075,35 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
compaction_job_stats, job_context->job_id);
// Move files to next level
int32_t moved_files = 0;
int64_t moved_bytes = 0;
for (unsigned int l = 0; l < c->num_input_levels(); l++) {
if (c->level(l) == c->output_level()) {
continue;
}
for (size_t i = 0; i < c->num_input_files(l); i++) {
FileMetaData* f = c->input(l, i);
c->edit()->DeleteFile(c->level(l), f->fd.GetNumber());
c->edit()->AddFile(
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_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);
ROCKS_LOG_BUFFER(
log_buffer,
"[%s] Moving #%" PRIu64 " to level-%d %" PRIu64 " bytes\n",
c->column_family_data()->GetName().c_str(), f->fd.GetNumber(),
c->output_level(), f->fd.GetFileSize());
++moved_files;
moved_bytes += f->fd.GetFileSize();
}
}
if (c->compaction_reason() == CompactionReason::kLevelMaxLevelSize &&
c->immutable_options().compaction_pri == kRoundRobin) {
int start_level = c->start_level();
@@ -4191,12 +4114,14 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
vstorage->GetNextCompactCursor(start_level, c->num_input_files(0)));
}
}
// Perform the trivial move
size_t moved_files = 0;
size_t moved_bytes = 0;
status = PerformTrivialMove(*c.get(), log_buffer, compaction_released,
moved_files, moved_bytes);
status = versions_->LogAndApply(
c->column_family_data(), read_options, write_options, c->edit(),
&mutex_, directories_.GetDbDir(),
/*new_descriptor_log=*/false, /*column_family_options=*/nullptr,
[&c, &compaction_released](const Status& s) {
c->ReleaseCompactionFiles(s);
compaction_released = true;
});
io_s = versions_->io_status();
InstallSuperVersionAndScheduleWork(
c->column_family_data(), job_context->superversion_contexts.data());
@@ -4211,7 +4136,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
<< "total_files_size" << moved_bytes;
}
ROCKS_LOG_BUFFER(
log_buffer, "[%s] Moved #%d files to level-%zu %zu bytes %s: %s\n",
log_buffer,
"[%s] Moved #%d files to level-%d %" PRIu64 " bytes %s: %s\n",
c->column_family_data()->GetName().c_str(), moved_files,
c->output_level(), moved_bytes, status.ToString().c_str(),
c->column_family_data()->current()->storage_info()->LevelSummary(&tmp));
@@ -4502,7 +4428,7 @@ Compaction* DBImpl::CreateIntendedCompactionForwardedToBottomPriorityPool(
c->output_level(), c->target_output_file_size(),
c->max_compaction_bytes(), c->output_path_id(),
c->output_compression(), c->output_compression_opts(),
c->GetOutputTemperature(), c->max_subcompactions(),
c->output_temperature(), c->max_subcompactions(),
c->grandparents(), std::nullopt /* earliest_snapshot */,
nullptr /* snapshot_checker */, c->compaction_reason());
-5
View File
@@ -614,11 +614,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
case kOptionsFile:
keep = (number >= optsfile_num2);
break;
case kCompactionProgressFile:
// Keep compaction progress files - they are managed
// separately by DBImplSecondary for now
keep = true;
break;
case kCurrentFile:
case kDBLockFile:
case kIdentityFile:
+3 -3
View File
@@ -293,9 +293,9 @@ Status DB::OpenAsFollower(
DBImplFollower* impl =
new DBImplFollower(tmp_opts, std::move(new_env), dbname, src_path);
impl->versions_.reset(new ReactiveVersionSet(
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_));
dbname, &impl->immutable_db_options_, impl->file_options_,
impl->table_cache_.get(), impl->write_buffer_manager_,
&impl->write_controller_, impl->io_tracer_));
impl->column_family_memtables_.reset(
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
+1 -1
View File
@@ -329,7 +329,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
}
FileTypeSet tmp_set = immutable_db_options_.checksum_handoff_file_types;
file->SetPreallocationBlockSize(
mutable_db_options_.manifest_preallocation_size);
immutable_db_options_.manifest_preallocation_size);
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
std::move(file), manifest, file_options, immutable_db_options_.clock,
io_tracer_, nullptr /* stats */,
+17 -668
View File
@@ -8,12 +8,7 @@
#include <cinttypes>
#include "db/arena_wrapped_db_iter.h"
#include "db/log_reader.h"
#include "db/log_writer.h"
#include "db/merge_context.h"
#include "db/version_edit.h"
#include "file/filename.h"
#include "file/writable_file_writer.h"
#include "logging/auto_roll_logger.h"
#include "logging/logging.h"
#include "monitoring/perf_context_imp.h"
@@ -783,9 +778,9 @@ Status DB::OpenAsSecondary(
handles->clear();
DBImplSecondary* impl = new DBImplSecondary(tmp_opts, dbname, secondary_path);
impl->versions_.reset(new ReactiveVersionSet(
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_));
dbname, &impl->immutable_db_options_, impl->file_options_,
impl->table_cache_.get(), impl->write_buffer_manager_,
&impl->write_controller_, impl->io_tracer_));
impl->column_family_memtables_.reset(
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
@@ -828,507 +823,18 @@ Status DB::OpenAsSecondary(
return s;
}
Status DBImplSecondary::ScanCompactionProgressFiles(
CompactionProgressFilesScan* scan_result) {
assert(scan_result != nullptr);
scan_result->Clear();
WriteOptions write_options(Env::IOActivity::kCompaction);
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (!s.ok()) {
return s;
}
std::vector<std::string> all_filenames;
s = fs_->GetChildren(secondary_path_, opts, &all_filenames, nullptr /* dbg*/);
if (!s.ok()) {
return s;
}
for (const auto& filename : all_filenames) {
if (filename == "." || filename == "..") {
continue;
}
uint64_t number;
FileType type;
if (!ParseFileName(filename, &number, &type)) {
continue;
}
// Categorize compaction progress files
if (type == kCompactionProgressFile) {
if (number > scan_result->latest_progress_timestamp) {
// Found a newer progress file
if (scan_result->HasLatestProgressFile()) {
// Previous "latest" becomes "old"
scan_result->old_progress_filenames.push_back(
scan_result->latest_progress_filename.value());
}
scan_result->latest_progress_timestamp = number;
scan_result->latest_progress_filename = filename;
} else {
// This is an older progress file
scan_result->old_progress_filenames.push_back(filename);
}
} else if (type == kTempFile &&
filename.find(kCompactionProgressFileNamePrefix) == 0) {
// Temporary progress files
scan_result->temp_progress_filenames.push_back(filename);
} else if (type == kTableFile) {
// Collect table file numbers for CleanupPhysicalCompactionOutputFiles
scan_result->table_file_numbers.push_back(number);
}
}
return Status::OK();
}
Status DBImplSecondary::DeleteCompactionProgressFiles(
const std::vector<std::string>& filenames) {
WriteOptions write_options(Env::IOActivity::kCompaction);
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (!s.ok()) {
return s;
}
for (const auto& filename : filenames) {
std::string file_path = secondary_path_ + "/" + filename;
Status delete_status = fs_->DeleteFile(file_path, opts, nullptr /* dbg */);
if (!delete_status.ok()) {
return delete_status;
}
}
return Status::OK();
}
Status DBImplSecondary::CleanupOldAndTemporaryCompactionProgressFiles(
bool preserve_latest, const CompactionProgressFilesScan& scan_result) {
std::vector<std::string> filenames_to_delete;
// Always delete old progress files
filenames_to_delete.insert(filenames_to_delete.end(),
scan_result.old_progress_filenames.begin(),
scan_result.old_progress_filenames.end());
// Always delete temp files
filenames_to_delete.insert(filenames_to_delete.end(),
scan_result.temp_progress_filenames.begin(),
scan_result.temp_progress_filenames.end());
// Conditionally delete latest file
if (!preserve_latest && scan_result.HasLatestProgressFile()) {
filenames_to_delete.push_back(scan_result.latest_progress_filename.value());
}
return DeleteCompactionProgressFiles(filenames_to_delete);
}
// Loads compaction progress from a file and cleans up extra output
// files. After loading the progress, this function identifies and deletes any
// SST files in the output folder that are NOT tracked in the
// progress. This ensures consistency between the progress file and
// actual output files on disk.
Status DBImplSecondary::LoadCompactionProgressAndCleanupExtraOutputFiles(
const std::string& compaction_progress_file_path,
const CompactionProgressFilesScan& scan_result) {
Status s = ParseCompactionProgressFile(compaction_progress_file_path,
&compaction_progress_);
if (s.ok()) {
s = CleanupPhysicalCompactionOutputFiles(true /* preserve_tracked_files */,
scan_result);
}
return s;
}
Status DBImplSecondary::ParseCompactionProgressFile(
const std::string& compaction_progress_file_path,
CompactionProgress* compaction_progress) {
std::unique_ptr<FSSequentialFile> file;
Status s = fs_->NewSequentialFile(compaction_progress_file_path,
FileOptions(), &file, nullptr /* dbg */);
if (!s.ok()) {
return s;
}
std::unique_ptr<SequentialFileReader> file_reader(new SequentialFileReader(
std::move(file), compaction_progress_file_path,
immutable_db_options_.log_readahead_size, io_tracer_, {} /* listeners */,
immutable_db_options_.rate_limiter.get()));
Status reader_status;
struct CompactionProgressReaderReporter : public log::Reader::Reporter {
Status* status;
explicit CompactionProgressReaderReporter(Status* s) : status(s) {}
void Corruption(size_t /*bytes*/, const Status& s,
uint64_t /*log_number*/) override {
if (status->ok()) {
*status = s;
}
}
void OldLogRecord(size_t /*bytes*/) override {
// Ignore old records
}
} progress_reporter(&reader_status);
log::Reader compaction_progress_reader(
immutable_db_options_.info_log, std::move(file_reader),
&progress_reporter, true /* checksum */, 0 /* log_num */);
// LIMITATION: Only supports resuming single subcompaction
SubcompactionProgressBuilder progress_builder;
Slice slice;
std::string record;
while (compaction_progress_reader.ReadRecord(&slice, &record)) {
if (!reader_status.ok()) {
return reader_status;
}
VersionEdit edit;
s = edit.DecodeFrom(slice);
if (!s.ok()) {
break;
}
bool res = progress_builder.ProcessVersionEdit(edit);
if (!res) {
break;
}
}
if (!s.ok()) {
return s;
}
if (progress_builder.HasAccumulatedSubcompactionProgress()) {
compaction_progress->clear();
compaction_progress->push_back(
progress_builder.GetAccumulatedSubcompactionProgress());
} else {
s = Status::NotFound("No compaction progress was persisted yet");
}
return s;
}
Status DBImplSecondary::RenameCompactionProgressFile(
const std::string& temp_file_path, std::string* final_file_path) {
uint64_t current_time = env_->NowMicros();
*final_file_path = CompactionProgressFileName(secondary_path_, current_time);
WriteOptions write_options(Env::IOActivity::kCompaction);
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (!s.ok()) {
return s;
}
s = fs_->RenameFile(temp_file_path, *final_file_path, opts,
nullptr /* dbg */);
return s;
}
Status DBImplSecondary::CleanupPhysicalCompactionOutputFiles(
bool preserve_tracked_files,
const CompactionProgressFilesScan& scan_result) {
std::unordered_set<uint64_t> files_to_preserve;
if (preserve_tracked_files) {
for (const auto& subcompaction_progress : compaction_progress_) {
for (const auto& file_metadata :
subcompaction_progress.output_level_progress.GetOutputFiles()) {
files_to_preserve.insert(file_metadata.fd.GetNumber());
}
for (const auto& file_metadata :
subcompaction_progress.proximal_output_level_progress
.GetOutputFiles()) {
files_to_preserve.insert(file_metadata.fd.GetNumber());
}
}
}
WriteOptions write_options(Env::IOActivity::kCompaction);
IOOptions opts;
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (!s.ok()) {
return s;
}
for (uint64_t file_number : scan_result.table_file_numbers) {
bool should_delete =
!preserve_tracked_files ||
(files_to_preserve.find(file_number) == files_to_preserve.end());
if (should_delete) {
std::string file_path = MakeTableFileName(secondary_path_, file_number);
Status delete_status =
fs_->DeleteFile(file_path, opts, nullptr /* dbg */);
if (!delete_status.ok()) {
return delete_status;
}
}
}
return Status::OK();
}
Status DBImplSecondary::InitializeCompactionWorkspace(
bool allow_resumption, std::unique_ptr<FSDirectory>* output_dir,
std::unique_ptr<log::Writer>* compaction_progress_writer) {
// Create output directory if it doest exist yet
Status s = CreateAndNewDirectory(fs_.get(), secondary_path_, output_dir);
if (!s.ok() || !allow_resumption) {
return s;
}
s = PrepareCompactionProgressState();
if (!s.ok()) {
return s;
}
s = FinalizeCompactionProgressWriter(compaction_progress_writer);
if (!s.ok()) {
return s;
}
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"Initialized compaction workspace with %zu subcompaction "
"progress to resume",
compaction_progress_.size());
return Status::OK();
}
// PrepareCompactionProgressState() manages compaction progress files and output
// files to ensure a clean, consistent state for resuming or starting fresh
// compaction.
//
// PRECONDITION:
// - This function is ONLY called when allow_resumption = true
// - The caller wants resumption support for this compaction attempt
//
// FILE SYSTEM STATE (before entering this function):
// - 0 or more compaction progress files may exist in `secondary_path_`:
// * Latest progress file (from the most recent compaction attempt)
// * Older progress files (left by crashing during a previous
// InitializeCompactionWorkspace() call)
// * Temporary progress files (left by crashing during a previous
// InitializeCompactionWorkspace() call)
// - 0 or more compaction output files may exist in `secondary_path_`
//
// POSTCONDITIONS (after this function):
// - IF the latest progress file exists AND it parses successfully AND
// actually contains valid compaction progress:
// * Exactly one latest progress file remains
// * All older and temporary compaction progress files are deleted
// * All corresponding compaction output files are preserved
// * All extra compaction output files are deleted (files left by
// compaction
// crashing before persisting the progress)
// * Result: Ready to resume compaction from the saved progress
// - OTHERWISE (no latest progress file OR it fails to parse OR it's
// invalid):
// * ALL compaction progress files are deleted (latest + older +
// temporary)
// * ALL compaction output files are deleted
// * Result: Ready to start fresh compaction (despite allow_resumption =
// true, we cannot resume because there's no valid progress to resume from)
//
// ERROR HANDLING:
// - ON ERROR (if any of the postconditions cannot be achieved):
// * Function returns error status
// * File system may be left in a partially modified state
// * Caller should manually clean up secondary_path_ before retrying
// * Subsequent OpenAndCompact() calls to this clean secondary_path_ will
// effectively start fresh compaction
Status DBImplSecondary::PrepareCompactionProgressState() {
Status s;
// STEP 1: Scan directory ONCE (includes progress files + table files)
CompactionProgressFilesScan scan_result;
s = ScanCompactionProgressFiles(&scan_result);
if (!s.ok()) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"Encountered error when scanning for compaction "
"progress files: %s",
s.ToString().c_str());
return s;
}
std::optional<std::string> latest_progress_file =
scan_result.latest_progress_filename;
// STEP 2: Determine if we should resume
bool should_resume = false;
if (latest_progress_file.has_value()) {
should_resume = true;
} else {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Did not find any latest compaction progress file. "
"Will perform clean up to start fresh compaction");
}
// STEP 3: Cleanup using pre-scanned results
if (should_resume) {
// Keep latest, delete old/temp
s = CleanupOldAndTemporaryCompactionProgressFiles(
true /* preserve_latest */, scan_result);
} else {
// Delete everything including latest
s = CleanupOldAndTemporaryCompactionProgressFiles(
false /* preserve_latest */, scan_result);
latest_progress_file.reset();
}
if (!s.ok()) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"Failed to clean up compaction progress file(s): %s. "
"Will fail the compaction",
s.ToString().c_str());
return s;
}
// STEP 4: Load progress if resuming
if (latest_progress_file.has_value()) {
uint64_t timestamp = scan_result.latest_progress_timestamp;
std::string compaction_progress_file_path =
CompactionProgressFileName(secondary_path_, timestamp);
s = LoadCompactionProgressAndCleanupExtraOutputFiles(
compaction_progress_file_path, scan_result);
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Failed to load the latest compaction "
"progress from %s: %s. Will perform clean up "
"to start fresh compaction",
latest_progress_file.value().c_str(),
s.ToString().c_str());
return HandleInvalidOrNoCompactionProgress(compaction_progress_file_path,
scan_result);
}
return s;
} else {
return HandleInvalidOrNoCompactionProgress(
std::nullopt /* compaction_progress_file_path */, scan_result);
}
}
uint64_t DBImplSecondary::CalculateResumedCompactionBytes(
const CompactionProgress& compaction_progress) const {
uint64_t total_resumed_bytes = 0;
for (const auto& subcompaction_progress : compaction_progress) {
for (const auto& file_meta :
subcompaction_progress.output_level_progress.GetOutputFiles()) {
total_resumed_bytes += file_meta.fd.file_size;
}
for (const auto& file_meta :
subcompaction_progress.proximal_output_level_progress
.GetOutputFiles()) {
total_resumed_bytes += file_meta.fd.file_size;
}
}
return total_resumed_bytes;
}
Status DBImplSecondary::HandleInvalidOrNoCompactionProgress(
const std::optional<std::string>& compaction_progress_file_path,
const CompactionProgressFilesScan& scan_result) {
compaction_progress_.clear();
Status s;
if (compaction_progress_file_path.has_value()) {
WriteOptions write_options(Env::IOActivity::kCompaction);
IOOptions opts;
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = fs_->DeleteFile(compaction_progress_file_path.value(), opts,
nullptr /* dbg */);
}
if (!s.ok()) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"Failed to remove invalid progress file: %s",
s.ToString().c_str());
return s;
}
}
s = CleanupPhysicalCompactionOutputFiles(false /* preserve_tracked_files */,
scan_result);
if (!s.ok()) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"Failed to cleanup existing compaction output files: %s",
s.ToString().c_str());
return s;
}
return Status::OK();
}
Status DBImplSecondary::CompactWithoutInstallation(
const OpenAndCompactOptions& options, ColumnFamilyHandle* cfh,
const CompactionServiceInput& input, CompactionServiceResult* result) {
if (options.canceled && options.canceled->load(std::memory_order_acquire)) {
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
}
std::unique_ptr<FSDirectory> output_dir;
std::unique_ptr<log::Writer> compaction_progress_writer;
InstrumentedMutexLock l(&mutex_);
auto cfd = static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
if (!cfd) {
return Status::InvalidArgument("Cannot find column family" +
cfh->GetName());
}
Status s;
// TODO(hx235): Resuming compaction is currently incompatible with
// paranoid_file_checks=true because OutputValidator hash verification would
// fail during compaction resumption. Before interruption, resuming
// compaction needs to persist the hash of each output file to enable
// validation after resumption. Alternatively and preferably, we could move
// the output verification to happen immediately after each output file is
// created. This workaround currently disables resuming compaction when
// paranoid_file_checks is enabled. Note that paranoid_file_checks is
// disabled by default.
bool allow_resumption =
options.allow_resumption &&
!cfd->GetLatestMutableCFOptions().paranoid_file_checks;
if (options.allow_resumption &&
cfd->GetLatestMutableCFOptions().paranoid_file_checks) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Resume compaction configured but disabled due to "
"incompatible with paranoid_file_checks=true");
}
mutex_.Unlock();
s = InitializeCompactionWorkspace(allow_resumption, &output_dir,
&compaction_progress_writer);
mutex_.Lock();
if (!s.ok()) {
return s;
}
std::unordered_set<uint64_t> input_set;
for (const auto& file_name : input.input_files) {
@@ -1350,7 +856,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
cfd->ioptions().level_compaction_dynamic_level_bytes);
std::vector<CompactionInputFiles> input_files;
s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
Status s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
&input_files, &input_set, vstorage, comp_options);
if (!s.ok()) {
ROCKS_LOG_ERROR(
@@ -1373,22 +879,23 @@ Status DBImplSecondary::CompactWithoutInstallation(
// input instead of recreating it in the remote worker
std::unique_ptr<Compaction> c;
assert(cfd->compaction_picker());
std::optional<SequenceNumber> earliest_snapshot = std::nullopt;
// Standalone Range Deletion Optimization is only supported in Universal
// Compactions - https://github.com/facebook/rocksdb/pull/13078
if (cfd->GetLatestCFOptions().compaction_style ==
CompactionStyle::kCompactionStyleUniversal) {
earliest_snapshot = !job_context.snapshot_seqs.empty()
? job_context.snapshot_seqs.front()
: kMaxSequenceNumber;
}
c.reset(cfd->compaction_picker()->PickCompactionForCompactFiles(
comp_options, input_files, input.output_level, vstorage,
cfd->GetLatestMutableCFOptions(), mutable_db_options_, 0,
earliest_snapshot, job_context.snapshot_checker));
/*earliest_snapshot=*/job_context.snapshot_seqs.empty()
? kMaxSequenceNumber
: job_context.snapshot_seqs.front(),
job_context.snapshot_checker));
assert(c != nullptr);
c->FinalizeInputInfo(version);
// Create output directory if it's not existed yet
std::unique_ptr<FSDirectory> output_dir;
s = CreateAndNewDirectory(fs_.get(), secondary_path_, &output_dir);
if (!s.ok()) {
return s;
}
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
immutable_db_options_.info_log.get());
@@ -1406,15 +913,13 @@ Status DBImplSecondary::CompactWithoutInstallation(
options.canceled ? *options.canceled : kManualCompactionCanceledFalse_,
input.db_id, db_session_id_, secondary_path_, input, result);
compaction_job.Prepare(compaction_progress_,
compaction_progress_writer.get());
compaction_job.Prepare();
mutex_.Unlock();
s = compaction_job.Run();
mutex_.Lock();
// These cleanup functions handle metadata and state cleanup only and
// not the physical files
// clean up
compaction_job.io_status().PermitUncheckedError();
compaction_job.CleanupCompaction();
c->ReleaseCompactionFiles(s);
@@ -1422,18 +927,6 @@ Status DBImplSecondary::CompactWithoutInstallation(
TEST_SYNC_POINT_CALLBACK("DBImplSecondary::CompactWithoutInstallation::End",
&s);
if (!compaction_progress_.empty() && s.ok()) {
uint64_t total_resumed_bytes =
CalculateResumedCompactionBytes(compaction_progress_);
if (total_resumed_bytes > 0 &&
immutable_db_options_.statistics != nullptr) {
RecordTick(immutable_db_options_.statistics.get(),
REMOTE_COMPACT_RESUMED_BYTES, total_resumed_bytes);
}
}
result->status = s;
return s;
}
@@ -1589,148 +1082,4 @@ Status DB::OpenAndCompact(
output, override_options);
}
Status DBImplSecondary::CreateCompactionProgressWriter(
const std::string& file_path,
std::unique_ptr<log::Writer>* compaction_progress_writer) {
std::unique_ptr<FSWritableFile> file;
Status s =
fs_->NewWritableFile(file_path, FileOptions(), &file, nullptr /* dbg */);
if (!s.ok()) {
return s;
}
std::unique_ptr<WritableFileWriter> file_writer(
new WritableFileWriter(std::move(file), file_path, FileOptions()));
compaction_progress_writer->reset(
new log::Writer(std::move(file_writer), 0 /* log_number */,
false /* recycle_log_files */));
return Status::OK();
}
Status DBImplSecondary::PersistInitialCompactionProgress(
log::Writer* compaction_progress_writer,
const CompactionProgress& compaction_progress) {
assert(compaction_progress_writer);
// LIMITATION: Only supports resuming single subcompaction
assert(compaction_progress.size() == 1);
const SubcompactionProgress& subcompaction_progress = compaction_progress[0];
VersionEdit edit;
edit.SetSubcompactionProgress(subcompaction_progress);
std::string record;
if (!edit.EncodeTo(&record)) {
return Status::IOError("Failed to encode the initial compaction progress");
}
WriteOptions write_options(Env::IOActivity::kCompaction);
Status s = compaction_progress_writer->AddRecord(write_options, record);
if (!s.ok()) {
return s;
}
IOOptions opts;
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (!s.ok()) {
return s;
}
s = compaction_progress_writer->file()->Sync(opts,
immutable_db_options_.use_fsync);
return s;
}
Status DBImplSecondary::HandleCompactionProgressWriterCreationFailure(
const std::string& temp_file_path, const std::string& final_file_path,
std::unique_ptr<log::Writer>* compaction_progress_writer) {
compaction_progress_writer->reset();
const std::vector<std::string> paths_to_delete = {final_file_path,
temp_file_path};
Status s;
for (const auto& file_path : paths_to_delete) {
WriteOptions write_options(Env::IOActivity::kCompaction);
IOOptions opts;
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (s.ok()) {
s = fs_->DeleteFile(file_path, opts, nullptr /* dbg */);
}
if (!s.ok()) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"Failed to cleanup the compaction progress file "
"during writer creation failure: %s",
s.ToString().c_str());
return s;
}
}
return s;
}
Status DBImplSecondary::FinalizeCompactionProgressWriter(
std::unique_ptr<log::Writer>* compaction_progress_writer) {
uint64_t timestamp = env_->NowMicros();
const std::string temp_file_path =
TempCompactionProgressFileName(secondary_path_, timestamp);
Status s = CreateCompactionProgressWriter(temp_file_path,
compaction_progress_writer);
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Failed to create compaction progress writer at "
"temp path %s: %s. Will perform clean up "
"to start compaction without progress persistence",
temp_file_path.c_str(), s.ToString().c_str());
return HandleCompactionProgressWriterCreationFailure(
temp_file_path, "" /* final_file_path */, compaction_progress_writer);
}
if (!compaction_progress_.empty()) {
s = PersistInitialCompactionProgress(compaction_progress_writer->get(),
compaction_progress_);
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Failed to persist the initial copmaction "
"progress: %s. Will perform clean up "
"to start compaction without progress persistence",
s.ToString().c_str());
return HandleCompactionProgressWriterCreationFailure(
temp_file_path, "" /* final_file_path */, compaction_progress_writer);
}
}
compaction_progress_writer->reset();
std::string final_file_path;
s = RenameCompactionProgressFile(temp_file_path, &final_file_path);
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Failed to rename temporary compaction progress "
"file from %s to %s: %s. Will perform clean up "
"to start compaction without progress persistence",
temp_file_path.c_str(), final_file_path.c_str(),
s.ToString().c_str());
return HandleCompactionProgressWriterCreationFailure(
temp_file_path, final_file_path, compaction_progress_writer);
}
s = CreateCompactionProgressWriter(final_file_path,
compaction_progress_writer);
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Failed to create the final compaction progress "
"writer: %s. Will attempt clean to start the compaction "
"without progress persistence",
s.ToString().c_str());
return HandleCompactionProgressWriterCreationFailure(
"" /* temp_file_path */, final_file_path, compaction_progress_writer);
}
return Status::OK();
}
} // namespace ROCKSDB_NAMESPACE
-83
View File
@@ -303,87 +303,6 @@ class DBImplSecondary : public DBImpl {
const CompactionServiceInput& input,
CompactionServiceResult* result);
private:
// Holds results of compaction progress files and output files from a single
// directory scan
struct CompactionProgressFilesScan {
// The latest (newest) progress file filename
std::optional<std::string> latest_progress_filename;
uint64_t latest_progress_timestamp = 0;
// Older progress file filenames (to be deleted)
autovector<std::string> old_progress_filenames;
// Temporary progress file filenames (to be deleted)
autovector<std::string> temp_progress_filenames;
// All output file numbers - for cleanup optimization
std::vector<uint64_t> table_file_numbers;
bool HasLatestProgressFile() const {
return latest_progress_filename.has_value();
}
void Clear() {
latest_progress_filename.reset();
latest_progress_timestamp = 0;
old_progress_filenames.clear();
temp_progress_filenames.clear();
table_file_numbers.clear();
}
};
Status InitializeCompactionWorkspace(
bool allow_resumption, std::unique_ptr<FSDirectory>* output_dir,
std::unique_ptr<log::Writer>* compaction_progress_writer);
Status PrepareCompactionProgressState();
Status ScanCompactionProgressFiles(CompactionProgressFilesScan* scan_result);
Status DeleteCompactionProgressFiles(
const std::vector<std::string>& filenames);
Status CleanupOldAndTemporaryCompactionProgressFiles(
bool preserve_latest, const CompactionProgressFilesScan& scan_result);
Status LoadCompactionProgressAndCleanupExtraOutputFiles(
const std::string& compaction_progress_file_path,
const CompactionProgressFilesScan& scan_result);
Status ParseCompactionProgressFile(
const std::string& compaction_progress_file_path,
CompactionProgress* compaction_progress);
Status HandleInvalidOrNoCompactionProgress(
const std::optional<std::string>& compaction_progress_file_path,
const CompactionProgressFilesScan& scan_result);
Status CleanupPhysicalCompactionOutputFiles(
bool preserve_tracked_files,
const CompactionProgressFilesScan& scan_result);
Status FinalizeCompactionProgressWriter(
std::unique_ptr<log::Writer>* compaction_progress_writer);
Status CreateCompactionProgressWriter(
const std::string& file_path,
std::unique_ptr<log::Writer>* compaction_progress_writer);
Status PersistInitialCompactionProgress(
log::Writer* compaction_progress_writer,
const CompactionProgress& compaction_progress);
Status RenameCompactionProgressFile(const std::string& temp_file_path,
std::string* final_file_path);
Status HandleCompactionProgressWriterCreationFailure(
const std::string& temp_file_path, const std::string& final_file_path,
std::unique_ptr<log::Writer>* compaction_progress_writer);
uint64_t CalculateResumedCompactionBytes(
const CompactionProgress& compaction_progress) const;
// Cache log readers for each log number, used for continue WAL replay
// after recovery
std::map<uint64_t, std::unique_ptr<LogReaderContainer>> log_readers_;
@@ -392,8 +311,6 @@ class DBImplSecondary : public DBImpl {
std::unordered_map<ColumnFamilyData*, uint64_t> cfd_to_current_log_;
const std::string secondary_path_;
CompactionProgress compaction_progress_;
};
} // namespace ROCKSDB_NAMESPACE
-68
View File
@@ -4384,74 +4384,6 @@ TEST_P(DBMultiScanIteratorTest, FailureTest) {
iter.reset();
}
TEST_P(DBMultiScanIteratorTest, OutOfL0FileRange) {
// Test that prepare does not fail scan when a scan range
// is outside of a L0 file's key range.
auto options = CurrentOptions();
options.compression = kNoCompression;
DestroyAndReopen(options);
Random rnd(301);
// Create a Lmax file
// key01 ~ key99
for (int i = 0; i < 100; ++i) {
std::stringstream ss;
ss << std::setw(2) << std::setfill('0') << i;
ASSERT_OK(Put("k" + ss.str(), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
// Create a L0 file
// key00 ~ key09
for (int i = 0; i < 10; ++i) {
std::stringstream ss;
ss << std::setw(2) << std::setfill('0') << i;
ASSERT_OK(Put("k" + ss.str(), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
// The second range is outside of L0 file's key range
std::vector<std::string> key_ranges({"k04", "k06", "k12", "k14"});
ReadOptions ro;
Slice ub;
ro.iterate_upper_bound = &ub;
ro.fill_cache = GetParam();
MultiScanArgs scan_options(BytewiseComparator());
scan_options.insert(key_ranges[0], key_ranges[1]);
scan_options.insert(key_ranges[2], key_ranges[3]);
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ro, cfh));
ASSERT_NE(iter, nullptr);
iter->Prepare(scan_options);
int count = 0;
ub = key_ranges[1];
iter->Seek(key_ranges[0]);
while (iter->status().ok() && iter->Valid()) {
ASSERT_GE(iter->key().compare(key_ranges[0]), 0);
ASSERT_LT(iter->key().compare(key_ranges[1]), 0);
count++;
iter->Next();
}
ASSERT_OK(iter->status()) << iter->status().ToString();
ASSERT_EQ(count, 2);
ub = key_ranges[3];
count = 0;
iter->Seek(key_ranges[2]);
while (iter->status().ok() && iter->Valid()) {
ASSERT_GE(iter->key().compare(key_ranges[2]), 0);
ASSERT_LT(iter->key().compare(key_ranges[3]), 0);
count++;
iter->Next();
}
ASSERT_OK(iter->status()) << iter->status().ToString();
ASSERT_EQ(count, 2);
}
TEST_P(DBMultiScanIteratorTest, RangeBetweenFiles) {
auto options = CurrentOptions();
options.target_file_size_base = 100 << 10; // 20KB
-35
View File
@@ -432,47 +432,12 @@ TEST_F(DBOptionsTest, SetWalBytesPerSync) {
ASSERT_GT(low_bytes_per_sync, counter);
}
TEST_F(DBOptionsTest, MutableManifestOptions) {
// These aren't end-to-end tests, but sufficient to ensure the VersionSet
// receives the updates with SetDBOptions
for (int64_t i : {0, 1, 100, 100000, 10000000}) {
ASSERT_OK(
db_->SetDBOptions({{"max_manifest_file_size", std::to_string(i)}}));
ASSERT_EQ(i,
static_cast<int64_t>(db_->GetDBOptions().max_manifest_file_size));
ASSERT_EQ(i,
static_cast<int64_t>(
dbfull()->GetVersionSet()->TEST_GetMinMaxManifestFileSize()));
if (i > 1) {
++i;
}
ASSERT_OK(
db_->SetDBOptions({{"max_manifest_space_amp_pct", std::to_string(i)}}));
ASSERT_EQ(i, static_cast<int64_t>(
db_->GetDBOptions().max_manifest_space_amp_pct));
ASSERT_EQ(i,
static_cast<int64_t>(
dbfull()->GetVersionSet()->TEST_GetMaxManifestSpaceAmpPct()));
if (i > 1) {
++i;
}
ASSERT_OK(db_->SetDBOptions(
{{"manifest_preallocation_size", std::to_string(i)}}));
ASSERT_EQ(i, static_cast<int64_t>(
db_->GetDBOptions().manifest_preallocation_size));
ASSERT_EQ(
i, static_cast<int64_t>(
dbfull()->GetVersionSet()->TEST_GetManifestPreallocationSize()));
}
}
TEST_F(DBOptionsTest, WritableFileMaxBufferSize) {
Options options;
options.create_if_missing = true;
options.writable_file_max_buffer_size = 1024 * 1024;
options.level0_file_num_compaction_trigger = 3;
options.max_manifest_file_size = 1;
options.max_manifest_space_amp_pct = 0;
options.env = env_;
int buffer_size = 1024 * 1024;
Reopen(options);
-101
View File
@@ -442,107 +442,6 @@ TEST_P(DBRateLimiterOnWriteWALTest, AutoWalFlush) {
EXPECT_EQ(actual_auto_wal_flush_request,
options_.rate_limiter->GetTotalRequests(Env::IO_USER));
}
class DBRateLimiterOnManualWALFlushTest
: public DBRateLimiterOnWriteTest,
public ::testing::WithParamInterface<Env::IOPriority> {
public:
static std::string GetTestNameSuffix(
::testing::TestParamInfo<Env::IOPriority> info) {
std::ostringstream oss;
if (info.param == Env::IO_USER) {
oss << "RateLimitManualWALFlush";
} else if (info.param == Env::IO_TOTAL) {
oss << "NoRateLimitManualWALFlush";
} else if (info.param == Env::IO_HIGH) {
oss << "RateLimitManualWALFlushWithHighPriority";
} else {
oss << "RateLimitManualWALFlushWithLowPriority";
}
return oss.str();
}
explicit DBRateLimiterOnManualWALFlushTest()
: rate_limiter_priority_(GetParam()) {}
void Init() {
options_ = GetOptions();
// Enable manual WAL flush mode
options_.manual_wal_flush = true;
Reopen(options_);
}
WriteOptions GetWriteOptions() {
WriteOptions write_options;
// WAL must be enabled for manual WAL flush to work
write_options.disableWAL = false;
// In manual WAL flush mode, WAL write rate limiting should be done through
// FlushWAL(), not WriteOptions::rate_limiter_priority
write_options.rate_limiter_priority = Env::IO_TOTAL;
return write_options;
}
protected:
Env::IOPriority rate_limiter_priority_;
};
INSTANTIATE_TEST_CASE_P(DBRateLimiterOnManualWALFlushTest,
DBRateLimiterOnManualWALFlushTest,
::testing::Values(Env::IO_TOTAL, Env::IO_USER,
Env::IO_HIGH, Env::IO_LOW),
DBRateLimiterOnManualWALFlushTest::GetTestNameSuffix);
TEST_P(DBRateLimiterOnManualWALFlushTest, ManualWALFlush) {
Init();
const bool no_rate_limit = (rate_limiter_priority_ == Env::IO_TOTAL);
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL));
for (bool sync : {false, true}) {
std::int64_t prev_total_request =
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
Status put_status = Put("key_" + std::to_string(sync),
"value_" + std::to_string(sync), GetWriteOptions());
EXPECT_TRUE(put_status.ok());
// Since manual_wal_flush is enabled and write_options.rate_limiter_priority
// is IO_TOTAL, no rate limiting should have occurred for this user write
EXPECT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
prev_total_request);
// Now explicitly flush the WAL with the test's rate_limiter_priority
prev_total_request = options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
std::int64_t prev_priority_request =
options_.rate_limiter->GetTotalRequests(rate_limiter_priority_);
FlushWALOptions flush_options;
flush_options.sync = sync;
flush_options.rate_limiter_priority = rate_limiter_priority_;
Status flush_status = db_->FlushWAL(flush_options);
EXPECT_TRUE(flush_status.ok());
std::int64_t manual_wal_flush_requests_total =
options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
prev_total_request;
std::int64_t manual_wal_flush_requests_for_priority =
options_.rate_limiter->GetTotalRequests(rate_limiter_priority_) -
prev_priority_request;
if (no_rate_limit) {
EXPECT_EQ(0, manual_wal_flush_requests_total);
EXPECT_EQ(0, manual_wal_flush_requests_for_priority);
} else {
EXPECT_EQ(manual_wal_flush_requests_total,
manual_wal_flush_requests_for_priority);
EXPECT_GT(manual_wal_flush_requests_for_priority, 0);
}
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
-245
View File
@@ -1492,246 +1492,6 @@ TEST_F(DBTest, MetaDataTest) {
CheckLiveFilesMeta(live_file_meta, files_by_level);
}
TEST_F(DBTest, GetColumnFamilyMetaDataWithKeyRangeAndLevel) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.disable_auto_compactions = true;
int64_t temp_time = 0;
ASSERT_OK(options.env->GetCurrentTime(&temp_time));
DestroyAndReopen(options);
Random rnd(301);
int key_index = 0;
for (int i = 0; i < 100; ++i) {
// Add a single blob reference to each file
std::string blob_index;
BlobIndex::EncodeBlob(&blob_index, /* blob_file_number */ i + 1000,
/* offset */ 1234, /* size */ 5678, kNoCompression);
WriteBatch batch;
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, Key(key_index),
blob_index));
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
++key_index;
// Fill up the rest of the file with random values.
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
ASSERT_OK(Flush());
}
std::vector<std::vector<FileMetaData>> files_by_level;
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files_by_level);
ASSERT_OK(options.env->GetCurrentTime(&temp_time));
ColumnFamilyMetaData cf_meta;
// Keys in the SST files are distributed
// (key000000, key000100) ->File 1
// (key000101, key000201) -> File 2
// (key000202, key000302) -> File 3
// (key009999, key010099) -> File 100
// With keySlice (key000050, key000150) => should only pick 2 files(instead of
// default 100 that is in the level)
auto startKey = Slice("key000050");
auto endKey = Slice("key000150");
GetColumnFamilyMetaDataOptions cf_options(startKey, endKey, 0);
db_->GetColumnFamilyMetaData(cf_options, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 1);
const auto& level_meta_from_cf = cf_meta.levels[0];
ASSERT_EQ(level_meta_from_cf.files.size(), 2);
ASSERT_LT(level_meta_from_cf.files[1].smallestkey,
std::string(startKey.data()));
ASSERT_GT(level_meta_from_cf.files[0].largestkey, std::string(endKey.data()));
GetColumnFamilyMetaDataOptions cf_option_default;
db_->GetColumnFamilyMetaData(cf_option_default, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 1);
ASSERT_EQ(cf_meta.levels[0].files.size(), 100);
// Test with start key valid and end key unbounded
// This should get all files from key000150 onwards (99 files)
auto startKeyUnbounded = Slice("key000150");
GetColumnFamilyMetaDataOptions cf_options_unbounded_end(startKeyUnbounded,
OptSlice(), 0);
db_->GetColumnFamilyMetaData(cf_options_unbounded_end, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 1);
ASSERT_EQ(cf_meta.levels[0].files.size(), 99);
// Test with end key valid and start key unbounded
// This should get all files from beginning to key000250 ( 3 files)
auto endKeyUnbounded = Slice("key000250");
GetColumnFamilyMetaDataOptions cf_options_unbounded_start(OptSlice(),
endKeyUnbounded, 0);
db_->GetColumnFamilyMetaData(cf_options_unbounded_start, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 1);
ASSERT_EQ(cf_meta.levels[0].files.size(), 3);
}
TEST_F(DBTest, GetColumnFamilyMetaDataBottommostLevel) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.num_levels = 7;
DestroyAndReopen(options);
Random rnd(301);
int key_index = 0;
for (int i = 0; i < 100; ++i) {
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
ASSERT_OK(Flush());
}
CompactRangeOptions compact_options;
compact_options.bottommost_level_compaction =
BottommostLevelCompaction::kForce;
compact_options.change_level = true;
compact_options.target_level = 6;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
// Nothing on Level 0 after compaction
ColumnFamilyMetaData cf_meta;
GetColumnFamilyMetaDataOptions cf_options_0(OptSlice(), OptSlice(), 0);
db_->GetColumnFamilyMetaData(cf_options_0, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 0);
ASSERT_EQ(cf_meta.file_count, 0);
// Data should be in Level 6
GetColumnFamilyMetaDataOptions cf_options(OptSlice(), OptSlice(), 6);
db_->GetColumnFamilyMetaData(cf_options, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 1);
ASSERT_EQ(cf_meta.levels[0].level, 6);
ASSERT_GT(cf_meta.levels[0].files.size(), 0);
size_t all_files = cf_meta.levels[0].files.size();
// Keys in the SST files are distributed across level 6
// Test with key range - should only return files within the range
auto startKey = Slice("key000050");
auto endKey = Slice("key000150");
GetColumnFamilyMetaDataOptions cf_options_range(startKey, endKey, 6);
db_->GetColumnFamilyMetaData(cf_options_range, &cf_meta);
ASSERT_EQ(cf_meta.levels.size(), 1);
ASSERT_EQ(cf_meta.levels[0].level, 6);
ASSERT_GT(cf_meta.levels[0].files.size(), 0);
size_t files_in_range = cf_meta.levels[0].files.size();
// Files in range should be less than or equal to all files
ASSERT_LE(files_in_range, all_files);
}
TEST_F(DBTest, GetColumnFamilyMetaDataMultipleLevels) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.num_levels = 7;
DestroyAndReopen(options);
Random rnd(301);
int key_index = 0;
for (int i = 0; i < 50; ++i) {
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
ASSERT_OK(Flush());
}
CompactRangeOptions compact_options;
compact_options.bottommost_level_compaction =
BottommostLevelCompaction::kForce;
compact_options.change_level = true;
compact_options.target_level = 6;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
for (int i = 0; i < 30; ++i) {
GenerateNewFile(&rnd, &key_index, /* nowait */ true);
ASSERT_OK(Flush());
}
// First verify both levels have files without key range filter
ColumnFamilyMetaData cf_meta_all_no_range;
GetColumnFamilyMetaDataOptions cf_options_all_no_range;
db_->GetColumnFamilyMetaData(cf_options_all_no_range, &cf_meta_all_no_range);
bool has_level_0 = false;
bool has_level_6 = false;
for (const auto& level : cf_meta_all_no_range.levels) {
if (level.level == 0 && level.files.size() > 0) {
has_level_0 = true;
}
if (level.level == 6 && level.files.size() > 0) {
has_level_6 = true;
}
}
ASSERT_TRUE(has_level_0);
ASSERT_TRUE(has_level_6);
// Test querying bottommost level only with key range
// Use a range that should be in the first set of files (now in level 6)
auto startKey = Slice("key000050");
auto endKey = Slice("key000150");
ColumnFamilyMetaData cf_meta_bottommost;
GetColumnFamilyMetaDataOptions cf_options_bottommost(startKey, endKey, 6);
db_->GetColumnFamilyMetaData(cf_options_bottommost, &cf_meta_bottommost);
ASSERT_EQ(cf_meta_bottommost.levels.size(), 1);
ASSERT_EQ(cf_meta_bottommost.levels[0].level, 6);
ASSERT_GT(cf_meta_bottommost.levels[0].files.size(), 0);
size_t level_6_files_in_range = cf_meta_bottommost.levels[0].files.size();
// Test querying all levels with same key range
ColumnFamilyMetaData cf_meta_all;
GetColumnFamilyMetaDataOptions cf_options_all(startKey, endKey);
db_->GetColumnFamilyMetaData(cf_options_all, &cf_meta_all);
size_t level_6_files_in_range_from_all = 0;
for (const auto& level : cf_meta_all.levels) {
if (level.level == 6) {
level_6_files_in_range_from_all = level.files.size();
}
}
ASSERT_GT(level_6_files_in_range_from_all, 0);
ASSERT_EQ(level_6_files_in_range, level_6_files_in_range_from_all);
}
TEST_F(DBTest, GetColumnFamilyMetaDataEmptyDB) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.num_levels = 7;
DestroyAndReopen(options);
// Test on empty database
ColumnFamilyMetaData cf_meta_empty_db;
GetColumnFamilyMetaDataOptions cf_options_empty_db;
db_->GetColumnFamilyMetaData(cf_options_empty_db, &cf_meta_empty_db);
ASSERT_EQ(cf_meta_empty_db.levels.size(), 0);
ASSERT_EQ(cf_meta_empty_db.file_count, 0);
ASSERT_EQ(cf_meta_empty_db.size, 0);
// Test on empty database with key range
auto startKey = Slice("key000050");
auto endKey = Slice("key000150");
ColumnFamilyMetaData cf_meta_empty_range;
GetColumnFamilyMetaDataOptions cf_options_empty_range(startKey, endKey);
db_->GetColumnFamilyMetaData(cf_options_empty_range, &cf_meta_empty_range);
ASSERT_EQ(cf_meta_empty_range.levels.size(), 0);
ASSERT_EQ(cf_meta_empty_range.file_count, 0);
ASSERT_EQ(cf_meta_empty_range.size, 0);
}
TEST_F(DBTest, AllMetaDataTest) {
Options options = CurrentOptions();
options.create_if_missing = true;
@@ -3775,11 +3535,6 @@ class ModelDB : public DB {
void GetColumnFamilyMetaData(ColumnFamilyHandle* /*column_family*/,
ColumnFamilyMetaData* /*metadata*/) override {}
void GetColumnFamilyMetaData(
ColumnFamilyHandle* /*column_family*/,
const GetColumnFamilyMetaDataOptions& /*options*/,
ColumnFamilyMetaData* /*metadata*/) override {}
Status GetDbIdentity(std::string& /*identity*/) const override {
return Status::OK();
}
-3
View File
@@ -5205,7 +5205,6 @@ TEST_F(DBTest2, SwitchMemtableRaceWithNewManifest) {
Options options = CurrentOptions();
DestroyAndReopen(options);
options.max_manifest_file_size = 10;
options.max_manifest_space_amp_pct = 0;
options.create_if_missing = true;
CreateAndReopenWithCF({"pikachu"}, options);
ASSERT_EQ(2, handles_.size());
@@ -5897,7 +5896,6 @@ TEST_P(RenameCurrentTest, Flush) {
Destroy(last_options_);
Options options = GetDefaultOptions();
options.max_manifest_file_size = 1;
options.max_manifest_space_amp_pct = 0;
options.create_if_missing = true;
Reopen(options);
ASSERT_OK(Put("key", "value"));
@@ -5917,7 +5915,6 @@ TEST_P(RenameCurrentTest, Compaction) {
Destroy(last_options_);
Options options = GetDefaultOptions();
options.max_manifest_file_size = 1;
options.max_manifest_space_amp_pct = 0;
options.create_if_missing = true;
Reopen(options);
ASSERT_OK(Put("a", "a_value"));
+1 -2
View File
@@ -454,8 +454,7 @@ Options DBTestBase::GetOptions(
options.allow_mmap_reads = can_allow_mmap;
break;
case kManifestFileSize:
options.max_manifest_file_size = 50; // 50 bytes
options.max_manifest_space_amp_pct = 0; // old behavior
options.max_manifest_file_size = 50; // 50 bytes
break;
case kPerfOptions:
options.delayed_write_rate = 8 * 1024 * 1024;
+2 -3
View File
@@ -1746,8 +1746,8 @@ class RecoveryTestHelper {
WriteController write_controller;
versions.reset(new VersionSet(
test->dbname_, &db_options, MutableDBOptions{options}, file_options,
table_cache.get(), &write_buffer_manager, &write_controller,
test->dbname_, &db_options, file_options, table_cache.get(),
&write_buffer_manager, &write_controller,
/*block_cache_tracer=*/nullptr,
/*io_tracer=*/nullptr, /*db_id=*/"", /*db_session_id=*/"",
options.daily_offpeak_time_utc,
@@ -2277,7 +2277,6 @@ TEST_F(DBWALTest, FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL) {
Options options = CurrentOptions();
// Small size to force manifest creation
options.max_manifest_file_size = 1;
options.max_manifest_space_amp_pct = 0;
options.track_and_verify_wals_in_manifest = true;
DestroyAndReopen(options);
-44
View File
@@ -333,50 +333,6 @@ TEST_F(FormatTest, ReplaceInternalKeyWithMinTimestamp) {
ASSERT_EQ(kTypeValue, new_key.type);
}
TEST(RocksdbVersionTest, Version) {
// Test preprocessor macros for versioning
ASSERT_GT(ROCKSDB_MAJOR, 0);
ASSERT_GE(ROCKSDB_MINOR, 0);
ASSERT_GE(ROCKSDB_PATCH, 0);
ASSERT_LT(ROCKSDB_MAJOR, 1000);
ASSERT_LT(ROCKSDB_MINOR, 1000);
ASSERT_LT(ROCKSDB_PATCH, 1000);
ASSERT_EQ(ROCKSDB_MAKE_VERSION_INT(123, 456, 789), 123456789);
ASSERT_GT(ROCKSDB_VERSION_INT, 9999999);
ASSERT_LT(ROCKSDB_VERSION_INT, 99999999);
static_assert(ROCKSDB_VERSION_GE(9, 8, 7));
static_assert(
ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH));
static_assert(
ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH - 1));
static_assert(
ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH - 100));
static_assert(
ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR - 1, ROCKSDB_PATCH + 1));
static_assert(ROCKSDB_VERSION_GE(ROCKSDB_MAJOR - 1, ROCKSDB_MINOR + 1,
ROCKSDB_PATCH + 1));
static_assert(
!ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH + 1));
static_assert(
!ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH + 100));
static_assert(
!ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR + 1, ROCKSDB_PATCH - 1));
static_assert(!ROCKSDB_VERSION_GE(ROCKSDB_MAJOR + 1, ROCKSDB_MINOR - 1,
ROCKSDB_PATCH - 1));
// More typical usage (but with literal numbers based on relevant API
// features)
#if ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH)
static_assert(true);
#else
static_assert(false);
#endif
#if !ROCKSDB_VERSION_GE(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH + 1)
static_assert(true);
#else
static_assert(false);
#endif
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+2 -1
View File
@@ -749,7 +749,8 @@ void ExternalSstFileIngestionJob::CreateEquivalentFileIngestingCompactions() {
cfd_->ioptions().compaction_style),
LLONG_MAX /* max compaction bytes, not applicable */,
0 /* output path ID, not applicable */, mutable_cf_options.compression,
mutable_cf_options.compression_opts, Temperature::kUnknown,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompaction, not applicable */,
{} /* grandparents, not applicable */,
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
+5 -22
View File
@@ -4094,17 +4094,6 @@ TEST_P(IngestDBGeneratedFileTest2, NonZeroSeqno) {
ingest_opts.fail_if_not_bottommost_level = std::get<3>(GetParam());
ingest_opts.link_files = std::get<4>(GetParam());
Random* rnd = Random::GetTLSInstance();
rnd->Reset(std::random_device{}());
std::ostringstream ingest_opts_trace;
ingest_opts_trace << "ingest_opts params: " << "snapshot_consistency="
<< ingest_opts.snapshot_consistency << ", "
<< "allow_global_seqno=" << ingest_opts.allow_global_seqno
<< ", " << "allow_blocking_flush="
<< ingest_opts.allow_blocking_flush << ", "
<< "fail_if_not_bottommost_level="
<< ingest_opts.fail_if_not_bottommost_level << ", "
<< "link_files=" << ingest_opts.link_files;
SCOPED_TRACE(ingest_opts_trace.str());
do {
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
@@ -4274,17 +4263,11 @@ TEST_P(IngestDBGeneratedFileTest2, NonZeroSeqno) {
s = db_->IngestExternalFile(overlap_cf, sst_file_paths, ingest_opts);
ASSERT_NOK(s);
if (ingest_opts.fail_if_not_bottommost_level) {
ASSERT_TRUE(s.ToString().find("Files cannot be ingested to Lmax") !=
std::string::npos)
<< s.ToString();
} else {
ASSERT_TRUE(s.ToString().find("An ingested file overlaps with existing "
"data in the DB and has been "
"assigned a non-zero sequence number") !=
std::string::npos)
<< s.ToString();
}
ASSERT_TRUE(s.ToString().find("An ingested file overlaps with existing "
"data in the DB and has been "
"assigned a non-zero sequence number") !=
std::string::npos)
<< s.ToString();
}
// Cleanup
+7 -7
View File
@@ -142,13 +142,13 @@ class FlushJobTestBase : public testing::Test {
column_families.emplace_back(cf_name, cf_options_);
}
versions_.reset(new VersionSet(
dbname_, &db_options_, MutableDBOptions{options_}, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
test::kUnitTestDbId, /*db_session_id=*/"",
/*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
versions_.reset(
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
test::kUnitTestDbId, /*db_session_id=*/"",
/*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
EXPECT_OK(versions_->Recover(column_families, false));
}
+4 -4
View File
@@ -59,7 +59,7 @@ class Reader {
// live while this Reader is in use.
//
// If "checksum" is true, verify checksums if available.
// TODO(hx235): separate WAL related parameters from general `Reader`
// TODO(hx235): seperate WAL related parameters from general `Reader`
// parameters
Reader(std::shared_ptr<Logger> info_log,
std::unique_ptr<SequentialFileReader>&& file, Reporter* reporter,
@@ -155,7 +155,7 @@ class Reader {
// which log number this is
uint64_t const log_number_;
// See `Options::track_and_verify_wals`
// See `Optinos::track_and_verify_wals`
bool track_and_verify_wals_;
// Below variables are used for WAL verification
// TODO(hx235): To revise `stop_replay_for_corruption_` inside `LogReader`
@@ -208,8 +208,8 @@ class Reader {
};
// Return type, or one of the preceding special values
// If WAL compression is enabled, fragment_checksum is the checksum of the
// fragment computed from the original buffer containing uncompressed
// If WAL compressioned is enabled, fragment_checksum is the checksum of the
// fragment computed from the orginal buffer containinng uncompressed
// fragment.
uint8_t ReadPhysicalRecord(Slice* result, size_t* drop_size,
uint64_t* fragment_checksum = nullptr);
+1 -1
View File
@@ -77,7 +77,7 @@ class Writer {
// Create a writer that will append data to "*dest".
// "*dest" must be initially empty.
// "*dest" must remain live while this Writer is in use.
// TODO(hx235): separate WAL related parameters from general `Reader`
// TODO(hx235): seperate WAL related parameters from general `Reader`
// parameters
explicit Writer(std::unique_ptr<WritableFileWriter>&& dest,
uint64_t log_number, bool recycle_log_files,
+2 -4
View File
@@ -112,8 +112,7 @@ class MemTableListTest : public testing::Test {
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
WriteController write_controller(10000000u);
VersionSet versions(dbname, &immutable_db_options,
MutableDBOptions{db_options}, env_options,
VersionSet versions(dbname, &immutable_db_options, env_options,
table_cache.get(), &write_buffer_manager,
&write_controller, /*block_cache_tracer=*/nullptr,
/*io_tracer=*/nullptr, /*db_id=*/"",
@@ -164,8 +163,7 @@ class MemTableListTest : public testing::Test {
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
WriteController write_controller(10000000u);
VersionSet versions(dbname, &immutable_db_options,
MutableDBOptions{db_options}, env_options,
VersionSet versions(dbname, &immutable_db_options, env_options,
table_cache.get(), &write_buffer_manager,
&write_controller, /*block_cache_tracer=*/nullptr,
/*io_tracer=*/nullptr, /*db_id=*/"",
+1 -1
View File
@@ -307,7 +307,7 @@ class MergeOutputIterator {
Slice key() { return Slice(*it_keys_); }
Slice value() { return Slice(*it_values_); }
bool Valid() const { return it_keys_ != merge_helper_->keys().rend(); }
bool Valid() { return it_keys_ != merge_helper_->keys().rend(); }
private:
const MergeHelper* merge_helper_;
+2 -2
View File
@@ -120,8 +120,8 @@ class Repairer {
/*io_tracer=*/nullptr, db_session_id_)),
wb_(db_options_.db_write_buffer_size),
wc_(db_options_.delayed_write_rate),
vset_(dbname_, &immutable_db_options_, MutableDBOptions{db_options_},
file_options_, raw_table_cache_.get(), &wb_, &wc_,
vset_(dbname_, &immutable_db_options_, file_options_,
raw_table_cache_.get(), &wb_, &wc_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", db_session_id_, db_options.daily_offpeak_time_utc,
/*error_handler=*/nullptr, /*read_only=*/false),
+139 -477
View File
@@ -112,9 +112,124 @@ bool VersionEdit::EncodeTo(std::string* dst,
f.epoch_number == kUnknownEpochNumber) {
return false;
}
EncodeToNewFile4(f, new_files_[i].first, ts_sz.value(),
has_min_log_number_to_keep_, min_log_number_to_keep_,
min_log_num_written, dst);
PutVarint32(dst, kNewFile4);
PutVarint32Varint64(dst, new_files_[i].first /* level */, f.fd.GetNumber());
PutVarint64(dst, f.fd.GetFileSize());
EncodeFileBoundaries(dst, f, ts_sz.value());
PutVarint64Varint64(dst, f.fd.smallest_seqno, f.fd.largest_seqno);
// Customized fields' format:
// +-----------------------------+
// | 1st field's tag (varint32) |
// +-----------------------------+
// | 1st field's size (varint32) |
// +-----------------------------+
// | bytes for 1st field |
// | (based on size decoded) |
// +-----------------------------+
// | |
// | ...... |
// | |
// +-----------------------------+
// | last field's size (varint32)|
// +-----------------------------+
// | bytes for last field |
// | (based on size decoded) |
// +-----------------------------+
// | terminating tag (varint32) |
// +-----------------------------+
//
// Customized encoding for fields:
// tag kPathId: 1 byte as path_id
// tag kNeedCompaction:
// now only can take one char value 1 indicating need-compaction
//
PutVarint32(dst, NewFileCustomTag::kOldestAncesterTime);
std::string varint_oldest_ancester_time;
PutVarint64(&varint_oldest_ancester_time, f.oldest_ancester_time);
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintOldestAncesterTime",
&varint_oldest_ancester_time);
PutLengthPrefixedSlice(dst, Slice(varint_oldest_ancester_time));
PutVarint32(dst, NewFileCustomTag::kFileCreationTime);
std::string varint_file_creation_time;
PutVarint64(&varint_file_creation_time, f.file_creation_time);
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintFileCreationTime",
&varint_file_creation_time);
PutLengthPrefixedSlice(dst, Slice(varint_file_creation_time));
PutVarint32(dst, NewFileCustomTag::kEpochNumber);
std::string varint_epoch_number;
PutVarint64(&varint_epoch_number, f.epoch_number);
PutLengthPrefixedSlice(dst, Slice(varint_epoch_number));
if (f.file_checksum_func_name != kUnknownFileChecksumFuncName) {
PutVarint32(dst, NewFileCustomTag::kFileChecksum);
PutLengthPrefixedSlice(dst, Slice(f.file_checksum));
PutVarint32(dst, NewFileCustomTag::kFileChecksumFuncName);
PutLengthPrefixedSlice(dst, Slice(f.file_checksum_func_name));
}
if (f.fd.GetPathId() != 0) {
PutVarint32(dst, NewFileCustomTag::kPathId);
char p = static_cast<char>(f.fd.GetPathId());
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (f.temperature != Temperature::kUnknown) {
PutVarint32(dst, NewFileCustomTag::kTemperature);
char p = static_cast<char>(f.temperature);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (f.marked_for_compaction) {
PutVarint32(dst, NewFileCustomTag::kNeedCompaction);
char p = static_cast<char>(1);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (has_min_log_number_to_keep_ && !min_log_num_written) {
PutVarint32(dst, NewFileCustomTag::kMinLogNumberToKeepHack);
std::string varint_log_number;
PutFixed64(&varint_log_number, min_log_number_to_keep_);
PutLengthPrefixedSlice(dst, Slice(varint_log_number));
min_log_num_written = true;
}
if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
PutVarint32(dst, NewFileCustomTag::kOldestBlobFileNumber);
std::string oldest_blob_file_number;
PutVarint64(&oldest_blob_file_number, f.oldest_blob_file_number);
PutLengthPrefixedSlice(dst, Slice(oldest_blob_file_number));
}
UniqueId64x2 unique_id = f.unique_id;
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:UniqueId", &unique_id);
if (unique_id != kNullUniqueId64x2) {
PutVarint32(dst, NewFileCustomTag::kUniqueId);
std::string unique_id_str = EncodeUniqueIdBytes(&unique_id);
PutLengthPrefixedSlice(dst, Slice(unique_id_str));
}
if (f.compensated_range_deletion_size) {
PutVarint32(dst, kCompensatedRangeDeletionSize);
std::string compensated_range_deletion_size;
PutVarint64(&compensated_range_deletion_size,
f.compensated_range_deletion_size);
PutLengthPrefixedSlice(dst, Slice(compensated_range_deletion_size));
}
if (f.tail_size) {
PutVarint32(dst, NewFileCustomTag::kTailSize);
std::string varint_tail_size;
PutVarint64(&varint_tail_size, f.tail_size);
PutLengthPrefixedSlice(dst, Slice(varint_tail_size));
}
if (!f.user_defined_timestamps_persisted) {
// The default value for the flag is true, it's only explicitly persisted
// when it's false. We are putting 0 as the value here to signal false
// (i.e. UDTS not persisted).
PutVarint32(dst, NewFileCustomTag::kUserDefinedTimestampsPersisted);
char p = static_cast<char>(0);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
dst);
PutVarint32(dst, NewFileCustomTag::kTerminate);
}
for (const auto& blob_file_addition : blob_file_additions_) {
@@ -173,142 +288,9 @@ bool VersionEdit::EncodeTo(std::string* dst,
char p = static_cast<char>(persist_user_defined_timestamps_);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (HasSubcompactionProgress()) {
PutVarint32(dst, kSubcompactionProgress);
std::string progress_data;
subcompaction_progress_.EncodeTo(&progress_data);
PutLengthPrefixedSlice(dst, progress_data);
}
return true;
}
void VersionEdit::EncodeToNewFile4(const FileMetaData& f, int level,
size_t ts_sz,
bool has_min_log_number_to_keep,
uint64_t min_log_number_to_keep,
bool& min_log_num_written,
std::string* dst) {
PutVarint32(dst, kNewFile4);
PutVarint32Varint64(dst, level, f.fd.GetNumber());
PutVarint64(dst, f.fd.GetFileSize());
EncodeFileBoundaries(dst, f, ts_sz);
PutVarint64Varint64(dst, f.fd.smallest_seqno, f.fd.largest_seqno);
// Customized fields' format:
// +-----------------------------+
// | 1st field's tag (varint32) |
// +-----------------------------+
// | 1st field's size (varint32) |
// +-----------------------------+
// | bytes for 1st field |
// | (based on size decoded) |
// +-----------------------------+
// | |
// | ...... |
// | |
// +-----------------------------+
// | last field's size (varint32)|
// +-----------------------------+
// | bytes for last field |
// | (based on size decoded) |
// +-----------------------------+
// | terminating tag (varint32) |
// +-----------------------------+
//
// Customized encoding for fields:
// tag kPathId: 1 byte as path_id
// tag kNeedCompaction:
// now only can take one char value 1 indicating need-compaction
//
PutVarint32(dst, NewFileCustomTag::kOldestAncesterTime);
std::string varint_oldest_ancester_time;
PutVarint64(&varint_oldest_ancester_time, f.oldest_ancester_time);
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintOldestAncesterTime",
&varint_oldest_ancester_time);
PutLengthPrefixedSlice(dst, Slice(varint_oldest_ancester_time));
PutVarint32(dst, NewFileCustomTag::kFileCreationTime);
std::string varint_file_creation_time;
PutVarint64(&varint_file_creation_time, f.file_creation_time);
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintFileCreationTime",
&varint_file_creation_time);
PutLengthPrefixedSlice(dst, Slice(varint_file_creation_time));
PutVarint32(dst, NewFileCustomTag::kEpochNumber);
std::string varint_epoch_number;
PutVarint64(&varint_epoch_number, f.epoch_number);
PutLengthPrefixedSlice(dst, Slice(varint_epoch_number));
if (f.file_checksum_func_name != kUnknownFileChecksumFuncName) {
PutVarint32(dst, NewFileCustomTag::kFileChecksum);
PutLengthPrefixedSlice(dst, Slice(f.file_checksum));
PutVarint32(dst, NewFileCustomTag::kFileChecksumFuncName);
PutLengthPrefixedSlice(dst, Slice(f.file_checksum_func_name));
}
if (f.fd.GetPathId() != 0) {
PutVarint32(dst, NewFileCustomTag::kPathId);
char p = static_cast<char>(f.fd.GetPathId());
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (f.temperature != Temperature::kUnknown) {
PutVarint32(dst, NewFileCustomTag::kTemperature);
char p = static_cast<char>(f.temperature);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (f.marked_for_compaction) {
PutVarint32(dst, NewFileCustomTag::kNeedCompaction);
char p = static_cast<char>(1);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
if (has_min_log_number_to_keep && !min_log_num_written) {
PutVarint32(dst, NewFileCustomTag::kMinLogNumberToKeepHack);
std::string varint_log_number;
PutFixed64(&varint_log_number, min_log_number_to_keep);
PutLengthPrefixedSlice(dst, Slice(varint_log_number));
min_log_num_written = true;
}
if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
PutVarint32(dst, NewFileCustomTag::kOldestBlobFileNumber);
std::string oldest_blob_file_number;
PutVarint64(&oldest_blob_file_number, f.oldest_blob_file_number);
PutLengthPrefixedSlice(dst, Slice(oldest_blob_file_number));
}
UniqueId64x2 unique_id = f.unique_id;
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:UniqueId", &unique_id);
if (unique_id != kNullUniqueId64x2) {
PutVarint32(dst, NewFileCustomTag::kUniqueId);
std::string unique_id_str = EncodeUniqueIdBytes(&unique_id);
PutLengthPrefixedSlice(dst, Slice(unique_id_str));
}
if (f.compensated_range_deletion_size) {
PutVarint32(dst, NewFileCustomTag::kCompensatedRangeDeletionSize);
std::string compensated_range_deletion_size;
PutVarint64(&compensated_range_deletion_size,
f.compensated_range_deletion_size);
PutLengthPrefixedSlice(dst, Slice(compensated_range_deletion_size));
}
if (f.tail_size) {
PutVarint32(dst, NewFileCustomTag::kTailSize);
std::string varint_tail_size;
PutVarint64(&varint_tail_size, f.tail_size);
PutLengthPrefixedSlice(dst, Slice(varint_tail_size));
}
if (!f.user_defined_timestamps_persisted) {
// The default value for the flag is true, it's only explicitly persisted
// when it's false. We are putting 0 as the value here to signal false
// (i.e. UDTS not persisted).
PutVarint32(dst, NewFileCustomTag::kUserDefinedTimestampsPersisted);
char p = static_cast<char>(0);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
dst);
PutVarint32(dst, NewFileCustomTag::kTerminate);
}
static bool GetInternalKey(Slice* input, InternalKey* dst) {
Slice str;
if (GetLengthPrefixedSlice(input, &str)) {
@@ -319,12 +301,12 @@ static bool GetInternalKey(Slice* input, InternalKey* dst) {
}
}
bool VersionEdit::GetLevel(Slice* input, int* level, int& max_level) {
bool VersionEdit::GetLevel(Slice* input, int* level, const char** /*msg*/) {
uint32_t v = 0;
if (GetVarint32(input, &v)) {
*level = v;
if (max_level < *level) {
max_level = *level;
if (max_level_ < *level) {
max_level_ = *level;
}
return true;
} else {
@@ -332,18 +314,16 @@ bool VersionEdit::GetLevel(Slice* input, int* level, int& max_level) {
}
}
const char* VersionEdit::DecodeNewFile4From(Slice* input, int& max_level,
uint64_t& min_log_number_to_keep,
bool& has_min_log_number_to_keep,
NewFiles& new_files,
FileMetaData& f) {
const char* VersionEdit::DecodeNewFile4From(Slice* input) {
const char* msg = nullptr;
int level = 0;
FileMetaData f;
uint64_t number = 0;
uint32_t path_id = 0;
uint64_t file_size = 0;
SequenceNumber smallest_seqno = 0;
SequenceNumber largest_seqno = kMaxSequenceNumber;
if (GetLevel(input, &level, max_level) && GetVarint64(input, &number) &&
if (GetLevel(input, &level, &msg) && GetVarint64(input, &number) &&
GetVarint64(input, &file_size) && GetInternalKey(input, &f.smallest) &&
GetInternalKey(input, &f.largest) &&
GetVarint64(input, &smallest_seqno) &&
@@ -401,10 +381,10 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input, int& max_level,
case kMinLogNumberToKeepHack:
// This is a hack to encode kMinLogNumberToKeep in a
// forward-compatible fashion.
if (!GetFixed64(&field, &min_log_number_to_keep)) {
if (!GetFixed64(&field, &min_log_number_to_keep_)) {
return "deleted log number malformatted";
}
has_min_log_number_to_keep = true;
has_min_log_number_to_keep_ = true;
break;
case kOldestBlobFileNumber:
if (!GetVarint64(&field, &f.oldest_blob_file_number)) {
@@ -456,12 +436,13 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input, int& max_level,
}
f.fd =
FileDescriptor(number, path_id, file_size, smallest_seqno, largest_seqno);
new_files.emplace_back(level, f);
new_files_.push_back(std::make_pair(level, f));
return nullptr;
}
void VersionEdit::EncodeFileBoundaries(std::string* dst,
const FileMetaData& meta, size_t ts_sz) {
const FileMetaData& meta,
size_t ts_sz) const {
if (ts_sz == 0 || meta.user_defined_timestamps_persisted) {
PutLengthPrefixedSlice(dst, meta.smallest.Encode());
PutLengthPrefixedSlice(dst, meta.largest.Encode());
@@ -564,8 +545,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
break;
case kCompactCursor:
if (GetLevel(&input, &level, max_level_) &&
GetInternalKey(&input, &key)) {
if (GetLevel(&input, &level, &msg) && GetInternalKey(&input, &key)) {
// Here we re-use the output format of compact pointer in LevelDB
// to persist compact_cursors_
compact_cursors_.push_back(std::make_pair(level, key));
@@ -578,8 +558,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
case kDeletedFile: {
uint64_t number = 0;
if (GetLevel(&input, &level, max_level_) &&
GetVarint64(&input, &number)) {
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number)) {
deleted_files_.insert(std::make_pair(level, number));
} else {
if (!msg) {
@@ -592,8 +571,8 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
case kNewFile: {
uint64_t number = 0;
uint64_t file_size = 0;
if (GetLevel(&input, &level, max_level_) &&
GetVarint64(&input, &number) && GetVarint64(&input, &file_size) &&
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
GetVarint64(&input, &file_size) &&
GetInternalKey(&input, &f.smallest) &&
GetInternalKey(&input, &f.largest)) {
f.fd = FileDescriptor(number, 0, file_size);
@@ -610,8 +589,8 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
uint64_t file_size = 0;
SequenceNumber smallest_seqno = 0;
SequenceNumber largest_seqno = kMaxSequenceNumber;
if (GetLevel(&input, &level, max_level_) &&
GetVarint64(&input, &number) && GetVarint64(&input, &file_size) &&
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
GetVarint64(&input, &file_size) &&
GetInternalKey(&input, &f.smallest) &&
GetInternalKey(&input, &f.largest) &&
GetVarint64(&input, &smallest_seqno) &&
@@ -633,9 +612,8 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
uint64_t file_size = 0;
SequenceNumber smallest_seqno = 0;
SequenceNumber largest_seqno = kMaxSequenceNumber;
if (GetLevel(&input, &level, max_level_) &&
GetVarint64(&input, &number) && GetVarint32(&input, &path_id) &&
GetVarint64(&input, &file_size) &&
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
GetVarint32(&input, &path_id) && GetVarint64(&input, &file_size) &&
GetInternalKey(&input, &f.smallest) &&
GetInternalKey(&input, &f.largest) &&
GetVarint64(&input, &smallest_seqno) &&
@@ -652,10 +630,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
}
case kNewFile4: {
FileMetaData ignored_file;
msg = DecodeNewFile4From(&input, max_level_, min_log_number_to_keep_,
has_min_log_number_to_keep_, new_files_,
ignored_file);
msg = DecodeNewFile4From(&input);
break;
}
@@ -792,23 +767,6 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
}
break;
case kSubcompactionProgress: {
Slice encoded;
if (!GetLengthPrefixedSlice(&input, &encoded)) {
msg = "SubcompactionProgress not prefixed by length";
break;
}
SubcompactionProgress progress;
Status s = progress.DecodeFrom(&encoded);
if (!s.ok()) {
return s;
}
SetSubcompactionProgress(progress);
break;
}
default:
if (tag & kTagSafeIgnoreMask) {
// Tag from future which can be safely ignored.
@@ -975,10 +933,6 @@ std::string VersionEdit::DebugString(bool hex_key) const {
r.append("\n FullHistoryTsLow: ");
r.append(Slice(full_history_ts_low_).ToString(hex_key));
}
if (HasSubcompactionProgress()) {
r.append("\n SubcompactionProgress: ");
r.append(subcompaction_progress_.ToString());
}
r.append("\n}\n");
return r;
}
@@ -1128,301 +1082,9 @@ std::string VersionEdit::DebugJSON(int edit_num, bool hex_key) const {
jw << "FullHistoryTsLow" << Slice(full_history_ts_low_).ToString(hex_key);
}
if (HasSubcompactionProgress()) {
jw << "SubcompactionProgress" << subcompaction_progress_.ToString();
}
jw.EndObject();
return jw.Get();
}
void SubcompactionProgressPerLevel::EncodeTo(std::string* dst) const {
if (num_processed_output_records_ > 0) {
PutVarint32(
dst,
SubcompactionProgressPerLevelCustomTag::kNumProcessedOutputRecords);
std::string varint_records;
PutVarint64(&varint_records, num_processed_output_records_);
PutLengthPrefixedSlice(dst, varint_records);
}
if (!output_files_.empty()) {
PutVarint32(dst, SubcompactionProgressPerLevelCustomTag::kOutputFilesDelta);
std::string files_data;
EncodeOutputFiles(&files_data);
PutLengthPrefixedSlice(dst, files_data);
}
PutVarint32(dst, SubcompactionProgressPerLevelCustomTag::
kSubcompactionProgressPerLevelTerminate);
}
Status SubcompactionProgressPerLevel::DecodeFrom(Slice* input) {
Clear();
while (true) {
uint32_t tag = 0;
if (!GetVarint32(input, &tag)) {
return Status::Corruption("SubcompactionProgressPerLevel", "tag error");
}
if (tag == SubcompactionProgressPerLevelCustomTag::
kSubcompactionProgressPerLevelTerminate) {
break;
}
Slice field;
if (!GetLengthPrefixedSlice(input, &field)) {
return Status::Corruption("SubcompactionProgressPerLevel",
"field length prefixed slice error");
}
switch (tag) {
case SubcompactionProgressPerLevelCustomTag::kNumProcessedOutputRecords: {
if (!GetVarint64(&field, &num_processed_output_records_)) {
return Status::Corruption("SubcompactionProgressPerLevel",
"invalid num_processed_output_records_");
}
break;
}
case SubcompactionProgressPerLevelCustomTag::kOutputFilesDelta: {
Status s = DecodeOutputFiles(&field, output_files_);
if (!s.ok()) {
return s;
}
break;
}
default:
// Forward compatibility: Handle unknown tags
if ((tag & SubcompactionProgressPerLevelCustomTag::
kSubcompactionProgressPerLevelCustomTagSafeIgnoreMask) !=
0) {
break;
} else {
return Status::NotSupported("SubcompactionProgress",
"unsupported critical custom field");
}
}
}
return Status::OK();
}
void SubcompactionProgressPerLevel::EncodeOutputFiles(std::string* dst) const {
size_t new_files_count =
output_files_.size() > last_persisted_output_files_count_
? output_files_.size() - last_persisted_output_files_count_
: 0;
assert(new_files_count > 0);
PutVarint32(dst, static_cast<uint32_t>(new_files_count));
for (size_t i = last_persisted_output_files_count_; i < output_files_.size();
++i) {
std::string file_dst;
bool ignored_min_log_written = false;
VersionEdit::EncodeToNewFile4(
output_files_[i], -1 /* level */, 0 /* ts_sz */,
false /* has_min_log_number_to_keep */, 0 /* min_log_number_to_keep */,
ignored_min_log_written, &file_dst);
PutLengthPrefixedSlice(dst, file_dst);
}
}
Status SubcompactionProgressPerLevel::DecodeOutputFiles(
Slice* input, autovector<FileMetaData>& output_files) {
uint32_t new_file_count = 0;
if (!GetVarint32(input, &new_file_count)) {
return Status::Corruption("SubcompactionProgressPerLevel",
"new output file count");
}
assert(output_files.size() == 0);
output_files.reserve(new_file_count);
for (uint32_t i = 0; i < new_file_count; ++i) {
Slice file_input;
if (!GetLengthPrefixedSlice(input, &file_input)) {
return Status::Corruption("SubcompactionProgressPerLevel",
"output file metadata");
}
uint32_t tag = 0;
if (!GetVarint32(&file_input, &tag) || tag != kNewFile4) {
return Status::Corruption("SubcompactionProgressPerLevel",
"expected kNewFile4 tag");
}
int ignored_max_level = -1;
uint64_t ignored_min_log_number_to_keep = 0;
bool ignored_has_min_log_number_to_keep = false;
VersionEdit::NewFiles ignored_new_files;
FileMetaData file;
const char* err = VersionEdit::DecodeNewFile4From(
&file_input, ignored_max_level, ignored_min_log_number_to_keep,
ignored_has_min_log_number_to_keep, ignored_new_files, file);
if (err != nullptr) {
return Status::Corruption("SubcompactionProgressPerLevel", err);
}
output_files.push_back(std::move(file));
}
return Status::OK();
}
void SubcompactionProgress::EncodeTo(std::string* dst) const {
if (!next_internal_key_to_compact.empty()) {
PutVarint32(dst, SubcompactionProgressCustomTag::kNextInternalKeyToCompact);
PutLengthPrefixedSlice(dst, next_internal_key_to_compact);
}
PutVarint32(dst, SubcompactionProgressCustomTag::kNumProcessedInputRecords);
std::string varint_records;
PutVarint64(&varint_records, num_processed_input_records);
PutLengthPrefixedSlice(dst, varint_records);
if (output_level_progress.GetOutputFiles().size() >
output_level_progress.GetLastPersistedOutputFilesCount()) {
PutVarint32(dst, SubcompactionProgressCustomTag::kOutputLevelProgress);
std::string level_progress_data;
output_level_progress.EncodeTo(&level_progress_data);
PutLengthPrefixedSlice(dst, level_progress_data);
}
if (proximal_output_level_progress.GetOutputFiles().size() >
proximal_output_level_progress.GetLastPersistedOutputFilesCount()) {
PutVarint32(dst,
SubcompactionProgressCustomTag::kProximalOutputLevelProgress);
std::string level_progress_data;
proximal_output_level_progress.EncodeTo(&level_progress_data);
PutLengthPrefixedSlice(dst, level_progress_data);
}
PutVarint32(dst,
SubcompactionProgressCustomTag::kSubcompactionProgressTerminate);
}
Status SubcompactionProgress::DecodeFrom(Slice* input) {
Clear();
while (true) {
uint32_t custom_tag = 0;
if (!GetVarint32(input, &custom_tag)) {
return Status::Corruption("SubcompactionProgress",
"custom field tag error");
}
if (custom_tag ==
SubcompactionProgressCustomTag::kSubcompactionProgressTerminate) {
break;
}
Slice field;
if (!GetLengthPrefixedSlice(input, &field)) {
return Status::Corruption("SubcompactionProgress",
"custom field length prefixed slice error");
}
switch (custom_tag) {
case SubcompactionProgressCustomTag::kNextInternalKeyToCompact:
next_internal_key_to_compact = field.ToString();
break;
case SubcompactionProgressCustomTag::kNumProcessedInputRecords:
if (!GetVarint64(&field, &num_processed_input_records)) {
return Status::Corruption("SubcompactionProgress",
"invalid num_processed_input_records");
}
break;
case SubcompactionProgressCustomTag::kOutputLevelProgress: {
Status s = output_level_progress.DecodeFrom(&field);
if (!s.ok()) {
return s;
}
break;
}
case SubcompactionProgressCustomTag::kProximalOutputLevelProgress: {
Status s = proximal_output_level_progress.DecodeFrom(&field);
if (!s.ok()) {
return s;
}
break;
}
default:
if ((custom_tag & SubcompactionProgressCustomTag::
kSubcompactionProgressCustomTagSafeIgnoreMask) !=
0) {
break;
} else {
return Status::NotSupported("SubcompactionProgress",
"unsupported critical custom field");
}
}
}
return Status::OK();
}
bool SubcompactionProgressBuilder::ProcessVersionEdit(const VersionEdit& edit) {
if (!edit.HasSubcompactionProgress()) {
return false;
}
const SubcompactionProgress& progress = edit.GetSubcompactionProgress();
MergeDeltaProgress(progress);
has_subcompaction_progress_ = true;
return true;
}
void SubcompactionProgressBuilder::MergeDeltaProgress(
const SubcompactionProgress& delta_progress) {
accumulated_subcompaction_progress_.next_internal_key_to_compact =
delta_progress.next_internal_key_to_compact;
accumulated_subcompaction_progress_.num_processed_input_records =
delta_progress.num_processed_input_records;
MaybeMergeDeltaProgressPerLevel(
accumulated_subcompaction_progress_.output_level_progress,
delta_progress.output_level_progress);
MaybeMergeDeltaProgressPerLevel(
accumulated_subcompaction_progress_.proximal_output_level_progress,
delta_progress.proximal_output_level_progress);
}
void SubcompactionProgressBuilder::MaybeMergeDeltaProgressPerLevel(
SubcompactionProgressPerLevel& accumulated_level_progress,
const SubcompactionProgressPerLevel& delta_level_progress) {
const auto& delta_files = delta_level_progress.GetOutputFiles();
if (delta_files.empty()) {
return;
}
for (const FileMetaData& file : delta_files) {
accumulated_level_progress.AddToOutputFiles(file); // Stored as copy
}
accumulated_level_progress.SetNumProcessedOutputRecords(
delta_level_progress.GetNumProcessedOutputRecords());
}
void SubcompactionProgressBuilder::Clear() {
accumulated_subcompaction_progress_.Clear();
has_subcompaction_progress_ = false;
}
} // namespace ROCKSDB_NAMESPACE
+8 -228
View File
@@ -72,23 +72,6 @@ enum Tag : uint32_t {
kWalAddition2,
kWalDeletion2,
kPersistUserDefinedTimestamps,
kSubcompactionProgress,
};
enum SubcompactionProgressPerLevelCustomTag : uint32_t {
kSubcompactionProgressPerLevelTerminate = 1, // End of fields marker
kOutputFilesDelta = 2,
kNumProcessedOutputRecords = 3,
kSubcompactionProgressPerLevelCustomTagSafeIgnoreMask = 1 << 16,
};
enum SubcompactionProgressCustomTag : uint32_t {
kSubcompactionProgressTerminate = 1, // End of fields marker
kNextInternalKeyToCompact = 2,
kNumProcessedInputRecords = 3,
kOutputLevelProgress = 4,
kProximalOutputLevelProgress = 5,
kSubcompactionProgressCustomTagSafeIgnoreMask = 1 << 16,
};
enum NewFileCustomTag : uint32_t {
@@ -457,191 +440,12 @@ struct LevelFilesBrief {
}
};
struct SubcompactionProgressPerLevel {
uint64_t GetNumProcessedOutputRecords() const {
return num_processed_output_records_;
}
void SetNumProcessedOutputRecords(uint64_t num) {
num_processed_output_records_ = num;
}
const autovector<FileMetaData>& GetOutputFiles() const {
return output_files_;
}
void AddToOutputFiles(const FileMetaData& file) {
output_files_.push_back(file);
}
size_t GetLastPersistedOutputFilesCount() const {
return last_persisted_output_files_count_;
}
void UpdateLastPersistedOutputFilesCount() {
last_persisted_output_files_count_ = output_files_.size();
}
void EncodeTo(std::string* dst) const;
Status DecodeFrom(Slice* input);
void Clear() {
num_processed_output_records_ = 0;
output_files_.clear();
last_persisted_output_files_count_ = 0;
}
std::string ToString() const {
std::ostringstream oss;
oss << "SubcompactionProgressPerLevel{";
oss << " num_processed_output_records=" << num_processed_output_records_;
oss << ", output_files_count=" << output_files_.size();
oss << ", last_persisted_output_files_count="
<< last_persisted_output_files_count_;
oss << " }";
return oss.str();
}
void TEST_ClearOutputFiles() { output_files_.clear(); }
private:
uint64_t num_processed_output_records_ = 0;
autovector<FileMetaData> output_files_ = {};
// Number of files already persisted to help calculate the new output files to
// persist in the future. This is to prevent having to persist all the output
// files metadata so far every time of a "snapshot" of a progress is persisted
// which can lead to O(1+2+...+n) = O(n^2) file metadata being persisted. The
// current approach of persisting only the delta should always persist
// exactly the number (n) of output files in total.
size_t last_persisted_output_files_count_ = 0;
void EncodeOutputFiles(std::string* dst) const;
Status DecodeOutputFiles(Slice* input,
autovector<FileMetaData>& temp_storage);
};
struct SubcompactionProgress {
std::string next_internal_key_to_compact;
uint64_t num_processed_input_records = 0;
SubcompactionProgressPerLevel output_level_progress;
SubcompactionProgressPerLevel proximal_output_level_progress;
SubcompactionProgress() = default;
void Clear() {
next_internal_key_to_compact.clear();
num_processed_input_records = 0;
output_level_progress.Clear();
proximal_output_level_progress.Clear();
}
void EncodeTo(std::string* dst) const;
Status DecodeFrom(Slice* input);
std::string ToString() const {
std::ostringstream oss;
oss << "SubcompactionProgress{";
oss << " next_internal_key_to_compact=";
if (next_internal_key_to_compact.empty()) {
oss << "";
} else {
ParsedInternalKey parsed_key;
Slice key_slice(next_internal_key_to_compact);
if (ParseInternalKey(key_slice, &parsed_key, false /* log_err_key */)
.ok()) {
oss << "user_key=\"" << parsed_key.user_key.ToString(false /* hex */)
<< "\" (hex:" << parsed_key.user_key.ToString(true /* hex */)
<< ")";
oss << ", seq=";
if (parsed_key.sequence == kMaxSequenceNumber) {
oss << "kMaxSequenceNumber";
} else {
oss << parsed_key.sequence;
}
oss << ", type=" << static_cast<int>(parsed_key.type);
} else {
oss << "raw=" << key_slice.ToString(true /* hex */);
}
}
oss << ", num_processed_input_records=" << num_processed_input_records;
oss << ", output_level_progress=" << output_level_progress.ToString();
oss << ", proximal_output_level_progress="
<< proximal_output_level_progress.ToString();
oss << " }";
return oss.str();
}
};
class VersionEdit;
// Builder class to reconstruct complete subcompaction progress object
// from multiple decoded VersionEdits containing delta output files information
// of the same subcompaction. See
// `SubcompactionProgressPerLevel::last_persisted_output_files_count_`'s comment
//
// WARNING: This class currently assumes all input VersionEdits contain progress
// information for the SAME subcompaction. It does not validate
// progress data from different subcompactions so mixing progress from
// multiple subcompactions can result in corrupted state silently. The caller is
// responsible for ensuring all VersionEdits processed by a single instance
// of this builder correspond to the same subcompaction.
class SubcompactionProgressBuilder {
public:
SubcompactionProgressBuilder() = default;
bool ProcessVersionEdit(const VersionEdit& edit);
const SubcompactionProgress& GetAccumulatedSubcompactionProgress() const {
return accumulated_subcompaction_progress_;
}
bool HasAccumulatedSubcompactionProgress() const {
return has_subcompaction_progress_;
}
void Clear();
private:
void MergeDeltaProgress(const SubcompactionProgress& delta_progress);
void MaybeMergeDeltaProgressPerLevel(
SubcompactionProgressPerLevel& accumulated_level_progress,
const SubcompactionProgressPerLevel& delta_level_progress);
SubcompactionProgress accumulated_subcompaction_progress_;
bool has_subcompaction_progress_ = false;
};
// Type alias for backward compatibility - vector of subcompaction progress
using CompactionProgress = std::vector<SubcompactionProgress>;
// The state of a DB at any given time is referred to as a Version.
// Any modification to the Version is considered a Version Edit. A Version is
// constructed by joining a sequence of Version Edits. Version Edits are written
// to the MANIFEST file.
class VersionEdit {
public:
// Retrieve the table files added as well as their associated levels.
using NewFiles = std::vector<std::pair<int, FileMetaData>>;
static void EncodeToNewFile4(const FileMetaData& f, int level, size_t ts_sz,
bool has_min_log_number_to_keep,
uint64_t min_log_number_to_keep,
bool& min_log_num_written, std::string* dst);
static const char* DecodeNewFile4From(Slice* input, int& max_level,
uint64_t& min_log_number_to_keep,
bool& has_min_log_number_to_keep,
NewFiles& new_files, FileMetaData& f);
void Clear();
void SetDBId(const std::string& db_id) {
@@ -760,6 +564,8 @@ class VersionEdit {
}
}
// Retrieve the table files added as well as their associated levels.
using NewFiles = std::vector<std::pair<int, FileMetaData>>;
const NewFiles& GetNewFiles() const { return new_files_; }
NewFiles& GetMutableNewFiles() { return new_files_; }
@@ -929,22 +735,6 @@ class VersionEdit {
full_history_ts_low_ = std::move(full_history_ts_low);
}
void SetSubcompactionProgress(const SubcompactionProgress& progress) {
has_subcompaction_progress_ = true;
subcompaction_progress_ = progress;
}
bool HasSubcompactionProgress() const { return has_subcompaction_progress_; }
const SubcompactionProgress& GetSubcompactionProgress() const {
return subcompaction_progress_;
}
void ClearSubcompactionProgress() {
has_subcompaction_progress_ = false;
subcompaction_progress_.Clear();
}
// return true on success.
// `ts_sz` is the size in bytes for the user-defined timestamp contained in
// a user key. This argument is optional because it's only required for
@@ -967,22 +757,15 @@ class VersionEdit {
std::string DebugJSON(int edit_num, bool hex_key = false) const;
private:
// Decode level information from serialized VersionEdit data and and track the
// maximum level seen.
//
// Parameters:
// input: Pointer to serialized data slice
// level: Output parameter for the decoded level value
// max_level: get updated if the decoded level is higher than passed in
// value
//
// Returns: true on successful decode, false on parse error
static bool GetLevel(Slice* input, int* level, int& max_level);
bool GetLevel(Slice* input, int* level, const char** msg);
const char* DecodeNewFile4From(Slice* input);
// Encode file boundaries `FileMetaData.smallest` and `FileMetaData.largest`.
// User-defined timestamps in the user key will be stripped if they shouldn't
// be persisted.
static void EncodeFileBoundaries(std::string* dst, const FileMetaData& meta,
size_t ts_sz);
void EncodeFileBoundaries(std::string* dst, const FileMetaData& meta,
size_t ts_sz) const;
int max_level_ = 0;
std::string db_id_;
@@ -1033,9 +816,6 @@ class VersionEdit {
std::string full_history_ts_low_;
bool persist_user_defined_timestamps_ = true;
bool has_subcompaction_progress_ = false;
SubcompactionProgress subcompaction_progress_;
// Newly created table files and blob files are eligible for deletion if they
// are not registered as live files after the background jobs creating them
// have finished. In case committing the VersionEdit containing such changes
-333
View File
@@ -794,339 +794,6 @@ TEST(FileMetaDataTest, UpdateBoundariesBlobIndex) {
}
}
class SubcompactionProgressTest : public VersionEditTest {
protected:
static constexpr uint64_t kTestFileSize = 1024;
static constexpr SequenceNumber kTestSmallestSeq = 50;
static constexpr SequenceNumber kTestLargestSeq = 150;
static constexpr uint64_t kTestOldestAncesterTime = 12345;
static constexpr uint64_t kTestFileCreationTime = 67890;
static constexpr uint64_t kTestEpochNumber = 10;
static const std::string kTestChecksumFuncName;
FileMetaData CreateTestFile(uint64_t file_number, const std::string& prefix) {
FileMetaData file;
file.fd = FileDescriptor(file_number, 0, kTestFileSize, kTestSmallestSeq,
kTestLargestSeq);
file.smallest = InternalKey(prefix + "a", kTestSmallestSeq, kTypeValue);
file.largest = InternalKey(prefix + "z", kTestLargestSeq, kTypeValue);
file.oldest_ancester_time = kTestOldestAncesterTime;
file.file_creation_time = kTestFileCreationTime;
file.epoch_number = kTestEpochNumber;
file.file_checksum = "checksum_" + std::to_string(file_number);
file.file_checksum_func_name = kTestChecksumFuncName;
file.marked_for_compaction = false;
file.temperature = Temperature::kUnknown;
return file;
}
// Store external file metadata objects for testing
// These simulate files owned by CompactionOutputs
std::vector<FileMetaData> compaction_output_files_;
std::vector<FileMetaData> proximal_level_compaction_output_files_;
SubcompactionProgress CreateSubcompactionProgress(
const std::string& next_key, uint64_t num_processed_input_records,
uint64_t num_processed_output_records,
uint64_t num_processed_proximal_level_output_records,
const std::vector<uint64_t>& output_file_numbers = {},
const std::vector<uint64_t>& proximal_file_numbers = {},
const std::string& file_prefix = "file_") {
SubcompactionProgress progress;
progress.next_internal_key_to_compact = next_key;
progress.num_processed_input_records = num_processed_input_records;
progress.output_level_progress.SetNumProcessedOutputRecords(
num_processed_output_records);
progress.proximal_output_level_progress.SetNumProcessedOutputRecords(
num_processed_proximal_level_output_records);
for (uint64_t file_num : output_file_numbers) {
FileMetaData file = CreateTestFile(file_num, file_prefix + "output_");
progress.output_level_progress.AddToOutputFiles(file);
}
for (uint64_t file_num : proximal_file_numbers) {
FileMetaData file = CreateTestFile(file_num, file_prefix + "proximal_");
progress.proximal_output_level_progress.AddToOutputFiles(file);
}
return progress;
}
std::pair<const VersionEdit, const SubcompactionProgress>
EncodeDecodeProgress(const SubcompactionProgress& progress) {
VersionEdit edit;
edit.SetSubcompactionProgress(progress);
std::string encoded;
EXPECT_TRUE(edit.EncodeTo(&encoded, 0 /* ts_sz */));
VersionEdit decoded_edit;
EXPECT_OK(decoded_edit.DecodeFrom(encoded));
EXPECT_TRUE(decoded_edit.HasSubcompactionProgress());
SubcompactionProgress decoded_progress =
decoded_edit.GetSubcompactionProgress();
return {std::move(decoded_edit), std::move(decoded_progress)};
}
void VerifyFileMetaDataEquality(const FileMetaData& expected,
const FileMetaData& actual) {
// Verify the major fields only
ASSERT_EQ(actual.fd.GetNumber(), expected.fd.GetNumber());
ASSERT_EQ(actual.fd.GetFileSize(), expected.fd.GetFileSize());
ASSERT_EQ(actual.smallest.Encode(), expected.smallest.Encode());
ASSERT_EQ(actual.largest.Encode(), expected.largest.Encode());
ASSERT_EQ(actual.oldest_ancester_time, expected.oldest_ancester_time);
ASSERT_EQ(actual.file_creation_time, expected.file_creation_time);
ASSERT_EQ(actual.epoch_number, expected.epoch_number);
ASSERT_EQ(actual.file_checksum, expected.file_checksum);
ASSERT_EQ(actual.file_checksum_func_name, expected.file_checksum_func_name);
ASSERT_EQ(actual.marked_for_compaction, expected.marked_for_compaction);
ASSERT_EQ(actual.temperature, expected.temperature);
}
void VerifyProgressEquality(const SubcompactionProgress& expected,
const SubcompactionProgress& actual) {
ASSERT_EQ(actual.next_internal_key_to_compact,
expected.next_internal_key_to_compact);
ASSERT_EQ(actual.num_processed_input_records,
expected.num_processed_input_records);
for (const bool is_proximal_level : {false, true}) {
const SubcompactionProgressPerLevel&
actual_subcompaction_progress_by_level =
is_proximal_level ? actual.proximal_output_level_progress
: actual.output_level_progress;
const SubcompactionProgressPerLevel&
expected_subcompaction_progress_by_level =
is_proximal_level ? expected.proximal_output_level_progress
: expected.output_level_progress;
ASSERT_EQ(
actual_subcompaction_progress_by_level.GetNumProcessedOutputRecords(),
expected_subcompaction_progress_by_level
.GetNumProcessedOutputRecords());
ASSERT_EQ(
actual_subcompaction_progress_by_level.GetOutputFiles().size(),
expected_subcompaction_progress_by_level.GetOutputFiles().size());
for (size_t i = 0;
i < expected_subcompaction_progress_by_level.GetOutputFiles().size();
++i) {
VerifyFileMetaDataEquality(
expected_subcompaction_progress_by_level.GetOutputFiles()[i],
actual_subcompaction_progress_by_level.GetOutputFiles()[i]);
}
}
}
};
const std::string SubcompactionProgressTest::kTestChecksumFuncName = "crc32c";
TEST_F(SubcompactionProgressTest, BasicEncodeDecode) {
// Create progress with files for both levels
SubcompactionProgress progress = CreateSubcompactionProgress(
"key_100", // next_internal_key_to_compact
500, // num_processed_input_records
400, // num_processed_output_records
100, // num_processed_proximal_level_output_records
{1}, // output_file_numbers
{2}, // proximal_file_numbers
"test_" // file_prefix
);
auto [ignored, decoded_progress] = EncodeDecodeProgress(progress);
VerifyProgressEquality(progress, decoded_progress);
}
TEST_F(SubcompactionProgressTest, OutputFilesDeltaEncodeDecode) {
// Test Delta Encoding/Decoding
SubcompactionProgress initial_progress = CreateSubcompactionProgress(
"key_100", // next_internal_key_to_compact
100, // num_processed_input_records
40, // num_processed_output_records
60, // num_processed_proximal_level_output_records
{1}, // output_file_numbers
{2}, // proximal_file_numbers
"initial_" // file_prefix
);
auto [initial_decoded_edit, ignored_1] =
EncodeDecodeProgress(initial_progress);
initial_progress.output_level_progress.UpdateLastPersistedOutputFilesCount();
initial_progress.proximal_output_level_progress
.UpdateLastPersistedOutputFilesCount();
// Add one new output file to output and proximal level
SubcompactionProgress updated_progress = initial_progress;
updated_progress.next_internal_key_to_compact = "key_300";
updated_progress.num_processed_input_records = 1000;
updated_progress.output_level_progress.SetNumProcessedOutputRecords(400);
updated_progress.proximal_output_level_progress.SetNumProcessedOutputRecords(
600);
FileMetaData new_file = CreateTestFile(3, "new_");
updated_progress.output_level_progress.AddToOutputFiles(new_file);
FileMetaData new_file_proximal = CreateTestFile(4, "new_");
updated_progress.proximal_output_level_progress.AddToOutputFiles(
new_file_proximal);
auto [delta_decoded_edit, delta_decoded_progress] =
EncodeDecodeProgress(updated_progress);
ASSERT_EQ(delta_decoded_progress.next_internal_key_to_compact,
updated_progress.next_internal_key_to_compact);
ASSERT_EQ(delta_decoded_progress.num_processed_input_records,
updated_progress.num_processed_input_records);
for (const bool& is_proximal_level : {false, true}) {
const SubcompactionProgressPerLevel& delta_progress_per_level =
is_proximal_level
? delta_decoded_progress.proximal_output_level_progress
: delta_decoded_progress.output_level_progress;
const SubcompactionProgressPerLevel& updated_progress_per_level =
is_proximal_level ? updated_progress.proximal_output_level_progress
: updated_progress.output_level_progress;
ASSERT_EQ(delta_progress_per_level.GetNumProcessedOutputRecords(),
updated_progress_per_level.GetNumProcessedOutputRecords());
// Only the newly added file since last persistence should be present
ASSERT_EQ(delta_progress_per_level.GetOutputFiles().size(), 1);
ASSERT_EQ(delta_progress_per_level.GetOutputFiles()[0].fd.GetNumber(),
is_proximal_level ? new_file_proximal.fd.GetNumber()
: new_file.fd.GetNumber());
}
// Test SubcompactionProgressBuilder
SubcompactionProgressBuilder builder;
ASSERT_FALSE(builder.HasAccumulatedSubcompactionProgress());
ASSERT_TRUE(builder.ProcessVersionEdit(initial_decoded_edit));
ASSERT_TRUE(builder.HasAccumulatedSubcompactionProgress());
ASSERT_TRUE(builder.ProcessVersionEdit(delta_decoded_edit));
const auto& accumulated_progress =
builder.GetAccumulatedSubcompactionProgress();
ASSERT_EQ(accumulated_progress.next_internal_key_to_compact,
updated_progress.next_internal_key_to_compact);
ASSERT_EQ(accumulated_progress.num_processed_input_records,
updated_progress.num_processed_input_records);
for (const bool& is_proximal_level : {false, true}) {
const SubcompactionProgressPerLevel& accumulated_progress_per_level =
is_proximal_level ? accumulated_progress.proximal_output_level_progress
: accumulated_progress.output_level_progress;
const SubcompactionProgressPerLevel& updated_progress_per_level =
is_proximal_level ? updated_progress.proximal_output_level_progress
: updated_progress.output_level_progress;
ASSERT_EQ(accumulated_progress_per_level.GetNumProcessedOutputRecords(),
updated_progress_per_level.GetNumProcessedOutputRecords());
ASSERT_EQ(accumulated_progress_per_level.GetOutputFiles().size(),
updated_progress_per_level.GetOutputFiles().size());
std::set<uint64_t> accumulated_file_numbers;
for (const auto& file : accumulated_progress_per_level.GetOutputFiles()) {
accumulated_file_numbers.insert(file.fd.GetNumber());
}
std::set<uint64_t> expected_file_numbers;
for (const auto& file : updated_progress_per_level.GetOutputFiles()) {
expected_file_numbers.insert(file.fd.GetNumber());
}
ASSERT_EQ(accumulated_file_numbers, expected_file_numbers);
}
// ===== PART 3: Test Builder Reset =====
builder.Clear();
ASSERT_FALSE(builder.HasAccumulatedSubcompactionProgress());
}
TEST_F(SubcompactionProgressTest, UnknownTags) {
SubcompactionProgress progress;
std::string encoded;
// 1. Test unknown ignorable tag
progress.next_internal_key_to_compact = "test_key";
progress.num_processed_input_records = 100;
PutVarint32(&encoded,
SubcompactionProgressCustomTag::kNextInternalKeyToCompact);
PutLengthPrefixedSlice(&encoded, progress.next_internal_key_to_compact);
PutVarint32(&encoded,
SubcompactionProgressCustomTag::kNumProcessedInputRecords);
std::string varint_records;
PutVarint64(&varint_records, progress.num_processed_input_records);
PutLengthPrefixedSlice(&encoded, varint_records);
// Manually encode with unknown ignorable tag (has
// SubcompactionProgressCustomTag::kSubcompactionProgressCustomTagSafeIgnoreMask
// bit set)
uint32_t unknown_ignorable_tag =
SubcompactionProgressCustomTag::
kSubcompactionProgressCustomTagSafeIgnoreMask +
1;
PutVarint32(&encoded, unknown_ignorable_tag);
PutLengthPrefixedSlice(&encoded, "future_data");
PutVarint32(&encoded,
SubcompactionProgressCustomTag::kSubcompactionProgressTerminate);
// Test decoding - should succeed and ignore unknown tag
Slice input(encoded);
SubcompactionProgress decoded_progress;
Status s = decoded_progress.DecodeFrom(&input);
ASSERT_OK(s);
// Verify known fields are preserved
ASSERT_EQ(decoded_progress.next_internal_key_to_compact,
progress.next_internal_key_to_compact);
ASSERT_EQ(decoded_progress.num_processed_input_records,
progress.num_processed_input_records);
// 2. Test unknown non-ignorable tag
encoded.clear();
PutVarint32(&encoded,
SubcompactionProgressCustomTag::kNextInternalKeyToCompact);
PutLengthPrefixedSlice(&encoded, "test_key");
// Manually encode with unknown non-ignorable tag (do not have
// SubcompactionProgressCustomTag::kSubcompactionProgressCustomTagSafeIgnoreMask
// bit set)
uint32_t unknown_critical_tag =
SubcompactionProgressCustomTag::
kSubcompactionProgressCustomTagSafeIgnoreMask -
1;
PutVarint32(&encoded, unknown_critical_tag);
PutLengthPrefixedSlice(&encoded, "critical_future_data");
PutVarint32(&encoded,
SubcompactionProgressCustomTag::kSubcompactionProgressTerminate);
// Test decoding - should fail on critical unknown tag
Slice critical_input(encoded);
SubcompactionProgress critical_progress;
Status critical_status = critical_progress.DecodeFrom(&critical_input);
ASSERT_NOK(critical_status);
ASSERT_TRUE(critical_status.IsNotSupported());
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+14 -127
View File
@@ -1188,8 +1188,8 @@ class LevelIterator final : public InternalIterator {
continue;
}
auto const metadata = flevel_->files[i].file_metadata;
if (metadata->num_entries == metadata->num_range_deletions) {
// Skip range deletion only files.
if (metadata->FileIsStandAloneRangeTombstone()) {
// Skip stand alone range deletion files.
continue;
}
auto& args = GetMultiScanArgForFile(i);
@@ -2024,79 +2024,6 @@ void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
}
}
void Version::GetColumnFamilyMetaData(
const GetColumnFamilyMetaDataOptions& options,
ColumnFamilyMetaData* cf_meta) {
assert(cf_meta);
assert(cfd_);
cf_meta->name = cfd_->GetName();
cf_meta->size = 0;
cf_meta->file_count = 0;
cf_meta->levels.clear();
cf_meta->blob_file_size = 0;
cf_meta->blob_file_count = 0;
cf_meta->blob_files.clear();
const auto& ioptions = cfd_->ioptions();
auto* vstorage = storage_info();
int first_level = (options.level >= 0) ? options.level : 0;
int last_level =
(options.level >= 0) ? options.level + 1 : cfd_->NumberLevels();
InternalKey ikey_start, ikey_end;
const InternalKey* begin = nullptr;
const InternalKey* end = nullptr;
if (options.range.start.has_value()) {
ikey_start = InternalKey(options.range.start.value(), kMaxSequenceNumber,
kValueTypeForSeek);
begin = &ikey_start;
}
if (options.range.limit.has_value()) {
ikey_end = InternalKey(options.range.limit.value(), kMaxSequenceNumber,
kValueTypeForSeek);
end = &ikey_end;
}
for (int l = first_level; l < last_level; ++l) {
uint64_t level_size = 0;
std::vector<SstFileMetaData> files;
std::vector<FileMetaData*> overlapping_files;
vstorage->GetOverlappingInputs(l, begin, end, &overlapping_files);
for (const auto& file : overlapping_files) {
uint32_t path_id = file->fd.GetPathId();
const auto& file_path = (path_id < ioptions.cf_paths.size())
? ioptions.cf_paths[path_id].path
: ioptions.cf_paths.back().path;
const uint64_t file_number = file->fd.GetNumber();
files.emplace_back(
MakeTableFileName("", file_number), file_number, file_path,
file->fd.GetFileSize(), file->fd.smallest_seqno,
file->fd.largest_seqno, file->smallest.user_key().ToString(),
file->largest.user_key().ToString(),
file->stats.num_reads_sampled.load(std::memory_order_relaxed),
file->being_compacted, file->temperature,
file->oldest_blob_file_number, file->TryGetOldestAncesterTime(),
file->TryGetFileCreationTime(), file->epoch_number,
file->file_checksum, file->file_checksum_func_name);
files.back().num_entries = file->num_entries;
files.back().num_deletions = file->num_deletions;
files.back().smallest = file->smallest.Encode().ToString();
files.back().largest = file->largest.Encode().ToString();
level_size += file->fd.GetFileSize();
cf_meta->file_count++;
}
if (!files.empty()) {
cf_meta->levels.emplace_back(l, level_size, std::move(files));
cf_meta->size += level_size;
}
}
}
uint64_t Version::GetSstFilesSize() {
uint64_t sst_files_size = 0;
for (int level = 0; level < storage_info_.num_levels_; level++) {
@@ -5380,7 +5307,6 @@ void AtomicGroupReadBuffer::Clear() {
VersionSet::VersionSet(
const std::string& dbname, const ImmutableDBOptions* _db_options,
const MutableDBOptions& mutable_db_options,
const FileOptions& storage_options, Cache* table_cache,
WriteBufferManager* write_buffer_manager, WriteController* write_controller,
BlockCacheTracer* const block_cache_tracer,
@@ -5409,7 +5335,6 @@ VersionSet::VersionSet(
prev_log_number_(0),
current_version_number_(0),
manifest_file_size_(0),
last_compacted_manifest_file_size_(0),
file_options_(storage_options),
block_cache_tracer_(block_cache_tracer),
io_tracer_(io_tracer),
@@ -5417,9 +5342,7 @@ VersionSet::VersionSet(
offpeak_time_option_(OffpeakTimeOption(daily_offpeak_time_utc)),
error_handler_(error_handler),
unchanging_(unchanging),
closed_(false) {
UpdatedMutableDbOptions(mutable_db_options, /*mu=*/nullptr);
}
closed_(false) {}
Status VersionSet::Close(FSDirectory* db_dir, InstrumentedMutex* mu) {
Status s;
@@ -5515,35 +5438,11 @@ void VersionSet::Reset() {
current_version_number_ = 0;
manifest_writers_.clear();
manifest_file_size_ = 0;
last_compacted_manifest_file_size_ = 0;
TuneMaxManifestFileSize();
obsolete_files_.clear();
obsolete_manifests_.clear();
wals_.Reset();
}
void VersionSet::UpdatedMutableDbOptions(
const MutableDBOptions& updated_options, InstrumentedMutex* mu) {
// Must be holding mutex if not called during initialization
if (manifest_file_size_ > 0) {
mu->AssertHeld();
}
file_options_.writable_file_max_buffer_size =
updated_options.writable_file_max_buffer_size;
min_max_manifest_file_size_ = updated_options.max_manifest_file_size;
max_manifest_space_amp_pct_ = static_cast<unsigned>(
std::max(updated_options.max_manifest_space_amp_pct, 0));
manifest_preallocation_size_ = updated_options.manifest_preallocation_size;
TuneMaxManifestFileSize();
}
void VersionSet::TuneMaxManifestFileSize() {
tuned_max_manifest_file_size_ =
std::max(min_max_manifest_file_size_,
last_compacted_manifest_file_size_ *
(100U + max_manifest_space_amp_pct_) / 100U);
}
void VersionSet::AppendVersion(ColumnFamilyData* column_family_data,
Version* v) {
// compute new compaction score
@@ -5787,11 +5686,10 @@ Status VersionSet::ProcessManifestWrites(
}
#endif // NDEBUG
uint64_t prev_manifest_file_size = manifest_file_size_;
assert(pending_manifest_file_number_ == 0);
if (!skip_manifest_write &&
(!descriptor_log_ ||
prev_manifest_file_size >= tuned_max_manifest_file_size_)) {
manifest_file_size_ > db_options_->max_manifest_file_size)) {
TEST_SYNC_POINT("VersionSet::ProcessManifestWrites:BeforeNewManifest");
new_descriptor_log = true;
} else {
@@ -5831,8 +5729,6 @@ Status VersionSet::ProcessManifestWrites(
IOStatus manifest_io_status;
manifest_io_status.PermitUncheckedError();
std::unique_ptr<log::Writer> new_desc_log_ptr;
// Save before releasing mu
uint64_t manifest_preallocation_size = manifest_preallocation_size_;
if (skip_manifest_write) {
if (s.ok()) {
constexpr bool update_stats = true;
@@ -5876,13 +5772,16 @@ Status VersionSet::ProcessManifestWrites(
// This is fine because everything inside of this block is serialized --
// only one thread can be here at the same time
// create new manifest file
ROCKS_LOG_INFO(db_options_->info_log, "Creating manifest %" PRIu64 "\n",
pending_manifest_file_number_);
std::string descriptor_fname =
DescriptorFileName(dbname_, pending_manifest_file_number_);
std::unique_ptr<FSWritableFile> descriptor_file;
io_s = NewWritableFile(fs_.get(), descriptor_fname, &descriptor_file,
opt_file_opts);
if (io_s.ok()) {
descriptor_file->SetPreallocationBlockSize(manifest_preallocation_size);
descriptor_file->SetPreallocationBlockSize(
db_options_->manifest_preallocation_size);
FileTypeSet tmp_set = db_options_->checksum_handoff_file_types;
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
std::move(descriptor_file), descriptor_fname, opt_file_opts, clock_,
@@ -5983,13 +5882,6 @@ Status VersionSet::ProcessManifestWrites(
if (s.ok()) {
// find offset in manifest file where this version is stored.
new_manifest_file_size = raw_desc_log_ptr->file()->GetFileSize();
if (new_descriptor_log) {
ROCKS_LOG_INFO(db_options_->info_log,
"Created manifest %" PRIu64
", compacted+appended from %" PRIu64 " to %" PRIu64 "\n",
pending_manifest_file_number_, prev_manifest_file_size,
new_manifest_file_size);
}
}
if (first_writer.edit_list.front()->IsColumnFamilyDrop()) {
@@ -6038,8 +5930,6 @@ Status VersionSet::ProcessManifestWrites(
descriptor_log_ = std::move(new_desc_log_ptr);
obsolete_manifests_.emplace_back(
DescriptorFileName("", manifest_file_number_));
last_compacted_manifest_file_size_ = new_manifest_file_size;
TuneMaxManifestFileSize();
}
// Install the new versions
@@ -6673,16 +6563,14 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
const ReadOptions read_options;
const WriteOptions write_options;
ImmutableDBOptions imm_db_options(*options);
MutableDBOptions mutable_db_options(*options);
ImmutableDBOptions db_options(*options);
ColumnFamilyOptions cf_options(*options);
std::shared_ptr<Cache> tc(NewLRUCache(options->max_open_files - 10,
options->table_cache_numshardbits));
WriteController wc(options->delayed_write_rate);
WriteBufferManager wb(options->db_write_buffer_size);
VersionSet versions(dbname, &imm_db_options, mutable_db_options, file_options,
tc.get(), &wb, &wc, nullptr /*BlockCacheTracer*/,
nullptr /*IOTracer*/,
VersionSet versions(dbname, &db_options, file_options, tc.get(), &wb, &wc,
nullptr /*BlockCacheTracer*/, nullptr /*IOTracer*/,
/*db_id*/ "",
/*db_session_id*/ "", options->daily_offpeak_time_utc,
/*error_handler_*/ nullptr, /*unchanging=*/false);
@@ -7734,13 +7622,12 @@ Status VersionSet::VerifyFileMetadata(const ReadOptions& read_options,
}
ReactiveVersionSet::ReactiveVersionSet(
const std::string& dbname, const ImmutableDBOptions* imm_db_options,
const MutableDBOptions& mutable_db_options,
const std::string& dbname, const ImmutableDBOptions* _db_options,
const FileOptions& _file_options, Cache* table_cache,
WriteBufferManager* write_buffer_manager, WriteController* write_controller,
const std::shared_ptr<IOTracer>& io_tracer)
: VersionSet(dbname, imm_db_options, mutable_db_options, _file_options,
table_cache, write_buffer_manager, write_controller,
: VersionSet(dbname, _db_options, _file_options, table_cache,
write_buffer_manager, write_controller,
/*block_cache_tracer=*/nullptr, io_tracer, /*db_id*/ "",
/*db_session_id*/ "", /*daily_offpeak_time_utc*/ "",
/*error_handler=*/nullptr, /*unchanging=*/false) {}
+5 -45
View File
@@ -1044,10 +1044,6 @@ class Version {
void GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta);
// Get column family metadata with optional filtering by key range and level.
void GetColumnFamilyMetaData(const GetColumnFamilyMetaDataOptions& options,
ColumnFamilyMetaData* cf_meta);
void GetSstFilesBoundaryKeys(Slice* smallest_user_key,
Slice* largest_user_key);
@@ -1190,9 +1186,7 @@ class AtomicGroupReadBuffer {
// but false for secondary instance or writable DB).
class VersionSet {
public:
VersionSet(const std::string& dbname,
const ImmutableDBOptions* imm_db_options,
const MutableDBOptions& mutable_db_options,
VersionSet(const std::string& dbname, const ImmutableDBOptions* db_options,
const FileOptions& file_options, Cache* table_cache,
WriteBufferManager* write_buffer_manager,
WriteController* write_controller,
@@ -1209,13 +1203,6 @@ class VersionSet {
virtual Status Close(FSDirectory* db_dir, InstrumentedMutex* mu);
// Requires: already holding DB mutex `mu`, to ensure
// * Safely read values from `updated_options`
// * Safely update fields on `this` (must be read elsewhere while holding mu)
// except `mu` can be nullptr during initialization
void UpdatedMutableDbOptions(const MutableDBOptions& updated_options,
InstrumentedMutex* mu);
Status LogAndApplyToDefaultColumnFamily(
const ReadOptions& read_options, const WriteOptions& write_options,
VersionEdit* edit, InstrumentedMutex* mu,
@@ -1561,6 +1548,10 @@ class VersionSet {
}
const FileOptions& file_options() { return file_options_; }
void ChangeFileOptions(const MutableDBOptions& new_options) {
file_options_.writable_file_max_buffer_size =
new_options.writable_file_max_buffer_size;
}
// TODO - Consider updating together when file options change in SetDBOptions
const OffpeakTimeOption& offpeak_time_option() {
@@ -1599,16 +1590,6 @@ class VersionSet {
bool& TEST_unchanging() { return const_cast<bool&>(unchanging_); }
uint64_t TEST_GetMinMaxManifestFileSize() {
return min_max_manifest_file_size_;
}
unsigned TEST_GetMaxManifestSpaceAmpPct() {
return max_manifest_space_amp_pct_;
}
size_t TEST_GetManifestPreallocationSize() {
return manifest_preallocation_size_;
}
protected:
struct ManifestWriter;
@@ -1629,7 +1610,6 @@ class VersionSet {
}
};
// Revert back to a post-construction state (keep same options/settings)
void Reset();
// Returns approximated offset of a key in a file for a given version.
@@ -1668,11 +1648,6 @@ class VersionSet {
ColumnFamilyData* cfd, const std::string& fpath,
int level, const FileMetaData& meta);
// Auto-tune next max size for the current manifest file based on its initial
// "compacted" size and other parameters saved in this VersionSet. Must be
// holding DB mutex if outside of DB startup.
void TuneMaxManifestFileSize();
// Protected by DB mutex.
WalSet wals_;
@@ -1724,20 +1699,6 @@ class VersionSet {
// Current size of manifest file
uint64_t manifest_file_size_;
// Size of the populated manifest file last time it was re-written from
// scratch.
uint64_t last_compacted_manifest_file_size_;
// Auto-tuned max allowed size for the current manifest file
uint64_t tuned_max_manifest_file_size_;
// Saved copy of max_manifest_file_size in (Mutable)DBOptions
uint64_t min_max_manifest_file_size_;
// Saved, sanitized copy from (Mutable)DBOptions
unsigned max_manifest_space_amp_pct_;
// Saved copy from (Mutable)DBOptions
size_t manifest_preallocation_size_;
// Obsolete files, or during DB shutdown any files not referenced by what's
// left of the in-memory LSM state.
std::vector<ObsoleteFileInfo> obsolete_files_;
@@ -1790,7 +1751,6 @@ class ReactiveVersionSet : public VersionSet {
public:
ReactiveVersionSet(const std::string& dbname,
const ImmutableDBOptions* _db_options,
const MutableDBOptions& mutable_db_options,
const FileOptions& _file_options, Cache* table_cache,
WriteBufferManager* write_buffer_manager,
WriteController* write_controller,
+39 -41
View File
@@ -1159,12 +1159,12 @@ class VersionSetTestBase {
: env_(nullptr),
dbname_(test::PerThreadDBPath(name)),
options_(),
imm_db_options_(options_),
db_options_(options_),
cf_options_(options_),
immutable_options_(imm_db_options_, cf_options_),
immutable_options_(db_options_, cf_options_),
mutable_cf_options_(cf_options_),
table_cache_(NewLRUCache(50000, 16)),
write_buffer_manager_(imm_db_options_.db_write_buffer_size),
write_buffer_manager_(db_options_.db_write_buffer_size),
shutting_down_(false),
table_factory_(std::make_shared<mock::MockTableFactory>()) {
EXPECT_OK(test::CreateEnvFromSystem(ConfigOptions(), &env_, &env_guard_));
@@ -1178,8 +1178,8 @@ class VersionSetTestBase {
EXPECT_OK(fs_->CreateDirIfMissing(dbname_, IOOptions(), nullptr));
options_.env = env_;
imm_db_options_.env = env_;
imm_db_options_.fs = fs_;
db_options_.env = env_;
db_options_.fs = fs_;
immutable_options_.env = env_;
immutable_options_.fs = fs_;
immutable_options_.clock = env_->GetSystemClock().get();
@@ -1188,17 +1188,16 @@ class VersionSetTestBase {
mutable_cf_options_.table_factory = table_factory_;
versions_.reset(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
reactive_versions_ = std::make_shared<ReactiveVersionSet>(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
nullptr);
imm_db_options_.db_paths.emplace_back(dbname_,
std::numeric_limits<uint64_t>::max());
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_, nullptr);
db_options_.db_paths.emplace_back(dbname_,
std::numeric_limits<uint64_t>::max());
}
virtual ~VersionSetTestBase() {
@@ -1221,7 +1220,7 @@ class VersionSetTestBase {
ASSERT_OK(
SetIdentityFile(WriteOptions(), env_, dbname_, Temperature::kUnknown));
VersionEdit new_db;
if (imm_db_options_.write_dbid_to_manifest) {
if (db_options_.write_dbid_to_manifest) {
DBOptions tmp_db_options;
tmp_db_options.env = env_;
std::unique_ptr<DBImpl> impl(new DBImpl(tmp_db_options, dbname_));
@@ -1382,8 +1381,8 @@ class VersionSetTestBase {
void ReopenDB() {
versions_.reset(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
@@ -1472,8 +1471,7 @@ class VersionSetTestBase {
const std::string dbname_;
EnvOptions env_options_;
Options options_;
ImmutableDBOptions imm_db_options_;
MutableDBOptions mutable_db_options_;
ImmutableDBOptions db_options_;
ColumnFamilyOptions cf_options_;
ImmutableOptions immutable_options_;
MutableCFOptions mutable_cf_options_;
@@ -1904,8 +1902,8 @@ TEST_F(VersionSetTest, WalAddition) {
// Recover a new VersionSet.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -1972,8 +1970,8 @@ TEST_F(VersionSetTest, WalCloseWithoutSync) {
// Recover a new VersionSet.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -2026,8 +2024,8 @@ TEST_F(VersionSetTest, WalDeletion) {
// Recover a new VersionSet, only the non-closed WAL should show up.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -2065,8 +2063,8 @@ TEST_F(VersionSetTest, WalDeletion) {
// Recover from the new MANIFEST, only the non-closed WAL should show up.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -2186,8 +2184,8 @@ TEST_F(VersionSetTest, DeleteWalsBeforeNonExistingWalNumber) {
// Recover a new VersionSet, WAL0 is deleted, WAL1 is not.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -2223,8 +2221,8 @@ TEST_F(VersionSetTest, DeleteAllWals) {
// Recover a new VersionSet, all WALs are deleted.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -2266,8 +2264,8 @@ TEST_F(VersionSetTest, AtomicGroupWithWalEdits) {
// kept.
{
std::unique_ptr<VersionSet> new_versions(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -2446,8 +2444,8 @@ class VersionSetWithTimestampTest : public VersionSetTest {
void VerifyFullHistoryTsLow(uint64_t expected_ts_low) {
std::unique_ptr<VersionSet> vset(new VersionSet(
dbname_, &imm_db_options_, mutable_db_options_, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*unchanging=*/false));
@@ -3502,7 +3500,7 @@ class VersionSetTestEmptyDb
std::unique_ptr<log::Writer>* log_writer) override {
assert(nullptr != log_writer);
VersionEdit new_db;
if (imm_db_options_.write_dbid_to_manifest) {
if (db_options_.write_dbid_to_manifest) {
ASSERT_OK(SetIdentityFile(WriteOptions(), env_, dbname_,
Temperature::kUnknown));
DBOptions tmp_db_options;
@@ -3534,7 +3532,7 @@ class VersionSetTestEmptyDb
const std::string VersionSetTestEmptyDb::kUnknownColumnFamilyName = "unknown";
TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest0) {
imm_db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
PrepareManifest(nullptr, nullptr, &log_writer_);
log_writer_.reset();
CreateCurrentFile();
@@ -3566,7 +3564,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest0) {
}
TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest1) {
imm_db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
PrepareManifest(nullptr, nullptr, &log_writer_);
// Only a subset of column families in the MANIFEST.
VersionEdit new_cf1;
@@ -3607,7 +3605,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest1) {
}
TEST_P(VersionSetTestEmptyDb, OpenFromInCompleteManifest2) {
imm_db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
PrepareManifest(nullptr, nullptr, &log_writer_);
// Write all column families but no log_number, next_file_number and
// last_sequence.
@@ -3653,7 +3651,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromInCompleteManifest2) {
}
TEST_P(VersionSetTestEmptyDb, OpenManifestWithUnknownCF) {
imm_db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
PrepareManifest(nullptr, nullptr, &log_writer_);
// Write all column families but no log_number, next_file_number and
// last_sequence.
@@ -3710,7 +3708,7 @@ TEST_P(VersionSetTestEmptyDb, OpenManifestWithUnknownCF) {
}
TEST_P(VersionSetTestEmptyDb, OpenCompleteManifest) {
imm_db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
PrepareManifest(nullptr, nullptr, &log_writer_);
// Write all column families but no log_number, next_file_number and
// last_sequence.
@@ -3830,7 +3828,7 @@ class VersionSetTestMissingFiles : public VersionSetTestBase,
ASSERT_OK(s);
log_writer->reset(new log::Writer(std::move(file_writer), 0, false));
VersionEdit new_db;
if (imm_db_options_.write_dbid_to_manifest) {
if (db_options_.write_dbid_to_manifest) {
DBOptions tmp_db_options;
tmp_db_options.env = env_;
std::unique_ptr<DBImpl> impl(new DBImpl(tmp_db_options, dbname_));
@@ -4090,7 +4088,7 @@ TEST_F(VersionSetTestMissingFiles, NoFileMissing) {
}
TEST_F(VersionSetTestMissingFiles, MinLogNumberToKeep2PC) {
imm_db_options_.allow_2pc = true;
db_options_.allow_2pc = true;
NewDB();
SstInfo sst(100, kDefaultColumnFamilyName, "a", 0 /* level */,
+2 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
@@ -23,8 +23,7 @@ class OfflineManifestWriter {
immutable_db_options_(WithDbPath(options, db_path)),
tc_(NewLRUCache(1 << 20 /* capacity */,
options.table_cache_numshardbits)),
versions_(db_path, &immutable_db_options_, MutableDBOptions{options},
sopt_, tc_.get(), &wb_, &wc_,
versions_(db_path, &immutable_db_options_, sopt_, tc_.get(), &wb_, &wc_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"",
options.daily_offpeak_time_utc,
+2 -2
View File
@@ -50,8 +50,8 @@ class WalManagerTest : public testing::Test {
db_options_.clock = env_->GetSystemClock().get();
versions_.reset(new VersionSet(
dbname_, &db_options_, MutableDBOptions{}, env_options_,
table_cache_.get(), &write_buffer_manager_, &write_controller_,
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
/*error_handler=*/nullptr, /*read_only=*/false));
+1 -1
View File
@@ -179,7 +179,7 @@ class WriteThread {
PostMemTableCallback* _post_memtable_callback = nullptr,
bool _ingest_wbwi = false)
: batch(_batch),
// TODO: store a copy of WriteOptions instead of its separated data
// TODO: store a copy of WriteOptions instead of its seperated data
// members
sync(write_options.sync),
no_slowdown(write_options.no_slowdown),
-1
View File
@@ -2,7 +2,6 @@ add_executable(db_stress${ARTIFACT_SUFFIX}
batched_ops_stress.cc
cf_consistency_stress.cc
db_stress.cc
db_stress_compaction_service.cc
db_stress_compression_manager.cc
db_stress_common.cc
db_stress_driver.cc
+32 -247
View File
@@ -13,7 +13,6 @@
#include <cmath>
#include "file/file_util.h"
#include "rocksdb/secondary_cache.h"
#include "util/file_checksum_helper.h"
#include "util/xxhash.h"
@@ -229,250 +228,14 @@ void CompressedCacheSetCapacityThread(void* v) {
}
}
#ifndef NDEBUG
static void SetupFaultInjectionForRemoteCompaction(SharedState* shared) {
if (!fault_fs_guard) {
return;
}
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kRead, shared->GetSeed(), FLAGS_read_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(FaultInjectionIOType::kRead);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kWrite, shared->GetSeed(), FLAGS_write_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(FaultInjectionIOType::kWrite);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataRead, shared->GetSeed(),
FLAGS_metadata_read_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataWrite, shared->GetSeed(),
FLAGS_metadata_write_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
}
#endif // NDEBUG
static CompactionServiceOptionsOverride CreateOverrideOptions(
const Options& options, const CompactionServiceJobInfo& job_info) {
CompactionServiceOptionsOverride override_options{
.env = db_stress_env,
.file_checksum_gen_factory = options.file_checksum_gen_factory,
.merge_operator = options.merge_operator,
.compaction_filter = options.compaction_filter,
.compaction_filter_factory = options.compaction_filter_factory,
.prefix_extractor = options.prefix_extractor,
.sst_partitioner_factory = options.sst_partitioner_factory,
.listeners = options.listeners,
.statistics = options.statistics,
.table_properties_collector_factories =
options.table_properties_collector_factories};
// TODO(jaykorean) - create a new compaction filter / merge operator and
// others for remote compactions
//
// Create a new Table Factory
ConfigOptions config_options;
config_options.ignore_unknown_options = false;
config_options.ignore_unsupported_options = false;
Status s = TableFactory::CreateFromString(config_options,
options.table_factory->Name(),
&override_options.table_factory);
if (s.ok()) {
std::string options_str;
s = options.table_factory->GetOptionString(config_options, &options_str);
if (s.ok()) {
s = override_options.table_factory->ConfigureFromString(config_options,
options_str);
}
}
if (!s.ok()) {
fprintf(stdout,
"Failed to set up TableFactory for remote compaction - (%s): %s\n",
job_info.db_name.c_str(), s.ToString().c_str());
}
return override_options;
}
static Status CleanupOutputDirectory(const std::string& output_directory) {
#ifndef NDEBUG
// Temporarily disable fault injection to ensure deletion always succeeds
if (fault_fs_guard) {
fault_fs_guard->DisableAllThreadLocalErrorInjection();
}
#endif // NDEBUG
Status s = DestroyDir(db_stress_env, output_directory);
if (!s.ok()) {
fprintf(stderr,
"Failed to destroy output directory %s when allow_resumption is "
"false: %s\n",
output_directory.c_str(), s.ToString().c_str());
}
if (s.ok()) {
s = db_stress_env->CreateDir(output_directory);
if (!s.ok()) {
fprintf(stderr,
"Failed to recreate output directory %s when allow_resumption is "
"false: %s\n",
output_directory.c_str(), s.ToString().c_str());
}
}
#ifndef NDEBUG
// Re-enable fault injection after deletion
if (fault_fs_guard) {
fault_fs_guard->EnableAllThreadLocalErrorInjection();
}
#endif // NDEBUG
return s;
}
// Set up cancellation mechanism for testing resumable remote compactions.
// Spawns a detached thread to trigger cancellation after a delay (50ms
// initially, or 2/3 of the previous successful compaction time for adaptive
// timing). First-time jobs are always canceled; retries have a 10% chance
// to test consecutive cancellation scenarios.
static std::shared_ptr<std::atomic<bool>> SetupCancellation(
OpenAndCompactOptions& open_compact_options, bool was_canceled,
Random& rand, uint64_t successful_compaction_end_to_end_micros) {
auto canceled = std::make_shared<std::atomic<bool>>(false);
open_compact_options.canceled = canceled.get();
bool should_cancel = !was_canceled || rand.OneIn(10);
if (should_cancel) {
std::thread interruption_thread(
[canceled, successful_compaction_end_to_end_micros]() {
uint64_t sleep_micros =
successful_compaction_end_to_end_micros == 0
? 50000
: successful_compaction_end_to_end_micros * 2 / 3;
std::this_thread::sleep_for(std::chrono::microseconds(sleep_micros));
canceled->store(true);
});
interruption_thread.detach();
}
return canceled;
}
// Process the result of OpenAndCompact operation
static void ProcessCompactionResult(
const Status& s, const std::string& job_id,
const CompactionServiceJobInfo& job_info,
const std::string& serialized_input, const std::string& output_directory,
const std::string& serialized_output, SharedState* shared,
uint64_t& successful_compaction_end_to_end_micros, uint64_t start_micros,
Env* env) {
if (s.IsManualCompactionPaused() && FLAGS_allow_resumption_one_in > 0) {
// Re-enqueue for retry
shared->EnqueueRemoteCompaction(job_id, job_info, serialized_input,
output_directory, true /* was_cancelled */);
return;
}
if (!s.ok()) {
if (!StressTest::IsErrorInjectedAndRetryable(s)) {
// Print in stdout instead of stderr to avoid stress test failure,
// because OpenAndCompact() failure doesn't necessarily mean
// primary db instance failure.
fprintf(stdout, "Failed to run OpenAndCompact(%s): %s\n",
job_info.db_name.c_str(), s.ToString().c_str());
}
} else {
// Track successful completion time
successful_compaction_end_to_end_micros = env->NowMicros() - start_micros;
}
// Add the output regardless of status, so that primary DB doesn't rely
// on the timeout to finish waiting. The actual failure from the
// deserialization can fail the compaction properly
shared->AddRemoteCompactionResult(job_id, s, serialized_output);
}
static void ProcessRemoteCompactionJob(
const std::string& job_id, const CompactionServiceJobInfo& job_info,
const std::string& serialized_input, const std::string& output_directory,
bool was_canceled, SharedState* shared, StressTest* stress_test,
Random& rand, uint64_t& successful_compaction_end_to_end_micros) {
auto options = stress_test->GetOptions(job_info.cf_id);
assert(options.env != nullptr);
auto override_options = CreateOverrideOptions(options, job_info);
OpenAndCompactOptions open_compact_options;
if (FLAGS_allow_resumption_one_in > 0) {
open_compact_options.allow_resumption =
rand.OneIn(FLAGS_allow_resumption_one_in);
} else {
open_compact_options.allow_resumption = false;
}
if (!open_compact_options.allow_resumption) {
CleanupOutputDirectory(output_directory);
}
std::shared_ptr<std::atomic<bool>> canceled = nullptr;
if (FLAGS_allow_resumption_one_in > 0) {
canceled = SetupCancellation(open_compact_options, was_canceled, rand,
successful_compaction_end_to_end_micros);
}
std::string serialized_output;
uint64_t start_micros = options.env->NowMicros();
Status s = DB::OpenAndCompact(open_compact_options, job_info.db_name,
output_directory, serialized_input,
&serialized_output, override_options);
ProcessCompactionResult(s, job_id, job_info, serialized_input,
output_directory, serialized_output, shared,
successful_compaction_end_to_end_micros, start_micros,
options.env);
}
void RemoteCompactionWorkerThread(void* v) {
assert(FLAGS_remote_compaction_worker_threads > 0);
assert(FLAGS_remote_compaction_worker_interval > 0);
auto* thread = static_cast<ThreadState*>(v);
SharedState* shared = thread->shared;
StressTest* stress_test = shared->GetStressTest();
assert(stress_test != nullptr);
#ifndef NDEBUG
SetupFaultInjectionForRemoteCompaction(shared);
#endif // NDEBUG
// Tracks the duration (in microseconds) of the most recent successfully
// completed compaction from start to finish. This value is used in
// SetupCancellation() to adaptively set up cancellation point for a
// compaction
uint64_t successful_compaction_end_to_end_micros = 0;
Random rand(static_cast<uint32_t>(FLAGS_seed));
// Main worker loop
while (true) {
// Check if we should stop
{
MutexLock l(shared->GetMutex());
if (shared->ShouldStopBgThread()) {
@@ -483,20 +246,42 @@ void RemoteCompactionWorkerThread(void* v) {
return;
}
}
std::string job_id;
CompactionServiceJobInfo job_info;
std::string serialized_input;
std::string output_directory;
bool was_canceled;
if (shared->DequeueRemoteCompaction(&job_id, &job_info, &serialized_input,
&output_directory, &was_canceled)) {
ProcessRemoteCompactionJob(
job_id, job_info, serialized_input, output_directory, was_canceled,
shared, stress_test, rand, successful_compaction_end_to_end_micros);
if (shared->DequeueRemoteCompaction(&job_id, &job_info,
&serialized_input)) {
auto options = stress_test->GetOptions(job_info.cf_id);
CompactionServiceOptionsOverride override_options{
.file_checksum_gen_factory = options.file_checksum_gen_factory,
.merge_operator = options.merge_operator,
.compaction_filter = options.compaction_filter,
.compaction_filter_factory = options.compaction_filter_factory,
.prefix_extractor = options.prefix_extractor,
.table_factory = options.table_factory,
.sst_partitioner_factory = options.sst_partitioner_factory,
.listeners = {},
.statistics = options.statistics,
.table_properties_collector_factories =
options.table_properties_collector_factories};
std::string tmp_output_dir = job_info.db_name + "/" + "tmp_output_" +
db_stress_env->GenerateUniqueId();
std::string serialized_output;
Status s = DB::OpenAndCompact(OpenAndCompactOptions{}, job_info.db_name,
tmp_output_dir, serialized_input,
&serialized_output, override_options);
if (!s.ok()) {
// Print in stdout instead of stderr to avoid stress test failure,
// because OpenAndCompact() failure doesn't necessarily mean
// primary db instance failure.
fprintf(stdout, "Failed to run OpenAndCompact(%s): %s\n",
job_info.db_name.c_str(), s.ToString().c_str());
}
// Add the output regardless of status, so that primary DB doesn't rely on
// the timeout to finish waiting. The actual failure from the
// deserialization can fail the compaction properly
shared->AddRemoteCompactionResult(job_id, serialized_output);
}
db_stress_env->SleepForMicroseconds(
thread->rand.Next() % FLAGS_remote_compaction_worker_interval * 1000 +
1);
-4
View File
@@ -248,7 +248,6 @@ DECLARE_string(fs_uri);
DECLARE_uint64(ops_per_thread);
DECLARE_uint64(log2_keys_per_lock);
DECLARE_uint64(max_manifest_file_size);
DECLARE_int32(max_manifest_space_amp_pct);
DECLARE_bool(in_place_update);
DECLARE_string(memtablerep);
DECLARE_int32(prefix_size);
@@ -411,8 +410,6 @@ DECLARE_uint64(max_sequential_skip_in_iterations);
DECLARE_bool(enable_sst_partitioner_factory);
DECLARE_bool(enable_do_not_compress_roles);
DECLARE_bool(block_align);
DECLARE_uint64(super_block_alignment_size);
DECLARE_uint64(super_block_alignment_space_overhead_ratio);
DECLARE_uint32(lowest_used_cache_tier);
DECLARE_bool(enable_custom_split_merge);
DECLARE_uint32(adm_policy);
@@ -430,7 +427,6 @@ DECLARE_bool(track_and_verify_wals);
DECLARE_int32(remote_compaction_worker_threads);
DECLARE_int32(remote_compaction_worker_interval);
DECLARE_bool(remote_compaction_failure_fall_back_to_local);
DECLARE_int32(allow_resumption_one_in);
DECLARE_bool(auto_refresh_iterator_with_snapshot);
DECLARE_uint32(memtable_op_scan_flush_trigger);
DECLARE_uint32(memtable_avg_op_scan_flush_trigger);
@@ -1,61 +0,0 @@
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#ifdef GFLAGS
#include "db_stress_tool/db_stress_compaction_service.h"
#include <string>
#include "db_stress_tool/db_stress_test_base.h"
#include "rocksdb/env.h"
namespace ROCKSDB_NAMESPACE {
CompactionServiceJobStatus DbStressCompactionService::Wait(
const std::string& scheduled_job_id, std::string* result) {
while (true) {
if (aborted_.load()) {
return CompactionServiceJobStatus::kAborted;
}
const auto& maybeResultStatus =
shared_->GetRemoteCompactionResult(scheduled_job_id, result);
if (maybeResultStatus.has_value()) {
auto s = maybeResultStatus.value();
if (s.ok()) {
assert(result);
assert(!result->empty());
return CompactionServiceJobStatus::kSuccess;
} else {
// Remote Compaction failed
if (failure_should_fall_back_to_local_) {
return CompactionServiceJobStatus::kUseLocal;
}
if (StressTest::IsErrorInjectedAndRetryable(s)) {
return CompactionServiceJobStatus::kUseLocal;
}
if (result && result->empty()) {
// If result is empty, set the compaction status in the result so
// that it can be bubbled up to main thread
CompactionServiceResult compaction_result;
compaction_result.status = s;
if (compaction_result.Write(result).ok()) {
assert(result);
assert(!result->empty());
}
}
return CompactionServiceJobStatus::kFailure;
}
} else {
// Remote Compaction is still running
Env::Default()->SleepForMicroseconds(kWaitIntervalInMicros);
}
}
return CompactionServiceJobStatus::kFailure;
}
} // namespace ROCKSDB_NAMESPACE
#endif // GFLAGS
+28 -17
View File
@@ -3,13 +3,11 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#ifdef GFLAGS
#pragma once
#include "db/compaction/compaction_job.h"
#include "db_stress_shared_state.h"
#include "db_stress_tool/db_stress_common.h"
#include "rocksdb/options.h"
#include "utilities/fault_injection_fs.h"
namespace ROCKSDB_NAMESPACE {
@@ -27,33 +25,47 @@ class DbStressCompactionService : public CompactionService {
const char* Name() const override { return kClassName(); }
static constexpr uint64_t kWaitIntervalInMicros = 10 * 1000; // 10ms
static constexpr const char* kTempOutputDirectoryPrefix = "tmp_output_";
static constexpr uint64_t kWaitTimeoutInMicros =
30 * 1000 * 1000; // 30 seconds
CompactionServiceScheduleResponse Schedule(
const CompactionServiceJobInfo& info,
const std::string& compaction_service_input) override {
std::string job_id = info.db_id + "_" + info.db_session_id + "_" +
std::to_string(info.job_id);
if (aborted_.load()) {
return CompactionServiceScheduleResponse(
job_id, CompactionServiceJobStatus::kUseLocal);
}
std::string output_directory = info.db_name + "/" +
kTempOutputDirectoryPrefix +
Env::Default()->GenerateUniqueId();
shared_->EnqueueRemoteCompaction(
job_id, info, compaction_service_input, output_directory,
false /* was_cancelled */); // Not canceled initially
shared_->EnqueueRemoteCompaction(job_id, info, compaction_service_input);
CompactionServiceScheduleResponse response(
job_id, CompactionServiceJobStatus::kSuccess);
return response;
}
CompactionServiceJobStatus Wait(const std::string& scheduled_job_id,
std::string* result) override;
std::string* result) override {
auto start = Env::Default()->NowMicros();
while (Env::Default()->NowMicros() - start < kWaitTimeoutInMicros) {
if (aborted_.load()) {
return CompactionServiceJobStatus::kUseLocal;
}
if (shared_->GetRemoteCompactionResult(scheduled_job_id, result).ok()) {
if (result && result->empty()) {
// Race: Remote worker aborted before client sets aborted_ = true
return CompactionServiceJobStatus::kUseLocal;
}
return CompactionServiceJobStatus::kSuccess;
}
Env::Default()->SleepForMicroseconds(kWaitIntervalInMicros);
}
if (failure_should_fall_back_to_local_) {
fprintf(stdout,
"Remote Compaction failed - fall back to local compaction!\n");
return CompactionServiceJobStatus::kUseLocal;
}
return CompactionServiceJobStatus::kFailure;
}
void OnInstallation(const std::string& scheduled_job_id,
CompactionServiceJobStatus /*status*/) override {
@@ -61,7 +73,7 @@ class DbStressCompactionService : public CompactionService {
std::string serialized;
CompactionServiceResult result;
if (shared_->GetRemoteCompactionResult(scheduled_job_id, &serialized)
.has_value()) {
.ok()) {
if (CompactionServiceResult::Read(serialized, &result).ok()) {
std::vector<std::string> filenames;
Status s = Env::Default()->GetChildren(result.output_path, &filenames);
@@ -88,6 +100,5 @@ class DbStressCompactionService : public CompactionService {
std::atomic_bool aborted_{false};
bool failure_should_fall_back_to_local_;
};
} // namespace ROCKSDB_NAMESPACE
#endif // GFLAGS
} // namespace ROCKSDB_NAMESPACE
+1 -21
View File
@@ -861,11 +861,6 @@ DEFINE_bool(remote_compaction_failure_fall_back_to_local, true,
"If true, remote compaction failures will be ignored and "
"compactions will fall back to local and retried");
DEFINE_int32(allow_resumption_one_in, 0,
"If non-zero, enable resumable compaction with 1/N probability "
"for each OpenAndCompact call.Requires "
"remote_compaction_worker_threads > 0");
DEFINE_uint32(ingest_wbwi_one_in, 0,
"If set, will call"
"IngestWriteBatchWithIndex() instead of regular write operations "
@@ -978,11 +973,7 @@ DEFINE_uint64(log2_keys_per_lock, 2, "Log2 of number of keys per lock");
static const bool FLAGS_log2_keys_per_lock_dummy __attribute__((__unused__)) =
RegisterFlagValidator(&FLAGS_log2_keys_per_lock, &ValidateUint32Range);
DEFINE_uint64(max_manifest_file_size, 16384,
"Maximum size of a MANIFEST file (without auto-tuning)");
DEFINE_int32(max_manifest_space_amp_pct, 500,
"Max manifest space amp percentage for auto-tuning");
DEFINE_uint64(max_manifest_file_size, 16384, "Maximum size of a MANIFEST file");
DEFINE_bool(in_place_update, false, "On true, does inplace update in memtable");
@@ -1441,17 +1432,6 @@ DEFINE_bool(block_align,
ROCKSDB_NAMESPACE::BlockBasedTableOptions().block_align,
"BlockBasedTableOptions.block_align");
DEFINE_uint64(
super_block_alignment_size,
ROCKSDB_NAMESPACE::BlockBasedTableOptions().super_block_alignment_size,
"BlockBasedTableOptions.super_block_alignment_size");
DEFINE_uint64(
super_block_alignment_space_overhead_ratio,
ROCKSDB_NAMESPACE::BlockBasedTableOptions()
.super_block_alignment_space_overhead_ratio,
"BlockBasedTableOptions.super_block_alignment_space_overhead_ratio");
DEFINE_uint32(
lowest_used_cache_tier,
static_cast<uint32_t>(ROCKSDB_NAMESPACE::Options().lowest_used_cache_tier),
+1 -6
View File
@@ -9,7 +9,6 @@
#include <mutex>
#include <unordered_set>
#include "db_stress_tool/db_stress_compaction_service.h"
#include "db_stress_tool/db_stress_shared_state.h"
#include "file/filename.h"
#include "file/writable_file_writer.h"
@@ -22,6 +21,7 @@
#include "util/gflags_compat.h"
#include "util/random.h"
#include "utilities/fault_injection_fs.h"
DECLARE_int32(compact_files_one_in);
extern std::shared_ptr<ROCKSDB_NAMESPACE::FaultInjectionTestFS> fault_fs_guard;
@@ -310,11 +310,6 @@ class DbStressListener : public EventListener {
}
}
}
// We can't do exact matching since remote workers use dynamic temp paths
if (file_dir.find(DbStressCompactionService::kTempOutputDirectoryPrefix) !=
std::string::npos) {
return;
}
assert(false);
#else
(void)file_dir;
+18 -46
View File
@@ -51,24 +51,6 @@ DECLARE_bool(enable_compaction_filter);
namespace ROCKSDB_NAMESPACE {
class StressTest;
struct RemoteCompactionQueueItem {
std::string job_id;
CompactionServiceJobInfo job_info;
std::string serialized_input;
std::string output_directory;
bool canceled;
RemoteCompactionQueueItem(const std::string& id,
const CompactionServiceJobInfo& info,
const std::string& input,
const std::string& output_dir, bool was_canceled)
: job_id(id),
job_info(info),
serialized_input(input),
output_directory(output_dir),
canceled(was_canceled) {}
};
// State shared by all concurrent executions of the same benchmark.
class SharedState {
public:
@@ -296,31 +278,23 @@ class SharedState {
void EnqueueRemoteCompaction(const std::string& job_id,
const CompactionServiceJobInfo& job_info,
const std::string& serialized_input,
const std::string& output_directory,
bool canceled) {
const std::string& serialized_input) {
MutexLock l(&remote_compaction_queue_mu_);
remote_compaction_queue_.emplace(job_id, job_info, serialized_input,
output_directory, canceled);
remote_compaction_queue_.emplace(job_id, job_info, serialized_input);
}
bool DequeueRemoteCompaction(std::string* job_id,
CompactionServiceJobInfo* job_info,
std::string* serialized_input,
std::string* output_directory, bool* canceled) {
std::string* serialized_input) {
assert(job_id);
assert(job_info);
assert(serialized_input);
assert(output_directory);
assert(canceled);
MutexLock l(&remote_compaction_queue_mu_);
if (!remote_compaction_queue_.empty()) {
const RemoteCompactionQueueItem& item = remote_compaction_queue_.front();
*job_id = item.job_id;
*job_info = item.job_info;
*serialized_input = item.serialized_input;
*output_directory = item.output_directory;
*canceled = item.canceled;
const auto [id, info, input] = remote_compaction_queue_.front();
*job_id = id;
*job_info = info;
*serialized_input = input;
remote_compaction_queue_.pop();
return true;
}
@@ -328,23 +302,20 @@ class SharedState {
}
void AddRemoteCompactionResult(const std::string& job_id,
const Status& status,
const std::string& result) {
MutexLock l(&remote_compaction_result_map_mu_);
remote_compaction_result_map_.emplace(
job_id, std::pair<Status, std::string>{status, result});
remote_compaction_result_map_.emplace(job_id, result);
}
std::optional<Status> GetRemoteCompactionResult(const std::string& job_id,
std::string* result) {
Status GetRemoteCompactionResult(const std::string& job_id,
std::string* result) {
MutexLock l(&remote_compaction_result_map_mu_);
if (remote_compaction_result_map_.find(job_id) !=
remote_compaction_result_map_.end()) {
const auto& pair = remote_compaction_result_map_.at(job_id);
*result = pair.second;
return pair.first;
*result = remote_compaction_result_map_.at(job_id);
return Status::OK();
}
return std::nullopt;
return Status::NotFound();
}
void RemoveRemoteCompactionResult(const std::string& job_id) {
@@ -506,14 +477,15 @@ class SharedState {
std::atomic<bool> verification_failure_;
std::atomic<bool> should_stop_test_;
// Queue for the remote compaction.
// Queue for the remote compaction. Tuple of job id, job info and serialized
// compaction_service_input
port::Mutex remote_compaction_queue_mu_;
std::queue<RemoteCompactionQueueItem> remote_compaction_queue_;
std::queue<std::tuple<std::string, CompactionServiceJobInfo, std::string>>
remote_compaction_queue_;
// Result Map for the remote compaciton. Key is the scheduled_job_id and value
// is serialized compaction_service_result
port::Mutex remote_compaction_result_map_mu_;
std::unordered_map<std::string, std::pair<Status, std::string>>
remote_compaction_result_map_;
std::unordered_map<std::string, std::string> remote_compaction_result_map_;
// Keys that should not be overwritten
const std::unordered_set<int64_t> no_overwrite_ids_;
+1 -19
View File
@@ -3675,21 +3675,8 @@ void StressTest::Open(SharedState* shared, bool reopen) {
"Compaction\n");
exit(1);
}
// Each DB open/reopen gets a fresh compaction service instance with a clean
// aborted_ state
auto compaction_service = std::make_shared<DbStressCompactionService>(
options_.compaction_service = std::make_shared<DbStressCompactionService>(
shared, FLAGS_remote_compaction_failure_fall_back_to_local);
options_.compaction_service = compaction_service;
}
if (FLAGS_allow_resumption_one_in > 0) {
if (FLAGS_remote_compaction_worker_threads == 0) {
fprintf(stderr,
"allow_resumption or randomize_allow_resumption requires "
"remote_compaction_worker_threads > 0\n");
exit(1);
}
}
if ((options_.enable_blob_files || options_.enable_blob_garbage_collection ||
@@ -4332,10 +4319,6 @@ void InitializeOptionsFromFlags(
static_cast<BlockBasedTableOptions::IndexShorteningMode>(
FLAGS_index_shortening);
block_based_options.block_align = FLAGS_block_align;
block_based_options.super_block_alignment_size =
fLU64::FLAGS_super_block_alignment_size;
block_based_options.super_block_alignment_space_overhead_ratio =
fLU64::FLAGS_super_block_alignment_space_overhead_ratio;
options.table_factory.reset(NewBlockBasedTableFactory(block_based_options));
options.db_write_buffer_size = FLAGS_db_write_buffer_size;
options.write_buffer_size = FLAGS_write_buffer_size;
@@ -4420,7 +4403,6 @@ void InitializeOptionsFromFlags(
options.compression_opts.checksum = true;
}
options.max_manifest_file_size = FLAGS_max_manifest_file_size;
options.max_manifest_space_amp_pct = FLAGS_max_manifest_space_amp_pct;
options.max_subcompactions = static_cast<uint32_t>(FLAGS_subcompactions);
options.allow_concurrent_memtable_write =
FLAGS_allow_concurrent_memtable_write;
+7 -8
View File
@@ -14,7 +14,6 @@
#include "db_stress_tool/db_stress_common.h"
#include "db_stress_tool/db_stress_shared_state.h"
#include "rocksdb/experimental.h"
#include "utilities/fault_injection_fs.h"
namespace ROCKSDB_NAMESPACE {
class SystemClock;
@@ -26,13 +25,6 @@ using experimental::SstQueryFilterConfigsManager;
class StressTest {
public:
static bool IsErrorInjectedAndRetryable(const Status& error_s) {
assert(!error_s.ok());
return error_s.getState() &&
FaultInjectionTestFS::IsInjectedError(error_s) &&
!status_to_io_status(Status(error_s)).GetDataLoss();
}
StressTest();
virtual ~StressTest() {}
@@ -358,6 +350,13 @@ class StressTest {
return Status::NotSupported("TestCustomOperations() must be overridden");
}
bool IsErrorInjectedAndRetryable(const Status& error_s) const {
assert(!error_s.ok());
return error_s.getState() &&
FaultInjectionTestFS::IsInjectedError(error_s) &&
!status_to_io_status(Status(error_s)).GetDataLoss();
}
void ProcessStatus(SharedState* shared, std::string msg, const Status& s,
bool ignore_injected_error = true) const;
+4 -4
View File
@@ -253,20 +253,20 @@ class PendingExpectedValue {
class ExpectedValueHelper {
public:
// Return whether the key associated with `pre_read_expected_value` and
// `post_read_expected_value` is expected not to exist from beginning till the
// `post_read_expected_value` is expected not to exist from begining till the
// end of the read
//
// The negation of `MustHaveNotExisted()` is "may have not existed".
// To assert some key must have existed, please use `MustHaveExisted()`
// To assert some key must have existsed, please use `MustHaveExisted()`
static bool MustHaveNotExisted(ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value);
// Return whether the key associated with `pre_read_expected_value` and
// `post_read_expected_value` is expected to exist from beginning till the end
// `post_read_expected_value` is expected to exist from begining till the end
// of the read.
//
// The negation of `MustHaveExisted()` is "may have existed".
// To assert some key must have not existed, please use
// To assert some key must have not existsed, please use
// `MustHaveNotExisted()`
static bool MustHaveExisted(ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value);
@@ -1,59 +0,0 @@
---
title: Unified Memory Tracking
layout: post
author: hx235
category: blog
---
## Context / Problem
Modern RocksDB deployments often run in environments with strict memory constraints—cloud VMs, containers, or hosts with hundreds of DB instances. Unpredictable memory usage can lead to out-of-memory (OOM) errors, degraded performance, or even service outages.
Historically, while the block cache was the main source of memory usage, other components—such as memtables, table readers, file metadata, and temporary buffers—could consume significant memory outside the block cache’s control. This made it difficult for users to set a single memory limit and guarantee resource usage stays within expectations.
## Goal
The goal of recent memory tracking work in RocksDB is to enable users to cap the total memory usage of RocksDB instances under a single, configurable limit—the block cache capacity. This is achieved by:
- **Tracking and charging** all major memory consumers (memtables, table readers, file metadata, compression buffers, filter construction) to the block cache.
- **Evicting** data blocks or other memory when the total tracked usage exceeds the configured limit.
- **Providing a fixed memory footprint** for RocksDB, making it easier to run in resource-constrained environments and avoid OOMs.
## Memtable Memory Charging
A major source of memory usage in RocksDB is the memtable. To ensure memtable memory is tracked and capped under a single limit, RocksDB provides the WriteBufferManager (WBM). When WBM is configured with a block cache, memtable memory usage is charged to the block cache. This helps prevent OOM errors and simplifies resource management.
```cpp
std::shared_ptr<Cache> cache = HyperClockCacheOptions(capacity).MakeSharedCache();;
DBOptions db_options;
db_options.write_buffer_manager = std::make_shared<WriteBufferManager>(.., cache);
```
## Other Memory Charging
Beyond memtables, RocksDB allows users to control memory charging for other internal roles using the cache_usage_options API. This provides fine-grained control over how memory is tracked for components like table readers, file metadata, compression dictionary buffers (`CompressionOptions::max_dict_buffer_bytes:`) and filter construction.
```cpp
struct CacheEntryRoleOptions {
enum class Decision {
kEnabled,
kDisabled,
kFallback,
};
Decision charged = Decision::kFallback;
};
struct CacheUsageOptions {
CacheEntryRoleOptions options;
std::map<CacheEntryRole, CacheEntryRoleOptions> options_overrides;
};
...
BlockBasedTableOptions table_options;
table_options.cache_usage_options.options.charged = CacheEntryRoleOptions::Decision::kFallback;
table_options.cache_usage_options.options_overrides[CacheEntryRole::kTableBuilder] = {
.charged = CacheEntryRoleOptions::Decision::kEnabled,
};
```
Default (`Decision::kFallback`) behavior for each memory type:
- `CacheEntryRole::kCompressionDictionaryBuildingBuffer`: `kEnabled`
- `CacheEntryRole::kFilterConstruction`: `kDisabled`
- `CacheEntryRole::kBlockBasedTableReader`: `kDisabled`
- `CacheEntryRole::kFileMetadata`: `kDisabled`
## Monitoring and Observability
RocksDB provides built-in statistics to help users monitor memory usage and cache behavior. The `DB::Properties::kBlockCacheEntryStats` exposes detailed statistics about block cache entries, including breakdowns by each `CacheEntryRole`. These statistics are essential for understanding memory consumption and tuning cache configuration.
@@ -1,74 +0,0 @@
---
title: IO Activity Tagging
layout: post
author: hx235
category: blog
---
## Context
RocksDB performs a variety of IO operations—user reads, background compactions, flushes, database opens, and verification tasks. Treating all these operations the same makes it difficult for file system implementers to optimize performance, prioritize latency-sensitive IOs, and diagnose bottlenecks. To solve that, RocksDB internally tags every IO operation with its activity type using the `IOActivity` enum. This automatic tagging provides precise context for each IO, enabling file systems to make smarter, context-aware decisions for scheduling, caching, and resource management.
## How Internal IO Tagging Works
RocksDB automatically assigns an `IOActivity` tag to each IO operation. This tag is propagated through the storage stack and included in the IO options passed to the file system.
```cpp
enum class IOActivity : uint8_t {
kFlush = 0, // IO for flush operations (background write)
kCompaction = 1, // IO for compaction (background read/write)
kDBOpen = 2, // IO during database open (read/write)
kGet = 3, // User Get() read
kMultiGet = 4, // User MultiGet() read
kDBIterator = 5, // User iterator read
kVerifyDBChecksum = 6, // Verification: DB checksum
kVerifyFileChecksums = 7, // Verification: file checksums
kGetEntity = 8, // Entity Get (e.g., wide-column)
kMultiGetEntity = 9, // Entity MultiGet
kGetFileChecksumsFromCurrentManifest = 10, // Manifest checksum reads
// 0x80–0xFE: Reserved for custom/internal use
kUnknown = 0xFF // Unknown/unspecified activity
};
```
## Access IO Tag in File System
Custom file systems can access the IOActivity tag via the IO options structure provided by RocksDB. This allows them to optimize behavior based on the specific IO activity.
```cpp
Status CustomFileSystem::Append(uint64_t offset, const Slice& data, const IOOptions& io_opts, ...) {
switch (io_opts.io_activity) {
case Env::IOActivity::kGet:
// Prioritize or cache user reads
break;
case Env::IOActivity::kCompaction:
// Throttle or deprioritize background compaction IO
break;
case Env::IOActivity::kDBOpen:
// Track or optimize DB open IO
break;
// ... handle other activities ...
default:
// Default handling
break;
}
}
```
## IO Activity Statistics in RocksDB
RocksDB provides detailed histograms for IO activities, allowing you to analyze both the aggregate time spent (in microseconds) and the count of IOs for each activity type.
```cpp
// Read Histograms
FILE_READ_FLUSH_MICROS
FILE_READ_COMPACTION_MICROS
FILE_READ_DB_OPEN_MICROS
FILE_READ_GET_MICROS
FILE_READ_MULTIGET_MICROS
FILE_READ_DB_ITERATOR_MICROS
FILE_READ_VERIFY_DB_CHECKSUM_MICROS
FILE_READ_VERIFY_FILE_CHECKSUMS_MICROS
// Write Histograms
FILE_WRITE_FLUSH_MICROS
FILE_WRITE_COMPACTION_MICROS
FILE_WRITE_DB_OPEN_MICROS
```
Thanks to Maciej Szeszko and Andrew Chang from the RocksDB team for their contributions in expanding and maintaining the IOActivity enum.
@@ -1,89 +0,0 @@
---
title: "Parallel Compression Revamp: Dramatically Reduced CPU Overhead"
layout: post
author: peterd
category: blog
---
The upcoming RocksDB 10.7 release includes a major revamp of parallel compression that **dramatically reduces the feature's CPU overhead by up to 65%** while maintaining or improving throughput for compression-heavy workloads. We expect this to broaden the set of workloads that could benefit from parallel compression, especially for **bulk SST generation and remote compaction use cases** that are less sensitive to CPU responsiveness.
## Background
Parallel compression in RocksDB (`CompressionOptions::parallel_threads > 1`) allows multiple threads to compress different blocks simultaneously during SST file generation, which can significantly improve compaction throughput for workloads where compression is a bottleneck. However, the original implementation had substantial CPU overhead that often outweighed the benefits, limiting its practical adoption.
## What's New: A Complete Reimplementation
The parallel compression framework has been completely rewritten from the ground up in [pull request #13910](https://github.com/facebook/rocksdb/pull/13910) to address the core inefficiencies:
### Ring Buffer Architecture
Instead of separate compression and write queues with complex thread coordination, the new implementation uses a ring buffer of blocks-in-progress that enables efficient work distribution across threads. This bounds working memory while enabling high throughput with minimal cross-thread synchronization.
![Ring Buffer Architecture](/static/images/parallel-compression/ring-buffer-architecture.svg)
### Work-Stealing Design
Previously, the calling thread could only generate uncompressed blocks, dedicated compression threads could only compress, and a writer thread could only write the SST file to storage. Now, all threads can participate in compression work in a quasi-work-stealing manner, dramatically reducing the need for threads to block waiting for work. While only one thread (the calling thread or "emit thread") can generate uncompressed SST blocks in the new implementation, feeding compression work to other threads and itself, all other threads are compatible with writing compressed blocks to storage.
### Auto-Scaling Thread Management
The ring buffer enables another key feature: auto-scaling of active threads based on ring buffer utilization. The framework intelligently wakes up idle worker threads only when there's sufficient work to justify the overhead, achieving near-maximum throughput while minimizing CPU waste from unnecessary thread wake-ups.
### Lock-Free Synchronization
The entire framework is now lock-free (and wait-free as long as compatible work units are available for each thread), based primarily on atomic operations. To cleanly pack and leverage many data fields into a single atomic value, I've developed a new `BitFields` utility API. This is proving useful for cleaning up the HyperClockCache implementation as well, and will be the topic of a later blog post.
Semaphores are used for lock-free management of idle threads (assuming a lock-free semaphore implementation, which is likely the case with `ROCKSDB_USE_STD_SEMAPHORES` but that is untrustworthy; see below).
## Performance Improvements
The results speak for themselves. Here's a comparison using `db_bench` fillseq benchmarks with various compression configurations:
### ZSTD Compression (Default Level)
Note:
* "throughput" = how quickly a given CPU-bound flush or compaction can complete
* "CPU increase" = total CPU usage in amount of time that each core was used
* "PT" = parallel_threads setting.
**Before:**
- PT=3: ~38% throughput increase for ~73% CPU increase
- PT=6: No throughput increase for ~70% CPU increase
**After:**
- PT=3: ~58% throughput increase for ~25% CPU increase
- PT=6: ~58% throughput increase for ~28% CPU increase
### High Compression Scenarios
For ZSTD compression level 8, the improvements are even more dramatic:
**Before:**
- PT=4: 2.6x throughput increase for 139% CPU increase
- PT=8: 3.6x throughput increase for 135% CPU increase
**After:**
- PT=4: 2.8x throughput increase for 114% CPU increase
- PT=8: 3.7x throughput increase for 116% CPU increase
## Compression Algorithm Optimizations
Alongside the parallel compression revamp, some optimizations have gone into the underlying compression implementations/integrations. Most notably, **LZ4HC received dramatic performance improvements** through better reuse of internal data structures between compression calls (detailed in [pull request #13805](https://github.com/facebook/rocksdb/pull/13805)). A small regression in LZ4 performance from that change was fixed in [pull request #14017](https://github.com/facebook/rocksdb/pull/14017).
While **ZSTD remains the gold standard** for medium-to-high compression ratios in RocksDB, these LZ4HC optimizations make it an increasingly attractive option for read-heavy workloads where LZ4's faster decompression can provide overall performance benefits.
## Production Ready
With these efficiency improvements, parallel compression is now considered **production-ready**. The feature has been thoroughly tested in both unit tests and stress testing, including validation on high-load scenarios with hundreds of concurrent compression jobs and thousands of threads.
Some notes on current limitations:
- Parallel compression is currently incompatible with `UserDefinedIndex` and with the deprecated `decouple_partitioned_filters=false` setting
- Maximum performance is available with `-DROCKSDB_USE_STD_SEMAPHORES` at compile time, though this is not currently recommended due to reported bugs in some implementations of C++20 semaphores
## Configuration Recommendations
The dramatically reduced CPU overhead means parallel compression is now viable for a broader range of workloads, particularly those using higher compression levels or compression-heavy scenarios like time-series data. However, simply enabling parallel compression could result in more *spiky* CPU loads for hosts serving live DB data. **Parallel compression might be most useful for bulk SST file generation and/or remote compaction workloads** because they are less sensitive to CPU responsiveness. In these scenarios there is little danger in setting `parallel_threads=8` even with the possibility of over-subscribing CPU cores, though the potentially safer "sweet spot" is typically around `parallel_threads=3`, depending on compression level, etc.
## Limitations and Future
Although this offers a great improvement in the implementation of an existing option, we recognize that this setup is suboptimal in a number of ways:
* There is no work sharing / thread pooling for these SST compression/writer threads among compactions in the same process, so not well able to fit the workload to available CPU cores and not able to use other SST file compression work to avoid a worker thread going to sleep.
* We are not (yet) using a framework that would allow micro-work sharing with things other than SST generation on a set of threads. That would be a good direction for effective sharing of CPU resources without spikes in usage, but might incur intolerable CPU overhead in managing work. With this "hand optimized" and specialized framework, we can at least evaluate such future endeavors against a perhaps ideal framework in terms of parallelizing with minimal overhead.
## Try It Out
Parallel compression revamp will be available in RocksDB 10.7. As always, we recommend testing in your specific environment to determine the optimal configuration for your workload.
@@ -1,136 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500" style="background-color: #fafafa;">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7"
refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#333" />
</marker>
<marker id="red-arrowhead" markerWidth="10" markerHeight="7"
refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#d32f2f" />
</marker>
<marker id="blue-arrowhead" markerWidth="10" markerHeight="7"
refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#1976d2" />
</marker>
<marker id="green-arrowhead" markerWidth="10" markerHeight="7"
refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#388e3c" />
</marker>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="2" dy="2" stdDeviation="2" flood-color="#00000020"/>
</filter>
</defs>
<!-- Title -->
<text x="400" y="30" font-family="Arial, sans-serif" font-size="18" font-weight="bold" text-anchor="middle" fill="#333">
Ring Buffer Architecture (8 slots shown) for Parallel Compression
</text>
<!-- Ring Buffer Array -->
<g transform="translate(100,150)">
<!-- Array slots laid out horizontally -->
<!-- Slot 0 -->
<rect x="0" y="0" width="70" height="50" fill="#f5f5f5" stroke="#666" stroke-width="1" rx="5" filter="url(#shadow)"/>
<text x="35" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 0</text>
<text x="35" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#666">Empty</text>
<!-- Slot 1 - NextToWrite=1, being written by Worker Thread 2 -->
<rect x="75" y="0" width="70" height="50" fill="#e8f5e8" stroke="#388e3c" stroke-width="3" rx="5" filter="url(#shadow)"/>
<text x="110" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 1</text>
<text x="110" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#388e3c">Writing...</text>
<!-- Slot 2 - Compressed -->
<rect x="150" y="0" width="70" height="50" fill="#fff3e0" stroke="#f57c00" stroke-width="2" rx="5" filter="url(#shadow)"/>
<text x="185" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 2</text>
<text x="185" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#666">Compressed</text>
<!-- Slot 3 - Being compressed by Worker Thread 1 -->
<rect x="225" y="0" width="70" height="50" fill="#e3f2fd" stroke="#1976d2" stroke-width="3" rx="5" filter="url(#shadow)"/>
<text x="260" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 3</text>
<text x="260" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#1976d2">Compressing...</text>
<!-- Slot 4 - NextToCompress=4, uncompressed -->
<rect x="300" y="0" width="70" height="50" fill="#ffebee" stroke="#d32f2f" stroke-width="2" rx="5" filter="url(#shadow)"/>
<text x="335" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 4</text>
<text x="335" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#666">Uncompressed</text>
<!-- Slot 5 - NextToEmit=5, adding block -->
<rect x="375" y="0" width="70" height="50" fill="#e1f5fe" stroke="#4fc3f7" stroke-width="3" rx="5" filter="url(#shadow)"/>
<text x="410" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 5</text>
<text x="410" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#4fc3f7">Adding block...</text>
<!-- Slot 6 -->
<rect x="450" y="0" width="70" height="50" fill="#f5f5f5" stroke="#666" stroke-width="1" rx="5" filter="url(#shadow)"/>
<text x="485" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 6</text>
<text x="485" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#666">Empty</text>
<!-- Slot 7 -->
<rect x="525" y="0" width="70" height="50" fill="#f5f5f5" stroke="#666" stroke-width="1" rx="5" filter="url(#shadow)"/>
<text x="560" y="20" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#333">Slot 7</text>
<text x="560" y="35" font-family="Arial, sans-serif" font-size="9" text-anchor="middle" fill="#666">Empty</text>
</g>
<!-- Arrows pointing to array positions - staggered vertically -->
<!-- NextToWrite=1 arrow (red) -->
<line x1="210" y1="100" x2="210" y2="145" stroke="#d32f2f" stroke-width="3" marker-end="url(#red-arrowhead)"/>
<text x="210" y="90" font-family="Arial, sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="#d32f2f">NextToWrite=1</text>
<!-- NextToCompress=4 arrow (blue) -->
<line x1="435" y1="110" x2="435" y2="145" stroke="#1976d2" stroke-width="3" marker-end="url(#blue-arrowhead)"/>
<text x="435" y="100" font-family="Arial, sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="#1976d2">NextToCompress=4</text>
<!-- NextToEmit=5 arrow (light blue) -->
<line x1="510" y1="120" x2="510" y2="145" stroke="#4fc3f7" stroke-width="3" marker-end="url(#arrowhead)"/>
<text x="510" y="110" font-family="Arial, sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="#4fc3f7">NextToEmit=5</text>
<!-- Worker Thread 2 (wider box) -->
<g transform="translate(30,250)">
<rect x="0" y="0" width="160" height="70" fill="#e8f5e8" stroke="#388e3c" stroke-width="2" rx="8" filter="url(#shadow)"/>
<text x="80" y="25" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="#333">Worker Thread 2</text>
<text x="80" y="40" font-family="Arial, sans-serif" font-size="11" text-anchor="middle" fill="#666">Currently writing</text>
<text x="80" y="55" font-family="Arial, sans-serif" font-size="11" text-anchor="middle" fill="#666">Can also compress</text>
</g>
<!-- Worker Thread 1 (wider box) -->
<g transform="translate(240,280)">
<rect x="0" y="0" width="160" height="70" fill="#e3f2fd" stroke="#1976d2" stroke-width="2" rx="8" filter="url(#shadow)"/>
<text x="80" y="25" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="#333">Worker Thread 1</text>
<text x="80" y="40" font-family="Arial, sans-serif" font-size="11" text-anchor="middle" fill="#666">Currently compressing</text>
<text x="80" y="55" font-family="Arial, sans-serif" font-size="11" text-anchor="middle" fill="#666">Can also write</text>
</g>
<!-- Emit Thread (wider box) -->
<g transform="translate(460,250)">
<rect x="0" y="0" width="180" height="70" fill="#e1f5fe" stroke="#4fc3f7" stroke-width="2" rx="8" filter="url(#shadow)"/>
<text x="90" y="25" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="#333">Emit Thread</text>
<text x="90" y="40" font-family="Arial, sans-serif" font-size="11" text-anchor="middle" fill="#666">Generates uncompressed blocks</text>
<text x="90" y="55" font-family="Arial, sans-serif" font-size="11" text-anchor="middle" fill="#666">Can help with compression</text>
</g>
<!-- Arrows from threads to array (no labels) -->
<!-- Worker Thread 2 to Slot 1 -->
<line x1="190" y1="285" x2="210" y2="210" stroke="#388e3c" stroke-width="2" marker-end="url(#green-arrowhead)"/>
<!-- Worker Thread 1 to Slot 3 -->
<line x1="320" y1="280" x2="360" y2="210" stroke="#1976d2" stroke-width="2" marker-end="url(#blue-arrowhead)"/>
<!-- Emit Thread to Slot 5 -->
<line x1="550" y1="250" x2="510" y2="210" stroke="#4fc3f7" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- SST File Output (centered below Worker Thread 2) -->
<g transform="translate(50,380)">
<rect x="0" y="0" width="120" height="40" fill="#e8f5e8" stroke="#388e3c" stroke-width="2" rx="5" filter="url(#shadow)"/>
<text x="60" y="25" font-family="Arial, sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="#333">SST File</text>
</g>
<!-- Arrow from Worker Thread 2 to SST file -->
<line x1="110" y1="320" x2="110" y2="380" stroke="#388e3c" stroke-width="2" marker-end="url(#green-arrowhead)"/>
<!-- Invariant (moved to the right) -->
<g transform="translate(400,430)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#333">Invariant:</text>
<text x="0" y="20" font-family="Arial, sans-serif" font-size="11" fill="#666">NextToWrite ≤ NextToCompress ≤ NextToEmit (modulo ring buffer size)</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.8 KiB

-1
View File
@@ -1272,7 +1272,6 @@ class PosixFileSystem : public FileSystem {
supported_ops |= (1 << FSSupportedOps::kAsyncIO);
}
#endif
supported_ops |= (1 << FSSupportedOps::kFSPrefetch);
}
#if defined(ROCKSDB_IOURING_PRESENT)
-1
View File
@@ -1406,7 +1406,6 @@ IOStatus PosixWritableFile::Truncate(uint64_t size, const IOOptions& /*opts*/,
filename_, errno);
} else {
filesize_ = size;
lseek(fd_, filesize_, SEEK_SET);
}
return s;
}
-43
View File
@@ -4,7 +4,6 @@
// (found in the LICENSE.Apache file in the root directory).
#include "test_util/testharness.h"
#include "util/random.h"
#ifdef ROCKSDB_LIB_IO_POSIX
#include "env/io_posix.h"
@@ -132,48 +131,6 @@ TEST_F(LogicalBlockSizeCacheTest, Ref) {
}
#endif
class PosixWritableFileTest : public testing::Test {};
TEST_F(PosixWritableFileTest, SeekAfterTruncate) {
std::shared_ptr<FileSystem> fs = FileSystem::Default();
std::string path =
test::PerThreadDBPath("PosixWritableFileTest_SeekAfterTruncate");
Random rnd(300);
std::unique_ptr<FSWritableFile> wfile;
ASSERT_OK(fs->NewWritableFile(path, FileOptions(), &wfile, nullptr));
ASSERT_OK(wfile->Append(rnd.RandomString(16384), IOOptions(), nullptr));
ASSERT_OK(wfile->Truncate(4096, IOOptions(), nullptr));
ASSERT_OK(wfile->Append(rnd.RandomString(4096), IOOptions(), nullptr));
ASSERT_OK(wfile->Close(IOOptions(), nullptr));
wfile.reset();
uint64_t size = 0;
ASSERT_OK(fs->GetFileSize(path, IOOptions(), &size, nullptr));
ASSERT_EQ(size, 8192);
ASSERT_OK(fs->DeleteFile(path, IOOptions(), nullptr));
}
TEST_F(PosixWritableFileTest, SeekAfterExtend) {
std::shared_ptr<FileSystem> fs = FileSystem::Default();
std::string path =
test::PerThreadDBPath("PosixWritableFileTest_SeekAfterTruncate");
Random rnd(300);
std::unique_ptr<FSWritableFile> wfile;
ASSERT_OK(fs->NewWritableFile(path, FileOptions(), &wfile, nullptr));
ASSERT_OK(wfile->Append(rnd.RandomString(4096), IOOptions(), nullptr));
ASSERT_OK(wfile->Truncate(8192, IOOptions(), nullptr));
ASSERT_OK(wfile->Append(rnd.RandomString(8192), IOOptions(), nullptr));
ASSERT_OK(wfile->Close(IOOptions(), nullptr));
wfile.reset();
uint64_t size = 0;
ASSERT_OK(fs->GetFileSize(path, IOOptions(), &size, nullptr));
ASSERT_EQ(size, 16384);
ASSERT_OK(fs->DeleteFile(path, IOOptions(), nullptr));
}
} // namespace ROCKSDB_NAMESPACE
#endif
-40
View File
@@ -25,7 +25,6 @@ namespace ROCKSDB_NAMESPACE {
const std::string kCurrentFileName = "CURRENT";
const std::string kOptionsFileNamePrefix = "OPTIONS-";
const std::string kCompactionProgressFileNamePrefix = "COMPACTION_PROGRESS-";
const std::string kTempFileNameSuffix = "dbtmp";
static const std::string kRocksDbTFileExt = "sst";
@@ -243,25 +242,6 @@ std::string TempOptionsFileName(const std::string& dbname, uint64_t file_num) {
return dbname + "/" + buffer;
}
std::string CompactionProgressFileName(const std::string& dbname,
uint64_t timestamp) {
char buffer[256];
snprintf(buffer, sizeof(buffer), "%s%llu",
kCompactionProgressFileNamePrefix.c_str(),
static_cast<unsigned long long>(timestamp));
return dbname + "/" + buffer;
}
std::string TempCompactionProgressFileName(const std::string& dbname,
uint64_t timestamp) {
char buffer[256];
snprintf(buffer, sizeof(buffer), "%s%llu.%s",
kCompactionProgressFileNamePrefix.c_str(),
static_cast<unsigned long long>(timestamp),
kTempFileNameSuffix.c_str());
return dbname + "/" + buffer;
}
std::string MetaDatabaseName(const std::string& dbname, uint64_t number) {
char buf[100];
snprintf(buf, sizeof(buf), "/METADB-%llu",
@@ -284,8 +264,6 @@ std::string IdentityFileName(const std::string& dbname) {
// dbname/METADB-[0-9]+
// dbname/OPTIONS-[0-9]+
// dbname/OPTIONS-[0-9]+.dbtmp
// dbname/COMPACTION_PROGRESS-[timestamp]
// dbname/COMPACTION_PROGRESS-[timestamp].dbtmp
// Disregards / at the beginning
bool ParseFileName(const std::string& fname, uint64_t* number, FileType* type,
WalFileType* log_type) {
@@ -361,24 +339,6 @@ bool ParseFileName(const std::string& fname, uint64_t* number,
}
*number = ts_suffix;
*type = is_temp_file ? kTempFile : kOptionsFile;
} else if (rest.starts_with(kCompactionProgressFileNamePrefix)) {
uint64_t timestamp;
bool is_temp_file = false;
rest.remove_prefix(kCompactionProgressFileNamePrefix.size());
const std::string kTempFileNameSuffixWithDot =
std::string(".") + kTempFileNameSuffix;
if (rest.ends_with(kTempFileNameSuffixWithDot)) {
rest.remove_suffix(kTempFileNameSuffixWithDot.size());
is_temp_file = true;
}
if (!ConsumeDecimalNumber(&rest, &timestamp)) {
return false;
}
if (!rest.empty()) {
return false;
}
*number = timestamp;
*type = is_temp_file ? kTempFile : kCompactionProgressFile;
} else {
// Avoid strtoull() to keep filename format independent of the
// current locale
+1 -14
View File
@@ -124,10 +124,7 @@ std::string OldInfoLogFileName(const std::string& dbname, uint64_t ts,
const std::string& log_dir = "");
extern const std::string kOptionsFileNamePrefix; // = "OPTIONS-"
extern const std::string
kCompactionProgressFileNamePrefix; // =
// "COMPACTION_PROGRESS-"
extern const std::string kTempFileNameSuffix; // = "dbtmp"
extern const std::string kTempFileNameSuffix; // = "dbtmp"
// Return a options file name given the "dbname" and file number.
// Format: OPTIONS-[number].dbtmp
@@ -138,16 +135,6 @@ std::string OptionsFileName(uint64_t file_num);
// Format: OPTIONS-[number]
std::string TempOptionsFileName(const std::string& dbname, uint64_t file_num);
// Return a compaction progress file name given the timestamp.
// Format: COMPACTION_PROGRESS-[timestamp]
std::string CompactionProgressFileName(const std::string& dbname,
uint64_t timestamp);
// Return a temp compaction progress file name given the timestamp.
// Format: COMPACTION_PROGRESS-[timestamp].dbtmp
std::string TempCompactionProgressFileName(const std::string& dbname,
uint64_t timestamp);
// Return the name to use for a metadatabase. The result will be prefixed with
// "dbname".
std::string MetaDatabaseName(const std::string& dbname, uint64_t number);
+3 -4
View File
@@ -204,14 +204,13 @@ IOStatus WritableFileWriter::Append(const IOOptions& opts, const Slice& data,
return s;
}
IOStatus WritableFileWriter::Pad(const IOOptions& opts, const size_t pad_bytes,
const size_t max_pad_size) {
(void)max_pad_size;
IOStatus WritableFileWriter::Pad(const IOOptions& opts,
const size_t pad_bytes) {
if (seen_error()) {
return GetWriterHasPreviousErrorStatus();
}
const IOOptions io_options = FinalizeIOOptions(opts);
assert(pad_bytes < max_pad_size);
assert(pad_bytes < kDefaultPageSize);
size_t left = pad_bytes;
size_t cap = buf_.Capacity() - buf_.CurrentSize();
+1 -2
View File
@@ -256,8 +256,7 @@ class WritableFileWriter {
IOStatus Append(const IOOptions& opts, const Slice& data,
uint32_t crc32c_checksum = 0);
IOStatus Pad(const IOOptions& opts, const size_t pad_bytes,
const size_t max_pad_size);
IOStatus Pad(const IOOptions& opts, const size_t pad_bytes);
IOStatus Flush(const IOOptions& opts);
-62
View File
@@ -156,61 +156,6 @@ enum class PrepopulateBlobCache : uint8_t {
kFlushOnly = 0x1, // Prepopulate blobs during flush only
};
// Bitmask enum for verify output flags during compaction.
// This allows fine-grained control over what verification is performed
// on compaction output files and when it's enabled.
enum class VerifyOutputFlags : uint32_t {
kVerifyNone = 0x0, // No verification
// First set of bits: type of verifications
kVerifyBlockChecksum = 1 << 0, // Verify block checksums
kVerifyIteration = 1 << 1, // Verify iteration and full key/value hash
// by comparing the one inserted into a
// file, and what is read back.
// TODO - Implement
// kVerifyFileChecksum = 1 << 2, // Verify file-level checksum
// Second set of bits: when to enable verification
kEnableForLocalCompaction = 1 << 10, // Enable for local compaction
kEnableForRemoteCompaction = 1 << 11, // Enable for remote compaction
// TODO - Implement
// kEnableForFlush = 1 << 12, // Enable for flush
kVerifyAll = 0xFFFFFFFF,
};
inline VerifyOutputFlags operator|(VerifyOutputFlags lhs,
VerifyOutputFlags rhs) {
using T = std::underlying_type_t<VerifyOutputFlags>;
return static_cast<VerifyOutputFlags>(static_cast<T>(lhs) |
static_cast<T>(rhs));
}
inline VerifyOutputFlags& operator|=(VerifyOutputFlags& lhs,
VerifyOutputFlags rhs) {
lhs = lhs | rhs;
return lhs;
}
inline VerifyOutputFlags operator&(VerifyOutputFlags lhs,
VerifyOutputFlags rhs) {
using T = std::underlying_type_t<VerifyOutputFlags>;
return static_cast<VerifyOutputFlags>(static_cast<T>(lhs) &
static_cast<T>(rhs));
}
inline VerifyOutputFlags& operator&=(VerifyOutputFlags& lhs,
VerifyOutputFlags rhs) {
lhs = lhs & rhs;
return lhs;
}
inline bool operator!(VerifyOutputFlags flag) {
return flag == VerifyOutputFlags::kVerifyNone;
}
struct AdvancedColumnFamilyOptions {
// The maximum number of write buffers that are built up in memory.
// The default and the minimum number is 2, so that when 1 write buffer
@@ -759,13 +704,6 @@ struct AdvancedColumnFamilyOptions {
// Dynamically changeable through SetOptions() API
bool paranoid_file_checks = false;
// Bitmask enum for output verification option.
//
// Default: 0 (kVerifyNone)
//
// Dynamically changeable (as a uint32_t) through SetOptions() API.
VerifyOutputFlags verify_output_flags = VerifyOutputFlags::kVerifyNone;
// In debug mode, RocksDB runs consistency checks on the LSM every time the
// LSM changes (Flush, Compaction, AddFile). When this option is true, these
// checks are also enabled in release mode. These checks were historically
+7 -39
View File
@@ -49,7 +49,6 @@ struct CompactRangeOptions;
struct DBOptions;
struct ExternalSstFileInfo;
struct FlushOptions;
struct FlushWALOptions;
struct Options;
struct ReadOptions;
struct TableProperties;
@@ -97,8 +96,8 @@ class ColumnFamilyHandle {
virtual const Comparator* GetComparator() const = 0;
};
static const int kMajorVersion = ROCKSDB_MAJOR;
static const int kMinorVersion = ROCKSDB_MINOR;
static const int kMajorVersion = __ROCKSDB_MAJOR__;
static const int kMinorVersion = __ROCKSDB_MINOR__;
struct GetMergeOperandsOptions {
using ContinueCallback = std::function<bool(Slice)>;
@@ -353,30 +352,16 @@ class DB {
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr);
// End EXPERIMENTAL
// Open DB and run the compaction.
// It's a read-only operation, the result won't be installed to the DB, it
// will be output to the `output_directory`. The API should only be used with
// `options.CompactionService` to run compaction triggered by
// `CompactionService`.
static Status OpenAndCompact(
const std::string& name, const std::string& output_directory,
const std::string& input, std::string* output,
const CompactionServiceOptionsOverride& override_options);
// Opens a database and runs compaction without modifying the original DB.
//
// This read-only operation outputs compaction results to `output_directory`
// instead of installing them back to the source database. Designed primarily
// for use with `CompactionService` to process remote compaction jobs.
//
// Parameters:
// - `options`: Additional controls
// * When `allow_resumption = false`: The `output_directory` MUST be empty
// before calling this function. Any existing files (including resume
// state or output files from previous runs) in the directory may
// cause correctness errors as the compaction will start from scratch.
// - `name`: Source database path
// - `output_directory`: Where compaction output files are written
// - `input`: Serialized compaction input information
// - `output`: Serialized compaction result
// - `override_options`: Configuration overrides for the operation
//
// Returns: Status of the compaction operation
static Status OpenAndCompact(
const OpenAndCompactOptions& options, const std::string& name,
const std::string& output_directory, const std::string& input,
@@ -1792,10 +1777,6 @@ class DB {
return Status::NotSupported("FlushWAL not implemented");
}
virtual Status FlushWAL(const FlushWALOptions& /*options*/) {
return Status::NotSupported("FlushWAL not implemented");
}
// Ensure all WAL writes have been synced to storage, so that (assuming OS
// and hardware support) data will survive power loss. This function does
// not imply FlushWAL, so `FlushWAL(true)` is recommended if using
@@ -1935,24 +1916,11 @@ class DB {
virtual void GetColumnFamilyMetaData(ColumnFamilyHandle* /*column_family*/,
ColumnFamilyMetaData* /*metadata*/) {}
// Obtains the LSM-tree meta data of the specified column family of the DB
// with optional filtering by key range and level.
virtual void GetColumnFamilyMetaData(
ColumnFamilyHandle* /*column_family*/,
const GetColumnFamilyMetaDataOptions& /*options*/,
ColumnFamilyMetaData* /*metadata*/) {}
// Get the metadata of the default column family.
void GetColumnFamilyMetaData(ColumnFamilyMetaData* metadata) {
GetColumnFamilyMetaData(DefaultColumnFamily(), metadata);
}
// Get the metadata of the default column family with optional filtering.
void GetColumnFamilyMetaData(const GetColumnFamilyMetaDataOptions& options,
ColumnFamilyMetaData* metadata) {
GetColumnFamilyMetaData(DefaultColumnFamily(), options, metadata);
}
// Obtains the LSM-tree meta data of all column families of the DB, including
// metadata for each live table (SST) file and each blob file in the DB.
virtual void GetAllColumnFamilyMetaData(
+5 -9
View File
@@ -90,7 +90,6 @@ enum FSSupportedOps {
kVerifyAndReconstructRead, // Supports a higher level of data integrity. See
// the verify_and_reconstruct_read flag in
// IOOptions.
kFSPrefetch, // Supports prefetch operations
};
// Per-request options that can be passed down to the FileSystem
@@ -772,13 +771,12 @@ class FileSystem : public Customizable {
// If async_io is supported by the underlying FileSystem, then supported_ops
// will have corresponding bit (i.e FSSupportedOps::kAsyncIO) set to 1.
//
// By default, async_io and prefetch operation are set and FS should override
// this API and set all the operations they support provided in FSSupportedOps
// (including async_io and prefetch).
// By default, async_io operation is set and FS should override this API and
// set all the operations they support provided in FSSupportedOps (including
// async_io).
virtual void SupportedOps(int64_t& supported_ops) {
supported_ops = 0;
supported_ops |= (1 << FSSupportedOps::kAsyncIO);
supported_ops |= (1 << FSSupportedOps::kFSPrefetch);
}
// If you're adding methods here, remember to add them to EnvWrapper too.
@@ -1166,10 +1164,8 @@ class FSWritableFile {
// Truncate is necessary to trim the file to the correct size
// before closing. It is not always possible to keep track of the file
// size due to whole pages writes. If called with other writes to follow,
// the behavior is file system specific. Posix will reseek to the new EOF.
// Other file systems may behave differently. Its the caller's
// responsibility to check the file system contract.
// size due to whole pages writes. The behavior is undefined if called
// with other writes to follow.
virtual IOStatus Truncate(uint64_t /*size*/, const IOOptions& /*options*/,
IODebugContext* /*dbg*/) {
return IOStatus::OK();
-14
View File
@@ -224,20 +224,6 @@ struct LevelMetaData {
const std::vector<SstFileMetaData> files;
};
// Options for filtering column family metadata by key range.
struct GetColumnFamilyMetaDataOptions {
RangeOpt range;
// The level to filter on. If -1, all levels are included.
int level = -1;
GetColumnFamilyMetaDataOptions() = default;
GetColumnFamilyMetaDataOptions(const OptSlice& _start_key,
const OptSlice& _end_key, int _level = -1)
: range(_start_key, _end_key), level(_level) {}
};
// The metadata that describes a column family.
struct ColumnFamilyMetaData {
ColumnFamilyMetaData() : size(0), file_count(0), name("") {}
+5 -125
View File
@@ -958,67 +958,12 @@ struct DBOptions {
// Default: 0
size_t recycle_log_file_num = 0;
// The manifest file is rolled over on reaching this limit AND the
// space amp limit described in max_manifest_space_amp_pct. More trade-off
// details there.
//
// NOTE: this option used to be a hard limit, but that made this a dangerous
// tuning parameter for optimizing manifest file size because the best
// size really depends on the DB size and average SST file size (and other
// settings). Now it is essentially a minimum for the auto-tuned max manifest
// file size.
//
// Until the max_manifest_space_amp_pct feature is fully validated to show a
// smaller default here like 1MB is appropriate, the default value is 1GB to
// match historical behavior (without it being a hard limit in case of giant
// compacted manifest size).
//
// This option is mutable with SetDBOptions(), taking effect on the next
// manifest write (e.g. completed DB compaction or flush).
// manifest file is rolled over on reaching this limit.
// The older manifest file be deleted.
// The default value is 1GB so that the manifest file can grow, but not
// reach the limit of storage capacity.
uint64_t max_manifest_file_size = 1024 * 1024 * 1024;
// This option mostly replaces max_manifest_file_size to control an auto-tuned
// balance of manifest write amplification and space amplification. A new
// manifest file is created with the "compacted" contents of the old one when
// current_manifest_size
// >
// max(max_manifest_file_size,
// est_compacted_manifest_size * (1 + max_manifest_space_amp_pct/100))
//
// where est_compacted_manifest_size is an estimate of how big a new compacted
// version of the current manifest would be. Currently, the estimate used is
// the last newly-written manifest, in its "compacted" form.
//
// Space amplification in the manifest file might be less of a concern for
// primary storage space and more of a concern for DB recover time and size of
// backup files that aren't incremental between backups. To minimize manifest
// churn on initial DB population, setting max_manifest_file_size to something
// not too small, like 1MB, should suffice. Similarly, write amp on the
// manifest file is likely not a direct concern but completed compactions and
// flushes cannot (currently) be committed while the (relatively small)
// manifest file is being compacted. Manifest compactions should not
// interfere with user write latency or throughput unless the DB is
// chronically stalling or close to stalling writes already.
//
// For this option to have a meaningful effect, it is recommended to set
// max_manifest_file_size to something modest like 1MB. Then we can interpret
// values for this option as follows, starting with minimum space amp and
// maximum write amp:
// * 0 - Every manifest write (flush, compaction, etc.) generates a whole new
// manifest. Only useful for testing.
// * very small - Doesn't take many manifest writes to generate a whole new
// manifest.
// * 100 - In a DB with pretty consistent number of SST files, etc., achieves
// about 1.0 write amp (writing about 2x the theoretical minimum) and a max of
// about 1.0 space amp (manifest up to 2x the compacted size).
// * 500 - Recommended and default: 0.2 write amp and up to roughly 5.0 space
// amp.
// * 10000 - 0.01 write amp and up to 100 space amp on the manifest.
//
// This option is mutable with SetDBOptions(), taking effect on the next
// manifest write (e.g. completed DB compaction or flush).
int max_manifest_space_amp_pct = 500;
// Number of shards used for table cache.
int table_cache_numshardbits = 6;
@@ -2379,23 +2324,6 @@ struct FlushOptions {
FlushOptions() : wait(true), allow_write_stall(false) {}
};
struct FlushWALOptions {
// If true, it calls `SyncWAL()` afterwards.
// Default: false
bool sync;
// For IO operations associated with flushing the WAL, charge the internal
// rate limiter (see `DBOptions::rate_limiter`) at the specified priority and
// pass the priority down to the file system through
// `IOOptions::rate_limiter_priority`. The special value `Env::IO_TOTAL`
// disables charging the rate limiter.
//
// Default: `Env::IO_TOTAL`
Env::IOPriority rate_limiter_priority;
FlushWALOptions() : sync(false), rate_limiter_priority(Env::IO_TOTAL) {}
};
// Create a Logger from provided DBOptions
Status CreateLoggerFromOptions(const std::string& dbname,
const DBOptions& options,
@@ -2433,22 +2361,11 @@ struct CompactionOptions {
// canceled variable in CompactionOptions, as it does for CompactRangeOptions
// - this is because ManualCompactionState is not used
// Create output compaction file using this file temperature. If unset, will
// default to "last_level_temperature" if output level is last level otherwise
// "default_write_temperature"
Temperature output_temperature_override = Temperature::kUnknown;
// Option to optimize the manual compaction by enabling trivial move for non
// overlapping files.
// Default: false
bool allow_trivial_move;
CompactionOptions()
: compression(kDisableCompressionOption),
output_file_size_limit(std::numeric_limits<uint64_t>::max()),
max_subcompactions(0),
canceled(nullptr),
allow_trivial_move(false) {}
canceled(nullptr) {}
};
// For level based compaction, we can configure if we want to skip/force
@@ -2869,43 +2786,6 @@ struct CompactionServiceOptionsOverride {
struct OpenAndCompactOptions {
// Allows cancellation of an in-progress compaction.
std::atomic<bool>* canceled = nullptr;
// EXPERIMENTAL
//
// Controls whether OpenAndCompact() should attempt to resume from previously
// persisted compaction progress or start fresh.
//
// When `allow_resumption = true`:
// - OpenAndCompact() attempts to resume from previously persisted compaction
// progress stored in `output_directory`
// - During execution, it periodically persists new progress to the same
// directory, allowing future calls to continue from where the previous
// compaction left off.
// - Fallback behavior: If resumption cannot be fulfilled (e.g., due to
// corrupted or missing resume state), the system will attempt to start a
// fresh compaction as a best-effort fallback by cleaning related files in
// the `output_directory` to achieve a clean state. If even the fresh
// compaction cannot be started, a non-OK status will be returned.
// - Important: Resume attempts will be ineffective if the underlying
// conditions that caused the previous OpenAndCompact() failure still
// persist. The same non-OK status will likely be returned unless the root
// cause has been resolved.
// - Progress persistence is sequential and best-effort, triggered upon
// completion of each new output file. If compaction is interrupted while
// creating an output file (before its completion), that partial work will
// need to be redone upon resumption.
//
// When `allow_resumption = false`:
// - OpenAndCompact() starts a fresh compaction from scratch.
// - No progress will be saved during execution, so interruptions require
// starting over completely.
// - CRITICAL REQUIREMENT: The `output_directory` associated MUST be empty
// before calling OpenAndCompact(). Any existing files (including resume
// state or output files from previous runs) may cause correctness errors.
//
// Limitation: Currently incompatible with paranoid_file_checks=true. The
// option is effectively disabled when `paranoid_file_checks` is enabled.
bool allow_resumption = false;
};
struct LiveFilesStorageInfoOptions {
-3
View File
@@ -440,9 +440,6 @@ enum Tickers : uint32_t {
REMOTE_COMPACT_READ_BYTES,
REMOTE_COMPACT_WRITE_BYTES,
// Bytes of output files successfully resumed during compaction
REMOTE_COMPACT_RESUMED_BYTES,
// Tiered storage related statistics
HOT_FILE_READ_BYTES,
WARM_FILE_READ_BYTES,
-24
View File
@@ -603,30 +603,6 @@ struct BlockBasedTableOptions {
// Align data blocks on lesser of page size and block size
bool block_align = false;
// Align data blocks on super block alignment. Avoid a data block split across
// super block boundaries. Works with/without compression.
//
// Here a "super block" refers to an aligned unit of underlying Filesystem
// storage for which there is an extra cost when a random read involves two
// such super blocks instead of just one. Configuring that size here suggests
// inserting padding in the SST file to avoid a single SST block splitting
// across two super blocks. Only power-of-two sizes are supported. See also
// super_block_alignment_space_overhead_ratio. Default to 0, which means super
// block alignment is disabled.
//
// Super block alignment size. Default to 0, which means super block alignment
// is disabled. If it is enabled, it needs to be a power of 2 and higher than
// block size.
size_t super_block_alignment_size = 0;
// This option constrols the storage space overhead of super block alignment.
// It is used to calculate the max padding size allowed for super block
// alignment. It is calculated in this way. If super_block_alignment_size is
// 2MB, and super_block_alignment_overhead_ratio is 128, then the max padding
// size allowed for super block alignment is 2MB / 128 = 16KB.
// Note that, when it is set to 0, super block alignment is disabled.
size_t super_block_alignment_space_overhead_ratio = 128;
// This enum allows trading off increased index size for improved iterator
// seek performance in some situations, particularly when block cache is
// disabled (ReadOptions::fill_cache = false) and direct IO is
+1 -2
View File
@@ -53,8 +53,7 @@ enum FileType {
kMetaDatabase,
kIdentityFile,
kOptionsFile,
kBlobFile,
kCompactionProgressFile
kBlobFile
};
// User-oriented representation of internal key types.
-10
View File
@@ -423,11 +423,7 @@ class StackableDB : public DB {
Status SyncWAL() override { return db_->SyncWAL(); }
using DB::FlushWAL;
Status FlushWAL(bool sync) override { return db_->FlushWAL(sync); }
Status FlushWAL(const FlushWALOptions& options) override {
return db_->FlushWAL(options);
}
Status LockWAL() override { return db_->LockWAL(); }
@@ -456,12 +452,6 @@ class StackableDB : public DB {
db_->GetColumnFamilyMetaData(column_family, cf_meta);
}
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
const GetColumnFamilyMetaDataOptions& options,
ColumnFamilyMetaData* metadata) override {
db_->GetColumnFamilyMetaData(column_family, options, metadata);
}
using DB::StartBlockCacheTrace;
Status StartBlockCacheTrace(
const TraceOptions& trace_options,
+8 -13
View File
@@ -12,20 +12,15 @@
// NOTE: in 'main' development branch, this should be the *next*
// minor or major version number planned for release.
#define ROCKSDB_MAJOR 10
#define ROCKSDB_MINOR 8
#define ROCKSDB_PATCH 3
#define ROCKSDB_MINOR 7
#define ROCKSDB_PATCH 6
// Make it easy to do conditional compilation based on version checks, i.e.
// #if ROCKSDB_VERSION_GE(4, 5, 6)
// int thisCoderequiresVersion_4_5_6_OrGreater;
// #else
// int thisCodeIsForOlderVersions;
// #endif
#define ROCKSDB_MAKE_VERSION_INT(a, b, c) ((a) * 1000000 + (b) * 1000 + (c))
#define ROCKSDB_VERSION_INT \
ROCKSDB_MAKE_VERSION_INT(ROCKSDB_MAJOR, ROCKSDB_MINOR, ROCKSDB_PATCH)
#define ROCKSDB_VERSION_GE(a, b, c) \
(ROCKSDB_VERSION_INT >= ROCKSDB_MAKE_VERSION_INT(a, b, c))
// Do not use these. We made the mistake of declaring macros starting with
// double underscore. Now we have to live with our choice. We'll deprecate these
// at some point
#define __ROCKSDB_MAJOR__ ROCKSDB_MAJOR
#define __ROCKSDB_MINOR__ ROCKSDB_MINOR
#define __ROCKSDB_PATCH__ ROCKSDB_PATCH
namespace ROCKSDB_NAMESPACE {
// Returns a set of properties indicating how/when/where this version of RocksDB
+1 -8
View File
@@ -5195,8 +5195,6 @@ class TickerTypeJni {
return -0x2F;
case ROCKSDB_NAMESPACE::Tickers::REMOTE_COMPACT_WRITE_BYTES:
return -0x30;
case ROCKSDB_NAMESPACE::Tickers::REMOTE_COMPACT_RESUMED_BYTES:
return -0x5F;
case ROCKSDB_NAMESPACE::Tickers::HOT_FILE_READ_BYTES:
return -0x31;
case ROCKSDB_NAMESPACE::Tickers::WARM_FILE_READ_BYTES:
@@ -5670,8 +5668,6 @@ class TickerTypeJni {
return ROCKSDB_NAMESPACE::Tickers::REMOTE_COMPACT_READ_BYTES;
case -0x30:
return ROCKSDB_NAMESPACE::Tickers::REMOTE_COMPACT_WRITE_BYTES;
case -0x5F:
return ROCKSDB_NAMESPACE::Tickers::REMOTE_COMPACT_RESUMED_BYTES;
case -0x31:
return ROCKSDB_NAMESPACE::Tickers::HOT_FILE_READ_BYTES;
case -0x32:
@@ -9156,7 +9152,7 @@ class BlockBasedTableOptionsJni
}
jmethodID method_id_init =
env->GetMethodID(jclazz, "<init>", "(ZZZZBBDBZJIIIJZZZZZIIZZJJBBJD)V");
env->GetMethodID(jclazz, "<init>", "(ZZZZBBDBZJIIIJZZZZZIIZZBBJD)V");
if (method_id_init == nullptr) {
// exception thrown: NoSuchMethodException or OutOfMemoryError
return nullptr;
@@ -9201,9 +9197,6 @@ class BlockBasedTableOptionsJni
table_factory_options->format_version,
table_factory_options->enable_index_compression,
table_factory_options->block_align,
static_cast<jlong>(table_factory_options->super_block_alignment_size),
static_cast<jlong>(
table_factory_options->super_block_alignment_space_overhead_ratio),
IndexShorteningModeJni::toJavaIndexShorteningMode(
table_factory_options->index_shortening),
FilterPolicyJni::toJavaIndexType(filter_policy_type),
+3 -8
View File
@@ -45,7 +45,7 @@ jlong Java_org_rocksdb_PlainTableConfig_newTableFactoryHandle(
/*
* Class: org_rocksdb_BlockBasedTableConfig
* Method: newTableFactoryHandle
* Signature: (ZZZZBBDBZJJJIIIJZZZJZZIIZZJJBJI)J
* Signature: (ZZZZBBDBZJJJJIIIJZZZJZZIIZZBJIJI)J
*/
jlong Java_org_rocksdb_BlockBasedTableConfig_newTableFactoryHandle(
JNIEnv *, jclass, jboolean jcache_index_and_filter_blocks,
@@ -63,9 +63,8 @@ jlong Java_org_rocksdb_BlockBasedTableConfig_newTableFactoryHandle(
jboolean jwhole_key_filtering, jboolean jverify_compression,
jint jread_amp_bytes_per_bit, jint jformat_version,
jboolean jenable_index_compression, jboolean jblock_align,
jlong jsuper_block_alignment_size,
jlong jsuper_block_alignment_space_overhead_ratio, jbyte jindex_shortening,
jlong jblock_cache_size, jint jblock_cache_num_shard_bits) {
jbyte jindex_shortening, jlong jblock_cache_size,
jint jblock_cache_num_shard_bits) {
ROCKSDB_NAMESPACE::BlockBasedTableOptions options;
options.cache_index_and_filter_blocks =
static_cast<bool>(jcache_index_and_filter_blocks);
@@ -137,10 +136,6 @@ jlong Java_org_rocksdb_BlockBasedTableConfig_newTableFactoryHandle(
options.enable_index_compression =
static_cast<bool>(jenable_index_compression);
options.block_align = static_cast<bool>(jblock_align);
options.super_block_alignment_size =
static_cast<size_t>(jsuper_block_alignment_size);
options.super_block_alignment_space_overhead_ratio =
static_cast<size_t>(jsuper_block_alignment_space_overhead_ratio);
options.index_shortening =
ROCKSDB_NAMESPACE::IndexShorteningModeJni::toCppIndexShorteningMode(
jindex_shortening);
Loaded 100 of 146 files, more files were not shown because too many files have changed in this diff. Show more