Compare commits

...
Author SHA1 Message Date
jainpr 33ce358130 Remove yield in point lock manager
The yield is actually of not much use because waitFor should already be doing that internally.
2025-07-23 09:33:33 -07:00
Ryan Hancock 351d212777 Ensure Property Bags are Pushed Down to BlockBasedIterator (#13795)
Summary:
This diff fixes up a miss in which the property_bag was not pushed down to the BlockBasedIterator.

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

Reviewed By: anand1976

Differential Revision: D78762294

Pulled By: krhancoc

fbshipit-source-id: 8970b0a87e35d07d5a0dd16f360ec96859f66550
2025-07-22 17:46:45 -07:00
Richard Barnes 668067e0bf Del redundant-static-def in internal_repo_rocksdb/repo/db/db_with_timestamp_basic_test.cc +1 (#13794)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13794

LLVM has a warning `-Wdeprecated-redundant-constexpr-static-def` which raises the warning:

> warning: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated

Since we are now on C++20, we can remove the out-of-line definition of constexpr static data members. This diff does so.

 - If you approve of this diff, please use the "Accept & Ship" button :-)

Reviewed By: meyering

Differential Revision: D78635037

fbshipit-source-id: a90c68469947705c65f36588b2d575237689dbe8
2025-07-22 11:49:12 -07:00
Richard Barnes 463f9fd9f2 Del redundant-static-def in internal_repo_rocksdb/repo/tools/sst_dump_test.cc +1 (#13793)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13793

LLVM has a warning `-Wdeprecated-redundant-constexpr-static-def` which raises the warning:

> warning: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated

Since we are now on C++20, we can remove the out-of-line definition of constexpr static data members. This diff does so.

 - If you approve of this diff, please use the "Accept & Ship" button :-)

Reviewed By: meyering

Differential Revision: D78635005

fbshipit-source-id: bd7cbfff0580b9579e78237ec4371615d3609536
2025-07-22 10:56:07 -07:00
Xingbo Wang ca5d60fd69 Switch back to FSWritableFile in external sst file ingestion job (#13791)
Summary:
This patch reverted "NewRandomRWFile" back to "ReopenWritableFile" in external sst file ingestion job when file is linked instead of copied. The reason is that some of the file systems do not support "NewRandomRWFile". A long term fix is being worked in progress.

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

Test Plan: Unit test

Reviewed By: pdillinger

Differential Revision: D78697825

Pulled By: xingbowang

fbshipit-source-id: d3651223ab1f2369aac34b772bba8049c6c2c628
2025-07-21 16:21:19 -07:00
Jay Huh c50a2b68bb Expose GetTtl() API in TTL DB (#13790)
Summary:
As title

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

Test Plan:
```
./ttl_test --gtest_filter="*TtlTest.ChangeTtlOnOpenDb*"
```

Reviewed By: cbi42

Differential Revision: D78670347

Pulled By: jaykorean

fbshipit-source-id: 1b2538d6cd0f2a0fbf397a5d2f677852f97272c4
2025-07-21 14:43:40 -07:00
Ryan Hancock fe68fbcd7f Prepare() Scan Option Pruning for LevelIterator (#13780)
Summary:
This diff introduces the ScanOption Pruning, previously the intent was to do prefetching for each sub-iterator of the level iterator, however since BlockBasedIterator does not prefetch asynchronously, this optimization does not make sense just yet.

For now we will prune the ScanOptions to the overlapping ranges and make sure they are properly piped to the underlying layers (during Prepare, and Seek).

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

Reviewed By: cbi42

Differential Revision: D78436869

Pulled By: krhancoc

fbshipit-source-id: 681fe7f7f88b04b5c2d60cb3a5de01e03f6f8431
2025-07-21 13:09:53 -07:00
Andrew Chang 57ff2b2492 Update for next release 10.6 (#13784)
Summary:
This includes:
1. Release notes from 10.5 branch
2. Version.h update
3. Format compatibility check
4. Folly commit hash update (I chose https://github.com/facebook/folly/releases/tag/v2025.06.30.00 because later commits were causing CI failures)

Previous release: https://github.com/facebook/rocksdb/pull/13719

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

Reviewed By: pdillinger

Differential Revision: D78587604

Pulled By: archang19

fbshipit-source-id: a8611ef4527c3c6ee5c830349b7ae41701c1efb6
2025-07-18 15:02:49 -07:00
Peter Dillinger 551ba21e9b Support recompress-with-CompressionManager in sst_dump (#13783)
Summary:
So that we can use --command=recompress with a custom CompressionManager. (It's not required for reading files using a custom CompressionManager because those can already use ObjectLibrary for dependency injection.)

Suggested follow-up:
* These tests should not be using C arrays, snprintf, manual delete, etc. except for thin compatibility with argc/argv.

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

Test Plan: unit test added, some manual testing

Reviewed By: archang19

Differential Revision: D78574434

Pulled By: pdillinger

fbshipit-source-id: 609e6c6439090e6b7e9b63fbd4c2d3f04b104fcf
2025-07-18 14:22:29 -07:00
Zaidoon Abd Al Hadi 9967c3255d expose flush reason for flush job info as well as compaction reason for sub compaction job info via c api (#13770)
Summary:
follow up to https://github.com/facebook/rocksdb/pull/13601

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

Reviewed By: hx235

Differential Revision: D78426229

Pulled By: cbi42

fbshipit-source-id: d583288b87f9ab0d05421b3daeb57e297edf5ad6
2025-07-18 11:08:03 -07:00
Changyu Bi 2850ccb96b Support Prepare() in BlockBasedTableIterator For MultiScan (#13778)
Summary:
initial support for Prepare() to optimize the performance of MultiScan when using block-based tables. In Prepare(), we do the following:
1. Load all data blocks that will be read in multiscan to block cache
2. Pin the data blocks during the scan
3. if I/O is needed, coalesce I/Os when they are adjacent.

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

Test Plan:
Added a new unit test.

Benchmark:
1. Set up the DB, I use FIFO here so that files will be in L0 and iterator will use BlockBasedTableIterator directly instead of LevelIterator, where Prepare() call is not implemented yet.
```
./db_bench --benchmarks="fillseq,compact" --disable_wal=1 --threads=1 --num_levels=1 --compaction_style=2 --fifo_compaction_max_table_files_size_mb=1000 --write_buffer_size=268435456
```

2. Multi-scan: based on https://github.com/facebook/rocksdb/issues/13765
```
./db_bench --db="/tmp/rocksdbtest-543376/dbbench" --use_existing_db=1 --benchmarks=multiscan --disable_auto_compactions=1 --seek_nexts=100 --threads=32 --duration=10 --statistics=1

multiscan_stride = 100
multiscan_size = 10
seek_nexts = 100

Main:
multiscan    :     449.386 micros/op 70562 ops/sec 10.359 seconds 730968 operations; (multscans:22999)
multiscan    :     453.606 micros/op 69433 ops/sec 10.369 seconds 719968 operations; (multscans:22999)
rocksdb.non.last.level.read.bytes COUNT : 47763519421
rocksdb.non.last.level.read.count COUNT : 21573878
Branch:
multiscan    :     332.670 micros/op 94698 ops/sec 10.285 seconds 973968 operations; (multscans:29999)
rocksdb.non.last.level.read.bytes COUNT : 111791308336
rocksdb.non.last.level.read.count COUNT : 1062942

With direct-IO:
./db_bench --db="/tmp/rocksdbtest-543376/dbbench" --use_existing_db=1 --benchmarks=multiscan --disable_auto_compactions=1 --seek_nexts=100 --threads=32 --duration=10 --statistics=1 --use_direct_reads=1

Main:
multiscan    :     586.045 micros/op 53825 ops/sec 10.366 seconds 557968 operations; (multscans:14999)
rocksdb.non.last.level.read.bytes COUNT : 69107458693
rocksdb.non.last.level.read.count COUNT : 6724651
Branch:
multiscan    :     386.679 micros/op 81282 ops/sec 10.359 seconds 841968 operations; (multscans:25999)
rocksdb.non.last.level.read.bytes COUNT : 96605800558
rocksdb.non.last.level.read.count COUNT : 918973
```

Throughput is 36% higher with non-direct IO and 50% higher with direct IO. The improvement is likely from doing less number of I/Os due to I/O coalescing during Prepare(), as shown in `rocksdb.non.last.level.read.count`. The total bytes read is more with this PR for the same reason.

3. Regular iterator:
```
./db_bench --use_existing_db=1 --db="/tmp/rocksdbtest-543376/dbbench" --benchmarks=seekrandom --disable_auto_compactions=1 --seek_nexts=10 --threads=32 --duration=10

Main:
seekrandom   :      13.014 micros/op 2456735 ops/sec 10.014 seconds 24602968 operations; 2717.8 MB/s (773999 of 773999 found)
Branch:
seekrandom   :      13.048 micros/op 2450554 ops/sec 10.013 seconds 24537968 operations; 2710.9 MB/s (772999 of 772999 found)
```
The result fluctuates but without noticeable regression.

Reviewed By: anand1976

Differential Revision: D78440807

Pulled By: cbi42

fbshipit-source-id: 80ac6fd222696fa65ac0b4b5441748be5ee0b979
2025-07-17 10:00:21 -07:00
Jay Huh 3bb3142b7e Upgrade Maven to 3.9.11 (#13779)
Summary:
Similar to https://github.com/facebook/rocksdb/pull/13684, the link for version 3.9.10 is broken again, and we are upgrading Maven as part of the fix.

This time, we are no longer using the link from https://dlcdn.apache.org/maven/maven-3/ because they occasionally remove versions, which can break our CI at any time.

Instead, changing the link to use Apache Archive which should be stable

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

Test Plan:
CI

`install-maven` step is now passing - https://github.com/facebook/rocksdb/actions/runs/16328986469/job/46126398150?pr=13779

Reviewed By: krhancoc

Differential Revision: D78428965

Pulled By: jaykorean

fbshipit-source-id: 9c218f6efbd1188be7847f43be338908efffe002
2025-07-17 07:56:20 -07:00
Hui Xiao 6e4113e92d Remove reductant Compaction parameters (#13777)
Summary:
**Context/Summary:** a small refactoring to make Compaction constructor simpler (though still complicated now).

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

Test Plan: Existing tests

Reviewed By: jaykorean

Differential Revision: D78385166

Pulled By: hx235

fbshipit-source-id: cd93d1ba3936d9f9077ffceb0dc4ef5506e51017
2025-07-16 14:06:56 -07:00
Xingbo Wang 0be850a000 Avoid divide by 0 in ComputeCompactionScore for FIFO compaction (#13767)
Summary:
When max_table_files_size was accidentally configured with 0 value, engine could crash on divide by 0 operation. Although RocksDB do configuration validation during bootstrap, it typically does not do this for runtime dynamic parameter validation. Therefore, there is a chance where max_table_files_size could be set to 0. This PR only focuses on fixing a code path where max_table_files_size ack as divisor.

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

Test Plan: Unit test.

Reviewed By: cbi42

Differential Revision: D78420516

Pulled By: xingbowang

fbshipit-source-id: 6fdcc85b28a2c6319066665262b981e513719703
2025-07-16 12:18:47 -07:00
Ryan Hancock e46972d7a4 Add Exit Hooks to ToolHooks (#13772)
Summary:
This diff introduces the ability to override behavior of exits, allow for users to catch exits in a try catch for example as opposed to fully exiting the process.

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

Reviewed By: hx235

Differential Revision: D78244499

Pulled By: krhancoc

fbshipit-source-id: b403327ed5b494a22b6beeaad4083945a1def0c7
2025-07-16 11:56:35 -07:00
Ryan Hancock b09e27b207 Add MultiScan DB Bench Benchmark (#13765)
Summary:
This diff add's a DB Bench Benchmark dedicated to sequential non-overlapping sets of scans using the MultiScan API.

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

Test Plan:
```
make release

// Setup the DB
./db_bench --db=$DB \
    --benchmarks="fillseq,compact" \
    --disable_wal=1 --key_size=$KEYSIZE \
    --value_size=$VALUESIZE --num=$NUMKEYS --threads=32

// Run the benchmark
./db_bench --use_existing_db=1 \
    --benchmarks=multiscan \
    --disable_auto_compactions=1 --seek_nexts=1000 \
    --key_size=$KEYSIZE --value_size=$VALUESIZE \
    --num=$NUMKEYS --threads=32 --duration=30
```

Reviewed By: anand1976

Differential Revision: D78129962

Pulled By: krhancoc

fbshipit-source-id: 1c524d531b62a8576374ed1377e29d59a83cedec
2025-07-16 11:42:47 -07:00
Anand Ananthabhotla 0788cb8a80 Add Prepare interface to user defined index iterator (#13728)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13728

The Prepare interface allows the user defined index iterator to prefetch index entries, as well as take custom scan termination criteria specified in the property_bag into account.

Reviewed By: pdillinger

Differential Revision: D76165546

fbshipit-source-id: 83d628598924aa7a60dff7ed62a16ae575b2c8ec
2025-07-16 00:16:03 -07:00
Anand Ananthabhotla 768ef1fad4 User defined index reader and iterator (#13727)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13727

Add UserDefinedIndexReader and UserDefinedIndexIterator. The BlockBasedTable reads the user defined index meta block during open, verifies the checksum, pins in cache or heap depending on configuration, and allocates a UserDefinedIndexReader object with the contents. Similar to the builder, an IndexReader wrapper is allocated. The wrapper forwards the calls to the native reader and/or user defined index reader as appropriate.

A new option, table_index_name, in ReadOptions specifies the index to use when creating a new Iterator.

Reviewed By: pdillinger

Differential Revision: D76165694

fbshipit-source-id: c30bde4c5ce91ea3dc9ad302e73fe4963c1ed457
2025-07-15 10:37:20 -07:00
anand76 f6841d1e68 Fix DeleteFile error handling in SstFileWriter::Finish (#13776)
Summary:
In SstFileWriter::Finish, the call to DeleteFile to delete the output file in case of an error may fail. The current behavior is to ignore the error. In stress tests, there may be expected failures due to error injection. Not acting on the return status will cause the ASSERT_STATUS_CHECKED test to fail, so silence it.

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

Reviewed By: mszeszko-meta

Differential Revision: D78307124

Pulled By: anand1976

fbshipit-source-id: d27d9397c15cac5cb33b27094c9123a3fde7fa24
2025-07-14 18:34:56 -07:00
Peter Dillinger 60a0172096 Compression API clarifcations/minor fixes (#13775)
Summary:
* A number of comments clarifying contracts, etc.
* Make ReleaseWorkingArea public instead of protected because there are some limited cases where a wrapper implementation might want to call it directly
* Check non-empty dictionary precondition on MaybeCloneForDict
* Expand testing of wrapped WorkingAreas
* Random documentation improvement in block_builder.cc

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

Test Plan: existing and expanded tests and assertions

Reviewed By: hx235

Differential Revision: D78304550

Pulled By: pdillinger

fbshipit-source-id: e5f064e8405a5a49be123ee13145cb3626bbbfbf
2025-07-14 17:26:22 -07:00
Xingbo Wang b7cd1fd662 Track FSRandomRWFile open/close in Fault injection fs (#13771)
Summary:
The Stress test was broken due to a change in switching from ReopenWritableFile to FSRandomRWFile for sync linked file in external Sst ingestion job. The Stress test is using FaultInjectionFs, which tracks the opening of ReopenWritableFile properly, but does not track FSRandomRWFile properly. This change fixes the tracking of FSRandomRWFile in FaultInjectionFs.

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

Test Plan: unit test, stress test

Reviewed By: mszeszko-meta

Differential Revision: D78282719

Pulled By: xingbowang

fbshipit-source-id: f8f2ed8a5b28a76836f75effbdfa2c3bb172dc51
2025-07-14 11:04:00 -07:00
Peter Dillinger 6c267a3217 Improve some unreachable-after-loop code (#13764)
Summary:
in log_reader.cc.
* `for (;;)` (with no matching break inside) should be more structurally recognizable to compilers as unreachable after compared to `while (true)` which compilers can treat as conditional for warning/error purposes because `true` might have come from a macro, etc.
* Comment the `break` statements to indicate they are for the `switch` (not the `for`)
* No code or annotation is apparently needed for the unreachable end of the non-void function, so just a comment

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

Test Plan: CI

Reviewed By: archang19

Differential Revision: D78135493

Pulled By: pdillinger

fbshipit-source-id: e313435a846a6e15346acf40404f755be98ab09a
2025-07-11 09:23:14 -07:00
Peter Dillinger f9f7ad702c Move some tests from db_test(2) to compression_test (#13763)
Summary:
... to improve compilation times on db_test and db_test2 and to consolidate more compression-related tests into compression_test.

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

Test Plan:
existing tests, and seems like I haven't thrown anything away:
```
$ git diff | grep -Ec '^[-]' # lines removed
1535
$ git diff | grep -Ec '^[+]' # lines added
1535
$
```

Reviewed By: hx235

Differential Revision: D78103064

Pulled By: pdillinger

fbshipit-source-id: 9cb4c1b2473d8928f890e72d3a9b5012617819a8
2025-07-10 13:23:15 -07:00
generatedunixname89002005232357 83b99db98a Revert D77424529
Summary:
This diff reverts D77424529
Unland reason: This diff broke our Windows 2022 build for Open Source CI (T230460952).

Depends on D77424529

Reviewed By: pdillinger

Differential Revision: D78107313

fbshipit-source-id: 6177448e1015c239abcebb0e68470dfd841b6fa0
2025-07-10 12:47:22 -07:00
Peter Dillinger 988357696d Improve internal lossless_cast to work on pointers (#13648)
Summary:
I was going to use this in some code I was working on but ended up not needing it. But it's useful nonetheless and I'm using it in a few places to replace reinterpret_cast.

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

Test Plan: existing tests, manually see compilation fail when pointed-to types are not same size integral types

Reviewed By: cbi42

Differential Revision: D75576195

Pulled By: pdillinger

fbshipit-source-id: e10c7a4959340f6f2b536de8088072a90e871fcf
2025-07-09 17:22:15 -07:00
Richard Barnes e929bde2bf Del redundant-static-def in rocksdb/src/table/block_based/filter_policy.cc +1
Summary:
LLVM has a warning `-Wdeprecated-redundant-constexpr-static-def` which raises the warning:

> warning: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated

Since we are now on C++20, we can remove the out-of-line definition of constexpr static data members. This diff does so.

 - If you approve of this diff, please use the "Accept & Ship" button :-)

Differential Revision: D77423205

fbshipit-source-id: 4ee4a390431a5d25e7733311f3fa40395dfd4bc0
2025-07-09 16:36:31 -07:00
Richard Barnes 9a64ebde0c Fix unused-return in internal_repo_rocksdb/repo/db/log_reader.cc +1
Summary:
LLVM has a warning `-Wunreachable-code-return` which identifies return statements that cannot be reached.

In innocuous situations such statements are often present:
* to satisfy a compiler warning that existed before `[[noreturn]]` was introduced. Now that we have `[[noreturn]]`, this use is not necessary.
* to specify a return type. But there are clearer ways to do this.
* in place of the more legible `__builtin_unreachable()` (which will soon become `std::unreachable()`). In this case, we should use the more legible alternative.
* because the programmer was afraid of the function unexpectedly returning. But we check for this condition with `-Wreturn-type`.

In dangerous situations such statements can obscure the intended execution of the program or even hide an erroneous early return.

In this diff, we remove one or more unreachable returns.

 - If you approve of this diff, please use the "Accept & Ship" button :-)

Differential Revision: D77424529

fbshipit-source-id: fe41b5a640264d0a299d5ad330c645f94b147323
2025-07-09 16:35:04 -07:00
Xingbo Wang 11a259a5f0 Support GetFileSize API in FSRandomAccessFile (#13676)
Summary:
Add file size validation in ReadFooterFromFile function.
    Deprecate skip_checking_sst_file_sizes_on_db_open option.
    This change is used to address this issue
    https://github.com/facebook/rocksdb/issues/13619
    It supports file size validation in ReadFooterFromFile. In favor of this
    change, CheckConsistency function and
    skip_checking_sst_file_sizes_on_db_open flag are deprecated.

    The CheckConsistency function checks each file size matches what was
    recorded in manifest during DB open. Meantime, ReadFooterFromFile was
    called for each file in LoadTables function. Since ReadFooterFromFile
    always validates file size, the CheckConsistency is redundant.

    In addtion, CheckConsistency is executed in a single thread. This could
    slow down DB open when a network file system is used. Therefore, the
    flag skip_checking_sst_file_sizes_on_db_open was added to skip this
    check. After this change, ReadFooterFromFile was executed in parallel
    through multiple threads. Therefore, the concern of DB open slowness is
    eliminated, and the flag could be deprecated.

    When paranoid check flag is set to true, corrupted file will fail to open the DB.
    When paranoid check flag is set to false, DB will still be able to open, the
    healthy ones can be accessed, while the corrupted ones not.

    There is 2 slight concerns of this change.

    *If max_open_files is set with smaller value, engine will not open all
    the files during DB open. This means if there is a corruption on file
    size, it will not be detected during DB open, but rather at a later
    time. Since the default is -1, which means open all the files, and it is
    rarely overridden and a lot of new features rely on it to be -1, the
    risk is very low.

    *If FIFO compaction is used, engine could fail to open DB unnecessarily
    on the corrupted files that would never be used again. However, this is
    a very rare case as well. The error could still be ignored by setting
    paranoid_checks operationally. The risk is very low.

    To remain backward compatibility. The public facing flag was kept and
    marked as no-op internally. Another change is required to fully remove
    the flag.

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

Test Plan:
make check
    A new unit test was added to validate file size check API works as
    expected.

Reviewed By: pdillinger

Differential Revision: D76168033

Pulled By: xingbowang

fbshipit-source-id: 8ceacf39bcfe02ff7aa289868c341366ee9f3a8e
2025-07-09 10:40:28 -07:00
Andrew Chang 29c65d8bff Remove stats_ field from SstFileManagerImpl (#13757)
Summary:
`SstFileManager` is supposed to be thread-safe for all of its public methods, but `SetStatisticsPtr` leads to a race condition because the access to `stat_` is not synchronized. We don't use `stat_` internally so we can get rid of it.

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

Test Plan: Existing unit tests.

Reviewed By: mszeszko-meta

Differential Revision: D77962592

Pulled By: archang19

fbshipit-source-id: e8e56194dda034935ddef44e479243770a73d065
2025-07-08 15:54:42 -07:00
Anand Ananthabhotla 9758482360 User defined index builder (#13726)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13726

Add UserDefinedIndexFactory and UserDefinedIndexBuilder interfaces to allow users to plugin custom index implementation into block based table. The factory is specified in BlockBasedTableOptions. If non-null, BlockBasedTableBuilder allocates a wrapper index builder encapsulating the native index and the custom index. The custom index is exposed to BlockBasedTableBuilder as a meta_block of type kUserDefinedIndex. This block type is not compressed.

The IndexBuilder OnKeyAdded interface is enhanced to accept the value in addition to the key. Only full values are supported, and parallel compression is not supported since we cannot obtain the value when calling OnKeyAdded.

Reviewed By: pdillinger

Differential Revision: D76165614

fbshipit-source-id: dfad9cbd6d0359987b7f4abe64cae58c472836f9
2025-07-08 15:10:10 -07:00
akabcenell 3381e4d787 Change GetWaitingTxns() to return blocking lock on timeout (#13754)
Summary:
While a transaction is waiting on a lock, we can use GetWaitingTxns() to determine the transactionID of the blocking transaction and the contended key. However, this gets cleared when the lock times out, so if a client has widespread timeout errors, you need to catch a transaction 'in the act' before they actually hit the timeout in order to understand the contention pattern. This diff adds a new TransactionOptions variable enable_get_waiting_txn_after_timeout, which persists the lock contention information after timeout so it can be accessed by the client after they have received the timeout error.

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

Test Plan:
- updated TransactionTest.WaitingTxn to test the changed behavior
- ran production shadow tests on traffic with frequent timeouts

Reviewed By: cbi42

Differential Revision: D77703598

Pulled By: akabcenell

fbshipit-source-id: b4448ca1b6a3694d51bfe1ce801b09eb376ff3e9
2025-07-08 15:01:59 -07:00
Alan Paxton 805ac7c887 Update compression libraries to latest releases (#13609)
Summary:
See `Makefile` for actual changes:

* ZLIB remains the same
* BZIP2 remains the same
* SNAPPY is a minor update
* LZ4 is a significant update with multithreaded/multicore compression https://github.com/lz4/lz4/releases/tag/v1.10.0
* ZSTD is a significant update RocksDB is called out as benefiting in particular from the performance improvements herein https://github.com/facebook/zstd/releases/tag/v1.5.7

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

Reviewed By: archang19

Differential Revision: D77877295

Pulled By: mszeszko-meta

fbshipit-source-id: bf9a257e8f68dec3d02743b339aa2df65df4ab2c
2025-07-07 13:28:49 -07:00
Peter Dillinger ab6ba62eb1 Possible fix for CacheWithSecondaryAdapter assertion failures (#13747)
Summary:
Was reading sec_cache_res_ratio_ outside of mutex and using the result for computation that needs to be synchronized

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

Test Plan: existing tests. Has been showing up in crash test, and there's no interesting concurrency here that would warrant a regression test based on sync points.

Reviewed By: cbi42

Differential Revision: D77607660

Pulled By: pdillinger

fbshipit-source-id: 12a71936b3558c7528d229a11c7d2e43982ad06b
2025-07-02 18:58:14 -07:00
Changyu Bi f081d145cf Backport internal changes (#13752)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13752

... to github repo. This include changes from D77323287,  D77473923 and the release note change in patch release: D77611483.

Reviewed By: archang19

Differential Revision: D77670619

fbshipit-source-id: 37d877f3317c71de190128fa4da6b18f6dfcf3c5
2025-07-02 17:31:16 -07:00
Changyu Bi 4f7d3a0cb2 Add a new periodic task to trigger compactions (#13736)
Summary:
address an existing limitation on compaction triggering mechanism that relies on events like flush/compaction/SetOptions. This is important for periodic compactions where files can become eligible without any of these events. The periodic task now runs every 12 hours and check CFs that enables `periodic_compaction_second` (TBD if we want to expand to all CFs) for eligible compactions.

Some of the periodic tasks probably don't need to run immediately after Register(). I'm keeping the existing behavior for now for patch release and to makes tests happy.

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

Test Plan:
- new unit test that fails before this change.
- ran crash test for hours with the periodic task running every 5 seconds: `python3 ./tools/db_crashtest.py blackbox --test_batches_snapshot=0 --periodic_compaction_seconds=10`

Reviewed By: pdillinger

Differential Revision: D77460715

Pulled By: cbi42

fbshipit-source-id: 00f61502753185e76830c9ed44c5ccc4f4f16bfa
2025-07-01 11:07:51 -07:00
Sujit Maharjan 3cc76aae83 Fix nightly build failure because preferred compression type was kNoCompression. (#13739)
Summary:
CostAwareCompressor simply ignores the preferred compression type as compression manager setting takes precedence over the compression type setting. Thus, I am removing the assert statement as it itself is unnecessary for this case.

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

Test Plan:
Run nightly build test
```bash
make V=1 J=4 -j4 check
```

Reviewed By: hx235

Differential Revision: D77470932

Pulled By: shubhajeet

fbshipit-source-id: ebb69367d2ffb9bd72432fd04b0cd12ce2d6240a
2025-06-28 09:24:14 -07:00
Peter Dillinger 80c9eec6b6 Improve debugging of CacheWithSecondaryAdapter failures (#13737)
Summary:
improve assertions, one apparently a previous typo in https://github.com/facebook/rocksdb/issues/13606 and one a suspected possible area of logic error

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

Test Plan: watch crash test

Reviewed By: anand1976

Differential Revision: D77453102

Pulled By: pdillinger

fbshipit-source-id: d4196910a9e8d59ef814130a52ff4ebf188a976d
2025-06-27 12:47:32 -07:00
Andrew Chang 7183422b17 Check that NewWritableFile succeeded when copying over backup files (#13734)
Summary:
I am seeing crashes during backups. The stack trace points back to `WritableFileWriter` creation inside `BackupEngineImpl::CopyOrCreateFile`. I believe the issue is that we are calling `writable_file_->GetRequiredBufferAlignment()` with a `null` `writable_file`.

https://github.com/facebook/rocksdb/blob/v10.2.1/utilities/backup/backup_engine.cc#L2396-L2397

https://github.com/facebook/rocksdb/blob/v10.2.1/file/writable_file_writer.h#L210

Here's how I think the flow is:

```cpp
  io_s = dst_env->GetFileSystem()->NewWritableFile(dst, dst_file_options,
                                                   &dst_file, nullptr);
// say there was some issue and dst_file is nullptr
// evaluates to false
if (io_s.ok() && !src.empty()) {
   // we don't go down this branch
    auto src_file_options = FileOptions(src_env_options);
    src_file_options.temperature = *src_temperature;
    io_s = src_env->GetFileSystem()->NewSequentialFile(src, src_file_options,
                                                       &src_file, nullptr);
  }
  // say this evaluates to true
  if (io_s.IsPathNotFound() && *src_temperature != Temperature::kUnknown) {
    // Retry without temperature hint in case the FileSystem is strict with
    // non-kUnknown temperature option
    io_s = src_env->GetFileSystem()->NewSequentialFile(
        src, FileOptions(src_env_options), &src_file, nullptr);
  }
// this is now from the NewSequentialFile call, not NewWritableFile
  if (!io_s.ok()) {
    return io_s;
  }
// dst_file is still nullptr
```

If the first `NewWritableFile` fails and `IsPathNotFound

Tests: existing unit tests

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

Reviewed By: pdillinger

Differential Revision: D77390694

Pulled By: archang19

fbshipit-source-id: 865a3a646079ae2349a3b6f25e53ae85df8e4985
2025-06-26 15:19:26 -07:00
Xingbo Wang ca2413545c Remove the 2 duplicated fields in block.h Block class (#13733)
Summary:
`data_` and `size_` fields are duplicated in `Block` class, as `contents_` field already have a `data` member variable, which contains `data` and `size` already. This reduces memory consumption by 16 bytes per block.

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

Test Plan: Unit test

Reviewed By: pdillinger

Differential Revision: D77389791

Pulled By: xingbowang

fbshipit-source-id: 50a56bc5fae494ed5bc39bdfde7303ca06ce87c6
2025-06-26 13:55:33 -07:00
Sujit Maharjan 4e425887e7 Removing typo sss in spelling of the compression (#13735)
Summary:
Corrected misspelling of "Compression". Changed "Compresssion" to "Compression".

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

Test Plan:
All the test case for compression is still working properly.
```bash
./compression_test
```

Reviewed By: hx235

Differential Revision: D77390273

Pulled By: shubhajeet

fbshipit-source-id: f5310e393e23f5d6c8310154cb929db4b6c60a77
2025-06-26 13:32:28 -07:00
anand76 8b84390517 Add upper bound support for forward scans in MultiScan (#13723)
Summary:
Respect the scan upper bound/limit, if specified, in `MultiScan`. This applies to block based table and other native RocksDB SSTs. In order to properly support it, the `MultiScan` object caches the `ReadOptions` passed by the user and sets the `iterate_upper_bound` as appropriate. We optimize for the case of either all scans specifying the upper bound, or none of them. In case of mixed scans, we reallocate the DB iterator anytime `ReadOptions` has to be updated.

Tests:
New unit tests in `db_iterator_test`

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

Reviewed By: cbi42

Differential Revision: D77385049

Pulled By: anand1976

fbshipit-source-id: 9c02d125770cbedbe6e8c10767ba537e7f7540e1
2025-06-26 12:19:16 -07:00
Sujit Maharjan fd95bc8f5a Custom Compressor for predicting the CPU and IO cost of the block level compression (#13711)
Summary:
This pull request implements the prediction aspect of auto-tuning compression in RocksDB, as part of Milestone 2. The goal is to optimize compression decisions to meet a given CPU and IO budget, based on the predicted CPU time and result compression ratio for compression decisions on a data block.

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

Test Plan:
Ran benchmark tests to evaluate performance impact of new algorithm
Verified that optimization does not compromise overall system performance
```bash
SUFFIX=`tty | sed 's|/|_|g'`; for ARGS in "-compression_parallel_threads=1 -compression_type=zstd -compression_manager=none"  "-compression_parallel_threads=4 -compression_type=zstd -compression_manager=none" "-compression_parallel_threads=1 -compression_type=zstd -compression_manager=costpredictor"  "-compression_parallel_threads=4 -compression_type=zstd -compression_manager=costpredictor" ; do echo $ARGS; (for I in `seq 1 20`; do ./db_bench -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 $ARGS 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done

```
parallel threads | 1 | 4
-- | -- | --
master branch | 1076660.5 ops | 1668411.3 ops
new code compression manager="none" | 1057155.35 ops (-1.81%) | 1648664.2 ops (-1.18%)
new code compression manager="costpredictor" | 1080794.8 ops (0.38%)| 1652720.35 ops (-0.94%)

Used the mean absolute percentage error (MAPE) to show accuracy of the predictor.
```bash
./db_bench --db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq --compaction_style=2 --num=10000000 --fifo_compaction_max_table_files_size_mb=1000 --fifo_compaction_allow_compaction=0 --disable_wal --write_buffer_size=12000000 --statistics --stats_level=5 --value_size=2000 --compression_manager=costpredictor --compression_type=zstd --progress_reports=false 2>&1 | tee /tmp/predict.log
```

compression_name | compression_level | MAPE (cpu cost) | MAPE (io cost) | average measured_time (micro sec) | average predicted_time (micro sec) | average measured_io (bytes) | average predicted_io (bytes)
-- | -- | -- | -- | -- | -- | -- | --
Snappy | 0 | 16.979548 | 3.138885 | 3.639488 | 2.98755 | 2257.655152 | 2178.070375
LZ4 | 1 | 15.508632 | 3.103681 | 4.733639 | 4.010361 | 2257.803299 | 2179.82233
LZ4 | 4 | 15.471204 | 3.102158 | 4.731955 | 4.006011 | 2258.529203 | 2179.778441
LZ4 | 9 | 15.429305 | 3.09599 | 4.729104 | 4.007059 | 2257.822368 | 2179.927506
LZ4HC | 1 | 7.254545 | 3.112858 | 79.64412 | 76.603272 | 2258.636774 | 2177.464922
LZ4HC | 4 | 7.249132 | 3.085802 | 79.591264 | 76.576416 | 2255.098757 | 2176.126082
LZ4HC | 9 | 7.248921 | 3.09695 | 79.719061 | 76.614155 | 2253.772057 | 2175.882686
ZSTD | 1 | 8.728305 | 3.223971 | 18.93434 | 17.882706 | 1957.773706 | 1890.895071
ZSTD | 15 | 4.853552 | 3.238199 | 329.396574 | 318.277613 | 1918.021616 | 1853.833546
ZSTD | 22 | 4.275209 | 3.243137 | 625.471394 | 596.254939 | 1919.035477 | 1853.44902

```bash
./db_bench --db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq --compaction_style=2 --num=10000000 --fifo_compaction_max_table_files_size_mb=1000 --fifo_compaction_allow_compaction=0 --disable_wal --write_buffer_size=12000000 --statistics --stats_level=5 --value_size=2000 --compression_manager=costpredictor --compression_type=zstd --progress_reports=false --write_buffer_size=140737488355328 --block_size=16382
```
Increasing the block size i.e. doubling the measured time reduces the MAPE by half.
compression_name | compression_level | MAPE (cpu cost) | MAPE (io cost) | average measured_time (micro sec) | average predicted_time (micro sec) | average measured_io (bytes) | average predicted_io (bytes)
-- | -- | -- | -- | -- | -- | -- | --
Snappy | 0 | 7.933944 | 0.061173 | 7.187587 | 6.815071 | 4466.536629 | 4465.925648
LZ4 | 1 | 5.614279 | 0.050215 | 8.526641 | 8.14445 | 4473.768752 | 4473.159792
LZ4 | 4 | 5.617925 | 0.050317 | 8.525155 | 8.144209 | 4473.772343 | 4473.159782
LZ4 | 9 | 5.65519 | 0.050249 | 8.530569 | 8.14836 | 4473.762187 | 4473.150695
LZ4HC | 1 | 4.259648 | 0.028564 | 98.273778 | 97.820515 | 4471.691596 | 4471.05918
LZ4HC | 4 | 4.269529 | 0.027665 | 98.240579 | 97.788721 | 4465.537078 | 4464.901328
LZ4HC | 9 | 4.274553 | 0.027555 | 98.319357 | 97.8637 | 4465.539437 | 4464.903889
ZSTD | 1 | 4.909716 | 0.155441 | 29.503133 | 29.047057 | 3713.562704 | 3712.978633
ZSTD | 15 | 1.310407 | 0.162864 | 643.803097 | 635.960631 | 3797.544307 | 3705.772419
ZSTD | 22 | 1.011497 | 0.155876 | 1221.189822 | 1220.693678 | 3705.556448 | 3704.972332

Reviewed By: hx235

Differential Revision: D77065528

Pulled By: shubhajeet

fbshipit-source-id: f7f4ae018f786bfeae3eacf0135055c63e142610
2025-06-26 08:59:56 -07:00
Changyu Bi 08dc5cacd9 Check op count in WBWI vs WB when ingesting WBWI (#13722)
Summary:
Large txn commit optimization requires all updates are added to a transaction's WriteBatchWithIndex. However, some usage of transactions may add updates directly to the WBWI's underlying write batch. In these cases, we should not attempt to ingest the WBWI since it will drop these updates. This PR adds sanity checking for this.

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

Test Plan:
- added checks in unit test and stress test
- manually check LOG files for the new unit test

Reviewed By: hx235

Differential Revision: D77247688

Pulled By: cbi42

fbshipit-source-id: 3d1c0c6e64d6d7dfd5578bc4d77abe44cac1e419
2025-06-25 13:32:08 -07:00
Changyu Bi 29ec7aaa51 Update CI jobs for upgrade and cost (#13717)
Summary:
The Windows 2019 will be [deprecated](https://github.com/actions/runner-images/issues/12045) soon so I'm updating it to Windows 2022, and removed the same job from nightly runs.

To save some CI cost, I moved some jobs into nightly since they have low failure rates and examples/fuzzers are not updated often: https://github.com/facebook/rocksdb/actions/metrics/performance?dateRangeType=DATE_RANGE_TYPE_PREVIOUS_MONTH&sort=failureRate%2CORDER_BY_DIRECTION_ASC&tab=jobs&filters=workflow_file_name%3Apr-jobs.yml.

I don't think microbench is used/looked at so I'm deleting it from nightly too.

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

Test Plan: CI

Reviewed By: jaykorean

Differential Revision: D77234715

Pulled By: cbi42

fbshipit-source-id: 75a5edf56391e4743efa1824b4070208ef10f280
2025-06-25 12:39:39 -07:00
Sujit Maharjan 820a30f0d2 Fix AutoSkipCompressionManager test should not be run with preferred compression kNoCompression (#13716)
Summary:
The nightly build was failing because we were using the AutoSkipCompressionManager with kNoCompression. The test cases should not be running with NoCompression.

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

Test Plan:
Run the test code being run on the nightly build.
```bash
  make V=1 J=4 -j4 check
```

Reviewed By: hx235

Differential Revision: D77042874

Pulled By: shubhajeet

fbshipit-source-id: 821643b30ca53b1855fc24e3bc0a319e4fec2876
2025-06-23 11:10:13 -07:00
Maciej Szeszko f2d03736a7 Start development 10.5 (#13719)
Summary:
* Release notes from 10.4 branch
* Update version.h
* Add [10.4.fb](https://github.com/facebook/rocksdb/tree/10.4.fb) (to check_format_compatible.sh
* Update folly commit hash.

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

Test Plan: Release collateral.

Reviewed By: anand1976

Differential Revision: D77062142

Pulled By: mszeszko-meta

fbshipit-source-id: 66c61323580386eb062e8763bba5d3480aadbc80
2025-06-20 21:02:39 -07:00
anand76 f340a2eccc Port codemod changes from fbcode/rocksdb (#13714)
Summary:
Port changes made directly in fbcode in order to facilitate the 10.4 release.

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

Test Plan: Existing tests

Reviewed By: mszeszko-meta

Differential Revision: D77038668

Pulled By: anand1976

fbshipit-source-id: 6b9b16d62bccf75923b525c1c24597a59920a948
2025-06-20 17:56:24 -07:00
Peter Dillinger 78c83ac1ec Publish/support format_version=7, related enhancements (#13713)
Summary:
* Make new format_version=7 a supported setting.
* Fix a bug in compressed_secondary_cache.cc that is newly exercised by custom compression types and showing up in crash test with tiered secondary cache
* Small change to handling of disabled compression in fv=7: use empty compression manager compatibility name.
* Get rid of GetDefaultBuiltinCompressionManager() in public API because it could cause unexpected+unsafe schema change on a user's CompressionManager if built upon the default built-in manager and we add a new built-in schema. Now must be referenced by explicit compression schema version in the public API. (That notion was already exposed in compressed secondary cache API, for better or worse.)
* Improve some error messages for compression misconfiguration
* Improve testing with ObjectLibrary and CompressionManagers
* Improve testing of compression_name table property in BlockBasedTableTest.BlockBasedTableProperties2
* Improve some comments

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

Test Plan: existing and updated tests. Notably, the crash test has already been running with (unpublished) format_version=7

Reviewed By: mszeszko-meta, hx235

Differential Revision: D77035482

Pulled By: pdillinger

fbshipit-source-id: 95278de8734a79706a22361bff2184b1edb230ca
2025-06-20 17:39:47 -07:00
Maciej Szeszko 190bb0bd24 Disable AutoSkipCompressionManager test (#13715)
Summary:
Auto skip compression manager code is currently running only in context of test / db bench. Disable failing test to unblock monthly minor release.

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

Test Plan: Disable test.

Reviewed By: hx235

Differential Revision: D77039218

Pulled By: mszeszko-meta

fbshipit-source-id: f9eeec8d5ca4efeaf1f490c5f091b3aff7861a4a
2025-06-20 12:38:32 -07:00
Peter Dillinger fdc2970d37 Connect custom compression to crash test and ObjectLibrary (#13710)
Summary:
Some pieces of follow-up to https://github.com/facebook/rocksdb/issues/13659.
_Recommend hiding whitespace for review_
* Add support for instantiating CompressionManagers through CreateFromString/ObjectLibrary.
* Pull CompressorCustomAlg and DecompressorCustomAlg out of db_test2, refactor/improvement them a bit, and put them in testutil.h for sharing with db_stress. Switched it from being built on snappy to being built on lz4 so that it can properly test dictionary compression.
* Add a custom compression manager for db_stress that uses these, and add to crash test. This depends on the ObjectLibrary stuff because some invocations of db_stress will not be configured with the custom compression manager but will need to access it to read some existing SST files.
* Remove some pieces where the concern of setting compression=kZSTD for compatibility purposes had leaked into configuring some tests and compression managers. After https://github.com/facebook/rocksdb/issues/13659 this compatibility concern is contained in the SST building code.
* Fix BuiltinDecompressorV2SnappyOnly hiding the (ignored) compression dictionary. SST read logic expects the serialized dictionary to be returned by the decompressor even if it's effectively ignored. Updated DBBlockCacheTest.CacheCompressionDict to cover this case.

For follow-up:
* Combine custom compression and mixed compression types in a file (not clean/easy without duplicating or majorly refactoring the mixed/random compressor)

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

Test Plan: unit tests updated

Reviewed By: hx235

Differential Revision: D76928974

Pulled By: pdillinger

fbshipit-source-id: 772cf9cb048d737699b0e2887c624fb64a68aa8c
2025-06-19 12:54:15 -07:00
Changyu Bi d55655a423 Add an optional min file size requirement for deletion triggered compaction (#13707)
Summary:
add the `min_file_size` parameter to CompactOnDeletionCollector. A file must be at least this size for it to qualify for DTC. This is useful when a user wants to specific a min file size requirement that is larger than the size constraint imposed by the sliding window's `deletion_trigger` requirement.

Added some comment explaining that the file_size provided to table property collector only includes data blocks and may not be up-to-date. This PR also updates DTC to consider SingleDelete and DeletionWithTimestamp as tombstones.

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

Test Plan:
- new unit test for when min_file_size is specified.
- existing unit test for when min_file_size is not specified.

Reviewed By: hx235, pdillinger

Differential Revision: D76837231

Pulled By: cbi42

fbshipit-source-id: 0782144e75aef9961bf03da2a2c4b3c613ce5db3
2025-06-19 11:04:35 -07:00
Changyu Bi c8aafdba33 Support concurrent write for vector memtable (#13675)
Summary:
Some usage of vector memtable is bottlenecked in the memtable insertion path when using multiple writers. This PR adds support for concurrent writes for the vector memtable. The updates from each concurrent writer are buffered in a thread local vector. When a writer is done, MemTable::BatchPostProcess() is called to flush the thread local updates to the main vector. TSAN test and function comment suggest that ApproximateMemoryUsage() needs to be thread-safe, so its implementation is updated to provide thread-safe access.

Together with unordered_write, benchmark shows much improved insertion throughput.

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

Test Plan:
- new unit test
- enabled some coverage of vector memtable in stress test
- Performance benchmark: benchmarked memtable insertion performance with by running fillrandom 20 times
  - Compare branch and main performance with one thread and write batch size 100:
    - main: 4896888.950 ops/sec
    - branch: 4923366.350 ops/sec
  - Benchmark this branch by configuring different threads, allow_concurrent_memtable_write, and unordered_write. Performance ratio is computed as current ops/sec divided by ops/sec at 1 thread with the same options.

allow_concurrent | unordered_write | Threads | ops/sec | Performance Ratio
-- | -- | -- | -- | --
0 | 0 | 1 | 4923367 | 1.0
0 | 0 | 2 | 5215640 | 1.1
0 | 0 | 4 | 5588510 | 1.1
0 | 0 | 8 | 6077525 | 1.2
1 | 0 | 1 | 4919060 | 1.0
1 | 0 | 2 | 5821922 | 1.2
1 | 0 | 4 | 7850395 | 1.6
1 | 0 | 8 | 10516600 | 2.1
1 | 1 | 1 | 5050004 | 1.0
1 | 1 | 2 | 8489834 | 1.7
1 | 1 | 4 | 14439513 | 2.9
1 | 1 | 8 | 21538098 | 4.3

```
mkdir -p /tmp/bench_$1
export TEST_TMPDIR=/tmp/bench_$1

memtablerep_value=${6:-vector}

(for I in $(seq 1 $2)
do
	/data/users/changyubi/vscode-root/rocksdb/$1 --benchmarks=fillrandom --seed=1722808058 --write_buffer_size=67108864 --min_write_buffer_number_to_merge=1000 --max_write_buffer_number=1000 --enable_pipelined_write=0 --memtablerep=$memtablerep_value --disable_auto_compactions=1 --disable_wal=1 --avoid_flush_during_shutdown=1 --allow_concurrent_memtable_write=${5:-0} --unordered_write=$4 --batch_size=1 --threads=$3 2>&1 | grep "fillrandom"
done;) | awk '{ t += $5; c++; print } END { printf ("%9.3f\n", 1.0 * t / c) }';
```

Reviewed By: pdillinger

Differential Revision: D76641755

Pulled By: cbi42

fbshipit-source-id: c107ba42749855ad4fd1f52491eb93900757542e
2025-06-18 17:32:59 -07:00
Peter Dillinger 1601da4049 Improve file checksum handling for ingestion (#13708)
Summary:
* Improve debugability with better error messages (including the returned status, not just log messages)
* Tolerate user providing file checksums recognized by the factory but not the same function as currently, generally provided by the factory. This makes it practical to transition from one type of checksum to another without major hiccups in ingestion workflows.

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

Test Plan: updated unit test, manually inspect LOG file from the unit test

Reviewed By: cbi42

Differential Revision: D76837804

Pulled By: pdillinger

fbshipit-source-id: 45b744829b3a125e9d0ee6874bd37ce534c2e13c
2025-06-17 21:34:34 -07:00
Sujit Maharjan 34d8f03af4 Moving predictor to WorkingArea to make it thread safe (#13706)
Summary:
**Summary:**

We need to move the Predictor to WorkingArea so that it is local to each thread and thus is thread safe.

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

Test Plan: It should pass the test case written in ./compression_test.

Reviewed By: pdillinger

Differential Revision: D76836846

Pulled By: shubhajeet

fbshipit-source-id: 0d0170baf65f4bb95ba107fec77151e66b8a4449
2025-06-17 19:17:25 -07:00
Sujit Maharjan 05996cd497 crash test and other refactoring (#13704)
Summary:
**Summary:**
AddressSanitizer failed complaining stack-use-after-return while executing AutoSkipCompressorWrapper::CompressBlock. This was caused because the AutoSkipCompressorWrapper was storing const reference pointer to Compression Options. It seems like the life time of the Compression Options can be shorter than the AutoSkipCompressorWrapper thus we need to copy the Compression Options and store it in AutoSkipCompressorWrapper.

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

Test Plan:
Run the crashtest again to verify that we don’t encounter the issue again.
```bash
make clean
COMPILE_WITH_ASAN=1 make -j80 dbg
mkdir -p /dev/shm/rocksdb_test/rocksdb_crashtest_blackbox
mkdir -p dev/shm/rocksdb_test/rocksdb_crashtest_expected
./db_stress --WAL_size_limit_MB=0 --WAL_ttl_seconds=0 --acquire_snapshot_one_in=100 --adaptive_readahead=0 --adm_policy=2 --advise_random_on_open=1 --allow_data_in_errors=True --allow_fallocate=1 --allow_setting_blob_options_dynamically=1 --allow_unprepared_value=1 --async_io=0 --auto_readahead_size=1 --auto_refresh_iterator_with_snapshot=0 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=1 --avoid_unnecessary_blocking_io=1 --backup_max_size=104857600 --backup_one_in=100000 --batch_protection_bytes_per_key=0 --bgerror_resume_retry_interval=1000000 --blob_cache_size=2097152 --blob_compaction_readahead_size=0 --blob_compression_type=snappy --blob_file_size=16777216 --blob_file_starting_level=2 --blob_garbage_collection_age_cutoff=0.5 --blob_garbage_collection_force_threshold=0.5 --block_align=0 --block_protection_bytes_per_key=2 --block_size=16384 --bloom_before_level=2147483646 --bloom_bits=9.703060295811829 --bottommost_compression_type=disable --bottommost_file_compaction_delay=0 --bytes_per_sync=262144 --cache_index_and_filter_blocks=1 --cache_index_and_filter_blocks_with_high_priority=0 --cache_size=33554432 --cache_type=fixed_hyper_clock_cache --charge_compression_dictionary_building_buffer=0 --charge_file_metadata=1 --charge_filter_construction=1 --charge_table_reader=0 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=1000000 --checksum_type=kXXH3 --clear_column_family_one_in=0 --compact_files_one_in=1000 --compact_range_one_in=1000000 --compaction_pri=1 --compaction_readahead_size=1048576 --compaction_style=0 --compaction_ttl=0 --compress_format_version=1 --compressed_secondary_cache_size=16777216 --compression_checksum=0 --compression_manager=autoskip --compression_max_dict_buffer_bytes=0 --compression_max_dict_bytes=0 --compression_parallel_threads=1 --compression_type=zlib --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --daily_offpeak_time_utc= --data_block_index_type=1 --db=/dev/shm/rocksdb_test/rocksdb_crashtest_blackbox --db_write_buffer_size=1048576 --decouple_partitioned_filters=1 --default_temperature=kUnknown --default_write_temperature=kWarm --delete_obsolete_files_period_micros=30000000 --delpercent=4 --delrangepercent=1 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_file_deletions_one_in=10000 --disable_manual_compaction_one_in=1000000 --disable_wal=0 --dump_malloc_stats=0 --enable_blob_files=1 --enable_blob_garbage_collection=1 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=0 --enable_do_not_compress_roles=0 --enable_index_compression=1 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=0 --enable_remote_compaction=0 --enable_sst_partitioner_factory=0 --enable_thread_tracking=1 --enable_write_thread_adaptive_yield=0 --error_recovery_with_no_fault_injection=0 --exclude_wal_from_write_fault_injection=0 --expected_values_dir=/dev/shm/rocksdb_test/rocksdb_crashtest_expected --fifo_allow_compaction=1 --file_checksum_impl=xxh64 --file_temperature_age_thresholds= --fill_cache=1 --flush_one_in=1000 --format_version=2 --get_all_column_family_metadata_one_in=10000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=100000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0 --index_block_restart_interval=15 --index_shortening=2 --index_type=2 --ingest_external_file_one_in=1000 --ingest_wbwi_one_in=0 --initial_auto_readahead_size=0 --inplace_update_support=0 --iterpercent=10 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100000 --last_level_temperature=kHot --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=0 --log_file_time_to_roll=0 --log_readahead_size=16777216 --long_running_snapshots=1 --low_pri_pool_ratio=0 --lowest_used_cache_tier=2 --manifest_preallocation_size=0 --manual_wal_flush_one_in=0 --mark_for_compaction_one_file_in=10 --max_auto_readahead_size=524288 --max_background_compactions=20 --max_bytes_for_level_base=10485760 --max_key=100000 --max_key_len=3 --max_log_file_size=1048576 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=1 --max_total_wal_size=0 --max_write_batch_group_size_bytes=16 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=1048576 --memtable_avg_op_scan_flush_trigger=0 --memtable_insert_hint_per_batch=1 --memtable_max_range_deletions=0 --memtable_op_scan_flush_trigger=1000 --memtable_prefix_bloom_size_ratio=0.01 --memtable_protection_bytes_per_key=4 --memtable_whole_key_filtering=1 --memtablerep=skip_list --metadata_charge_policy=0 --metadata_read_fault_one_in=0 --metadata_write_fault_one_in=1000 --min_blob_size=16 --min_write_buffer_number_to_merge=2 --mmap_read=1 --mock_direct_io=False --nooverwritepercent=1 --num_bottom_pri_threads=1 --num_file_reads_for_auto_readahead=1 --open_files=100 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=0 --open_read_fault_one_in=0 --open_write_fault_one_in=16 --ops_per_thread=100000000 --optimize_filters_for_hits=0 --optimize_filters_for_memory=0 --optimize_multiget_for_io=1 --paranoid_file_checks=0 --paranoid_memory_checks=0 --partition_filters=1 --partition_pinning=3 --pause_background_one_in=1000000 --periodic_compaction_seconds=0 --prefix_size=7 --prefixpercent=5 --prepopulate_blob_cache=0 --prepopulate_block_cache=1 --preserve_internal_time_seconds=0 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=0 --readahead_size=16384 --readpercent=45 --recycle_log_file_num=0 --reopen=0 --report_bg_io_stats=0 --reset_stats_one_in=10000 --sample_for_compression=0 --secondary_cache_fault_one_in=32 --secondary_cache_uri= --set_options_one_in=1000 --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=1048576 --sqfc_name=bar --sqfc_version=2 --sst_file_manager_bytes_per_sec=0 --sst_file_manager_bytes_per_truncate=0 --stats_dump_period_sec=0 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=0 --subcompactions=3 --sync=0 --sync_fault_injection=0 --table_cache_numshardbits=-1 --target_file_size_base=524288 --target_file_size_multiplier=2 --test_batches_snapshots=0 --test_ingest_standalone_range_deletion_one_in=10 --top_level_index_pinning=2 --uncache_aggressiveness=5100 --universal_max_read_amp=0 --universal_reduce_file_locking=1 --unpartitioned_pinning=2 --use_adaptive_mutex=1 --use_adaptive_mutex_lru=0 --use_attribute_group=1 --use_blob_cache=1 --use_delta_encoding=0 --use_direct_io_for_flush_and_compaction=0 --use_direct_reads=0 --use_full_merge_v1=0 --use_get_entity=0 --use_merge=0 --use_multi_cf_iterator=1 --use_multi_get_entity=0 --use_multiget=1 --use_put_entity_one_in=10 --use_shared_block_and_blob_cache=0 --use_sqfc_for_range_queries=1 --use_timed_put_one_in=0 --use_write_buffer_manager=1 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000 --verify_compression=0 --verify_db_one_in=100000 --verify_file_checksums_one_in=1000000 --verify_iterator_with_expected_state_one_in=5 --verify_sst_unique_id_in_manifest=1 --wal_bytes_per_sync=0 --wal_compression=zstd --write_buffer_size=1048576 --write_dbid_to_manifest=1 --write_fault_one_in=128 --write_identity_file=0 --writepercent=35
```

Reviewed By: hx235

Differential Revision: D76826904

Pulled By: shubhajeet

fbshipit-source-id: c4a1522d3fed37bdd3e711f4c99c16d7bd1d794f
2025-06-17 11:28:33 -07:00
anand76 25837eeee5 Change NewExternalTableFactory to return unique_ptr (#13705)
Summary:
Change NewExternalTableFactory API and remove the just added NewExternalTableFactoryAsUniquePtr.

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

Reviewed By: jaykorean

Differential Revision: D76827580

Pulled By: anand1976

fbshipit-source-id: 251ad0e498b62059b8417ff967ca74146de43e2f
2025-06-17 10:50:33 -07:00
Sujit Maharjan c6cfbc2919 clang tidy warning lsh == rhs. lhs or rhs not initialized. (#13703)
Summary:
**Summary**:

Clang tidy was throwing error that the gtest assertion EXPECT_EQ was being carried out variables that was not initialized.

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

Test Plan:
Ran the clang-tidy operations to make sure the same error does not appear.
```bash
  CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze
```

Reviewed By: hx235, pdillinger

Differential Revision: D76777988

Pulled By: shubhajeet

fbshipit-source-id: b9bfe26a2264d4c21224ab53a0b0307596d7f49d
2025-06-17 09:47:47 -07:00
anand76 d27b47f439 Add NewExternalTableFactoryAsUniquePtr API (#13694)
Summary:
The Object registry requires object to be allocated as std::unique_ptr. Hence we provide a new API for external table plugins to allocate and return a unique_ptr ExternalTableFactory wrapper.

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

Reviewed By: jaykorean

Differential Revision: D76767974

Pulled By: anand1976

fbshipit-source-id: ac59c523a11679ca7c9f0b280325c7873c6b4c07
2025-06-16 17:02:40 -07:00
Peter Dillinger 9d490593d0 Preliminary support for custom compression algorithms (#13659)
Summary:
This change builds on https://github.com/facebook/rocksdb/issues/13540 and https://github.com/facebook/rocksdb/issues/13626 in allowing a CompressionManager / Compressor / Decompressor to use a custom compression algorithm, with a distinct CompressionType. For background, review the API comments on CompressionManager and its CompatibilityName() function.

Highlights:
* Reserve and name 127 new CompressionTypes that can be used for custom compression algorithms / schemas. In many or most cases I expect the enumerators such as `kCustomCompression8F` to be used in user code rather than casting between integers and CompressionTypes, as I expect the supported custom compression algorithms to be identifiable / enumerable at compile time.
* When using these custom compression types, a CompressionManager must use a CompatibilityName() other than the built-in one AND new format_version=7 (see below).
* When building new SST files, track the full set of CompressionTypes actually used (usually just one aside from kNoCompression), using our efficient bitset SmallEnumSet, which supports fast iteration over the bits set to 1. Ideally, to support mixed or non-mixed compression algorithms in a file as efficiently as possible, we would know the set of CompressionTypes as SST file open time.
* New schema for `TableProperties::compression_name` in format_version=7 to represent the CompressionManager's CompatibilityName(), the set of CompressionTypes used, and potentially more in the future, while keeping the data relatively human-readable.
  * It would be possible to do this without a new format_version, but then the only way to ensure incompatible versions fail is with an unsupported CompressionType tag, not with a compression_name property. Therefore, (a) I prefer not to put something misleading in the `compression_name` property (a built-in compression name) when there is nuance because of a CompressionManager, and (b) I prefer better, more consistent error messages that refer to either format_version or the CompressionManager's CompatibilityName(), rather than an unrecognized custom CompressionType value (which could have come from various CompressionManagers).
* The current configured CompressionManager is passed in to TableReaders so that it (or one it knows about) can be used if it matches the CompatibilityName() used for compression in the SST file. Until the connection with ObjectRegistry is implemented, the only way to read files generated with a particular CompressionManager using custom compression algorithms is to configure it (or a known relative; see FindCompatibleCompressionManager()) in the ColumnFamilyOptions.
* Optimized snappy compression with BuiltinDecompressorV2SnappyOnly, to offset some small added overheads with the new tracking. This is essentially an early part of the planned refactoring that will get rid of the old internal compression APIs.
* Another small optimization in eliminating an unnecessary key copy in flush (builder.cc).
* Fix some handling of named CompressionManagers in CompressionManager::CreateFromString() (problem seen in https://github.com/facebook/rocksdb/issues/13647)

Smaller things:
* Adds Name() and GetId() functions to Compressor for debugging/logging purposes. (Compressor and Decompressor are not expected to be Customizable because they are only instantiated by a CompressionManager.)
* When using an explicit compression_manager, the GetId() of the CompressionManager and the Compressor used to build the file are stored as bonus entries in the compression_options table property. This table property is not parsed anywhere, so it is currently for human reading, but still could be parsed with the new underscore-prefixed bonus entries. IMHO, this is preferable to additional table properties, which would increase memory fragmentation in the TableProperties objects and likely take slightly more CPU on SST open and slightly more storage.
* ReleaseWorkingArea() function from protected to public to make wrappers work, because of a quirk in C++ (vs. Java) in which you cannot access protected members of another instance of the same class (sigh)
* Added `CompressionManager:: SupportsCompressionType()` for early options sanity checking.

Follow-up before release:
* Make format_version=7 official / supported
* Stress test coverage

Sooner than later:
* Update tests for RoundRobinManager and SimpleMixedCompressionManager to take advantage of e.g. set of compression types in compression_name property
* ObjectRegistry stuff
* Refactor away old internal compression APIs

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

Test Plan:
Basic unit test added.

## Performance

### SST write performance
```
SUFFIX=`tty | sed 's|/|_|g'`; for ARGS in "-compression_type=none" "-compression_type=snappy" "-compression_type=zstd" "-compression_type=snappy -verify_compression=1" "-compression_type=zstd -verify_compression=1" "-compression_type=zstd -compression_max_dict_bytes=8180"; do echo $ARGS; (for I in `seq 1 20`; do BIN=/dev/shm/dbbench${SUFFIX}.bin; rm -f $BIN; cp db_bench $BIN; $BIN -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 $ARGS 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```

Ops/sec, Before -> After, both fv=6:
-compression_type=none
1894386 -> 1858403 (-2.0%)
-compression_type=snappy
1859131 -> 1807469 (-2.8%)
-compression_type=zstd
1191428 -> 1214374 (+1.9%)
-compression_type=snappy -verify_compression=1
1861819 -> 1858342 (+0.2%)
-compression_type=zstd -verify_compression=1
979435 -> 995870 (+1.6%)
-compression_type=zstd -compression_max_dict_bytes=8180
905349 -> 940563 (+3.9%)

Ops/sec, Before fv=6 -> After fv=7:
-compression_type=none
1879365 -> 1836159 (-2.3%)
-compression_type=snappy
1865460 -> 1830916 (-1.9%)
-compression_type=zstd
1191428 -> 1210260 (+1.6%)
-compression_type=snappy -verify_compression=1
1866756 -> 1818989 (-2.6%)
-compression_type=zstd -verify_compression=1
982640 -> 997129 (+1.5%)
-compression_type=zstd -compression_max_dict_bytes=8180
912608 -> 937248 (+2.7%)

### SST read performance
Create DBs
```
for COMP in none snappy zstd; do echo $ARGS; ./db_bench -db=/dev/shm/dbbench-7-$COMP --benchmarks=fillseq,flush -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -compression_type=$COMP -format_version=7; done
```
And test
```
for COMP in none
snappy zstd none; do echo $COMP; (for I in `seq 1 8`; do ./db_bench -readonly -db=/dev/shm/dbbench
-7-$COMP --benchmarks=readrandom -num=10000000 -duration=20 -threads=8 2>&1 | grep micros/op; done
) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```

Ops/sec, Before -> After (both fv=6)
none
1491732 -> 1500209 (+0.6%)
snappy
1157216 -> 1169202 (+1.0%)
zstd
695414 -> 703719 (+1.2%)
none (again)
1491787 -> 1528789 (+2.4%)

Ops/sec, Before fv=6 -> After fv=7:
none
1492278 -> 1508668 (+1.1%)
snappy
1140769 -> 1152613 (+1.0%)
zstd
696437 -> 696511 (+0.0%)
none (again)
1500585 -> 1512037 (+0.7%)

Overall, I think we can take the read CPU improvement in exchange for the hit (in some cases) on background write CPU

Reviewed By: hx235

Differential Revision: D76520739

Pulled By: pdillinger

fbshipit-source-id: e73bd72502ff85c8779cba313f26f7d1fd50be3a
2025-06-16 14:19:03 -07:00
181 changed files with 7733 additions and 2749 deletions

No files matched your search

+4 -4
View File
@@ -4,8 +4,8 @@ runs:
steps:
- name: Install Maven
run: |
wget --no-check-certificate https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz
tar zxf apache-maven-3.9.10-bin.tar.gz
echo "export M2_HOME=$(pwd)/apache-maven-3.9.10" >> $GITHUB_ENV
echo "$(pwd)/apache-maven-3.9.10/bin" >> $GITHUB_PATH
wget --no-check-certificate https://archive.apache.org/dist/maven/maven-3/3.9.11/binaries/apache-maven-3.9.11-bin.tar.gz
tar zxf apache-maven-3.9.11-bin.tar.gz
echo "export M2_HOME=$(pwd)/apache-maven-3.9.11" >> $GITHUB_ENV
echo "$(pwd)/apache-maven-3.9.11/bin" >> $GITHUB_PATH
shell: bash
+56 -21
View File
@@ -27,18 +27,6 @@ jobs:
git config --global --add safe.directory /__w/rocksdb/rocksdb
tools/check_format_compatible.sh
- uses: "./.github/actions/post-steps"
build-linux-run-microbench:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: DEBUG_LEVEL=0 make -j32 run_microbench
- uses: "./.github/actions/post-steps"
build-linux-non-shm:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
@@ -91,15 +79,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/windows-build-steps"
build-windows-vs2022:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: windows-2022
env:
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/windows-build-steps"
build-linux-arm-test-full:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
@@ -110,3 +89,59 @@ jobs:
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev
- run: make V=1 J=4 -j4 check
- uses: "./.github/actions/post-steps"
build-examples:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 4-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- name: Build examples
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
- uses: "./.github/actions/post-steps"
build-fuzzers:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 4-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- name: Build rocksdb lib
run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
- name: Build fuzzers
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
- uses: "./.github/actions/post-steps"
build-linux-gcc-11-no_test_run:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-folly-lite-no-test:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
env:
CC: gcc-10
CXX: g++-10
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
- uses: "./.github/actions/post-steps"
+8 -63
View File
@@ -109,22 +109,6 @@ jobs:
- uses: "./.github/actions/build-folly"
- 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-cmake-with-folly-lite-no-test:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
env:
CC: gcc-10
CXX: g++-10
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/setup-folly"
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
- uses: "./.github/actions/post-steps"
build-linux-make-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
@@ -198,7 +182,7 @@ jobs:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
- run: "./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression\n"
- run: "./sst_dump --help | grep -E -q 'Supported built-in compression types: kNoCompression$' # Verify no compiled in compression\n"
- uses: "./.github/actions/post-steps"
# ======================== Linux No Test Runs ======================= #
build-linux-release:
@@ -242,34 +226,6 @@ jobs:
- run: apt-get remove -y libgflags-dev
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
- run: if ./db_stress --version; then false; else true; fi
build-examples:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 4-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- name: Build examples
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
- uses: "./.github/actions/post-steps"
build-fuzzers:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 4-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- name: Build rocksdb lib
run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
- name: Build fuzzers
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
- uses: "./.github/actions/post-steps"
build-linux-clang-no_test_run:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
@@ -317,18 +273,7 @@ jobs:
- uses: "./.github/actions/pre-steps"
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
- uses: "./.github/actions/post-steps"
build-linux-gcc-11-no_test_run:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 16-core-ubuntu
container:
image: zjay437/rocksdb:0.6
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
- uses: "./.github/actions/post-steps"
# ======================== Linux Other Checks ======================= #
build-linux-clang10-clang-analyze:
if: ${{ github.repository_owner == 'facebook' }}
@@ -469,11 +414,11 @@ jobs:
- uses: "./.github/actions/post-steps"
# ======================== Windows with Tests ======================= #
# NOTE: some windows jobs are in "nightly" to save resources
build-windows-vs2019:
build-windows-vs2022:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: windows-2019
runs-on: windows-2022
env:
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
steps:
- uses: actions/checkout@v4.1.0
@@ -484,7 +429,7 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: evolvedbinary/rocksjava:centos6_x64-be
image: evolvedbinary/rocksjava:centos7_x64-be
options: --shm-size=16gb
steps:
# The docker image is intentionally based on an OS that has an older GLIBC version.
@@ -512,7 +457,7 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: evolvedbinary/rocksjava:centos6_x64-be
image: evolvedbinary/rocksjava:centos7_x64-be
options: --shm-size=16gb
steps:
# The docker image is intentionally based on an OS that has an older GLIBC version.
@@ -607,7 +552,7 @@ jobs:
runs-on:
labels: 4-core-ubuntu
container:
image: evolvedbinary/rocksjava:rockylinux8_x64-be
image: evolvedbinary/rocksjava:alpine3_x64-be
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
+2 -1
View File
@@ -88,6 +88,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/memtable_list.cc",
"db/merge_helper.cc",
"db/merge_operator.cc",
"db/multi_scan.cc",
"db/output_validator.cc",
"db/periodic_task_scheduler.cc",
"db/range_del_aggregator.cc",
@@ -249,7 +250,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"trace_replay/trace_record_result.cc",
"trace_replay/trace_replay.cc",
"util/async_file_reader.cc",
"util/auto_skip_compressor.cc",
"util/auto_tune_compressor.cc",
"util/build_version.cc",
"util/cleanable.cc",
"util/coding.cc",
+3 -2
View File
@@ -721,6 +721,7 @@ set(SOURCES
db/memtable_list.cc
db/merge_helper.cc
db/merge_operator.cc
db/multi_scan.cc
db/output_validator.cc
db/periodic_task_scheduler.cc
db/range_del_aggregator.cc
@@ -874,13 +875,13 @@ set(SOURCES
trace_replay/trace_record.cc
trace_replay/trace_replay.cc
util/async_file_reader.cc
util/auto_tune_compressor.cc
util/cleanable.cc
util/coding.cc
util/compaction_job_stats_impl.cc
util/comparator.cc
util/compression.cc
util/simple_mixed_compressor.cc
util/auto_skip_compressor.cc
util/compression_context_cache.cc
util/concurrent_task_limiter_impl.cc
util/crc32c.cc
@@ -1447,7 +1448,6 @@ if(WITH_TESTS)
table/table_test.cc
table/block_fetcher_test.cc
test_util/testutil_test.cc
util/compression_test.cc
trace_replay/block_cache_tracer_test.cc
trace_replay/io_tracer_test.cc
tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc
@@ -1459,6 +1459,7 @@ if(WITH_TESTS)
util/autovector_test.cc
util/bloom_test.cc
util/coding_test.cc
util/compression_test.cc
util/crc32c_test.cc
util/defer_test.cc
util/dynamic_bloom_test.cc
+36
View File
@@ -1,6 +1,42 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 10.5.0 (07/18/2025)
### Public API Changes
* 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 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.
### Bug Fixes
* Fix a bug in BackupEngine that can crash backup due to a null FSWritableFile passed to WritableFileWriter.
* Fix DB::NewMultiScan iterator to respect the scan upper bound specified in ScanOptions
### Performance Improvements
* Optimized MultiScan using BlockBasedTable to coalesce I/Os and prefetch all data blocks.
## 10.4.0 (06/20/2025)
### New Features
* Add a new CF option `memtable_avg_op_scan_flush_trigger` that supports triggering memtable flush when an iterator scans through an expensive range of keys, with the average number of skipped keys from the active memtable exceeding the threshold.
* Vector based memtable now supports concurrent writers (DBOptions::allow_concurrent_memtable_write) #13675.
* Add new experimental `TransactionOptions::large_txn_commit_optimize_byte_threshold` to enable optimizations for large transaction commit by transaction batch data size.
* Add a new option `CompactionOptionsUniversal::reduce_file_locking` and if it's true, auto universal compaction picking will adjust to minimize locking of input files when bottom priority compactions are waiting to run. This can increase the likelihood of existing L0s being selected for compaction, thereby improving write stall and reducing read regression.
* Add new `format_version=7` to aid experimental support of custom compression algorithms with CompressionManager and block-based table. This format version includes changing the format of `TableProperties::compression_name`.
### Public API Changes
* Change NewExternalTableFactory to return a unique_ptr instead of shared_ptr.
* Add an optional min file size requirement for deletion triggered compaction. It can be specified when creating `CompactOnDeletionCollectorFactory`.
### Behavior Changes
* `TransactionOptions::large_txn_commit_optimize_threshold` now has default value 0 for disabled. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` now has no effect on transactions.
### Bug Fixes
* Fix a bug where CreateColumnFamilyWithImport() could miss the SST file for the memtable flush it triggered. The exported CF then may not contain the updates in the memtable when CreateColumnFamilyWithImport() is called.
* Fix iterator operations returning NotImplemented status if disallow_memtable_writes and paranoid_memory_checks CF options are both set.
* Fixed handling of file checksums in IngestExternalFile() to allow providing checksums using recognized but not necessarily the DB's preferred checksum function, to ease migration between checksum functions.
## 10.3.0 (05/17/2025)
### New Features
* Add new experimental `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature` along with `CompactionOptionsFIFO::trivial_copy_buffer_size` to allow optimizing FIFO compactions with tiering when kChangeTemperature to move files from source tier FileSystem to another tier FileSystem via trivial and direct copying raw sst file instead of reading thru the content of the SST file then rebuilding the table files.
+19 -19
View File
@@ -2147,14 +2147,14 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
SNAPPY_VER ?= 1.2.1
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
SNAPPY_VER ?= 1.2.2
SNAPPY_SHA256 ?= 90f74bc1fbf78a6c56b3c4a082a05103b3a56bb17bca1a27e052ea11723292dc
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
LZ4_VER ?= 1.9.4
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
LZ4_VER ?= 1.10.0
LZ4_SHA256 ?= 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
ZSTD_VER ?= 1.5.5
ZSTD_SHA256 ?= 98e9c3d949d1b924e28e01eccb7deed865eefebf25c2f21c702e5cd5b63b85e1
ZSTD_VER ?= 1.5.7
ZSTD_SHA256 ?= 37d7284556b20954e56e1ca85b80226768902e2edabd3b649e9e72c0c9012ee3
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
CURL_SSL_OPTS ?= --tlsv1
@@ -2375,27 +2375,27 @@ rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86
rocksdbjavastaticdockerx86:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerx86_64:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_x64-be --platform linux/amd64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerppc64le:
mkdir -p java/target
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_ppc64le-be --platform linux/ppc64le --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerarm64v8:
mkdir -p java/target
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_arm64v8-be --platform linux/aarch64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockers390x:
mkdir -p java/target
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_s390x-be --platform linux/s390x --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerriscv64:
mkdir -p java/target
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_riscv64-be --platform linux/riscv64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerx86musl:
mkdir -p java/target
@@ -2403,19 +2403,19 @@ rocksdbjavastaticdockerx86musl:
rocksdbjavastaticdockerx86_64musl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_x64-musl-be --platform linux/amd64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerppc64lemusl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_ppc64le-musl-be --platform linux/ppc64le --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockerarm64v8musl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_arm64v8-musl-be --platform linux/aarch64 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticdockers390xmusl:
mkdir -p java/target
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
docker run --rm --name rocksdb_linux_s390x-musl-be --platform linux/s390x --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
@@ -2470,8 +2470,8 @@ jtest_run:
jtest: rocksdbjava
cd java;$(MAKE) sample test
jpmd: rocksdbjava rocksdbjavageneratepom
cd java;$(MAKE) pmd
jpmd: rocksdbjavageneratepom
cd java;$(MAKE) java java_test pmd
jdb_bench:
cd java;$(MAKE) db_bench;
@@ -2492,7 +2492,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 d17bf897cb5bbf8f07b122a614e8cffdc38edcde
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
+7 -6
View File
@@ -80,15 +80,16 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
ptr = reinterpret_cast<CacheAllocationPtr*>(handle_value);
handle_value_charge = cache_->GetCharge(lru_handle);
data_ptr = ptr->get();
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
static_cast<uint32_t*>(&type_32));
const char* limit = ptr->get() + handle_value_charge;
data_ptr =
GetVarint32Ptr(data_ptr, limit, static_cast<uint32_t*>(&type_32));
type = static_cast<CompressionType>(type_32);
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
static_cast<uint32_t*>(&source_32));
data_ptr =
GetVarint32Ptr(data_ptr, limit, static_cast<uint32_t*>(&source_32));
source = static_cast<CacheTier>(source_32);
uint64_t data_size = 0;
data_ptr = GetVarint64Ptr(data_ptr, ptr->get() + handle_value_charge,
static_cast<uint64_t*>(&data_size));
data_ptr =
GetVarint64Ptr(data_ptr, limit, static_cast<uint64_t*>(&data_size));
assert(handle_value_charge > data_size);
handle_value_charge = data_size;
}
+4 -5
View File
@@ -130,7 +130,7 @@ CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
"Secondary cache reserved: %zu\n",
pri_cache_res_->GetTotalMemoryUsed(), sec_capacity,
sec_reserved_);
assert(pri_cache_res_mismatch);
assert(!pri_cache_res_mismatch);
}
}
#endif // NDEBUG
@@ -489,12 +489,10 @@ const char* CacheWithSecondaryAdapter::Name() const {
// as well. At the moment, we don't have a good way of handling the case
// where the new capacity < total cache reservations.
void CacheWithSecondaryAdapter::SetCapacity(size_t capacity) {
size_t sec_capacity = static_cast<size_t>(
capacity * (distribute_cache_res_ ? sec_cache_res_ratio_ : 0.0));
size_t old_sec_capacity = 0;
if (distribute_cache_res_) {
MutexLock m(&cache_res_mutex_);
size_t sec_capacity = static_cast<size_t>(capacity * sec_cache_res_ratio_);
size_t old_sec_capacity = 0;
Status s = secondary_cache_->GetCapacity(old_sec_capacity);
if (!s.ok()) {
@@ -613,6 +611,7 @@ Status CacheWithSecondaryAdapter::UpdateCacheReservationRatio(
// cache utilization (increase in capacity - increase in share of cache
// reservation)
// 3. Increase secondary cache capacity
assert(new_sec_reserved >= sec_reserved_);
s = secondary_cache_->Deflate(new_sec_reserved - sec_reserved_);
assert(s.ok());
s = pri_cache_res_->UpdateCacheReservation(
+2 -2
View File
@@ -218,8 +218,7 @@ Status BuildTable(
const Slice& key = c_iter.key();
const Slice& value = c_iter.value();
ParsedInternalKey ikey = c_iter.ikey();
key_after_flush_buf.assign(key.data(), key.size());
Slice key_after_flush = key_after_flush_buf;
Slice key_after_flush = key;
Slice value_after_flush = value;
if (ikey.type == kTypeValuePreferredSeqno) {
@@ -237,6 +236,7 @@ Status BuildTable(
std::min(smallest_preferred_seqno, preferred_seqno);
} else {
// Cannot get a useful preferred seqno, convert it to a kTypeValue.
key_after_flush_buf.assign(key.data(), key.size());
UpdateInternalKey(&key_after_flush_buf, ikey.sequence, kTypeValue);
ikey = ParsedInternalKey(ikey.user_key, ikey.sequence, kTypeValue);
key_after_flush = key_after_flush_buf;
+13
View File
@@ -925,6 +925,10 @@ void rocksdb_backup_engine_options_destroy(
delete options;
}
void rocksdb_status_ptr_get_error(rocksdb_status_ptr_t* status, char** errptr) {
SaveError(errptr, *(status->rep));
}
rocksdb_checkpoint_t* rocksdb_checkpoint_object_create(rocksdb_t* db,
char** errptr) {
Checkpoint* checkpoint;
@@ -3061,6 +3065,10 @@ uint64_t rocksdb_flushjobinfo_smallest_seqno(
return info->rep.smallest_seqno;
}
uint32_t rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.flush_reason);
}
void rocksdb_reset_status(rocksdb_status_ptr_t* status_ptr) {
auto ptr = status_ptr->rep;
*ptr = Status::OK();
@@ -3192,6 +3200,11 @@ int rocksdb_subcompactionjobinfo_output_level(
return info->rep.output_level;
}
uint32_t rocksdb_subcompactionjobinfo_compaction_reason(
const rocksdb_subcompactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.compaction_reason);
}
/* ExternalFileIngestionInfo */
const char* rocksdb_externalfileingestioninfo_cf_name(
-12
View File
@@ -2230,10 +2230,6 @@ int main(int argc, char** argv) {
rocksdb_options_set_skip_stats_update_on_db_open(o, 1);
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(o, 1);
CheckCondition(
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
rocksdb_options_set_max_write_buffer_number(o, 97);
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
@@ -2493,8 +2489,6 @@ int main(int argc, char** argv) {
CheckCondition(2.0 ==
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
CheckCondition(
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
CheckCondition(23 ==
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
@@ -2681,12 +2675,6 @@ int main(int argc, char** argv) {
CheckCondition(0 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(copy, 0);
CheckCondition(
0 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
CheckCondition(
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
rocksdb_options_set_max_write_buffer_number(copy, 2000);
CheckCondition(2000 == rocksdb_options_get_max_write_buffer_number(copy));
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
+37 -10
View File
@@ -110,23 +110,48 @@ void GetInternalTblPropCollFactory(
}
}
Status CheckCompressionSupportedWithManager(
CompressionType type, UnownedPtr<CompressionManager> mgr) {
if (mgr) {
if (!mgr->SupportsCompressionType(type)) {
return Status::NotSupported("Compression type " +
CompressionTypeToString(type) +
" is not recognized/supported by this "
"version of CompressionManager " +
mgr->GetId());
}
} else {
if (!CompressionTypeSupported(type)) {
if (type <= kLastBuiltinCompression) {
return Status::InvalidArgument("Compression type " +
CompressionTypeToString(type) +
" is not linked with the binary.");
} else {
return Status::NotSupported(
"Compression type " + CompressionTypeToString(type) +
" is not recognized/supported by built-in CompressionManager.");
}
}
}
return Status::OK();
}
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
if (!cf_options.compression_per_level.empty()) {
for (size_t level = 0; level < cf_options.compression_per_level.size();
++level) {
if (!CompressionTypeSupported(cf_options.compression_per_level[level])) {
return Status::InvalidArgument(
"Compression type " +
CompressionTypeToString(cf_options.compression_per_level[level]) +
" is not linked with the binary.");
Status s = CheckCompressionSupportedWithManager(
cf_options.compression_per_level[level],
cf_options.compression_manager.get());
if (!s.ok()) {
return s;
}
}
} else {
if (!CompressionTypeSupported(cf_options.compression)) {
return Status::InvalidArgument(
"Compression type " +
CompressionTypeToString(cf_options.compression) +
" is not linked with the binary.");
Status s = CheckCompressionSupportedWithManager(
cf_options.compression, cf_options.compression_manager.get());
if (!s.ok()) {
return s;
}
}
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
@@ -1608,6 +1633,8 @@ Status ColumnFamilyData::SetOptions(
Status s = GetColumnFamilyOptionsFromMap(config_opts, cf_opts, options_map,
&cf_opts);
if (s.ok()) {
// FIXME: we should call SanitizeOptions() too or consolidate it with
// ValidateOptions().
s = ValidateOptions(db_opts, cf_opts);
}
if (s.ok()) {
+8 -8
View File
@@ -284,9 +284,9 @@ Compaction::Compaction(
CompressionOptions _compression_opts, Temperature _output_temperature,
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
std::optional<SequenceNumber> _earliest_snapshot,
const SnapshotChecker* _snapshot_checker, bool _manual_compaction,
const std::string& _trim_ts, double _score, bool _deletion_compaction,
bool l0_files_might_overlap, CompactionReason _compaction_reason,
const SnapshotChecker* _snapshot_checker,
CompactionReason _compaction_reason, const std::string& _trim_ts,
double _score, bool l0_files_might_overlap,
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
double _blob_garbage_collection_age_cutoff)
: input_vstorage_(vstorage),
@@ -304,7 +304,9 @@ Compaction::Compaction(
output_compression_(_compression),
output_compression_opts_(_compression_opts),
output_temperature_(_output_temperature),
deletion_compaction_(_deletion_compaction),
deletion_compaction_(_compaction_reason == CompactionReason::kFIFOTtl ||
_compaction_reason ==
CompactionReason::kFIFOMaxSize),
l0_files_might_overlap_(l0_files_might_overlap),
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
grandparents_(std::move(_grandparents)),
@@ -321,7 +323,8 @@ Compaction::Compaction(
? false
: IsBottommostLevel(output_level_, vstorage, inputs_)),
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
is_manual_compaction_(_manual_compaction),
is_manual_compaction_(_compaction_reason ==
CompactionReason::kManualCompaction),
trim_ts_(_trim_ts),
is_trivial_move_(false),
compaction_reason_(_compaction_reason),
@@ -349,9 +352,6 @@ Compaction::Compaction(
immutable_options_, start_level_,
output_level_)) {
MarkFilesBeingCompacted(true);
if (is_manual_compaction_) {
compaction_reason_ = CompactionReason::kManualCompaction;
}
if (max_subcompactions_ == 0) {
max_subcompactions_ = _mutable_db_options.max_subcompactions;
}
+2 -3
View File
@@ -94,10 +94,9 @@ class Compaction {
std::vector<FileMetaData*> grandparents,
std::optional<SequenceNumber> earliest_snapshot,
const SnapshotChecker* snapshot_checker,
bool manual_compaction = false, const std::string& trim_ts = "",
double score = -1, bool deletion_compaction = false,
CompactionReason compaction_reason,
const std::string& trim_ts = "", double score = -1,
bool l0_files_might_overlap = true,
CompactionReason compaction_reason = CompactionReason::kUnknown,
BlobGarbageCollectionPolicy blob_garbage_collection_policy =
BlobGarbageCollectionPolicy::kUseDefault,
double blob_garbage_collection_age_cutoff = -1);
+5 -3
View File
@@ -458,9 +458,10 @@ class CompactionJobTestBase : public testing::Test {
ReadOptions read_opts;
Status s = cf_options_.table_factory->NewTableReader(
read_opts,
TableReaderOptions(cfd->ioptions(), nullptr, FileOptions(),
TableReaderOptions(cfd->ioptions(), /*prefix_extractor=*/nullptr,
/*compression_manager=*/nullptr, FileOptions(),
cfd_->internal_comparator(),
0 /* block_protection_bytes_per_key */),
/*block_protection_bytes_per_key=*/0),
std::move(freader), file_size, &table_reader, false);
ASSERT_OK(s);
assert(table_reader);
@@ -650,7 +651,8 @@ class CompactionJobTestBase : public testing::Test {
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
cfd->GetLatestMutableCFOptions().compression_opts,
Temperature::kUnknown, max_subcompactions, grandparents,
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr,
CompactionReason::kManualCompaction);
compaction.FinalizeInputInfo(cfd->current());
assert(db_options_.info_log);
+6 -8
View File
@@ -381,7 +381,7 @@ Compaction* CompactionPicker::CompactFiles(
mutable_cf_options.default_write_temperature,
compact_options.max_subcompactions,
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, true);
/* snapshot_checker */ nullptr, CompactionReason::kManualCompaction);
RegisterCompaction(c);
return c;
}
@@ -680,10 +680,9 @@ Compaction* CompactionPicker::CompactRange(
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions,
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
/* snapshot_checker */ nullptr, CompactionReason::kManualCompaction,
trim_ts, /* score */ -1,
/* l0_files_might_overlap */ true,
compact_range_options.blob_garbage_collection_policy,
compact_range_options.blob_garbage_collection_age_cutoff);
@@ -873,9 +872,8 @@ Compaction* CompactionPicker::CompactRange(
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions, std::move(grandparents),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
/* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
CompactionReason::kManualCompaction, trim_ts, /* score */ -1,
/* l0_files_might_overlap */ true,
compact_range_options.blob_garbage_collection_policy,
compact_range_options.blob_garbage_collection_age_cutoff);
+10 -16
View File
@@ -127,11 +127,9 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* snapshot_checker */ nullptr, CompactionReason::kFIFOTtl,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
CompactionReason::kFIFOTtl);
/* l0_files_might_overlap */ true);
return c;
}
@@ -200,11 +198,10 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
mutable_cf_options.default_write_temperature,
0 /* max_subcompactions */, {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, /* is manual */ false,
/* snapshot_checker */ nullptr,
CompactionReason::kFIFOReduceNumFiles,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ false,
/* l0_files_might_overlap */ true,
CompactionReason::kFIFOReduceNumFiles);
/* l0_files_might_overlap */ true);
return c;
}
}
@@ -297,11 +294,9 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* snapshot_checker */ nullptr, CompactionReason::kFIFOMaxSize,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ true,
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
/* l0_files_might_overlap */ true);
return c;
}
@@ -416,10 +411,9 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
mutable_cf_options.compression, mutable_cf_options.compression_opts,
compaction_target_temp,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false, /* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kChangeTemperature);
/* snapshot_checker */ nullptr, CompactionReason::kChangeTemperature,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* l0_files_might_overlap */ true);
return c;
}
+2 -4
View File
@@ -145,7 +145,6 @@ class LevelCompactionBuilder {
int parent_index_ = -1;
int base_index_ = -1;
double start_level_score_ = 0;
bool is_manual_ = false;
bool is_l0_trivial_move_ = false;
CompactionInputFiles start_level_inputs_;
std::vector<CompactionInputFiles> compaction_inputs_;
@@ -561,9 +560,8 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, std::move(grandparents_),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
is_manual_,
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
l0_files_might_overlap, compaction_reason_);
compaction_reason_,
/* trim_ts */ "", start_level_score_, l0_files_might_overlap);
// If it's level 0 compaction, make sure we don't execute any other level 0
// compactions in parallel
+12 -17
View File
@@ -1097,10 +1097,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents,
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false, /* trim_ts */ "", score_,
false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
/* snapshot_checker */ nullptr, compaction_reason,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
}
// Look at overall size amplification. If size amplification
@@ -1447,10 +1446,9 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kUniversalSizeAmplification);
CompactionReason::kUniversalSizeAmplification,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
}
// Pick files marked for compaction. Typically, files are marked by
@@ -1600,11 +1598,9 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
snapshot_checker_,
/* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kFilesMarkedForCompaction);
snapshot_checker_, CompactionReason::kFilesMarkedForCompaction,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
}
Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
@@ -1700,10 +1696,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
/* snapshot_checker */ nullptr, compaction_reason,
/* trim_ts */ "", score_,
/* l0_files_might_overlap */ true);
}
Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
+4 -3
View File
@@ -93,9 +93,10 @@ Status VerifySstFileChecksumInternal(const Options& options,
nullptr /* file_read_hist */, ioptions.rate_limiter.get()));
const bool kImmortal = true;
auto reader_options = TableReaderOptions(
ioptions, options.prefix_extractor, env_options, internal_comparator,
options.block_protection_bytes_per_key, false /* skip_filters */,
!kImmortal, false /* force_direct_prefetch */, -1 /* level */);
ioptions, options.prefix_extractor, options.compression_manager.get(),
env_options, internal_comparator, options.block_protection_bytes_per_key,
false /* skip_filters */, !kImmortal, false /* force_direct_prefetch */,
-1 /* level */);
reader_options.largest_seqno = largest_seqno;
s = options.table_factory->NewTableReader(
read_options, reader_options, std::move(file_reader), file_size,
+74 -3
View File
@@ -556,6 +556,74 @@ TEST_F(CorruptionTest, TableFileFooterNotMagic) {
ASSERT_TRUE(s.ToString().find(".sst") != std::string::npos);
}
TEST_F(CorruptionTest, DBOpenWithWrongFileSize) {
// Validate that when paranoid flag is true, DB::Open() fails if one of the
// file corrupted. Validate that when paranoid flag is false, DB::Open()
// succeed if one of the file corrupted, and the healthy file is readable.
CloseDb();
const std::string test_cf_name = "test_cf";
std::vector<ColumnFamilyDescriptor> cf_descs;
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
cf_descs.emplace_back(test_cf_name, ColumnFamilyOptions());
{
options_.create_missing_column_families = true;
std::vector<ColumnFamilyHandle*> cfhs;
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
assert(db_ != nullptr); // suppress false clang-analyze report
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k", "v"));
ASSERT_OK(db_->Put(WriteOptions(), cfhs[1], "k1", "v1"));
ASSERT_OK(db_->Put(WriteOptions(), cfhs[0], "k2", "v2"));
for (auto* cfh : cfhs) {
delete cfh;
}
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable());
// ********************************************
// Corrupt the file by making the file bigger
std::vector<LiveFileMetaData> metadata;
db_->GetLiveFilesMetaData(&metadata);
std::string filename = dbname_ + metadata[0].name;
const auto& fs = options_.env->GetFileSystem();
{
std::unique_ptr<FSWritableFile> f;
ASSERT_OK(fs->ReopenWritableFile(filename, FileOptions(), &f, nullptr));
ASSERT_OK(f->Append("blahblah", IOOptions(), nullptr));
ASSERT_OK(f->Close(IOOptions(), nullptr));
}
CloseDb();
}
// DB failed to open due to one of the file is corrupted, as paranoid flag is
// true
options_.paranoid_checks = true;
std::vector<ColumnFamilyHandle*> cfhs;
auto s = DB::Open(options_, dbname_, cf_descs, &cfhs, &db_);
ASSERT_TRUE(s.IsCorruption());
ASSERT_TRUE(s.ToString().find("file size mismatch") != std::string::npos);
// DB opened successfully, as paranoid flag is false, validate the one that is
// healthy is still accessible
options_.paranoid_checks = false;
ASSERT_OK(DB::Open(options_, dbname_, cf_descs, &cfhs, &db_));
assert(db_ != nullptr); // suppress false clang-analyze report
std::string v;
ASSERT_OK(db_->Get(ReadOptions(), cfhs[1], "k1", &v));
ASSERT_EQ(v, "v1");
// Validate the default column family is corrupted
Check(0, 0);
s = db_->Get(ReadOptions(), cfhs[0], "k1", &v);
ASSERT_TRUE(s.IsCorruption());
delete cfhs[1];
delete cfhs[0];
}
TEST_F(CorruptionTest, TableFileWrongSize) {
Build(100);
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
@@ -579,13 +647,16 @@ TEST_F(CorruptionTest, TableFileWrongSize) {
// DB actually accepts this without paranoid checks, relying on size
// recorded in manifest to locate the SST footer.
options_.paranoid_checks = false;
options_.skip_checking_sst_file_sizes_on_db_open = false;
Reopen();
Check(100, 100);
// As footer could not be extraced, file is completely unreadable
Check(0, 0);
std::string v;
auto s = db_->Get(ReadOptions(), "k1", &v);
ASSERT_TRUE(s.IsCorruption());
// But reports the issue with paranoid checks
options_.paranoid_checks = true;
Status s = TryReopen();
s = TryReopen();
ASSERT_TRUE(s.IsCorruption());
ASSERT_TRUE(s.ToString().find("file size mismatch") != std::string::npos);
+67 -57
View File
@@ -829,68 +829,78 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
const int kNumEntriesPerFile = 128;
const int kNumBytesPerEntry = 1024;
// Try all the available libraries that support dictionary compression
std::vector<CompressionType> compression_types;
if (Zlib_Supported()) {
compression_types.push_back(kZlibCompression);
}
if (LZ4_Supported()) {
compression_types.push_back(kLZ4Compression);
compression_types.push_back(kLZ4HCCompression);
}
if (ZSTD_Supported()) {
compression_types.push_back(kZSTD);
}
std::vector<CompressionType> dict_compressions =
GetSupportedDictCompressions();
Random rnd(301);
for (auto compression_type : compression_types) {
Options options = CurrentOptions();
options.bottommost_compression = compression_type;
options.bottommost_compression_opts.max_dict_bytes = 4096;
options.bottommost_compression_opts.enabled = true;
options.create_if_missing = true;
options.num_levels = 2;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
BlockBasedTableOptions table_options;
table_options.cache_index_and_filter_blocks = true;
table_options.block_cache.reset(new MockCache());
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
DestroyAndReopen(options);
// Format version before and after compression handling changes
for (int format_version : {6, 7}) {
// Test all supported compression types because (at least historically)
// dictionary compression could be enabled and a dictionary block saved
// but ignored by some compression types. Ensure we at least don't crash
// or return corruption for those.
for (auto compression_type : GetSupportedCompressions()) {
// Extra handling checks only for types actually supporting dictionary
// compression.
bool dict_supported =
std::count(dict_compressions.begin(), dict_compressions.end(),
compression_type) > 0;
RecordCacheCountersForCompressionDict(options);
Options options = CurrentOptions();
options.bottommost_compression = compression_type;
options.bottommost_compression_opts.max_dict_bytes = 4096;
options.bottommost_compression_opts.enabled = true;
options.create_if_missing = true;
options.num_levels = 2;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
BlockBasedTableOptions table_options;
table_options.cache_index_and_filter_blocks = true;
table_options.block_cache.reset(new MockCache());
table_options.format_version = format_version;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
DestroyAndReopen(options);
for (int i = 0; i < kNumFiles; ++i) {
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
for (int j = 0; j < kNumEntriesPerFile; ++j) {
std::string value = rnd.RandomString(kNumBytesPerEntry);
ASSERT_OK(Put(Key(j * kNumFiles + i), value.c_str()));
RecordCacheCountersForCompressionDict(options);
for (int i = 0; i < kNumFiles; ++i) {
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
for (int j = 0; j < kNumEntriesPerFile; ++j) {
std::string value = rnd.RandomString(kNumBytesPerEntry);
ASSERT_OK(Put(Key(j * kNumFiles + i), value.c_str()));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(0, NumTableFilesAtLevel(0));
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
if (dict_supported) {
// Compression dictionary blocks are preloaded.
CheckCacheCountersForCompressionDict(
options, kNumFiles /* expected_compression_dict_misses */,
0 /* expected_compression_dict_hits */,
kNumFiles /* expected_compression_dict_inserts */);
}
// Seek to a key in a file. It should cause the SST's dictionary
// meta-block to be read.
RecordCacheCounters(options);
RecordCacheCountersForCompressionDict(options);
ReadOptions read_options;
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
if (dict_supported) {
// Two block hits: index and dictionary since they are prefetched
// One block missed/added: data block
CheckCacheCounters(options, 1 /* expected_misses */,
2 /* expected_hits */, 1 /* expected_inserts */,
0 /* expected_failures */);
CheckCacheCountersForCompressionDict(
options, 0 /* expected_compression_dict_misses */,
1 /* expected_compression_dict_hits */,
0 /* expected_compression_dict_inserts */);
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(0, NumTableFilesAtLevel(0));
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
// Compression dictionary blocks are preloaded.
CheckCacheCountersForCompressionDict(
options, kNumFiles /* expected_compression_dict_misses */,
0 /* expected_compression_dict_hits */,
kNumFiles /* expected_compression_dict_inserts */);
// Seek to a key in a file. It should cause the SST's dictionary meta-block
// to be read.
RecordCacheCounters(options);
RecordCacheCountersForCompressionDict(options);
ReadOptions read_options;
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
// Two block hits: index and dictionary since they are prefetched
// One block missed/added: data block
CheckCacheCounters(options, 1 /* expected_misses */, 2 /* expected_hits */,
1 /* expected_inserts */, 0 /* expected_failures */);
CheckCacheCountersForCompressionDict(
options, 0 /* expected_compression_dict_misses */,
1 /* expected_compression_dict_hits */,
0 /* expected_compression_dict_inserts */);
}
}
+53
View File
@@ -11093,6 +11093,59 @@ TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
}
class PeriodicCompactionListener : public EventListener {
public:
explicit PeriodicCompactionListener() {}
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
if (ci.compaction_reason == CompactionReason::kPeriodicCompaction) {
++num_periodic_compactions;
}
}
std::atomic<int> num_periodic_compactions = 0;
};
TEST_F(DBCompactionTest, PeriodicTask) {
// Tests that when no trigger event is fired (flush/compaction/setoptions),
// periodic compaction is still triggered by a scheduled periodic function.
auto mock_clock = std::make_shared<MockSystemClock>(env_->GetSystemClock());
mock_clock->SetCurrentTime(100);
mock_clock->InstallTimedWaitFixCallback();
auto mock_env = std::make_unique<CompositeEnvWrapper>(env_, mock_clock);
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg) {
auto periodic_task_scheduler_ptr =
static_cast<PeriodicTaskScheduler*>(arg);
periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock.get());
});
Options options;
options.env = mock_env.get();
options.compaction_style = kCompactionStyleUniversal;
options.statistics = CreateDBStatistics();
int kPeriodicCompactionSeconds = 7 * 24 * 60 * 60; // 1 week
options.periodic_compaction_seconds = kPeriodicCompactionSeconds;
options.num_levels = 50;
auto listener = std::make_shared<PeriodicCompactionListener>();
options.listeners.push_back(listener);
ASSERT_OK(TryReopen(options));
Random* rnd = Random::GetTLSInstance();
for (int k = 0; k < 10; ++k) {
ASSERT_OK(Put(Key(k), rnd->RandomString(100)));
}
ASSERT_OK(Flush());
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
ASSERT_EQ(1, NumTableFilesAtLevel(49));
dbfull()->TEST_WaitForPeriodicTaskRun(
[&] { mock_clock->MockSleepForSeconds(kPeriodicCompactionSeconds + 1); });
ASSERT_OK(db_->WaitForCompact({}));
ASSERT_EQ(listener->num_periodic_compactions, 1);
Close();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+39 -4
View File
@@ -17,9 +17,10 @@ class DBEncryptionTest : public DBTestBase {
public:
DBEncryptionTest()
: DBTestBase("db_encryption_test", /*env_do_fsync=*/true) {}
Env* GetTargetEnv() {
Env* GetNonEncryptedEnv() {
if (encrypted_env_ != nullptr) {
return (static_cast<EnvWrapper*>(encrypted_env_))->target();
return (static_cast_with_check<CompositeEnvWrapper>(encrypted_env_))
->env_target();
} else {
return env_;
}
@@ -38,7 +39,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
auto status = env_->GetChildren(dbname_, &fileNames);
ASSERT_OK(status);
Env* target = GetTargetEnv();
Env* target = GetNonEncryptedEnv();
int hits = 0;
for (auto it = fileNames.begin(); it != fileNames.end(); ++it) {
if (*it == "LOCK") {
@@ -89,7 +90,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
}
TEST_F(DBEncryptionTest, ReadEmptyFile) {
auto defaultEnv = GetTargetEnv();
auto defaultEnv = GetNonEncryptedEnv();
// create empty file for reading it back in later
auto envOptions = EnvOptions(CurrentOptions());
@@ -116,6 +117,40 @@ TEST_F(DBEncryptionTest, ReadEmptyFile) {
ASSERT_TRUE(data.empty());
}
TEST_F(DBEncryptionTest, NotSupportedGetFileSize) {
// Validate envrypted env does not support GetFileSize.
// The goal of the test is to validate the encrypted env/fs does not support
// GetFileSize API on FSRandomAccessFile interface.
// This test combined with the rest of the integration tests validate that
// the new API GetFileSize on FSRandomAccessFile interface is not required to
// be supported for database to work properly.
// The GetFileSize API is used in ReadFooterFromFile() API to get the file
// size. When GetFileSize API is not supported, the ReadFooterFromFile() API
// will use FileSystem GetFileSize API as fallback. Refer to the
// EncryptedRandomAccessFile class definition for more details.
if (!encrypted_env_) {
return;
}
auto fs = encrypted_env_->GetFileSystem();
// create empty file for reading it back in later
auto filePath = dbname_ + "/empty.empty";
// Create empty file
CreateFile(fs.get(), filePath, "", false);
// Open it for reading footer
std::unique_ptr<FSRandomAccessFile> randomAccessFile;
auto status = fs->NewRandomAccessFile(filePath, FileOptions(),
&randomAccessFile, nullptr);
ASSERT_OK(status);
uint64_t fileSize;
status = randomAccessFile->GetFileSize(&fileSize);
ASSERT_TRUE(status.IsNotSupported());
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+50 -89
View File
@@ -253,6 +253,9 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
periodic_task_functions_.emplace(
PeriodicTaskType::kRecordSeqnoTime,
[this]() { this->RecordSeqnoToTimeMapping(); });
periodic_task_functions_.emplace(
PeriodicTaskType::kTriggerCompaction,
[this]() { this->TriggerPeriodicCompaction(); });
versions_.reset(new VersionSet(
dbname_, &immutable_db_options_, file_options_, table_cache_.get(),
@@ -787,7 +790,8 @@ Status DBImpl::StartPeriodicTaskScheduler() {
Status s = periodic_task_scheduler_.Register(
PeriodicTaskType::kDumpStats,
periodic_task_functions_.at(PeriodicTaskType::kDumpStats),
mutable_db_options_.stats_dump_period_sec);
mutable_db_options_.stats_dump_period_sec,
/*run_immediately=*/true);
if (!s.ok()) {
return s;
}
@@ -796,7 +800,8 @@ Status DBImpl::StartPeriodicTaskScheduler() {
Status s = periodic_task_scheduler_.Register(
PeriodicTaskType::kPersistStats,
periodic_task_functions_.at(PeriodicTaskType::kPersistStats),
mutable_db_options_.stats_persist_period_sec);
mutable_db_options_.stats_persist_period_sec,
/*run_immediately=*/true);
if (!s.ok()) {
return s;
}
@@ -804,7 +809,15 @@ Status DBImpl::StartPeriodicTaskScheduler() {
Status s = periodic_task_scheduler_.Register(
PeriodicTaskType::kFlushInfoLog,
periodic_task_functions_.at(PeriodicTaskType::kFlushInfoLog));
periodic_task_functions_.at(PeriodicTaskType::kFlushInfoLog),
/*run_immediately=*/true);
if (s.ok()) {
s = periodic_task_scheduler_.Register(
PeriodicTaskType::kTriggerCompaction,
periodic_task_functions_.at(PeriodicTaskType::kTriggerCompaction),
/*run_immediately=*/false);
}
return s;
}
@@ -855,7 +868,7 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker() {
s = periodic_task_scheduler_.Register(
PeriodicTaskType::kRecordSeqnoTime,
periodic_task_functions_.at(PeriodicTaskType::kRecordSeqnoTime),
seqno_time_cadence);
seqno_time_cadence, /*run_immediately=*/true);
}
return s;
@@ -1365,7 +1378,7 @@ Status DBImpl::SetDBOptions(
s = periodic_task_scheduler_.Register(
PeriodicTaskType::kDumpStats,
periodic_task_functions_.at(PeriodicTaskType::kDumpStats),
new_options.stats_dump_period_sec);
new_options.stats_dump_period_sec, /*run_immediately=*/true);
}
if (new_options.max_total_wal_size !=
mutable_db_options_.max_total_wal_size) {
@@ -1380,7 +1393,7 @@ Status DBImpl::SetDBOptions(
s = periodic_task_scheduler_.Register(
PeriodicTaskType::kPersistStats,
periodic_task_functions_.at(PeriodicTaskType::kPersistStats),
new_options.stats_persist_period_sec);
new_options.stats_persist_period_sec, /*run_immediately=*/true);
}
}
mutex_.Lock();
@@ -3823,10 +3836,8 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
std::unique_ptr<MultiScan> DBImpl::NewMultiScan(
const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
const std::vector<ScanOptions>& scan_opts) {
std::unique_ptr<Iterator> iter(NewIterator(_read_options, column_family));
iter->Prepare(scan_opts);
std::unique_ptr<MultiScan> ms_iter =
std::make_unique<MultiScan>(scan_opts, std::move(iter));
std::unique_ptr<MultiScan> ms_iter = std::make_unique<MultiScan>(
_read_options, scan_opts, this, column_family);
return ms_iter;
}
@@ -5044,85 +5055,6 @@ void DBImpl::GetAllColumnFamilyMetaData(
}
}
Status DBImpl::CheckConsistency() {
mutex_.AssertHeld();
std::vector<LiveFileMetaData> metadata;
versions_->GetLiveFilesMetaData(&metadata);
TEST_SYNC_POINT("DBImpl::CheckConsistency:AfterGetLiveFilesMetaData");
std::string corruption_messages;
if (immutable_db_options_.skip_checking_sst_file_sizes_on_db_open) {
// Instead of calling GetFileSize() for each expected file, call
// GetChildren() for the DB directory and check that all expected files
// are listed, without checking their sizes.
// Since sst files might be in different directories, do it for each
// directory separately.
std::map<std::string, std::vector<std::string>> files_by_directory;
for (const auto& md : metadata) {
// md.name has a leading "/". Remove it.
std::string fname = md.name;
if (!fname.empty() && fname[0] == '/') {
fname = fname.substr(1);
}
files_by_directory[md.db_path].push_back(fname);
}
IOOptions io_opts;
io_opts.do_not_recurse = true;
for (const auto& dir_files : files_by_directory) {
std::string directory = dir_files.first;
std::vector<std::string> existing_files;
Status s = fs_->GetChildren(directory, io_opts, &existing_files,
/*IODebugContext*=*/nullptr);
if (!s.ok()) {
corruption_messages +=
"Can't list files in " + directory + ": " + s.ToString() + "\n";
continue;
}
std::sort(existing_files.begin(), existing_files.end());
for (const std::string& fname : dir_files.second) {
if (!std::binary_search(existing_files.begin(), existing_files.end(),
fname) &&
!std::binary_search(existing_files.begin(), existing_files.end(),
Rocks2LevelTableFileName(fname))) {
corruption_messages +=
"Missing sst file " + fname + " in " + directory + "\n";
}
}
}
} else {
for (const auto& md : metadata) {
// md.name has a leading "/".
std::string file_path = md.db_path + md.name;
uint64_t fsize = 0;
TEST_SYNC_POINT("DBImpl::CheckConsistency:BeforeGetFileSize");
Status s = env_->GetFileSize(file_path, &fsize);
if (!s.ok() &&
env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok()) {
s = Status::OK();
}
if (!s.ok()) {
corruption_messages +=
"Can't access " + md.name + ": " + s.ToString() + "\n";
} else if (fsize != md.size) {
corruption_messages += "Sst file size mismatch: " + file_path +
". Size recorded in manifest " +
std::to_string(md.size) + ", actual size " +
std::to_string(fsize) + "\n";
}
}
}
if (corruption_messages.size() == 0) {
return Status::OK();
} else {
return Status::Corruption(corruption_messages);
}
}
Status DBImpl::GetDbIdentity(std::string& identity) const {
identity.assign(db_id_);
return Status::OK();
@@ -6884,6 +6816,35 @@ void DBImpl::RecordSeqnoToTimeMapping() {
sv_context.Clean();
}
void DBImpl::TriggerPeriodicCompaction() {
TEST_SYNC_POINT("DBImpl::TriggerPeriodicCompaction:StartRunning");
{
InstrumentedMutexLock l(&mutex_);
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"Running the periodic task to trigger compactions.");
for (ColumnFamilyData* cfd : *versions_->GetColumnFamilySet()) {
if (cfd->IsDropped()) {
continue;
}
if (cfd->GetLatestCFOptions().periodic_compaction_seconds &&
!cfd->queued_for_compaction()) {
cfd->current()->storage_info()->ComputeCompactionScore(
cfd->ioptions(), cfd->GetLatestMutableCFOptions());
EnqueuePendingCompaction(cfd);
if (cfd->queued_for_compaction()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"Periodic task to trigger compaction queued Column "
"family [%s] for compaction.",
cfd->GetName().c_str());
}
}
}
MaybeScheduleFlushOrCompaction();
bg_cv_.SignalAll();
}
}
void DBImpl::TrackOrUntrackFiles(
const std::vector<std::string>& existing_data_files, bool track) {
auto sfm = static_cast_with_check<SstFileManagerImpl>(
+6 -4
View File
@@ -804,10 +804,6 @@ class DBImpl : public DB {
// being detected.
const Snapshot* GetSnapshotForWriteConflictBoundary();
// checks if all live files exist on file system and that their file sizes
// match to our in-memory records
virtual Status CheckConsistency();
// max_file_num_to_ignore allows bottom level compaction to filter out newly
// compacted SST files. Setting max_file_num_to_ignore to kMaxUint64 will
// disable the filtering
@@ -1286,6 +1282,12 @@ class DBImpl : public DB {
// For the background timer job
void RecordSeqnoToTimeMapping();
// Compactions rely on an event triggers like flush/compaction/SetOptions.
// We need to trigger periodic compactions even when there is no such trigger.
// This function checks and schedules available compactions and will run
// periodically.
void TriggerPeriodicCompaction();
// REQUIRES: DB mutex held
std::pair<SequenceNumber, uint64_t> GetSeqnoToTimeSample() const;
+11 -11
View File
@@ -1849,10 +1849,9 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
0 /* max_subcompactions, not applicable */,
{} /* grandparents, not applicable */,
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
false /* is deletion compaction, not applicable */,
false /* l0_files_might_overlap, not applicable */,
CompactionReason::kRefitLevel));
CompactionReason::kRefitLevel, "" /* trim_ts */,
-1 /* score, not applicable */,
false /* l0_files_might_overlap, not applicable */));
cfd->compaction_picker()->RegisterCompaction(c.get());
TEST_SYNC_POINT("DBImpl::ReFitLevel:PostRegisterCompaction");
VersionEdit edit;
@@ -4424,13 +4423,14 @@ Compaction* DBImpl::CreateIntendedCompactionForwardedToBottomPriorityPool(
c->ReleaseCompactionFiles(Status::OK());
Compaction* intended_compaction = new Compaction(
vstorage, io, mo, mutable_db_options_, std::move(inputs),
c->output_level(), c->target_output_file_size(),
c->max_compaction_bytes(), c->output_path_id(), c->output_compression(),
c->output_compression_opts(), c->output_temperature(),
c->max_subcompactions(), c->grandparents(),
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */);
Compaction* intended_compaction =
new Compaction(vstorage, io, mo, mutable_db_options_, std::move(inputs),
c->output_level(), c->target_output_file_size(),
c->max_compaction_bytes(), c->output_path_id(),
c->output_compression(), c->output_compression_opts(),
c->output_temperature(), c->max_subcompactions(),
c->grandparents(), std::nullopt /* earliest_snapshot */,
nullptr /* snapshot_checker */, c->compaction_reason());
cfd->compaction_picker()->RegisterCompaction(intended_compaction);
vstorage->ComputeCompactionScore(io, mo);
-3
View File
@@ -70,9 +70,6 @@ Status DBImplFollower::Recover(
}
return s;
}
if (immutable_db_options_.paranoid_checks && s.ok()) {
s = CheckConsistency();
}
if (s.ok()) {
default_cf_handle_ = new ColumnFamilyHandleImpl(
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
-9
View File
@@ -191,12 +191,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
"wal_compression is disabled since only zstd is supported");
}
if (!result.paranoid_checks) {
result.skip_checking_sst_file_sizes_on_db_open = true;
ROCKS_LOG_INFO(result.info_log,
"file size check will be skipped during open.");
}
return result;
}
@@ -694,9 +688,6 @@ Status DBImpl::Recover(
s = MaybeUpdateNextFileNumber(recovery_ctx);
}
if (immutable_db_options_.paranoid_checks && s.ok()) {
s = CheckConsistency();
}
if (s.ok() && !read_only) {
// TODO: share file descriptors (FSDirectory) with SetDirectories above
std::map<std::string, std::shared_ptr<FSDirectory>> created_dirs;
-46
View File
@@ -49,9 +49,6 @@ Status DBImplSecondary::Recover(
}
return s;
}
if (immutable_db_options_.paranoid_checks && s.ok()) {
s = CheckConsistency();
}
// Initial max_total_in_memory_state_ before recovery logs.
max_total_in_memory_state_ = 0;
for (auto cfd : *versions_->GetColumnFamilySet()) {
@@ -653,49 +650,6 @@ Status DBImplSecondary::NewIterators(
return Status::OK();
}
Status DBImplSecondary::CheckConsistency() {
mutex_.AssertHeld();
Status s = DBImpl::CheckConsistency();
// If DBImpl::CheckConsistency() which is stricter returns success, then we
// do not need to give a second chance.
if (s.ok()) {
return s;
}
// It's possible that DBImpl::CheckConssitency() can fail because the primary
// may have removed certain files, causing the GetFileSize(name) call to
// fail and returning a PathNotFound. In this case, we take a best-effort
// approach and just proceed.
TEST_SYNC_POINT_CALLBACK(
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", &s);
if (immutable_db_options_.skip_checking_sst_file_sizes_on_db_open) {
return Status::OK();
}
std::vector<LiveFileMetaData> metadata;
versions_->GetLiveFilesMetaData(&metadata);
std::string corruption_messages;
for (const auto& md : metadata) {
// md.name has a leading "/".
std::string file_path = md.db_path + md.name;
uint64_t fsize = 0;
s = env_->GetFileSize(file_path, &fsize);
if (!s.ok() &&
(env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok() ||
s.IsPathNotFound())) {
s = Status::OK();
}
if (!s.ok()) {
corruption_messages +=
"Can't access " + md.name + ": " + s.ToString() + "\n";
}
}
return corruption_messages.empty() ? Status::OK()
: Status::Corruption(corruption_messages);
}
Status DBImplSecondary::TryCatchUpWithPrimary() {
assert(versions_.get() != nullptr);
Status s;
-6
View File
@@ -248,12 +248,6 @@ class DBImplSecondary : public DBImpl {
Status MaybeInitLogReader(uint64_t log_number,
log::FragmentBufferedReader** log_reader);
// Check if all live files exist on file system and that their file sizes
// matche to the in-memory records. It is possible that some live files may
// have been deleted by the primary. In this case, CheckConsistency() does
// not flag the missing file as inconsistency.
Status CheckConsistency() override;
#ifndef NDEBUG
Status TEST_CompactWithoutInstallation(const OpenAndCompactOptions& options,
ColumnFamilyHandle* cfh,
+2 -3
View File
@@ -856,8 +856,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
write_group, current_sequence, column_family_memtables_.get(),
&flush_scheduler_, &trim_history_scheduler_,
write_options.ignore_missing_column_families,
0 /*recovery_log_number*/, this, parallel, seq_per_batch_,
batch_per_txn_);
0 /*recovery_log_number*/, this, seq_per_batch_, batch_per_txn_);
} else {
write_group.last_sequence = last_sequence;
write_thread_.LaunchParallelMemTableWriters(&write_group);
@@ -1115,7 +1114,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
memtable_write_group, w.sequence, column_family_memtables_.get(),
&flush_scheduler_, &trim_history_scheduler_,
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
false /*concurrent_memtable_writes*/, seq_per_batch_, batch_per_txn_);
seq_per_batch_, batch_per_txn_);
if (memtable_write_group.status
.ok()) { // Don't publish a partial batch write
versions_->SetLastSequence(memtable_write_group.last_sequence);
-3
View File
@@ -511,7 +511,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
valid_ = true;
return true;
break;
case kTypeMerge:
if (!PrepareValueInternal()) {
return false;
@@ -523,7 +522,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
current_entry_is_merged_ = true;
valid_ = true;
return MergeValuesNewToOld(); // Go to a different state machine
break;
default:
valid_ = false;
status_ = Status::Corruption(
@@ -1124,7 +1122,6 @@ bool DBIter::FindValueForCurrentKey() {
}
return true;
}
break;
case kTypeValue:
case kTypeValuePreferredSeqno:
SetValueAndColumnsFromPlain(pinned_value_);
+186
View File
@@ -8,6 +8,8 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include <functional>
#include <iomanip>
#include <iostream>
#include "db/arena_wrapped_db_iter.h"
#include "db/db_iter.h"
@@ -4139,6 +4141,190 @@ TEST_P(DBIteratorTest, AverageMemtableOpsScanFlushTriggerByOverwrites) {
ASSERT_OK(db_->WaitForCompact({}));
ASSERT_EQ(1, NumTableFilesAtLevel(0));
}
class DBMultiScanIteratorTest : public DBTestBase {
public:
DBMultiScanIteratorTest()
: DBTestBase("db_multi_scan_iterator_test", /*env_do_fsync=*/true) {}
};
TEST_F(DBMultiScanIteratorTest, BasicTest) {
// Create a file
for (int i = 0; i < 100; ++i) {
std::stringstream ss;
ss << std::setw(2) << std::setfill('0') << i;
ASSERT_OK(Put("k" + ss.str(), "val" + ss.str()));
}
ASSERT_OK(Flush());
std::vector<std::string> key_ranges({"k03", "k10", "k25", "k50"});
ReadOptions ro;
std::vector<ScanOptions> scan_options(
{ScanOptions(key_ranges[0], key_ranges[1]),
ScanOptions(key_ranges[2], key_ranges[3])});
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
std::unique_ptr<MultiScan> iter =
dbfull()->NewMultiScan(ro, cfh, scan_options);
try {
int idx = 0;
int count = 0;
for (auto range : *iter) {
for (auto it : range) {
ASSERT_GE(it.first.ToString().compare(key_ranges[idx]), 0);
ASSERT_LT(it.first.ToString().compare(key_ranges[idx + 1]), 0);
count++;
}
idx += 2;
}
ASSERT_EQ(count, 32);
} catch (MultiScanException& ex) {
// Make sure exception contains the status
ASSERT_NOK(ex.status());
std::cerr << "Iterator returned status " << ex.what();
abort();
} catch (std::logic_error& ex) {
std::cerr << "Iterator returned logic error " << ex.what();
abort();
}
iter.reset();
// Test the overlapping scan case
key_ranges[1] = "k30";
scan_options[0] = ScanOptions(key_ranges[0], key_ranges[1]);
iter = dbfull()->NewMultiScan(ro, cfh, scan_options);
try {
int idx = 0;
int count = 0;
for (auto range : *iter) {
for (auto it : range) {
ASSERT_GE(it.first.ToString().compare(key_ranges[idx]), 0);
ASSERT_LT(it.first.ToString().compare(key_ranges[idx + 1]), 0);
count++;
}
idx += 2;
}
ASSERT_EQ(count, 52);
} catch (MultiScanException& ex) {
// Make sure exception contains the status
ASSERT_NOK(ex.status());
std::cerr << "Iterator returned status " << ex.what();
abort();
} catch (std::logic_error& ex) {
std::cerr << "Iterator returned logic error " << ex.what();
abort();
}
iter.reset();
// Test the no limit scan case
scan_options[0] = ScanOptions(key_ranges[0]);
scan_options[1] = ScanOptions(key_ranges[2]);
iter = dbfull()->NewMultiScan(ro, cfh, scan_options);
try {
int idx = 0;
int count = 0;
for (auto range : *iter) {
for (auto it : range) {
ASSERT_GE(it.first.ToString().compare(key_ranges[idx]), 0);
if (it.first.ToString().compare(key_ranges[idx + 1]) == 0) {
break;
}
count++;
}
idx += 2;
}
ASSERT_EQ(count, 52);
} catch (MultiScanException& ex) {
// Make sure exception contains the status
ASSERT_NOK(ex.status());
std::cerr << "Iterator returned status " << ex.what();
abort();
} catch (std::logic_error& ex) {
std::cerr << "Iterator returned logic error " << ex.what();
abort();
}
iter.reset();
}
TEST_F(DBMultiScanIteratorTest, MixedBoundsTest) {
// Create a file
for (int i = 0; i < 100; ++i) {
std::stringstream ss;
ss << std::setw(2) << std::setfill('0') << i;
ASSERT_OK(Put("k" + ss.str(), "val" + ss.str()));
}
ASSERT_OK(Flush());
std::vector<std::string> key_ranges(
{"k03", "k10", "k25", "k50", "k75", "k90"});
ReadOptions ro;
std::vector<ScanOptions> scan_options(
{ScanOptions(key_ranges[0], key_ranges[1]), ScanOptions(key_ranges[2]),
ScanOptions(key_ranges[4], key_ranges[5])});
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
std::unique_ptr<MultiScan> iter =
dbfull()->NewMultiScan(ro, cfh, scan_options);
try {
int idx = 0;
int count = 0;
for (auto range : *iter) {
for (auto it : range) {
ASSERT_GE(it.first.ToString().compare(
scan_options[idx].range.start->ToString()),
0);
if (scan_options[idx].range.limit) {
ASSERT_LT(it.first.ToString().compare(
scan_options[idx].range.limit->ToString()),
0);
}
count++;
}
idx++;
}
ASSERT_EQ(count, 97);
} catch (MultiScanException& ex) {
// Make sure exception contains the status
ASSERT_NOK(ex.status());
std::cerr << "Iterator returned status " << ex.what();
abort();
} catch (std::logic_error& ex) {
std::cerr << "Iterator returned logic error " << ex.what();
abort();
}
iter.reset();
scan_options[0] = ScanOptions(key_ranges[0]);
scan_options[1] = ScanOptions(key_ranges[2], key_ranges[3]);
scan_options[2] = ScanOptions(key_ranges[4]);
iter = dbfull()->NewMultiScan(ro, cfh, scan_options);
try {
int idx = 0;
int count = 0;
for (auto range : *iter) {
for (auto it : range) {
ASSERT_GE(it.first.ToString().compare(
scan_options[idx].range.start->ToString()),
0);
if (scan_options[idx].range.limit) {
ASSERT_LT(it.first.ToString().compare(
scan_options[idx].range.limit->ToString()),
0);
}
count++;
}
idx++;
}
ASSERT_EQ(count, 147);
} catch (MultiScanException& ex) {
// Make sure exception contains the status
ASSERT_NOK(ex.status());
std::cerr << "Iterator returned status " << ex.what();
abort();
} catch (std::logic_error& ex) {
std::cerr << "Iterator returned logic error " << ex.what();
abort();
}
iter.reset();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+90
View File
@@ -424,6 +424,96 @@ TEST_F(DBMemTableTest, IntegrityChecks) {
ASSERT_FALSE(iter->Valid());
}
}
TEST_F(DBMemTableTest, VectorConcurrentInsert) {
Options options;
options.create_if_missing = true;
options.create_missing_column_families = true;
options.allow_concurrent_memtable_write = true;
options.memtable_factory.reset(new VectorRepFactory());
DestroyAndReopen(options);
CreateAndReopenWithCF({"cf1"}, options);
// Multi-threaded writes
{
WriteOptions write_options;
std::vector<port::Thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&, i]() {
int start = i * 100;
int end = start + 100;
WriteBatch batch;
for (int j = start; j < end; ++j) {
ASSERT_OK(
batch.Put(handles_[0], Key(j), "value" + std::to_string(j)));
}
ASSERT_OK(db_->Write(write_options, &batch));
});
}
for (auto& t : threads) {
t.join();
}
std::unique_ptr<Iterator> iter(
db_->NewIterator(ReadOptions(), handles_[0]));
iter->SeekToFirst();
for (int i = 0; i < 1000; ++i) {
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key().ToString(), Key(i));
ASSERT_EQ(iter->value().ToString(), "value" + std::to_string(i));
iter->Next();
}
ASSERT_FALSE(iter->Valid());
ASSERT_OK(iter->status());
}
// Multi-threaded writes, multi CF
{
WriteOptions write_options;
std::vector<port::Thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&, i]() {
int start = i * 100;
int end = start + 100;
WriteBatch batch;
for (int j = start; j < end; ++j) {
ASSERT_OK(batch.Put(handles_[0], Key(j), "CF0" + std::to_string(j)));
ASSERT_OK(batch.Put(handles_[1], Key(j), "CF1" + std::to_string(j)));
}
ASSERT_OK(db_->Write(write_options, &batch));
});
}
for (auto& t : threads) {
t.join();
}
std::unique_ptr<Iterator> iter0(
db_->NewIterator(ReadOptions(), handles_[0]));
std::unique_ptr<Iterator> iter1(
db_->NewIterator(ReadOptions(), handles_[1]));
iter0->SeekToFirst();
iter1->SeekToFirst();
for (int i = 0; i < 1000; ++i) {
ASSERT_TRUE(iter0->Valid());
ASSERT_EQ(iter0->key().ToString(), Key(i));
ASSERT_EQ(iter0->value().ToString(), "CF0" + std::to_string(i));
iter0->Next();
ASSERT_TRUE(iter1->Valid());
ASSERT_EQ(iter1->key().ToString(), Key(i));
ASSERT_EQ(iter1->value().ToString(), "CF1" + std::to_string(i));
iter1->Next();
}
ASSERT_FALSE(iter0->Valid());
ASSERT_OK(iter0->status());
ASSERT_FALSE(iter1->Valid());
ASSERT_OK(iter1->status());
}
ASSERT_OK(Flush(0));
ASSERT_OK(Flush(1));
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+3 -40
View File
@@ -605,6 +605,9 @@ class TraceFileEnv : public EnvWrapper {
char* scratch) const override {
return target_->Read(offset, n, result, scratch);
}
Status GetFileSize(uint64_t* file_size) override {
return target_->GetFileSize(file_size);
}
private:
std::unique_ptr<RandomAccessFile> target_;
@@ -1291,46 +1294,6 @@ TEST_F(DBSecondaryTest, CatchUpAfterFlush) {
ASSERT_OK(iter3->status());
}
TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
bool called = false;
Options options;
options.env = env_;
options.disable_auto_compactions = true;
Reopen(options);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->SetCallBack(
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", [&](void* arg) {
ASSERT_NE(nullptr, arg);
called = true;
auto* s = static_cast<Status*>(arg);
ASSERT_NOK(*s);
});
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::CheckConsistency:AfterGetLiveFilesMetaData",
"BackgroundCallCompaction:0"},
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
"DBImpl::CheckConsistency:BeforeGetFileSize"}});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put("a", "value0"));
ASSERT_OK(Put("c", "value0"));
ASSERT_OK(Flush());
ASSERT_OK(Put("b", "value1"));
ASSERT_OK(Put("d", "value1"));
ASSERT_OK(Flush());
port::Thread thread([this]() {
Options opts;
opts.env = env_;
opts.max_open_files = -1;
OpenSecondary(opts);
});
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
thread.join();
ASSERT_TRUE(called);
}
TEST_F(DBSecondaryTest, StartFromInconsistent) {
Options options = CurrentOptions();
DestroyAndReopen(options);
-54
View File
@@ -135,21 +135,6 @@ TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
Destroy(options);
}
// Check that we don't crash when opening DB with
// DBOptions::skip_checking_sst_file_sizes_on_db_open = true.
TEST_F(DBSSTTest, SkipCheckingSSTFileSizesOnDBOpen) {
ASSERT_OK(Put("pika", "choo"));
ASSERT_OK(Flush());
// Just open the DB with the option set to true and check that we don't crash.
Options options;
options.env = env_;
options.skip_checking_sst_file_sizes_on_db_open = true;
Reopen(options);
ASSERT_EQ("choo", Get("pika"));
}
TEST_F(DBSSTTest, DontDeleteMovedFile) {
// This test triggers move compaction and verifies that the file is not
// deleted when it's part of move compaction
@@ -1748,45 +1733,6 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBSSTTest, OpenDBWithoutGetFileSizeInvocations) {
Options options = CurrentOptions();
std::unique_ptr<MockEnv> env{MockEnv::Create(Env::Default())};
options.env = env.get();
options.disable_auto_compactions = true;
options.compression = kNoCompression;
options.enable_blob_files = true;
options.blob_file_size = 32; // create one blob per file
options.skip_checking_sst_file_sizes_on_db_open = true;
DestroyAndReopen(options);
// Generate 5 files in L0
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 10; j++) {
std::string val = "val_file_" + std::to_string(i);
ASSERT_OK(Put(Key(j), val));
}
ASSERT_OK(Flush());
}
Close();
bool is_get_file_size_called = false;
SyncPoint::GetInstance()->SetCallBack(
"MockFileSystem::GetFileSize:CheckFileType", [&](void* arg) {
std::string* filename = static_cast<std::string*>(arg);
if (filename->find(".blob") != std::string::npos) {
is_get_file_size_called = true;
}
});
SyncPoint::GetInstance()->EnableProcessing();
Reopen(options);
ASSERT_FALSE(is_get_file_size_called);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
Destroy(options);
}
TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
+2 -294
View File
@@ -1278,12 +1278,6 @@ class DelayFilterFactory : public CompactionFilterFactory {
};
} // anonymous namespace
static std::string CompressibleString(Random* rnd, int len) {
std::string r;
test::CompressibleString(rnd, 0.8, len, &r);
return r;
}
TEST_F(DBTest, FailMoreDbPaths) {
Options options = CurrentOptions();
options.db_paths.emplace_back(dbname_, 10000000);
@@ -5407,271 +5401,6 @@ TEST_F(DBTest, FlushOnDestroy) {
CancelAllBackgroundWork(db_);
}
TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
if (!Snappy_Supported()) {
return;
}
const int kNKeys = 120;
int keys[kNKeys];
for (int i = 0; i < kNKeys; i++) {
keys[i] = i;
}
Random rnd(301);
Options options;
options.env = env_;
options.create_if_missing = true;
options.db_write_buffer_size = 20480;
options.write_buffer_size = 20480;
options.max_write_buffer_number = 2;
options.level0_file_num_compaction_trigger = 2;
options.level0_slowdown_writes_trigger = 2;
options.level0_stop_writes_trigger = 2;
options.target_file_size_base = 20480;
options.level_compaction_dynamic_level_bytes = true;
options.max_bytes_for_level_base = 102400;
options.max_bytes_for_level_multiplier = 4;
options.max_background_compactions = 1;
options.num_levels = 5;
options.statistics = CreateDBStatistics();
options.compression_per_level.resize(3);
// No compression for L0
options.compression_per_level[0] = kNoCompression;
// No compression for the Ln whre L0 is compacted to
options.compression_per_level[1] = kNoCompression;
// Snappy compression for Ln+1
options.compression_per_level[2] = kSnappyCompression;
OnFileDeletionListener* listener = new OnFileDeletionListener();
options.listeners.emplace_back(listener);
DestroyAndReopen(options);
// Insert more than 80K. L4 should be base level. Neither L0 nor L4 should
// be compressed, so there shouldn't be any compression.
for (int i = 0; i < 20; i++) {
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_EQ(NumTableFilesAtLevel(3), 0);
ASSERT_TRUE(NumTableFilesAtLevel(0) > 0 || NumTableFilesAtLevel(4) > 0);
// Verify there was no compression
auto num_block_compressed =
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
ASSERT_EQ(num_block_compressed, 0);
// Insert 400KB and there will be some files end up in L3. According to the
// above compression settings for each level, there will be some compression.
ASSERT_OK(options.statistics->Reset());
ASSERT_EQ(num_block_compressed, 0);
for (int i = 20; i < 120; i++) {
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_GE(NumTableFilesAtLevel(3), 1);
ASSERT_GE(NumTableFilesAtLevel(4), 1);
// Verify there was compression
num_block_compressed =
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
ASSERT_GT(num_block_compressed, 0);
// Make sure data in files in L3 is not compacted by removing all files
// in L4 and calculate number of rows
ASSERT_OK(dbfull()->SetOptions({
{"disable_auto_compactions", "true"},
}));
ColumnFamilyMetaData cf_meta;
db_->GetColumnFamilyMetaData(&cf_meta);
// Ensure that L1+ files are non-overlapping and together with L0 encompass
// full key range between smallestkey and largestkey from CF file metadata.
int largestkey_in_prev_level = -1;
int keys_found = 0;
for (int level = (int)cf_meta.levels.size() - 1; level >= 0; level--) {
int files_in_level = (int)cf_meta.levels[level].files.size();
int largestkey_in_prev_file = -1;
for (int j = 0; j < files_in_level; j++) {
int smallestkey = IdFromKey(cf_meta.levels[level].files[j].smallestkey);
int largestkey = IdFromKey(cf_meta.levels[level].files[j].largestkey);
int num_entries = (int)cf_meta.levels[level].files[j].num_entries;
ASSERT_EQ(num_entries, largestkey - smallestkey + 1);
keys_found += num_entries;
if (level > 0) {
if (j == 0) {
ASSERT_GT(smallestkey, largestkey_in_prev_level);
}
if (j > 0) {
ASSERT_GT(smallestkey, largestkey_in_prev_file);
}
if (j == files_in_level - 1) {
largestkey_in_prev_level = largestkey;
}
}
largestkey_in_prev_file = largestkey;
}
}
ASSERT_EQ(keys_found, kNKeys);
for (const auto& file : cf_meta.levels[4].files) {
listener->SetExpectedFileName(dbname_ + file.name);
const RangeOpt ranges(file.smallestkey, file.largestkey);
// Given verification from above, we're guaranteed that by deleting all the
// files in [<smallestkey>, <largestkey>] range, we're effectively deleting
// that very single file and nothing more.
EXPECT_OK(dbfull()->DeleteFilesInRanges(dbfull()->DefaultColumnFamily(),
&ranges, true /* include_end */));
}
listener->VerifyMatchedCount(cf_meta.levels[4].files.size());
int num_keys = 0;
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
num_keys++;
}
ASSERT_OK(iter->status());
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_GE(NumTableFilesAtLevel(3), 1);
ASSERT_EQ(NumTableFilesAtLevel(4), 0);
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(3), num_keys * 4000U + num_keys * 10U);
}
TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
if (!Snappy_Supported() || !LZ4_Supported() || !Zlib_Supported()) {
return;
}
const int kNKeys = 500;
int keys[kNKeys];
for (int i = 0; i < kNKeys; i++) {
keys[i] = i;
}
RandomShuffle(std::begin(keys), std::end(keys));
Random rnd(301);
Options options;
options.create_if_missing = true;
options.db_write_buffer_size = 6000000;
options.write_buffer_size = 600000;
options.max_write_buffer_number = 2;
options.level0_file_num_compaction_trigger = 2;
options.level0_slowdown_writes_trigger = 2;
options.level0_stop_writes_trigger = 2;
options.soft_pending_compaction_bytes_limit = 1024 * 1024;
options.target_file_size_base = 20;
options.env = env_;
options.level_compaction_dynamic_level_bytes = true;
options.max_bytes_for_level_base = 200;
options.max_bytes_for_level_multiplier = 8;
options.max_background_compactions = 1;
options.num_levels = 5;
std::shared_ptr<mock::MockTableFactory> mtf(new mock::MockTableFactory);
options.table_factory = mtf;
options.compression_per_level.resize(3);
options.compression_per_level[0] = kNoCompression;
options.compression_per_level[1] = kLZ4Compression;
options.compression_per_level[2] = kZlibCompression;
DestroyAndReopen(options);
// When base level is L4, L4 is LZ4.
std::atomic<int> num_zlib(0);
std::atomic<int> num_lz4(0);
std::atomic<int> num_no(0);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
if (compaction->output_level() == 4) {
ASSERT_TRUE(compaction->output_compression() == kLZ4Compression);
num_lz4.fetch_add(1);
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
auto* compression = static_cast<CompressionType*>(arg);
ASSERT_TRUE(*compression == kNoCompression);
num_no.fetch_add(1);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 100; i++) {
std::string value = rnd.RandomString(200);
ASSERT_OK(Put(Key(keys[i]), value));
if (i % 25 == 24) {
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_EQ(NumTableFilesAtLevel(3), 0);
ASSERT_GT(NumTableFilesAtLevel(4), 0);
ASSERT_GT(num_no.load(), 2);
ASSERT_GT(num_lz4.load(), 0);
int prev_num_files_l4 = NumTableFilesAtLevel(4);
// After base level turn L4->L3, L3 becomes LZ4 and L4 becomes Zlib
num_lz4.store(0);
num_no.store(0);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
if (compaction->output_level() == 4 && compaction->start_level() == 3) {
ASSERT_TRUE(compaction->output_compression() == kZlibCompression);
num_zlib.fetch_add(1);
} else {
ASSERT_TRUE(compaction->output_compression() == kLZ4Compression);
num_lz4.fetch_add(1);
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
auto* compression = static_cast<CompressionType*>(arg);
ASSERT_TRUE(*compression == kNoCompression);
num_no.fetch_add(1);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 101; i < 500; i++) {
std::string value = rnd.RandomString(200);
ASSERT_OK(Put(Key(keys[i]), value));
if (i % 100 == 99) {
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_GT(NumTableFilesAtLevel(3), 0);
ASSERT_GT(NumTableFilesAtLevel(4), prev_num_files_l4);
ASSERT_GT(num_no.load(), 2);
ASSERT_GT(num_lz4.load(), 0);
ASSERT_GT(num_zlib.load(), 0);
}
TEST_F(DBTest, DynamicCompactionOptions) {
// minimum write buffer size is enforced at 64KB
const uint64_t k32KB = 1 << 15;
@@ -6209,8 +5938,8 @@ TEST_F(DBTest, L0L1L2AndUpHitCounter) {
}
TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
bool& allow_unsupported_fv =
BlockBasedTableFactory::AllowUnsupportedFormatVersion();
// Allow testing format_version=1
bool& allow_unsupported_fv = TEST_AllowUnsupportedFormatVersion();
SaveAndRestore guard(&allow_unsupported_fv);
ASSERT_FALSE(allow_unsupported_fv);
@@ -7349,27 +7078,6 @@ TEST_F(DBTest, LastWriteBufferDelay) {
}
#endif // !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
TEST_F(DBTest, FailWhenCompressionNotSupportedTest) {
CompressionType compressions[] = {kZlibCompression, kBZip2Compression,
kLZ4Compression, kLZ4HCCompression,
kXpressCompression};
for (auto comp : compressions) {
if (!CompressionTypeSupported(comp)) {
// not supported, we should fail the Open()
Options options = CurrentOptions();
options.compression = comp;
ASSERT_TRUE(!TryReopen(options).ok());
// Try if CreateColumnFamily also fails
options.compression = kNoCompression;
ASSERT_OK(TryReopen(options));
ColumnFamilyOptions cf_options(options);
cf_options.compression = comp;
ColumnFamilyHandle* handle;
ASSERT_TRUE(!db_->CreateColumnFamily(cf_options, "name", &handle).ok());
}
}
}
TEST_F(DBTest, CreateColumnFamilyShouldFailOnIncompatibleOptions) {
Options options = CurrentOptions();
options.max_open_files = 100;
-917
View File
@@ -29,7 +29,6 @@
#include "test_util/testutil.h"
#include "util/defer.h"
#include "util/random.h"
#include "util/simple_mixed_compressor.h"
#include "utilities/fault_injection_env.h"
namespace ROCKSDB_NAMESPACE {
@@ -1186,922 +1185,6 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
ASSERT_EQ(index, keys_cf.size());
}
TEST_F(DBTest2, PresetCompressionDict) {
// Verifies that compression ratio improves when dictionary is enabled, and
// improves even further when the dictionary is trained by ZSTD.
const size_t kBlockSizeBytes = 4 << 10;
const size_t kL0FileBytes = 128 << 10;
const size_t kApproxPerBlockOverheadBytes = 50;
const int kNumL0Files = 5;
Options options;
// Make sure to use any custom env that the test is configured with.
options.env = CurrentOptions().env;
options.allow_concurrent_memtable_write = false;
options.arena_block_size = kBlockSizeBytes;
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.level0_file_num_compaction_trigger = kNumL0Files;
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kL0FileBytes / kBlockSizeBytes));
options.num_levels = 2;
options.target_file_size_base = kL0FileBytes;
options.target_file_size_multiplier = 2;
options.write_buffer_size = kL0FileBytes;
BlockBasedTableOptions table_options;
table_options.block_size = kBlockSizeBytes;
std::vector<CompressionType> compression_types;
if (Zlib_Supported()) {
compression_types.push_back(kZlibCompression);
}
#if LZ4_VERSION_NUMBER >= 10400 // r124+
compression_types.push_back(kLZ4Compression);
compression_types.push_back(kLZ4HCCompression);
#endif // LZ4_VERSION_NUMBER >= 10400
if (ZSTD_Supported()) {
compression_types.push_back(kZSTD);
}
enum DictionaryTypes : int {
kWithoutDict,
kWithDict,
kWithZSTDfinalizeDict,
kWithZSTDTrainedDict,
kDictEnd,
};
for (auto compression_type : compression_types) {
options.compression = compression_type;
size_t bytes_without_dict = 0;
size_t bytes_with_dict = 0;
size_t bytes_with_zstd_finalize_dict = 0;
size_t bytes_with_zstd_trained_dict = 0;
for (int i = kWithoutDict; i < kDictEnd; i++) {
// First iteration: compress without preset dictionary
// Second iteration: compress with preset dictionary
// Third iteration (zstd only): compress with zstd-trained dictionary
//
// To make sure the compression dictionary has the intended effect, we
// verify the compressed size is smaller in successive iterations. Also in
// the non-first iterations, verify the data we get out is the same data
// we put in.
switch (i) {
case kWithoutDict:
options.compression_opts.max_dict_bytes = 0;
options.compression_opts.zstd_max_train_bytes = 0;
break;
case kWithDict:
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
options.compression_opts.zstd_max_train_bytes = 0;
break;
case kWithZSTDfinalizeDict:
if (compression_type != kZSTD ||
!ZSTD_FinalizeDictionarySupported()) {
continue;
}
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
options.compression_opts.use_zstd_dict_trainer = false;
break;
case kWithZSTDTrainedDict:
if (compression_type != kZSTD || !ZSTD_TrainDictionarySupported()) {
continue;
}
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
options.compression_opts.use_zstd_dict_trainer = true;
break;
default:
assert(false);
}
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(301);
std::string seq_datas[10];
for (int j = 0; j < 10; ++j) {
seq_datas[j] =
rnd.RandomString(kBlockSizeBytes - kApproxPerBlockOverheadBytes);
}
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
for (int j = 0; j < kNumL0Files; ++j) {
for (size_t k = 0; k < kL0FileBytes / kBlockSizeBytes + 1; ++k) {
auto key_num = j * (kL0FileBytes / kBlockSizeBytes) + k;
ASSERT_OK(Put(1, Key(static_cast<int>(key_num)),
seq_datas[(key_num / 10) % 10]));
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
ASSERT_EQ(j + 1, NumTableFilesAtLevel(0, 1));
}
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1],
true /* disallow_trivial_move */));
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
ASSERT_GT(NumTableFilesAtLevel(1, 1), 0);
// Get the live sst files size
size_t total_sst_bytes = TotalSize(1);
if (i == kWithoutDict) {
bytes_without_dict = total_sst_bytes;
} else if (i == kWithDict) {
bytes_with_dict = total_sst_bytes;
} else if (i == kWithZSTDfinalizeDict) {
bytes_with_zstd_finalize_dict = total_sst_bytes;
} else if (i == kWithZSTDTrainedDict) {
bytes_with_zstd_trained_dict = total_sst_bytes;
}
for (size_t j = 0; j < kNumL0Files * (kL0FileBytes / kBlockSizeBytes);
j++) {
ASSERT_EQ(seq_datas[(j / 10) % 10], Get(1, Key(static_cast<int>(j))));
}
if (i == kWithDict) {
ASSERT_GT(bytes_without_dict, bytes_with_dict);
} else if (i == kWithZSTDTrainedDict) {
// In zstd compression, it is sometimes possible that using a finalized
// dictionary does not get as good a compression ratio as raw content
// dictionary. But using a dictionary should always get better
// compression ratio than not using one.
ASSERT_TRUE(bytes_with_dict > bytes_with_zstd_finalize_dict ||
bytes_without_dict > bytes_with_zstd_finalize_dict);
} else if (i == kWithZSTDTrainedDict) {
// In zstd compression, it is sometimes possible that using a trained
// dictionary does not get as good a compression ratio as without
// training.
// But using a dictionary (with or without training) should always get
// better compression ratio than not using one.
ASSERT_TRUE(bytes_with_dict > bytes_with_zstd_trained_dict ||
bytes_without_dict > bytes_with_zstd_trained_dict);
}
DestroyAndReopen(options);
}
}
}
TEST_F(DBTest2, PresetCompressionDictLocality) {
if (!ZSTD_Supported()) {
return;
}
// Verifies that compression dictionary is generated from local data. The
// verification simply checks all output SSTs have different compression
// dictionaries. We do not verify effectiveness as that'd likely be flaky in
// the future.
const int kNumEntriesPerFile = 1 << 10; // 1KB
const int kNumBytesPerEntry = 1 << 10; // 1KB
const int kNumFiles = 4;
Options options = CurrentOptions();
options.compression = kZSTD;
options.compression_opts.max_dict_bytes = 1 << 14; // 16KB
options.compression_opts.zstd_max_train_bytes = 1 << 18; // 256KB
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
BlockBasedTableOptions table_options;
table_options.cache_index_and_filter_blocks = true;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Reopen(options);
Random rnd(301);
for (int i = 0; i < kNumFiles; ++i) {
for (int j = 0; j < kNumEntriesPerFile; ++j) {
ASSERT_OK(Put(Key(i * kNumEntriesPerFile + j),
rnd.RandomString(kNumBytesPerEntry)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_EQ(NumTableFilesAtLevel(1), i + 1);
}
// Store all the dictionaries generated during a full compaction.
std::vector<std::string> compression_dicts;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BlockBasedTableBuilder::WriteCompressionDictBlock:RawDict",
[&](void* arg) {
compression_dicts.emplace_back(static_cast<Slice*>(arg)->ToString());
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
CompactRangeOptions compact_range_opts;
compact_range_opts.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(compact_range_opts, nullptr, nullptr));
// Dictionary compression should not be so good as to compress four totally
// random files into one. If it does then there's probably something wrong
// with the test.
ASSERT_GT(NumTableFilesAtLevel(1), 1);
// Furthermore, there should be one compression dictionary generated per file.
// And they should all be different from each other.
ASSERT_EQ(NumTableFilesAtLevel(1),
static_cast<int>(compression_dicts.size()));
for (size_t i = 1; i < compression_dicts.size(); ++i) {
std::string& a = compression_dicts[i - 1];
std::string& b = compression_dicts[i];
size_t alen = a.size();
size_t blen = b.size();
ASSERT_TRUE(alen != blen || memcmp(a.data(), b.data(), alen) != 0);
}
}
class PresetCompressionDictTest
: public DBTestBase,
public testing::WithParamInterface<std::tuple<CompressionType, bool>> {
public:
PresetCompressionDictTest()
: DBTestBase("db_test2", false /* env_do_fsync */),
compression_type_(std::get<0>(GetParam())),
bottommost_(std::get<1>(GetParam())) {}
protected:
const CompressionType compression_type_;
const bool bottommost_;
};
INSTANTIATE_TEST_CASE_P(
DBTest2, PresetCompressionDictTest,
::testing::Combine(::testing::ValuesIn(GetSupportedDictCompressions()),
::testing::Bool()));
TEST_P(PresetCompressionDictTest, Flush) {
// Verifies that dictionary is generated and written during flush only when
// `ColumnFamilyOptions::compression` enables dictionary. Also verifies the
// size of the dictionary is within expectations according to the limit on
// buffering set by `CompressionOptions::max_dict_buffer_bytes`.
const size_t kValueLen = 256;
const size_t kKeysPerFile = 1 << 10;
const size_t kDictLen = 16 << 10;
const size_t kBlockLen = 4 << 10;
Options options = CurrentOptions();
if (bottommost_) {
options.bottommost_compression = compression_type_;
options.bottommost_compression_opts.enabled = true;
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
} else {
options.compression = compression_type_;
options.compression_opts.max_dict_bytes = kDictLen;
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
}
options.memtable_factory.reset(test::NewSpecialSkipListFactory(kKeysPerFile));
options.statistics = CreateDBStatistics();
BlockBasedTableOptions bbto;
bbto.block_size = kBlockLen;
bbto.cache_index_and_filter_blocks = true;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
Reopen(options);
Random rnd(301);
for (size_t i = 0; i <= kKeysPerFile; ++i) {
ASSERT_OK(Put(Key(static_cast<int>(i)), rnd.RandomString(kValueLen)));
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
// We can use `BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT` to detect whether a
// compression dictionary exists since dictionaries would be preloaded when
// the flush finishes.
if (bottommost_) {
// Flush is never considered bottommost. This should change in the future
// since flushed files may have nothing underneath them, like the one in
// this test case.
ASSERT_EQ(
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
0);
} else {
ASSERT_GT(
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
0);
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
// number of bytes needs to be adjusted in case the cached block is in
// ZSTD's digested dictionary format.
if (compression_type_ != kZSTD) {
// Although we limited buffering to `kBlockLen`, there may be up to two
// blocks of data included in the dictionary since we only check limit
// after each block is built.
ASSERT_LE(TestGetTickerCount(options,
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
2 * kBlockLen);
}
}
}
TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
// Verifies that dictionary is generated and written during compaction to
// non-bottommost level only when `ColumnFamilyOptions::compression` enables
// dictionary. Also verifies the size of the dictionary is within expectations
// according to the limit on buffering set by
// `CompressionOptions::max_dict_buffer_bytes`.
const size_t kValueLen = 256;
const size_t kKeysPerFile = 1 << 10;
const size_t kDictLen = 16 << 10;
const size_t kBlockLen = 4 << 10;
Options options = CurrentOptions();
if (bottommost_) {
options.bottommost_compression = compression_type_;
options.bottommost_compression_opts.enabled = true;
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
} else {
options.compression = compression_type_;
options.compression_opts.max_dict_bytes = kDictLen;
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
}
options.disable_auto_compactions = true;
options.statistics = CreateDBStatistics();
BlockBasedTableOptions bbto;
bbto.block_size = kBlockLen;
bbto.cache_index_and_filter_blocks = true;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
Reopen(options);
Random rnd(301);
for (size_t j = 0; j <= kKeysPerFile; ++j) {
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(2);
for (int i = 0; i < 2; ++i) {
for (size_t j = 0; j <= kKeysPerFile; ++j) {
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
}
ASSERT_OK(Flush());
}
ASSERT_EQ("2,0,1", FilesPerLevel(0));
uint64_t prev_compression_dict_bytes_inserted =
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT);
// This L0->L1 compaction merges the two L0 files into L1. The produced L1
// file is not bottommost due to the existing L2 file covering the same key-
// range.
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
ASSERT_EQ("0,1,1", FilesPerLevel(0));
// We can use `BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT` to detect whether a
// compression dictionary exists since dictionaries would be preloaded when
// the compaction finishes.
if (bottommost_) {
ASSERT_EQ(
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
prev_compression_dict_bytes_inserted);
} else {
ASSERT_GT(
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
prev_compression_dict_bytes_inserted);
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
// number of bytes needs to be adjusted in case the cached block is in
// ZSTD's digested dictionary format.
if (compression_type_ != kZSTD) {
// Although we limited buffering to `kBlockLen`, there may be up to two
// blocks of data included in the dictionary since we only check limit
// after each block is built.
ASSERT_LE(TestGetTickerCount(options,
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
prev_compression_dict_bytes_inserted + 2 * kBlockLen);
}
}
}
TEST_P(PresetCompressionDictTest, CompactBottommost) {
// Verifies that dictionary is generated and written during compaction to
// non-bottommost level only when either `ColumnFamilyOptions::compression` or
// `ColumnFamilyOptions::bottommost_compression` enables dictionary. Also
// verifies the size of the dictionary is within expectations according to the
// limit on buffering set by `CompressionOptions::max_dict_buffer_bytes`.
const size_t kValueLen = 256;
const size_t kKeysPerFile = 1 << 10;
const size_t kDictLen = 16 << 10;
const size_t kBlockLen = 4 << 10;
Options options = CurrentOptions();
if (bottommost_) {
options.bottommost_compression = compression_type_;
options.bottommost_compression_opts.enabled = true;
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
} else {
options.compression = compression_type_;
options.compression_opts.max_dict_bytes = kDictLen;
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
}
options.disable_auto_compactions = true;
options.statistics = CreateDBStatistics();
BlockBasedTableOptions bbto;
bbto.block_size = kBlockLen;
bbto.cache_index_and_filter_blocks = true;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
Reopen(options);
Random rnd(301);
for (int i = 0; i < 2; ++i) {
for (size_t j = 0; j <= kKeysPerFile; ++j) {
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
}
ASSERT_OK(Flush());
}
ASSERT_EQ("2", FilesPerLevel(0));
uint64_t prev_compression_dict_bytes_inserted =
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT);
CompactRangeOptions cro;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ("0,1", FilesPerLevel(0));
ASSERT_GT(
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
prev_compression_dict_bytes_inserted);
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
// number of bytes needs to be adjusted in case the cached block is in ZSTD's
// digested dictionary format.
if (compression_type_ != kZSTD) {
// Although we limited buffering to `kBlockLen`, there may be up to two
// blocks of data included in the dictionary since we only check limit after
// each block is built.
ASSERT_LE(
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
prev_compression_dict_bytes_inserted + 2 * kBlockLen);
}
}
class CompactionCompressionListener : public EventListener {
public:
explicit CompactionCompressionListener(Options* db_options)
: db_options_(db_options) {}
void OnCompactionCompleted(DB* db, const CompactionJobInfo& ci) override {
// Figure out last level with files
int bottommost_level = 0;
for (int level = 0; level < db->NumberLevels(); level++) {
std::string files_at_level;
ASSERT_TRUE(
db->GetProperty("rocksdb.num-files-at-level" + std::to_string(level),
&files_at_level));
if (files_at_level != "0") {
bottommost_level = level;
}
}
if (db_options_->bottommost_compression != kDisableCompressionOption &&
ci.output_level == bottommost_level) {
ASSERT_EQ(ci.compression, db_options_->bottommost_compression);
} else if (db_options_->compression_per_level.size() != 0) {
ASSERT_EQ(ci.compression,
db_options_->compression_per_level[ci.output_level]);
} else {
ASSERT_EQ(ci.compression, db_options_->compression);
}
max_level_checked = std::max(max_level_checked, ci.output_level);
}
int max_level_checked = 0;
const Options* db_options_;
};
enum CompressionFailureType {
kTestCompressionFail,
kTestDecompressionFail,
kTestDecompressionCorruption
};
class CompressionFailuresTest
: public DBTest2,
public testing::WithParamInterface<std::tuple<
CompressionFailureType, CompressionType, uint32_t, uint32_t>> {
public:
CompressionFailuresTest() {
std::tie(compression_failure_type_, compression_type_,
compression_max_dict_bytes_, compression_parallel_threads_) =
GetParam();
}
CompressionFailureType compression_failure_type_ = kTestCompressionFail;
CompressionType compression_type_ = kNoCompression;
uint32_t compression_max_dict_bytes_ = 0;
uint32_t compression_parallel_threads_ = 0;
};
INSTANTIATE_TEST_CASE_P(
DBTest2, CompressionFailuresTest,
::testing::Combine(::testing::Values(kTestCompressionFail,
kTestDecompressionFail,
kTestDecompressionCorruption),
::testing::ValuesIn(GetSupportedCompressions()),
::testing::Values(0, 10), ::testing::Values(1, 4)));
TEST_P(CompressionFailuresTest, CompressionFailures) {
if (compression_type_ == kNoCompression) {
return;
}
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.max_bytes_for_level_base = 1024;
options.max_bytes_for_level_multiplier = 2;
options.num_levels = 7;
options.max_background_compactions = 1;
options.target_file_size_base = 512;
BlockBasedTableOptions table_options;
table_options.block_size = 512;
table_options.verify_compression = true;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.compression = compression_type_;
options.compression_opts.parallel_threads = compression_parallel_threads_;
options.compression_opts.max_dict_bytes = compression_max_dict_bytes_;
options.bottommost_compression_opts.parallel_threads =
compression_parallel_threads_;
options.bottommost_compression_opts.max_dict_bytes =
compression_max_dict_bytes_;
if (compression_failure_type_ == kTestCompressionFail) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompressData:TamperWithReturnValue", [](void* arg) {
bool* ret = static_cast<bool*>(arg);
*ret = false;
});
} else if (compression_failure_type_ == kTestDecompressionFail) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DecompressBlockData:TamperWithReturnValue", [](void* arg) {
Status* ret = static_cast<Status*>(arg);
ASSERT_OK(*ret);
*ret = Status::Corruption("kTestDecompressionFail");
});
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DecompressBlockData:TamperWithDecompressionOutput", [](void* arg) {
BlockContents* contents = static_cast<BlockContents*>(arg);
// Ensure uncompressed data != original data
const size_t len = contents->data.size() + 1;
std::unique_ptr<char[]> fake_data(new char[len]());
*contents = BlockContents(std::move(fake_data), len);
});
}
std::map<std::string, std::string> key_value_written;
const int kKeySize = 5;
const int kValUnitSize = 16;
const int kValSize = 256;
Random rnd(405);
Status s = Status::OK();
DestroyAndReopen(options);
// Write 10 random files
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
std::string key = rnd.RandomString(kKeySize);
// Ensure good compression ratio
std::string valueUnit = rnd.RandomString(kValUnitSize);
std::string value;
for (int k = 0; k < kValSize; k += kValUnitSize) {
value += valueUnit;
}
s = Put(key, value);
if (compression_failure_type_ == kTestCompressionFail) {
key_value_written[key] = value;
ASSERT_OK(s);
}
}
s = Flush();
if (compression_failure_type_ == kTestCompressionFail) {
ASSERT_OK(s);
}
s = dbfull()->TEST_WaitForCompact();
if (compression_failure_type_ == kTestCompressionFail) {
ASSERT_OK(s);
}
if (i == 4) {
// Make compression fail at the mid of table building
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
}
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
if (compression_failure_type_ == kTestCompressionFail) {
// Should be kNoCompression, check content consistency
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
std::string key = db_iter->key().ToString();
std::string value = db_iter->value().ToString();
ASSERT_NE(key_value_written.find(key), key_value_written.end());
ASSERT_EQ(key_value_written[key], value);
key_value_written.erase(key);
}
ASSERT_OK(db_iter->status());
ASSERT_EQ(0, key_value_written.size());
} else if (compression_failure_type_ == kTestDecompressionFail) {
ASSERT_EQ(std::string(s.getState()),
"Could not decompress: kTestDecompressionFail");
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
ASSERT_EQ(std::string(s.getState()),
"Decompressed block did not match pre-compression block");
}
}
TEST_F(DBTest2, CompressionOptions) {
if (!Zlib_Supported() || !Snappy_Supported()) {
return;
}
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.max_bytes_for_level_base = 100;
options.max_bytes_for_level_multiplier = 2;
options.num_levels = 7;
options.max_background_compactions = 1;
CompactionCompressionListener* listener =
new CompactionCompressionListener(&options);
options.listeners.emplace_back(listener);
const int kKeySize = 5;
const int kValSize = 20;
Random rnd(301);
std::vector<uint32_t> compression_parallel_threads = {1, 4};
std::map<std::string, std::string> key_value_written;
for (int iter = 0; iter <= 2; iter++) {
listener->max_level_checked = 0;
if (iter == 0) {
// Use different compression algorithms for different levels but
// always use Zlib for bottommost level
options.compression_per_level = {kNoCompression, kNoCompression,
kNoCompression, kSnappyCompression,
kSnappyCompression, kSnappyCompression,
kZlibCompression};
options.compression = kNoCompression;
options.bottommost_compression = kZlibCompression;
} else if (iter == 1) {
// Use Snappy except for bottommost level use ZLib
options.compression_per_level = {};
options.compression = kSnappyCompression;
options.bottommost_compression = kZlibCompression;
} else if (iter == 2) {
// Use Snappy everywhere
options.compression_per_level = {};
options.compression = kSnappyCompression;
options.bottommost_compression = kDisableCompressionOption;
}
for (auto num_threads : compression_parallel_threads) {
options.compression_opts.parallel_threads = num_threads;
options.bottommost_compression_opts.parallel_threads = num_threads;
DestroyAndReopen(options);
// Write 10 random files
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
std::string key = rnd.RandomString(kKeySize);
std::string value = rnd.RandomString(kValSize);
key_value_written[key] = value;
ASSERT_OK(Put(key, value));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
// Make sure that we wrote enough to check all 7 levels
ASSERT_EQ(listener->max_level_checked, 6);
// Make sure database content is the same as key_value_written
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
std::string key = db_iter->key().ToString();
std::string value = db_iter->value().ToString();
ASSERT_NE(key_value_written.find(key), key_value_written.end());
ASSERT_EQ(key_value_written[key], value);
key_value_written.erase(key);
}
ASSERT_OK(db_iter->status());
ASSERT_EQ(0, key_value_written.size());
}
}
}
TEST_F(DBTest2, RoundRobinManager) {
if (ZSTD_Supported()) {
auto mgr = std::make_shared<RoundRobinManager>(
GetDefaultBuiltinCompressionManager());
for (CompressionType type : {kZSTD}) {
std::vector<std::string> values;
for (bool use_wrapper : {true}) {
SCOPED_TRACE("Compression type: " + std::to_string(type) +
(use_wrapper ? " with " : " no ") + "wrapper");
Options options = CurrentOptions();
options.compression = type;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
BlockBasedTableOptions bbto;
bbto.enable_index_compression = false;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
options.compression_manager = use_wrapper ? mgr : nullptr;
DestroyAndReopen(options);
Random rnd(301);
constexpr int kCount = 13;
// Highly compressible blocks, except 1 non-compressible. Half of the
// compressible are morked for bypass and 1 marked for rejection. Values
// are large enough to ensure just 1 k-v per block.
for (int i = 0; i < kCount; ++i) {
std::string value;
if (i == 6) {
// One non-compressible block
value = rnd.RandomBinaryString(20000);
} else {
test::CompressibleString(&rnd, 0.1, 20000, &value);
}
values.push_back(value);
ASSERT_OK(Put(Key(i), value));
ASSERT_EQ(Get(Key(i)), value);
}
ASSERT_OK(Flush());
// Ensure well-formed for reads
for (int i = 0; i < kCount; ++i) {
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
ASSERT_EQ(Get(Key(i)), values[i]);
}
ASSERT_EQ(Get(Key(kCount)), "NOT_FOUND");
}
}
}
}
TEST_F(DBTest2, RandomMixedCompressionManager) {
if (ZSTD_Supported()) {
auto mgr = std::make_shared<RandomMixedCompressionManager>(
GetDefaultBuiltinCompressionManager());
// Currently mixedmanager only supports with preffered compression manager
// zstd
for (CompressionType type : {kZSTD}) {
std::vector<std::string> values;
for (bool use_wrapper : {true}) {
SCOPED_TRACE("Compression type: " + std::to_string(type) +
(use_wrapper ? " with " : " no ") + "wrapper");
Options options = CurrentOptions();
options.compression = type;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
BlockBasedTableOptions bbto;
bbto.enable_index_compression = false;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
options.compression_manager = use_wrapper ? mgr : nullptr;
DestroyAndReopen(options);
Random rnd(301);
constexpr int kCount = 13;
// Highly compressible blocks, except 1 non-compressible. Half of the
// compressible are morked for bypass and 1 marked for rejection. Values
// are large enough to ensure just 1 k-v per block.
for (int i = 0; i < kCount; ++i) {
std::string value;
if (i == 6) {
// One non-compressible block
value = rnd.RandomBinaryString(20000);
} else {
test::CompressibleString(&rnd, 0.1, 20000, &value);
}
values.push_back(value);
ASSERT_OK(Put(Key(i), value));
ASSERT_EQ(Get(Key(i)), value);
}
ASSERT_OK(Flush());
// Ensure well-formed for reads
for (int i = 0; i < kCount; ++i) {
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
ASSERT_EQ(Get(Key(i)), values[i]);
}
ASSERT_EQ(Get(Key(kCount)), "NOT_FOUND");
}
}
}
}
TEST_F(DBTest2, CompressionManagerWrapper) {
// Test that we can use a custom CompressionManager to wrap the built-in
// CompressionManager, thus adopting a custom *strategy* based on existing
// algorithms. This will "mark" some blocks (in their contents) as "do not
// compress", i.e. no attempt to compress, and some blocks as "reject
// compression", i.e. compression attempted but rejected because of ratio
// or otherwise. These cases are distinguishable for statistics that
// approximate "wasted effort".
static std::string kDoNotCompress = "do_not_compress";
static std::string kRejectCompression = "reject_compression";
struct MyCompressor : public CompressorWrapper {
using CompressorWrapper::CompressorWrapper;
Status CompressBlock(Slice uncompressed_data,
std::string* compressed_output,
CompressionType* out_compression_type,
ManagedWorkingArea* working_area) override {
auto begin = uncompressed_data.data();
auto end = uncompressed_data.data() + uncompressed_data.size();
if (std::search(begin, end, kDoNotCompress.begin(),
kDoNotCompress.end()) != end) {
// Do not attempt compression
EXPECT_EQ(*out_compression_type, kNoCompression);
return Status::OK();
} else if (std::search(begin, end, kRejectCompression.begin(),
kRejectCompression.end()) != end) {
// Simulate attempted & rejected compression
*compressed_output = "blah";
EXPECT_EQ(*out_compression_type, kNoCompression);
return Status::OK();
} else {
return wrapped_->CompressBlock(uncompressed_data, compressed_output,
out_compression_type, working_area);
}
}
};
struct MyManager : public CompressionManagerWrapper {
using CompressionManagerWrapper::CompressionManagerWrapper;
const char* Name() const override { return wrapped_->Name(); }
std::unique_ptr<Compressor> GetCompressorForSST(
const FilterBuildingContext& context, const CompressionOptions& opts,
CompressionType preferred) override {
return std::make_unique<MyCompressor>(
wrapped_->GetCompressorForSST(context, opts, preferred));
}
};
auto mgr = std::make_shared<MyManager>(GetDefaultBuiltinCompressionManager());
for (CompressionType type : GetSupportedCompressions()) {
for (bool use_wrapper : {false, true}) {
if (type == kNoCompression) {
continue;
}
SCOPED_TRACE("Compression type: " + std::to_string(type) +
(use_wrapper ? " with " : " no ") + "wrapper");
Options options = CurrentOptions();
options.compression = type;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
BlockBasedTableOptions bbto;
bbto.enable_index_compression = false;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
options.compression_manager = use_wrapper ? mgr : nullptr;
DestroyAndReopen(options);
auto PopStat = [&](Tickers t) -> uint64_t {
return options.statistics->getAndResetTickerCount(t);
};
Random rnd(301);
constexpr int kCount = 13;
// Highly compressible blocks, except 1 non-compressible. Half of the
// compressible are morked for bypass and 1 marked for rejection. Values
// are large enough to ensure just 1 k-v per block.
for (int i = 0; i < kCount; ++i) {
std::string value;
if (i == 6) {
// One non-compressible block
value = rnd.RandomBinaryString(20000);
} else {
test::CompressibleString(&rnd, 0.1, 20000, &value);
if ((i % 2) == 0) {
// Half for bypass
value += kDoNotCompress;
} else if (i == 7) {
// One for rejection
value += kRejectCompression;
}
}
ASSERT_OK(Put(Key(i), value));
}
ASSERT_OK(Flush());
if (use_wrapper) {
EXPECT_EQ(kCount / 2 - 1, PopStat(NUMBER_BLOCK_COMPRESSED));
EXPECT_EQ(kCount / 2, PopStat(NUMBER_BLOCK_COMPRESSION_BYPASSED));
EXPECT_EQ(1 + 1, PopStat(NUMBER_BLOCK_COMPRESSION_REJECTED));
} else {
EXPECT_EQ(kCount - 1, PopStat(NUMBER_BLOCK_COMPRESSED));
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSION_BYPASSED));
EXPECT_EQ(1, PopStat(NUMBER_BLOCK_COMPRESSION_REJECTED));
}
// Ensure well-formed for reads
for (int i = 0; i < kCount; ++i) {
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
}
ASSERT_EQ(Get(Key(kCount)), "NOT_FOUND");
}
}
}
class CompactionStallTestListener : public EventListener {
public:
CompactionStallTestListener()
+8
View File
@@ -452,6 +452,10 @@ class SpecialEnv : public EnvWrapper {
return s;
}
Status GetFileSize(uint64_t* s) override {
return target_->GetFileSize(s);
}
private:
std::unique_ptr<RandomAccessFile> target_;
anon::AtomicCounter* counter_;
@@ -478,6 +482,10 @@ class SpecialEnv : public EnvWrapper {
return target_->Prefetch(offset, n);
}
Status GetFileSize(uint64_t* s) override {
return target_->GetFileSize(s);
}
private:
std::unique_ptr<RandomAccessFile> target_;
std::atomic<uint64_t>* fail_cnt_;
-1
View File
@@ -2400,7 +2400,6 @@ class DataVisibilityTest : public DBBasicTestWithTimestampBase {
}
}
};
constexpr int DataVisibilityTest::kTestDataSize;
// Application specifies timestamp but not snapshot.
// reader writer
+93 -29
View File
@@ -16,6 +16,7 @@
#include "test_util/testharness.h"
#include "test_util/testutil.h"
#include "util/defer.h"
#include "util/file_checksum_helper.h"
#include "util/random.h"
#include "utilities/fault_injection_env.h"
@@ -346,7 +347,8 @@ class ChecksumVerifyHelper {
Status GetSingleFileChecksumAndFuncName(
const std::string& file_path, std::string* file_checksum,
std::string* file_checksum_func_name) {
std::string* file_checksum_func_name,
const std::string& requested_func_name = {}) {
Status s;
EnvOptions soptions;
std::unique_ptr<SequentialFile> file_reader;
@@ -364,6 +366,8 @@ class ChecksumVerifyHelper {
return Status::OK();
} else {
FileChecksumGenContext gen_context;
gen_context.file_name = file_path;
gen_context.requested_checksum_func_name = requested_func_name;
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
file_checksum_gen_factory->CreateFileChecksumGenerator(gen_context);
*file_checksum_func_name = file_checksum_gen->Name();
@@ -439,10 +443,50 @@ TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
DestroyAndRecreateExternalSSTFilesDir();
}
namespace {
class VariousFileChecksumGenerator : public FileChecksumGenCrc32c {
public:
explicit VariousFileChecksumGenerator(const std::string& name)
: FileChecksumGenCrc32c({}), name_(name) {}
const char* Name() const override { return name_.c_str(); }
std::string GetChecksum() const override {
return FileChecksumGenCrc32c::GetChecksum() + "_" + name_;
}
private:
const std::string name_;
};
class VariousFileChecksumGenFactory : public FileChecksumGenFactory {
public:
std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
const FileChecksumGenContext& context) override {
static RelaxedAtomic<int> counter{0};
if (Slice(context.requested_checksum_func_name).starts_with("Various")) {
return std::make_unique<VariousFileChecksumGenerator>(
context.requested_checksum_func_name);
} else if (context.requested_checksum_func_name.empty()) {
// Lacking a specific request, use a different function name for each
// result.
return std::make_unique<VariousFileChecksumGenerator>(
"Various" + std::to_string(counter.FetchAddRelaxed(1)));
} else {
return nullptr;
}
}
static const char* kClassName() { return "VariousFileChecksumGenFactory"; }
const char* Name() const override { return kClassName(); }
};
} // namespace
TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
Options old_options = CurrentOptions();
Options options = CurrentOptions();
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
options.file_checksum_gen_factory =
std::make_shared<VariousFileChecksumGenFactory>();
const ImmutableCFOptions ioptions(options);
ChecksumVerifyHelper checksum_helper(options);
@@ -463,7 +507,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_EQ(file1_info.largest_key, Key(1099));
std::string file_checksum1, file_checksum_func_name1;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
file1, &file_checksum1, &file_checksum_func_name1));
file1, &file_checksum1, &file_checksum_func_name1,
file1_info.file_checksum_func_name));
ASSERT_EQ(file1_info.file_checksum, file_checksum1);
ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name1);
@@ -482,7 +527,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_EQ(file2_info.largest_key, Key(1299));
std::string file_checksum2, file_checksum_func_name2;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
file2, &file_checksum2, &file_checksum_func_name2));
file2, &file_checksum2, &file_checksum_func_name2,
file2_info.file_checksum_func_name));
ASSERT_EQ(file2_info.file_checksum, file_checksum2);
ASSERT_EQ(file2_info.file_checksum_func_name, file_checksum_func_name2);
@@ -501,7 +547,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_EQ(file3_info.largest_key, Key(1499));
std::string file_checksum3, file_checksum_func_name3;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
file3, &file_checksum3, &file_checksum_func_name3));
file3, &file_checksum3, &file_checksum_func_name3,
file3_info.file_checksum_func_name));
ASSERT_EQ(file3_info.file_checksum, file_checksum3);
ASSERT_EQ(file3_info.file_checksum_func_name, file_checksum_func_name3);
@@ -520,7 +567,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_EQ(file4_info.largest_key, Key(1799));
std::string file_checksum4, file_checksum_func_name4;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
file4, &file_checksum4, &file_checksum_func_name4));
file4, &file_checksum4, &file_checksum_func_name4,
file4_info.file_checksum_func_name));
ASSERT_EQ(file4_info.file_checksum, file_checksum4);
ASSERT_EQ(file4_info.file_checksum_func_name, file_checksum_func_name4);
@@ -539,7 +587,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_EQ(file5_info.largest_key, Key(1999));
std::string file_checksum5, file_checksum_func_name5;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
file5, &file_checksum5, &file_checksum_func_name5));
file5, &file_checksum5, &file_checksum_func_name5,
file5_info.file_checksum_func_name));
ASSERT_EQ(file5_info.file_checksum, file_checksum5);
ASSERT_EQ(file5_info.file_checksum_func_name, file_checksum_func_name5);
@@ -558,7 +607,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_EQ(file6_info.largest_key, Key(2199));
std::string file_checksum6, file_checksum_func_name6;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
file6, &file_checksum6, &file_checksum_func_name6));
file6, &file_checksum6, &file_checksum_func_name6,
file6_info.file_checksum_func_name));
ASSERT_EQ(file6_info.file_checksum, file_checksum6);
ASSERT_EQ(file6_info.file_checksum_func_name, file_checksum_func_name6);
@@ -628,18 +678,23 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
}
ASSERT_OK(env_->FileExists(file2));
// Enable verify_file_checksum option
// No checksum information is provided, generate it when ingesting
std::vector<std::string> checksum, checksum_func;
s = AddFileWithFileChecksum({file3}, checksum, checksum_func, true, false,
false, false);
// Enable verify_file_checksum option. No checksum information is provided,
// so it is generated when ingesting. The configured checksum factory will
// use a different function than before.
s = AddFileWithFileChecksum({file3}, {}, {}, true, false, false, false);
ASSERT_OK(s) << s.ToString();
std::vector<LiveFileMetaData> live_files2;
dbfull()->GetLiveFilesMetaData(&live_files2);
for (const auto& f : live_files2) {
if (set1.find(f.name) == set1.end()) {
ASSERT_EQ(f.file_checksum, file_checksum3);
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name3);
// Recomputed checksum, different function
EXPECT_NE(f.file_checksum_func_name, file_checksum_func_name3);
std::string cur_checksum3, cur_checksum_func_name3;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
dbname_ + f.name, &cur_checksum3, &cur_checksum_func_name3,
f.file_checksum_func_name));
EXPECT_EQ(f.file_checksum, cur_checksum3);
EXPECT_EQ(f.file_checksum_func_name, cur_checksum_func_name3);
set1.insert(f.name);
}
}
@@ -653,8 +708,9 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_NOK(s) << s.ToString();
// Does not enable verify_file_checksum options
// Checksum function name matches, store the checksum being ingested.
s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4},
// Checksum function name is recognized, so store the checksum being ingested.
std::string file_checksum_func_name4alt = "VariousABCD";
s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4alt},
false, false, false, false);
ASSERT_OK(s) << s.ToString();
std::vector<LiveFileMetaData> live_files3;
@@ -663,7 +719,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
if (set1.find(f.name) == set1.end()) {
ASSERT_FALSE(f.file_checksum == file_checksum4);
ASSERT_EQ(f.file_checksum, "asd");
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name4);
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name4alt);
set1.insert(f.name);
}
}
@@ -672,7 +728,8 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
// enable verify_file_checksum options, DB enable checksum, and enable
// write_global_seq. So the checksum stored is different from the one
// ingested due to the sequence number changes.
// ingested due to the sequence number changes. The checksum function name
// may also change since the checksum is recomputed.
s = AddFileWithFileChecksum({file5}, {file_checksum5},
{file_checksum_func_name5}, true, false, false,
true);
@@ -681,11 +738,14 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
dbfull()->GetLiveFilesMetaData(&live_files4);
for (const auto& f : live_files4) {
if (set1.find(f.name) == set1.end()) {
// Recomputed checksum, different function
EXPECT_NE(f.file_checksum_func_name, file_checksum_func_name5);
std::string cur_checksum5, cur_checksum_func_name5;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
dbname_ + f.name, &cur_checksum5, &cur_checksum_func_name5));
ASSERT_EQ(f.file_checksum, cur_checksum5);
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name5);
dbname_ + f.name, &cur_checksum5, &cur_checksum_func_name5,
f.file_checksum_func_name));
EXPECT_EQ(f.file_checksum, cur_checksum5);
EXPECT_EQ(f.file_checksum_func_name, cur_checksum_func_name5);
set1.insert(f.name);
}
}
@@ -693,18 +753,22 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
ASSERT_OK(env_->FileExists(file5));
// Does not enable verify_file_checksum options and also the ingested file
// checksum information is empty. DB will generate and store the checksum
// in Manifest.
std::vector<std::string> files_c6, files_name6;
s = AddFileWithFileChecksum({file6}, files_c6, files_name6, false, false,
false, false);
// checksum information is empty. DB will generate and store file checksum
// in Manifest, which could be different from the previous invocation.
s = AddFileWithFileChecksum({file6}, {}, {}, false, false, false, false);
ASSERT_OK(s) << s.ToString();
std::vector<LiveFileMetaData> live_files6;
dbfull()->GetLiveFilesMetaData(&live_files6);
for (const auto& f : live_files6) {
if (set1.find(f.name) == set1.end()) {
ASSERT_EQ(f.file_checksum, file_checksum6);
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name6);
// Recomputed checksum, different function
EXPECT_NE(f.file_checksum_func_name, file_checksum_func_name6);
std::string cur_checksum6, cur_checksum_func_name6;
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
dbname_ + f.name, &cur_checksum6, &cur_checksum_func_name6,
f.file_checksum_func_name));
EXPECT_EQ(f.file_checksum, cur_checksum6);
EXPECT_EQ(f.file_checksum_func_name, cur_checksum_func_name6);
set1.insert(f.name);
}
}
+55 -40
View File
@@ -156,6 +156,14 @@ Status ExternalSstFileIngestionJob::Prepare(
// It is unsafe to assume application had sync the file and file
// directory before ingest the file. For integrity of RocksDB we need
// to sync the file.
// TODO(xingbo), We should in general be moving away from production
// uses of ReuseWritableFile (except explicitly for WAL recycling),
// ReopenWritableFile, and NewRandomRWFile. We should create a
// FileSystem::SyncFile/FsyncFile API that by default does the
// re-open+sync+close combo but can (a) be reused easily, and (b) be
// overridden to do that more cleanly, e.g. in EncryptedEnv.
// https://github.com/facebook/rocksdb/issues/13741
std::unique_ptr<FSWritableFile> file_to_sync;
Status s = fs_->ReopenWritableFile(path_inside_db, env_options_,
&file_to_sync, nullptr);
@@ -260,10 +268,6 @@ Status ExternalSstFileIngestionJob::Prepare(
} else {
need_generate_file_checksum_ = true;
}
FileChecksumGenContext gen_context;
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
db_options_.file_checksum_gen_factory->CreateFileChecksumGenerator(
gen_context);
std::vector<std::string> generated_checksums;
std::vector<std::string> generated_checksum_func_names;
// Step 1: generate the checksum for ingested sst file.
@@ -271,7 +275,9 @@ Status ExternalSstFileIngestionJob::Prepare(
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
std::string generated_checksum;
std::string generated_checksum_func_name;
std::string requested_checksum_func_name;
std::string requested_checksum_func_name =
i < files_checksum_func_names.size() ? files_checksum_func_names[i]
: "";
// TODO: rate limit file reads for checksum calculation during file
// ingestion.
// TODO: plumb Env::IOActivity
@@ -314,40 +320,50 @@ Status ExternalSstFileIngestionJob::Prepare(
if (files_checksum_func_names[i] !=
generated_checksum_func_names[i]) {
status = Status::InvalidArgument(
"Checksum function name does not match with the checksum "
"function name of this DB");
ROCKS_LOG_WARN(
db_options_.info_log,
"Sst file checksum verification of file: %s failed: %s",
external_files_paths[i].c_str(), status.ToString().c_str());
"DB file checksum gen factory " +
std::string(db_options_.file_checksum_gen_factory->Name()) +
" generated checksum function name " +
generated_checksum_func_names[i] + " for file " +
external_files_paths[i] +
" which does not match requested/provided " +
files_checksum_func_names[i]);
break;
}
if (files_checksums[i] != generated_checksums[i]) {
status = Status::Corruption(
"Ingested checksum does not match with the generated "
"checksum");
ROCKS_LOG_WARN(
db_options_.info_log,
"Sst file checksum verification of file: %s failed: %s",
files_to_ingest_[i].internal_file_path.c_str(),
status.ToString().c_str());
"Checksum verification mismatch for ingestion file " +
external_files_paths[i] + " using function " +
generated_checksum_func_names[i] + ". Expected: " +
Slice(files_checksums[i]).ToString(/*hex=*/true) +
" Computed: " +
Slice(generated_checksums[i]).ToString(/*hex=*/true));
break;
}
}
} else {
// If verify_file_checksum is not enabled, we only verify the
// checksum function name. If it does not match, fail the ingestion.
// If matches, we trust the ingested checksum information and store
// in the Manifest.
// If verify_file_checksum is not enabled, we only verify the factory
// recognizes the checksum function name. If it does not match, fail
// the ingestion. If matches, we trust the ingested checksum
// information and store in the Manifest.
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
if (files_checksum_func_names[i] != file_checksum_gen->Name()) {
FileChecksumGenContext gen_context;
gen_context.file_name = files_to_ingest_[i].internal_file_path;
gen_context.requested_checksum_func_name =
files_checksum_func_names[i];
auto file_checksum_gen =
db_options_.file_checksum_gen_factory
->CreateFileChecksumGenerator(gen_context);
if (file_checksum_gen == nullptr ||
files_checksum_func_names[i] != file_checksum_gen->Name()) {
status = Status::InvalidArgument(
"Checksum function name does not match with the checksum "
"function name of this DB");
ROCKS_LOG_WARN(
db_options_.info_log,
"Sst file checksum verification of file: %s failed: %s",
external_files_paths[i].c_str(), status.ToString().c_str());
"Checksum function name " + files_checksum_func_names[i] +
" for file " + external_files_paths[i] +
" not recognized by DB checksum gen factory" +
db_options_.file_checksum_gen_factory->Name() +
(file_checksum_gen ? (" Returned function " +
std::string(file_checksum_gen->Name()))
: ""));
break;
}
files_to_ingest_[i].file_checksum = files_checksums[i];
@@ -362,12 +378,11 @@ Status ExternalSstFileIngestionJob::Prepare(
status = Status::InvalidArgument(
"The checksum information of ingested sst files are nonempty and "
"the size of checksums or the size of the checksum function "
"names "
"does not match with the number of ingested sst files");
ROCKS_LOG_WARN(
db_options_.info_log,
"The ingested sst files checksum information is incomplete: %s",
status.ToString().c_str());
"names does not match with the number of ingested sst files");
}
if (!status.ok()) {
ROCKS_LOG_WARN(db_options_.info_log, "Ingestion failed: %s",
status.ToString().c_str());
}
}
}
@@ -682,10 +697,9 @@ void ExternalSstFileIngestionJob::CreateEquivalentFileIngestingCompactions() {
0 /* max_subcompaction, not applicable */,
{} /* grandparents, not applicable */,
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
false /* is deletion compaction, not applicable */,
files_overlap_ /* l0_files_might_overlap, not applicable */,
CompactionReason::kExternalSstIngestion));
CompactionReason::kExternalSstIngestion, "" /* trim_ts */,
-1 /* score, not applicable */,
files_overlap_ /* l0_files_might_overlap, not applicable */));
}
}
@@ -834,7 +848,8 @@ Status ExternalSstFileIngestionJob::ResetTableReader(
ro,
TableReaderOptions(
cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
env_options_, cfd_->internal_comparator(),
sv->mutable_cf_options.compression_manager.get(), env_options_,
cfd_->internal_comparator(),
sv->mutable_cf_options.block_protection_bytes_per_key,
/*skip_filters*/ false, /*immortal*/ false,
/*force_direct_prefetch*/ false, /*level*/ -1,
+2 -1
View File
@@ -324,7 +324,8 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
status = sv->mutable_cf_options.table_factory->NewTableReader(
TableReaderOptions(
cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
env_options_, cfd_->internal_comparator(),
sv->mutable_cf_options.compression_manager.get(), env_options_,
cfd_->internal_comparator(),
sv->mutable_cf_options.block_protection_bytes_per_key,
/*skip_filters*/ false, /*immortal*/ false,
/*force_direct_prefetch*/ false, /*level*/ -1,
+11 -11
View File
@@ -95,7 +95,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
uint64_t prospective_record_offset = 0;
Slice fragment;
while (true) {
for (;;) {
uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
size_t drop_size = 0;
const uint8_t record_type =
@@ -140,7 +140,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
prospective_record_offset = physical_record_offset;
scratch->assign(fragment.data(), fragment.size());
in_fragmented_record = true;
break;
break; // switch
case kMiddleType:
case kRecyclableMiddleType:
@@ -153,7 +153,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
}
scratch->append(fragment.data(), fragment.size());
}
break;
break; // switch
case kLastType:
case kRecyclableLastType:
@@ -171,7 +171,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
first_record_read_ = true;
return true;
}
break;
break; // switch
case kSetCompressionType: {
if (compression_type_record_read_) {
@@ -193,7 +193,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
} else {
InitCompression(compression_record);
}
break;
break; // switch
}
case kPredecessorWALInfoType:
case kRecyclePredecessorWALInfoType: {
@@ -210,7 +210,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
MaybeVerifyPredecessorWALInfo(wal_recovery_mode, fragment,
recorded_predecessor_wal_info);
}
break;
break; // switch
}
case kUserDefinedTimestampSizeType:
case kRecyclableUserDefinedTimestampSizeType: {
@@ -235,7 +235,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
ReportCorruption(fragment.size(), s.getState());
}
}
break;
break; // switch
}
case kBadHeader:
@@ -304,7 +304,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
in_fragmented_record = false;
scratch->clear();
}
break;
break; // switch
case kBadRecordLen:
if (eof_) {
@@ -337,7 +337,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
in_fragmented_record = false;
scratch->clear();
}
break;
break; // switch
default: {
if ((record_type & kRecordTypeSafeIgnoreMask) == 0) {
@@ -349,11 +349,11 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
}
in_fragmented_record = false;
scratch->clear();
break;
break; // switch
}
}
}
return false;
// unreachable
}
void Reader::MaybeVerifyPredecessorWALInfo(
+2 -1
View File
@@ -196,10 +196,11 @@ bool MemTable::ShouldFlushNow() {
// allocate one more block.
const double kAllowOverAllocationRatio = 0.6;
// range deletion use skip list which allocates all memeory through `arena_`
assert(range_del_table_->ApproximateMemoryUsage() == 0);
// If arena still have room for new block allocation, we can safely say it
// shouldn't flush.
auto allocated_memory = table_->ApproximateMemoryUsage() +
range_del_table_->ApproximateMemoryUsage() +
arena_.MemoryAllocatedBytes();
approximate_memory_usage_.store(allocated_memory, std::memory_order_relaxed);
+5
View File
@@ -688,6 +688,7 @@ class MemTable final : public ReadOnlyMemTable {
// Update counters and flush status after inserting a whole write batch
// Used in concurrent memtable inserts.
void BatchPostProcess(const MemTablePostProcessInfo& update_counters) {
table_->BatchPostProcess();
num_entries_.fetch_add(update_counters.num_entries,
std::memory_order_relaxed);
data_size_.fetch_add(update_counters.data_size, std::memory_order_relaxed);
@@ -698,6 +699,10 @@ class MemTable final : public ReadOnlyMemTable {
if (update_counters.num_range_deletes > 0) {
num_range_deletes_.fetch_add(update_counters.num_range_deletes,
std::memory_order_relaxed);
// noop for skip-list memtable
// Besides correctness test in stress test, memtable flush record count
// check will catch this if it were not noop.
// range_del_table_->BatchPostProcess();
}
UpdateFlushState();
}
+1 -1
View File
@@ -250,7 +250,7 @@ class MergeHelper {
// Parallel with keys_; stores the operands
mutable MergeContext merge_context_;
StopWatchNano filter_timer_;
StopWatchNano<> filter_timer_;
uint64_t total_filter_time_;
Statistics* stats_;
+70
View File
@@ -0,0 +1,70 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// 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 "rocksdb/db.h"
namespace ROCKSDB_NAMESPACE {
using MultiScanIterator = MultiScan::MultiScanIterator;
MultiScan::MultiScan(const ReadOptions& read_options,
const std::vector<ScanOptions>& scan_opts, DB* db,
ColumnFamilyHandle* cfh)
: read_options_(read_options), scan_opts_(scan_opts), db_(db), cfh_(cfh) {
bool slow_path = false;
// Setup read_options with iterate_uuper_bound based on the first scan.
// Subsequent scans will update and allocate a new DB iterator as necessary
if (scan_opts[0].range.limit) {
upper_bound_ = *scan_opts[0].range.limit;
read_options_.iterate_upper_bound = &upper_bound_;
} else {
read_options_.iterate_upper_bound = nullptr;
}
for (auto opts : scan_opts) {
// Check that all the ScanOptions either specify an upper bound or not. If
// its mixed we take the slow path which avoids calling Prepare: we have to
// reallocate the Iterator with updated read_options everytime we switch
// between upper bound or no upper bound, which complicates Prepare.
if (opts.range.limit.has_value() != scan_opts[0].range.limit.has_value()) {
slow_path = true;
break;
}
}
db_iter_.reset(db->NewIterator(read_options_, cfh));
if (!slow_path) {
db_iter_->Prepare(scan_opts);
}
}
MultiScanIterator& MultiScanIterator::operator++() {
if (idx_ >= scan_opts_.size()) {
throw std::logic_error("Index out of range");
}
idx_++;
if (idx_ < scan_opts_.size()) {
// Check if we need to update read_options_
if (scan_opts_[idx_].range.limit.has_value() !=
(read_options_.iterate_upper_bound != nullptr)) {
if (scan_opts_[idx_].range.limit) {
*upper_bound_ = *scan_opts_[idx_].range.limit;
read_options_.iterate_upper_bound = upper_bound_;
} else {
read_options_.iterate_upper_bound = nullptr;
}
db_iter_.reset(db_->NewIterator(read_options_, cfh_));
scan_.Reset(db_iter_.get());
} else if (scan_opts_[idx_].range.limit) {
*upper_bound_ = *scan_opts_[idx_].range.limit;
}
db_iter_->Seek(*scan_opts_[idx_].range.start);
status_ = db_iter_->status();
if (!status_.ok()) {
throw MultiScanException(status_);
}
}
return *this;
}
} // namespace ROCKSDB_NAMESPACE
+15 -7
View File
@@ -26,6 +26,7 @@ static const std::map<PeriodicTaskType, uint64_t> kDefaultPeriodSeconds = {
{PeriodicTaskType::kPersistStats, kInvalidPeriodSec},
{PeriodicTaskType::kFlushInfoLog, 10},
{PeriodicTaskType::kRecordSeqnoTime, kInvalidPeriodSec},
{PeriodicTaskType::kTriggerCompaction, 12 * 60 * 60} // 12 hours
};
static const std::map<PeriodicTaskType, std::string> kPeriodicTaskTypeNames = {
@@ -33,16 +34,20 @@ static const std::map<PeriodicTaskType, std::string> kPeriodicTaskTypeNames = {
{PeriodicTaskType::kPersistStats, "pst_st"},
{PeriodicTaskType::kFlushInfoLog, "flush_info_log"},
{PeriodicTaskType::kRecordSeqnoTime, "record_seq_time"},
{PeriodicTaskType::kTriggerCompaction, "trigger_compaction"},
};
Status PeriodicTaskScheduler::Register(PeriodicTaskType task_type,
const PeriodicTaskFunc& fn) {
return Register(task_type, fn, kDefaultPeriodSeconds.at(task_type));
const PeriodicTaskFunc& fn,
bool run_immediately) {
return Register(task_type, fn, kDefaultPeriodSeconds.at(task_type),
run_immediately);
}
Status PeriodicTaskScheduler::Register(PeriodicTaskType task_type,
const PeriodicTaskFunc& fn,
uint64_t repeat_period_seconds) {
uint64_t repeat_period_seconds,
bool run_immediately) {
MutexLock l(&timer_mutex);
static std::atomic<uint64_t> initial_delay(0);
@@ -65,10 +70,13 @@ Status PeriodicTaskScheduler::Register(PeriodicTaskType task_type,
std::string unique_id =
kPeriodicTaskTypeNames.at(task_type) + std::to_string(id_++);
bool succeeded = timer_->Add(
fn, unique_id,
(initial_delay.fetch_add(1) % repeat_period_seconds) * kMicrosInSecond,
repeat_period_seconds * kMicrosInSecond);
uint64_t initial_delay_micros =
(initial_delay.fetch_add(1) % repeat_period_seconds) * kMicrosInSecond;
if (!run_immediately) {
initial_delay_micros += repeat_period_seconds * kMicrosInSecond;
}
bool succeeded = timer_->Add(fn, unique_id, initial_delay_micros,
repeat_period_seconds * kMicrosInSecond);
if (!succeeded) {
return Status::Aborted("Failed to register periodic task");
}
+6 -2
View File
@@ -21,6 +21,7 @@ enum class PeriodicTaskType : uint8_t {
kPersistStats,
kFlushInfoLog,
kRecordSeqnoTime,
kTriggerCompaction,
kMax,
};
@@ -42,13 +43,16 @@ class PeriodicTaskScheduler {
PeriodicTaskScheduler& operator=(PeriodicTaskScheduler&&) = delete;
// Register a task with its default repeat period. Thread safe call.
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn);
// @param run_immediately If true, the task will run soon after it's
// scheduled, instead of waiting for the repeat period.
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn,
bool run_immediately);
// Register a task with specified repeat period. 0 is an invalid argument
// (kInvalidPeriodSec). To stop the task, please use Unregister().
// Thread safe call.
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn,
uint64_t repeat_period_seconds);
uint64_t repeat_period_seconds, bool run_immediately);
// Unregister the task. Thread safe call.
Status Unregister(PeriodicTaskType task_type);
+22 -4
View File
@@ -56,6 +56,12 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::FlushInfoLog:StartRunning",
[&](void*) { flush_info_log_counter++; });
int trigger_compaction_counter = 0;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::TriggerPeriodicCompaction:StartRunning",
[&](void*) { trigger_compaction_counter++; });
SyncPoint::GetInstance()->EnableProcessing();
Reopen(options);
@@ -70,7 +76,7 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
const PeriodicTaskScheduler& scheduler =
dbfull()->TEST_GetPeriodicTaskScheduler();
ASSERT_EQ(3, scheduler.TEST_GetValidTaskNum());
ASSERT_EQ((int)PeriodicTaskType::kMax - 1, scheduler.TEST_GetValidTaskNum());
ASSERT_EQ(1, dump_st_counter);
ASSERT_EQ(1, pst_st_counter);
@@ -103,14 +109,14 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
ASSERT_EQ(3, pst_st_counter);
ASSERT_EQ(4, flush_info_log_counter);
ASSERT_EQ(1u, scheduler.TEST_GetValidTaskNum());
ASSERT_EQ(2u, scheduler.TEST_GetValidTaskNum());
// Re-enable one task
ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "5"}}));
ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec);
ASSERT_EQ(0u, dbfull()->GetDBOptions().stats_persist_period_sec);
ASSERT_EQ(2, scheduler.TEST_GetValidTaskNum());
ASSERT_EQ(3, scheduler.TEST_GetValidTaskNum());
dbfull()->TEST_WaitForPeriodicTaskRun(
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
@@ -118,6 +124,16 @@ TEST_F(PeriodicTaskSchedulerTest, Basic) {
ASSERT_EQ(3, pst_st_counter);
ASSERT_EQ(5, flush_info_log_counter);
ASSERT_EQ(0, trigger_compaction_counter);
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
mock_clock_->MockSleepForSeconds(static_cast<int>(12 * 60 * 60));
});
ASSERT_EQ(1, trigger_compaction_counter);
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
mock_clock_->MockSleepForSeconds(static_cast<int>(12 * 60 * 60));
});
ASSERT_EQ(2, trigger_compaction_counter);
Close();
}
@@ -150,7 +166,9 @@ TEST_F(PeriodicTaskSchedulerTest, MultiInstances) {
auto dbi = static_cast_with_check<DBImpl>(dbs[kInstanceNum - 1]);
const PeriodicTaskScheduler& scheduler = dbi->TEST_GetPeriodicTaskScheduler();
ASSERT_EQ(kInstanceNum * 3, scheduler.TEST_GetValidTaskNum());
// kRecordSeqnoTime is not registered since the feature is not enabled
ASSERT_EQ(kInstanceNum * ((int)PeriodicTaskType::kMax - 1),
scheduler.TEST_GetValidTaskNum());
int expected_run = kInstanceNum;
dbi->TEST_WaitForPeriodicTaskRun(
+2 -1
View File
@@ -146,7 +146,8 @@ Status TableCache::GetTableReader(
s = mutable_cf_options.table_factory->NewTableReader(
ro,
TableReaderOptions(
ioptions_, mutable_cf_options.prefix_extractor, file_options,
ioptions_, mutable_cf_options.prefix_extractor,
mutable_cf_options.compression_manager.get(), file_options,
internal_comparator,
mutable_cf_options.block_protection_bytes_per_key, skip_filters,
immortal_tables_, false /* force_direct_prefetch */, level,
+81 -14
View File
@@ -95,6 +95,8 @@ namespace ROCKSDB_NAMESPACE {
namespace {
using ScanOptionsMap = std::unordered_map<size_t, std::vector<ScanOptions>>;
// Find File in LevelFilesBrief data structure
// Within an index range defined by left and right
int FindFileInRange(const InternalKeyComparator& icmp,
@@ -1100,9 +1102,47 @@ class LevelIterator final : public InternalIterator {
}
void Prepare(const std::vector<ScanOptions>* scan_opts) override {
// We assume here that scan_opts is sorted such that
// scan_opts[0].range.start < scan_opts[1].range.start, and non overlapping
scan_opts_ = scan_opts;
if (file_iter_.iter()) {
file_iter_.Prepare(scan_opts_);
if (scan_opts_ == nullptr) {
return;
}
file_to_scan_opts_ = std::make_unique<ScanOptionsMap>();
for (size_t k = 0; k < scan_opts_->size(); k++) {
const ScanOptions& opt = scan_opts_->at(k);
auto start = opt.range.start;
auto end = opt.range.limit;
if (!start.has_value()) {
continue;
}
// We can capture this case in the future, but for now lets skip this.
if (!end.has_value()) {
continue;
}
InternalKey istart(start.value(), kMaxSequenceNumber, kValueTypeForSeek);
InternalKey iend(end.value(), 0, kValueTypeForSeekForPrev);
// TODO: This needs to be optimized, right now we iterate twice, which
// we dont need to. We can do this in N rather than 2N.
size_t fstart = FindFile(icomparator_, *flevel_, istart.Encode());
size_t fend = FindFile(icomparator_, *flevel_, iend.Encode());
// We need to check the relevant cases
// Cases:
// 1. [ S E ]
// 2. [ S ] [ E ]
// 3. [ S ] ...... [ E ]
for (auto i = fstart; i <= fend; i++) {
if (i < flevel_->num_files) {
(*file_to_scan_opts_)[i].emplace_back(start.value(), end.value());
(*file_to_scan_opts_)[i].back().property_bag = opt.property_bag;
}
}
}
}
@@ -1233,6 +1273,9 @@ class LevelIterator final : public InternalIterator {
bool to_return_sentinel_ = false;
const std::vector<ScanOptions>* scan_opts_;
// Our stored scan_opts for each prefix
std::unique_ptr<ScanOptionsMap> file_to_scan_opts_ = nullptr;
// Sets flags for if we should return the sentinel key next.
// The condition for returning sentinel is reaching the end of current
// file_iter_: !Valid() && status.().ok().
@@ -1494,7 +1537,18 @@ bool LevelIterator::SkipEmptyFileForward() {
// LevelIterator::Seek*, it should also call Seek* into the corresponding
// range tombstone iterator.
if (file_iter_.iter() != nullptr) {
file_iter_.SeekToFirst();
// If we are doing prepared scan opts then we should seek to the values
// specified by the scan opts
if (scan_opts_ && (*file_to_scan_opts_)[file_index_].size()) {
const ScanOptions& opts = file_to_scan_opts_->at(file_index_).front();
if (opts.range.start.has_value()) {
InternalKey target(*opts.range.start.AsPtr(), kMaxSequenceNumber,
kValueTypeForSeek);
file_iter_.Seek(target.Encode());
}
} else {
file_iter_.SeekToFirst();
}
if (range_tombstone_iter_) {
if (*range_tombstone_iter_) {
(*range_tombstone_iter_)->SeekToFirst();
@@ -1542,10 +1596,15 @@ void LevelIterator::SetFileIterator(InternalIterator* iter) {
}
InternalIterator* old_iter = file_iter_.Set(iter);
// Since this is a new table iterator, no need to call Prepare() if
// scan_opts_ is null
if (iter && scan_opts_) {
file_iter_.Prepare(scan_opts_);
if (file_to_scan_opts_.get() &&
file_to_scan_opts_->find(file_index_) != file_to_scan_opts_->end()) {
const std::vector<ScanOptions>& opts =
file_to_scan_opts_->at(file_index_);
file_iter_.Prepare(&opts);
} else {
file_iter_.Prepare(scan_opts_);
}
}
// Update the read pattern for PrefetchBuffer.
@@ -1582,6 +1641,7 @@ void LevelIterator::InitFileIterator(size_t new_file_index) {
}
}
}
} // anonymous namespace
Status Version::GetTableProperties(const ReadOptions& read_options,
@@ -2741,9 +2801,10 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
RecordTick(db_statistics_, MULTIGET_COROUTINE_COUNT,
mget_tasks.size());
// Collect all results so far
std::vector<Status> statuses = folly::coro::blockingWait(
folly::coro::collectAllRange(std::move(mget_tasks))
.scheduleOn(&range->context()->executor()));
std::vector<Status> statuses =
folly::coro::blockingWait(co_withExecutor(
&range->context()->executor(),
folly::coro::collectAllRange(std::move(mget_tasks))));
if (s.ok()) {
for (Status stat : statuses) {
if (!stat.ok()) {
@@ -3028,9 +3089,10 @@ Status Version::MultiGetAsync(
assert(waiting.size());
RecordTick(db_statistics_, MULTIGET_COROUTINE_COUNT, mget_tasks.size());
// Collect all results so far
std::vector<Status> statuses = folly::coro::blockingWait(
folly::coro::collectAllRange(std::move(mget_tasks))
.scheduleOn(&range->context()->executor()));
std::vector<Status> statuses =
folly::coro::blockingWait(co_withExecutor(
&range->context()->executor(),
folly::coro::collectAllRange(std::move(mget_tasks))));
mget_tasks.clear();
if (s.ok()) {
for (Status stat : statuses) {
@@ -3507,8 +3569,13 @@ void VersionStorageInfo::ComputeCompactionScore(
}
if (compaction_style_ == kCompactionStyleFIFO) {
score = static_cast<double>(total_size) /
mutable_cf_options.compaction_options_fifo.max_table_files_size;
auto max_table_files_size =
mutable_cf_options.compaction_options_fifo.max_table_files_size;
if (max_table_files_size == 0) {
// avoid divide 0
max_table_files_size = 1;
}
score = static_cast<double>(total_size) / max_table_files_size;
if (score < 1 &&
mutable_cf_options.compaction_options_fifo.allow_compaction) {
score = std::max(
+2 -6
View File
@@ -551,9 +551,6 @@ Status WriteBatchInternal::Iterate(const WriteBatch* wb,
if (LIKELY(!s.IsTryAgain())) {
last_was_try_again = false;
tag = 0;
column_family = 0; // default
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
&blob, &xid, &write_unix_time);
if (!s.ok()) {
@@ -1897,7 +1894,6 @@ Status WriteBatch::VerifyChecksum() const {
// ReadRecordFromWriteBatch
key.clear();
value.clear();
column_family = 0;
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
&blob, &xid, /*write_unix_time=*/nullptr);
if (!s.ok()) {
@@ -3214,11 +3210,11 @@ Status WriteBatchInternal::InsertInto(
ColumnFamilyMemTables* memtables, FlushScheduler* flush_scheduler,
TrimHistoryScheduler* trim_history_scheduler,
bool ignore_missing_column_families, uint64_t recovery_log_number, DB* db,
bool concurrent_memtable_writes, bool seq_per_batch, bool batch_per_txn) {
bool seq_per_batch, bool batch_per_txn) {
MemTableInserter inserter(
sequence, memtables, flush_scheduler, trim_history_scheduler,
ignore_missing_column_families, recovery_log_number, db,
concurrent_memtable_writes, nullptr /* prot_info */,
/*concurrent_memtable_writes=*/false, nullptr /* prot_info */,
nullptr /*has_valid_writes*/, seq_per_batch, batch_per_txn);
for (auto w : write_group) {
if (w->CallbackFailed()) {
+5 -4
View File
@@ -185,18 +185,19 @@ class WriteBatchInternal {
// If flush_scheduler is non-null, it will be invoked if the memtable
// should be flushed.
//
// Under concurrent use, the caller is responsible for making sure that
// the memtables object itself is thread-local.
// This overload is for non-concurrent insertion only.
static Status InsertInto(
WriteThread::WriteGroup& write_group, SequenceNumber sequence,
ColumnFamilyMemTables* memtables, FlushScheduler* flush_scheduler,
TrimHistoryScheduler* trim_history_scheduler,
bool ignore_missing_column_families = false, uint64_t log_number = 0,
DB* db = nullptr, bool concurrent_memtable_writes = false,
bool seq_per_batch = false, bool batch_per_txn = true);
DB* db = nullptr, bool seq_per_batch = false, bool batch_per_txn = true);
// Convenience form of InsertInto when you have only one batch
// next_seq returns the seq after last sequence number used in MemTable insert
//
// Under concurrent use, the caller is responsible for making sure that
// the memtables object itself is thread-local.
static Status InsertInto(
const WriteBatch* batch, ColumnFamilyMemTables* memtables,
FlushScheduler* flush_scheduler,
@@ -0,0 +1,65 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// 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).
#pragma once
#include "test_util/testutil.h"
namespace ROCKSDB_NAMESPACE {
class DbStressCustomCompressionManager : public CompressionManager {
public:
const char* Name() const override {
return "DbStressCustomCompressionManager";
}
const char* CompatibilityName() const override { return "DbStressCustom1"; }
bool SupportsCompressionType(CompressionType type) const override {
return default_->SupportsCompressionType(type) ||
type == kCustomCompressionAA || type == kCustomCompressionAB ||
type == kCustomCompressionAC;
}
std::unique_ptr<Compressor> GetCompressor(const CompressionOptions& opts,
CompressionType type) override {
// db_stress never specifies a custom type, so we randomly use them anyway
// when this compression manager is used.
std::array<CompressionType, 4> choices = {
type, kCustomCompressionAA, kCustomCompressionAB, kCustomCompressionAC};
type = choices[Random::GetTLSInstance()->Uniform(4)];
switch (static_cast<unsigned char>(type)) {
case kCustomCompressionAA:
return std::make_unique<
test::CompressorCustomAlg<kCustomCompressionAA>>();
case kCustomCompressionAB:
return std::make_unique<
test::CompressorCustomAlg<kCustomCompressionAB>>();
case kCustomCompressionAC:
return std::make_unique<
test::CompressorCustomAlg<kCustomCompressionAC>>();
// Also support built-in compression algorithms
default:
return GetBuiltinV2CompressionManager()->GetCompressor(opts, type);
}
}
std::shared_ptr<Decompressor> GetDecompressor() override {
return std::make_shared<test::DecompressorCustomAlg>();
}
std::shared_ptr<Decompressor> GetDecompressorForTypes(
const CompressionType* types_begin,
const CompressionType* types_end) override {
auto decomp = std::make_shared<test::DecompressorCustomAlg>();
decomp->SetAllowedTypes(types_begin, types_end);
return decomp;
}
protected:
std::shared_ptr<CompressionManager> default_ =
GetBuiltinV2CompressionManager();
};
} // namespace ROCKSDB_NAMESPACE
+34 -24
View File
@@ -11,6 +11,7 @@
#include <ios>
#include <thread>
#include "db_stress_tool/db_stress_compression_manager.h"
#include "db_stress_tool/db_stress_listener.h"
#include "rocksdb/io_status.h"
#include "rocksdb/options.h"
@@ -875,6 +876,10 @@ Status StressTest::CommitTxn(Transaction& txn, ThreadState* thread) {
return Status::InvalidArgument("CommitTxn when FLAGS_use_txn is not set");
}
Status s = Status::OK();
// We don't issue write to transaction's underlying WriteBatch in stress test
assert(txn.GetWriteBatch()->GetWriteBatch()->Count());
assert(txn.GetWriteBatch()->GetWBWIOpCount() ==
txn.GetWriteBatch()->GetWriteBatch()->Count());
if (FLAGS_use_optimistic_txn) {
assert(optimistic_txn_db_);
s = txn.Commit();
@@ -3412,32 +3417,37 @@ void StressTest::Open(SharedState* shared, bool reopen) {
InitializeOptionsFromFlags(cache_, filter_policy_, options_);
}
InitializeOptionsGeneral(cache_, filter_policy_, sqfc_factory_, options_);
if (!strcasecmp(FLAGS_compression_manager.c_str(), "mixed") ||
!strcasecmp(FLAGS_compression_manager.c_str(), "randommixed")) {
// Currently limited to ZSTD compression. Table property compression_name
// needs to set to zstd for now even when there can be more than one
// algorithm in the table under your compressor.
if (!ZSTD_Supported()) {
fprintf(stderr,
"ZSTD compression not supported thus mixed compression cannot be "
"used\n");
exit(1);
}
if (!strcasecmp(FLAGS_compression_manager.c_str(), "mixed")) {
auto mgr = std::make_shared<RoundRobinManager>(
GetDefaultBuiltinCompressionManager());
options_.compression_manager = mgr;
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "randommixed")) {
auto mgr = std::make_shared<RandomMixedCompressionManager>(
GetDefaultBuiltinCompressionManager());
options_.compression_manager = mgr;
}
options_.compression = kZSTD;
options_.bottommost_compression = kZSTD;
{
// We must register any compression managers with a custom
// CompatibilityName() so that if it was used in a past invocation but not
// the current invocation, we can still read the SST files requiring it.
static std::once_flag loaded;
std::call_once(loaded, [&]() {
TEST_AllowUnsupportedFormatVersion() = true;
auto& library = *ObjectLibrary::Default();
library.AddFactory<CompressionManager>(
DbStressCustomCompressionManager().CompatibilityName(),
[](const std::string& /*uri*/,
std::unique_ptr<CompressionManager>* guard,
std::string* /*errmsg*/) {
*guard = std::make_unique<DbStressCustomCompressionManager>();
return guard->get();
});
});
}
if (!strcasecmp(FLAGS_compression_manager.c_str(), "custom")) {
options_.compression_manager =
std::make_shared<DbStressCustomCompressionManager>();
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "mixed")) {
options_.compression_manager =
std::make_shared<RoundRobinManager>(GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "randommixed")) {
options_.compression_manager =
std::make_shared<RandomMixedCompressionManager>(
GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "autoskip")) {
options_.compression_manager =
CreateAutoSkipCompressionManager(GetDefaultBuiltinCompressionManager());
CreateAutoSkipCompressionManager(GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "none")) {
// Nothing to do using default compression manager
} else {
+4
View File
@@ -100,6 +100,10 @@ class CompositeRandomAccessFileWrapper : public RandomAccessFile {
return target_->InvalidateCache(offset, length);
}
Status GetFileSize(uint64_t* size) override {
return target_->GetFileSize(size);
}
private:
std::unique_ptr<FSRandomAccessFile> target_;
};
Vendored
+4
View File
@@ -186,6 +186,10 @@ class LegacyRandomAccessFileWrapper : public FSRandomAccessFile {
IOStatus InvalidateCache(size_t offset, size_t length) override {
return status_to_io_status(target_->InvalidateCache(offset, length));
}
IOStatus GetFileSize(uint64_t* result) override {
auto status = target_->GetFileSize(result);
return status_to_io_status(std::move(status));
}
private:
std::unique_ptr<RandomAccessFile> target_;
+38 -3
View File
@@ -665,17 +665,52 @@ class EncryptedFileSystemImpl : public EncryptedFileSystem {
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_writes) {
if (options.use_mmap_reads || options.use_mmap_writes) {
return IOStatus::InvalidArgument();
}
size_t prefix_length = 0;
std::unique_ptr<BlockAccessCipherStream> stream;
// Open file using underlying Env implementation
std::unique_ptr<FSWritableFile> underlying;
IOStatus status =
auto status =
FileSystemWrapper::ReopenWritableFile(fname, options, &underlying, dbg);
if (!status.ok()) {
return status;
}
return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
if (underlying->GetFileSize(options.io_options, dbg) != 0) {
// read the cipher stream from file for non-empty file
std::unique_ptr<FSRandomAccessFile> underlying_file_reader;
status = FileSystemWrapper::NewRandomAccessFile(
fname, options, &underlying_file_reader, dbg);
if (!status.ok()) {
return status;
}
status = CreateRandomReadCipherStream(
fname, underlying_file_reader, options, &prefix_length, &stream, dbg);
if (!status.ok()) {
return status;
}
} else {
// create cipher stream for new or empty file
status = CreateWritableCipherStream(fname, underlying, options,
&prefix_length, &stream, dbg);
if (!status.ok()) {
return status;
}
}
if (stream) {
result->reset(new EncryptedWritableFile(
std::move(underlying), std::move(stream), prefix_length));
} else {
result->reset(underlying.release());
}
return status;
}
IOStatus ReuseWritableFile(const std::string& fname,
+4 -2
View File
@@ -169,8 +169,9 @@ class PosixClock : public SystemClock {
struct timespec ts;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
return (static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec) / 1000;
#endif
#else
return 0;
#endif
}
uint64_t CPUNanos() override {
@@ -179,8 +180,9 @@ class PosixClock : public SystemClock {
struct timespec ts;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
return static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
#endif
#else
return 0;
#endif
}
void SleepForMicroseconds(int micros) override { usleep(micros); }
+36 -1
View File
@@ -3467,7 +3467,6 @@ class ReadAsyncRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
private:
ReadAsyncFS& fs_;
std::unique_ptr<FSRandomAccessFile> file_;
int counter = 0;
};
@@ -3657,6 +3656,42 @@ TEST(EnvTestMisc, StaticDestruction) {
static_destruction_tester.activated = true;
}
// Test GetFileSize API
class TestGetFileSize : public testing::Test {
public:
TestGetFileSize() { env_ = Env::Default(); }
Env* env_;
};
// Validate GetFileSize API returns the right value.
// Use the default implementation from env
TEST_F(TestGetFileSize, GetFileSize) {
EnvOptions soptions;
auto fs = env_->GetFileSystem();
std::string fname = test::PerThreadDBPath(env_, "getFileSizeTestfile");
// randomize file size
auto rnd = Random::GetTLSInstance();
auto expectedFileSize = rnd->Uniform(256 * 1024) + 1;
auto content = rnd->RandomBinaryString(static_cast<int>(expectedFileSize));
ASSERT_OK(CreateFile(fs.get(), fname, content, false));
std::unique_ptr<FSRandomAccessFile> file;
ASSERT_OK(fs->NewRandomAccessFile(fname, FileOptions(), &file, nullptr));
uint64_t fileSizeFromFileSystemAPI;
ASSERT_OK(
fs->GetFileSize(fname, IOOptions(), &fileSizeFromFileSystemAPI, nullptr));
ASSERT_EQ(fileSizeFromFileSystemAPI, expectedFileSize);
uint64_t fileSizeFromFsRandomAccessFileAPI;
ASSERT_OK(file->GetFileSize(&fileSizeFromFsRandomAccessFileAPI));
ASSERT_EQ(fileSizeFromFsRandomAccessFileAPI, expectedFileSize);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+20 -5
View File
@@ -243,7 +243,7 @@ class PosixFileSystem : public FileSystem {
// Use mmap when virtual address-space is plentiful.
uint64_t size;
IOOptions opts;
s = GetFileSize(fname, opts, &size, nullptr);
s = GetFileSizeOnOpenedFile(fd, fname, &size);
if (s.ok()) {
void* base = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
if (base != MAP_FAILED) {
@@ -324,7 +324,7 @@ class PosixFileSystem : public FileSystem {
}
uint64_t initial_file_size = 0;
if (reopen) {
s = GetFileSize(fname, IOOptions(), &initial_file_size, nullptr);
s = GetFileSizeOnOpenedFile(fd, fname, &initial_file_size);
if (!s.ok()) {
close(fd);
return s;
@@ -509,7 +509,7 @@ class PosixFileSystem : public FileSystem {
uint64_t size;
if (status.ok()) {
IOOptions opts;
status = GetFileSize(fname, opts, &size, nullptr);
status = GetFileSizeOnOpenedFile(fd, fname, &size);
}
void* base = nullptr;
if (status.ok()) {
@@ -671,7 +671,7 @@ class PosixFileSystem : public FileSystem {
IOStatus GetFileSize(const std::string& fname, const IOOptions& /*opts*/,
uint64_t* size, IODebugContext* /*dbg*/) override {
struct stat sbuf;
struct stat sbuf {};
if (stat(fname.c_str(), &sbuf) != 0) {
*size = 0;
return IOError("while stat a file for size", fname, errno);
@@ -868,7 +868,6 @@ class PosixFileSystem : public FileSystem {
IOOptions opts;
return CreateDirIfMissing(*result, opts, nullptr);
}
return IOStatus::OK();
}
IOStatus GetFreeSpace(const std::string& fname, const IOOptions& /*opts*/,
@@ -975,6 +974,22 @@ class PosixFileSystem : public FileSystem {
private:
bool forceMmapOff_ = false; // do we override Env options?
// This is a faster API comparing to the public method that uses stat to get
// file size. However this API only works on opened file.
IOStatus GetFileSizeOnOpenedFile(const int fd, const std::string& name,
uint64_t* size) {
struct stat sb {};
*size = 0;
// Get file information using fstat
if (fstat(fd, &sb) == -1) {
return IOError(
"while fstat a file for size with fd " + std::to_string(fd), name,
errno);
}
*size = sb.st_size;
return IOStatus::OK();
}
#ifdef OS_LINUX
// Get the minimum "linux system limit" (i.e, the largest I/O size that the OS
// can issue to block devices under a directory, also known as
+16
View File
@@ -607,6 +607,17 @@ PosixRandomAccessFile::PosixRandomAccessFile(
PosixRandomAccessFile::~PosixRandomAccessFile() { close(fd_); }
IOStatus PosixRandomAccessFile::GetFileSize(uint64_t* result) {
struct stat sbuf {};
if (fstat(fd_, &sbuf) != 0) {
*result = 0;
return IOError("While fstat with fd " + std::to_string(fd_), filename_,
errno);
}
*result = sbuf.st_size;
return IOStatus::OK();
}
IOStatus PosixRandomAccessFile::Read(uint64_t offset, size_t n,
const IOOptions& /*opts*/, Slice* result,
char* scratch,
@@ -1056,6 +1067,11 @@ IOStatus PosixMmapReadableFile::InvalidateCache(size_t offset, size_t length) {
#endif
}
IOStatus PosixMmapReadableFile::GetFileSize(uint64_t* result) {
*result = length_;
return IOStatus::OK();
}
/*
* PosixMmapFile
*
+3
View File
@@ -352,6 +352,8 @@ class PosixRandomAccessFile : public FSRandomAccessFile {
void* cb_arg, void** io_handle,
IOHandleDeleter* del_fn,
IODebugContext* dbg) override;
virtual IOStatus GetFileSize(uint64_t* result) override;
};
class PosixWritableFile : public FSWritableFile {
@@ -437,6 +439,7 @@ class PosixMmapReadableFile : public FSRandomAccessFile {
char* scratch, IODebugContext* dbg) const override;
void Hint(AccessPattern pattern) override;
IOStatus InvalidateCache(size_t offset, size_t length) override;
virtual IOStatus GetFileSize(uint64_t* result) override;
};
class PosixMmapFile : public FSWritableFile {
+5
View File
@@ -322,6 +322,11 @@ class MockRandomAccessFile : public FSRandomAccessFile {
}
}
IOStatus GetFileSize(uint64_t* size) override {
*size = file_->Size();
return IOStatus::OK();
}
private:
MemFile* file_;
bool use_direct_io_;
+4
View File
@@ -108,6 +108,10 @@ class ReadaheadRandomAccessFile : public FSRandomAccessFile {
bool use_direct_io() const override { return file_->use_direct_io(); }
IOStatus GetFileSize(uint64_t* result) override {
return file_->GetFileSize(result);
}
private:
// Tries to read from buffer_ n bytes starting at offset. If anything was read
// from the cache, it sets cached_len to the number of bytes actually read,
-2
View File
@@ -162,7 +162,6 @@ class SstFileManagerImpl : public SstFileManager {
void Close();
void SetStatisticsPtr(const std::shared_ptr<Statistics>& stats) override {
stats_ = stats;
delete_scheduler_.SetStatisticsPtr(stats);
}
@@ -216,7 +215,6 @@ class SstFileManagerImpl : public SstFileManager {
std::list<ErrorHandler*> error_handler_list_;
// Pointer to ErrorHandler instance that is currently processing recovery
ErrorHandler* cur_instance_;
std::shared_ptr<Statistics> stats_;
};
} // namespace ROCKSDB_NAMESPACE
+2 -1
View File
@@ -91,7 +91,8 @@ TableReader* NewTableReader(const std::string& sst_file_path,
}
if (s.ok()) {
ImmutableOptions iopts(options, cf_ioptions);
TableReaderOptions t_opt(iopts, /*prefix_extractor=*/nullptr, env_options,
TableReaderOptions t_opt(iopts, /*prefix_extractor=*/nullptr,
/*compression_manager=*/nullptr, env_options,
cf_ioptions.internal_comparator,
0 /* block_protection_bytes_per_key */);
t_opt.largest_seqno = kMaxSequenceNumber;
+139 -29
View File
@@ -50,11 +50,24 @@ struct FilterBuildingContext;
// a number of built-in CompressionTypes that ignore any dictionary block in
// the file; therefore they cannot accommodate dictionary compression in the
// future without a schema change / extension.)
//
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
// because RocksDB is not exception-safe. This could cause undefined behavior
// including data loss, unreported corruption, deadlocks, and more.
class Compressor {
public:
Compressor() = default;
virtual ~Compressor() = default;
// Class name for logging / debugging purposes
virtual const char* Name() const = 0;
// Potentially more elaborate identifier for logging / debugging purposes
virtual std::string GetId() const {
std::string id = Name();
return id;
}
// Returns the max total bytes of for all sampled blocks for creating the data
// dictionary, or zero indicating dictionary compression should not be
// used/configured. This will typically be called after
@@ -125,15 +138,15 @@ class Compressor {
// EXTENSIBLE or reinterpret_cast-able by custom Compressor implementations
struct WorkingArea {};
protected:
// To allow for flexible re-use / reclaimation, we have explicit Get and
// Release functions, and usually wrap in a special RAII smart pointer.
// For example, a WorkingArea could be saved/recycled in thread-local or
// core-local storage, or heap managed, etc., though an explicit WorkingArea
// is only advised for repeated compression (by a single thread).
// ReleaseWorkingArea() in not intended to be called directly, but used by
// ManagedWorkingArea.
virtual void ReleaseWorkingArea(WorkingArea*) {}
public:
using ManagedWorkingArea =
ManagedPtr<WorkingArea, Compressor, &Compressor::ReleaseWorkingArea>;
@@ -212,6 +225,10 @@ class Compressor {
// decompressed into part of a single buffer allocated to hold a block's
// uncompressed contents along with an in-memory object representation of the
// block (to reduce fragmentation and other overheads of separate objects).
//
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
// because RocksDB is not exception-safe. This could cause undefined behavior
// including data loss, unreported corruption, deadlocks, and more.
class Decompressor {
public:
Decompressor() = default;
@@ -228,21 +245,19 @@ class Decompressor {
// EXTENSIBLE or reinterpret_cast-able by custom Compressor implementations
struct WorkingArea {};
protected:
// To allow for flexible re-use / reclaimation, we have explicit Obtain and
// Release functions, which are typically wrapped in a special RAII smart
// pointer. For example, a WorkingArea could be saved/recycled in thread-local
// or core-local storage, or heap managed, etc., though an explicit
// WorkingArea is only advised for repeated decompression (by a single
// thread).
// thread). ReleaseWorkingArea() in not intended to be called directly, but
// used by ManagedWorkingArea.
virtual void ReleaseWorkingArea(WorkingArea* wa) {
// Default implementation: no working area
(void)wa;
assert(wa == nullptr);
}
public:
using ManagedWorkingArea =
ManagedPtr<WorkingArea, Decompressor, &Decompressor::ReleaseWorkingArea>;
@@ -266,11 +281,14 @@ class Decompressor {
// dictionary is processed into a form reusable by repeated compressions in
// many threads, that happens within this call.
//
// Must return OK if storing a result in `out`. Otherwise, could return values
// like NotSupported - dictionary compression is not (yet) supported for this
// kind of Decompressor.
// Corruption - dictionary is malformed (though many implementations will
// accept any data as a dictionary)
// Must return OK if and only if storing a result in `out`. Otherwise, could
// return values like NotSupported - dictionary compression is not (yet)
// supported for this kind of Decompressor. Corruption - dictionary is
// malformed (though many implementations will accept any data as a
// dictionary)
//
// RocksDB promises not to call this function with an empty dictionary slice
// (equivalent to no dictionary).
virtual Status MaybeCloneForDict(const Slice& /*serialized_dict*/,
std::unique_ptr<Decompressor>* /*out*/) {
return Status::NotSupported(
@@ -332,6 +350,10 @@ class Decompressor {
// (because that would break backward compatibility, potential quiet
// corruption)
// TODO: consider adding optional streaming compression support (low priority)
//
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
// because RocksDB is not exception-safe. This could cause undefined behavior
// including data loss, unreported corruption, deadlocks, and more.
class CompressionManager
: public std::enable_shared_from_this<CompressionManager>,
public Customizable {
@@ -346,22 +368,31 @@ class CompressionManager
// should have the same CompatibilityName(), so that a compatible
// CompressionManager/Decompressor might be used if the original is
// unavailable. (Name() can be useful in addition to CompatibilityName() for
// understanding what compression strategy was used.)
// understanding what compression strategy was used.) This name should be
// limited to legal variable names in C++ (alphanumeric and underscores).
virtual const char* CompatibilityName() const = 0;
// Default implementation checks the current compatibility name and returns
// this CompressionManager (via `out`) if appropriate, and otherwise defers
// to CreateFromString().
virtual Status FindCompatibleCompressionManager(
Slice compatibility_name, std::shared_ptr<CompressionManager>* out);
// to CreateFromString(). Failure should simply be a matter of "not found" in
// which case nullptr is returned.
virtual std::shared_ptr<CompressionManager> FindCompatibleCompressionManager(
Slice compatibility_name);
// Create a CompressionManager from a string, including built-in
// Create or find a CompressionManager from a string, including built-in
// CompressionManager types.
// TODO: ObjectLibrary stuff
static Status CreateFromString(const ConfigOptions& config_options,
const std::string& id,
std::shared_ptr<CompressionManager>* result);
// Returns false iff a configuration that would pass the given compression
// type to GetCompressor/GetCompressorForSST should be rejected (not
// supported)
virtual bool SupportsCompressionType(CompressionType type) const = 0;
// TODO: function to check compatibility with or sanitize CompressionOptions
// ************************* Compressor creation *********************** //
// Returning nullptr means compression is entirely disabled for the file,
// which is valid at the discretion of the CompressionManager. Returning
@@ -410,6 +441,14 @@ class CompressionManager
// Safe default implementation
return GetDecompressor();
}
// Get a decompressor that is allowed to have support only for the
// CompressionTypes used by the given Compressor.
virtual std::shared_ptr<Decompressor> GetDecompressorForCompressor(
const Compressor& compressor) {
// Reasonable default implementation
return GetDecompressorOptimizeFor(compressor.GetPreferredCompressionType());
}
};
// ************************* Utility wrappers etc. *********************** //
@@ -442,6 +481,10 @@ class CompressorWrapper : public Compressor {
return wrapped_->ObtainWorkingArea();
}
// NOTE: Don't need to override ReleaseWorkingArea() here because
// ManagedWorkingArea takes care of calling it on the Compressor that created
// the WorkingArea.
Status CompressBlock(Slice uncompressed_data, std::string* compressed_output,
CompressionType* out_compression_type,
ManagedWorkingArea* working_area) override {
@@ -453,6 +496,55 @@ class CompressorWrapper : public Compressor {
std::unique_ptr<Compressor> wrapped_;
};
class DecompressorWrapper : public Decompressor {
public:
explicit DecompressorWrapper(std::shared_ptr<Decompressor> decompressor)
: wrapped_(std::move(decompressor)) {}
// No copies
DecompressorWrapper(const DecompressorWrapper&) = delete;
DecompressorWrapper& operator=(const DecompressorWrapper&) = delete;
const char* Name() const override { return wrapped_->Name(); }
void ReleaseWorkingArea(WorkingArea* wa) override {
wrapped_->ReleaseWorkingArea(wa);
}
// NOTE: Don't need to override ReleaseWorkingArea() here because
// ManagedWorkingArea takes care of calling it on the Decompressor that
// created the WorkingArea.
ManagedWorkingArea ObtainWorkingArea(CompressionType preferred) override {
return wrapped_->ObtainWorkingArea(preferred);
}
const Slice& GetSerializedDict() const override {
return wrapped_->GetSerializedDict();
}
Status MaybeCloneForDict(const Slice& serialized_dict,
std::unique_ptr<Decompressor>* out) override {
// NOTE: derived class probably needs to override this to ensure a
// derived wrapper around the new Decompressor
return wrapped_->MaybeCloneForDict(serialized_dict, out);
}
size_t ApproximateOwnedMemoryUsage() const override {
return wrapped_->ApproximateOwnedMemoryUsage();
}
Status ExtractUncompressedSize(Args& args) override {
return wrapped_->ExtractUncompressedSize(args);
}
Status DecompressBlock(const Args& args, char* uncompressed_output) override {
return wrapped_->DecompressBlock(args, uncompressed_output);
}
protected:
std::shared_ptr<Decompressor> wrapped_;
};
// TODO: CompressorBase, for custom compressions
class CompressionManagerWrapper : public CompressionManager {
@@ -465,10 +557,13 @@ class CompressionManagerWrapper : public CompressionManager {
return wrapped_->CompatibilityName();
}
Status FindCompatibleCompressionManager(
Slice compatibility_name,
std::shared_ptr<CompressionManager>* out) override {
return wrapped_->FindCompatibleCompressionManager(compatibility_name, out);
std::shared_ptr<CompressionManager> FindCompatibleCompressionManager(
Slice compatibility_name) override {
return wrapped_->FindCompatibleCompressionManager(compatibility_name);
}
bool SupportsCompressionType(CompressionType type) const override {
return wrapped_->SupportsCompressionType(type);
}
std::unique_ptr<Compressor> GetCompressorForSST(
@@ -497,19 +592,34 @@ class CompressionManagerWrapper : public CompressionManager {
return wrapped_->GetDecompressorForTypes(types_begin, types_end);
}
std::shared_ptr<Decompressor> GetDecompressorForCompressor(
const Compressor& compressor) override {
return wrapped_->GetDecompressorForCompressor(compressor);
}
protected:
std::shared_ptr<CompressionManager> wrapped_;
};
// Compression manager that implements built-in compression strategy. The
// behavior of
// compression_manager=nullptr with this
const std::shared_ptr<CompressionManager>&
GetDefaultBuiltinCompressionManager();
// Gets CompressionManager designed for the automated compression strategy.
// Compression manager that implements the second schema for RocksDB built-in
// compression support. (The first schema is intentionally not provided here.)
// *** CURRENT STATE ***
// This is currently the latest schema for built-in compression, and the
// compression manager used when compression_manager=nullptr.
const std::shared_ptr<CompressionManager>& GetBuiltinV2CompressionManager();
// NOTE: No GetLatestBuiltinCompressionManager() is provided because that could
// lead to unexpected schema changes for user CompressionManagers building on
// the built-in schema, in the unlikely/rare case of a new built-in schema.
// Creates CompressionManager designed for the automated compression strategy.
// This may include deciding to compress or not.
// In future should be able to select compression algorithm based on the CPU
// utilization and IO constraints.
// EXPERIMENTAL
std::shared_ptr<CompressionManagerWrapper> CreateAutoSkipCompressionManager(
std::shared_ptr<CompressionManager> wrapped);
std::shared_ptr<CompressionManager> wrapped = nullptr);
// Creates CompressionManager designed for the CPU and IO cost aware compression
// strategy
// EXPERIMENTAL
std::shared_ptr<CompressionManagerWrapper> CreateCostAwareCompressionManager(
std::shared_ptr<CompressionManager> wrapped = nullptr);
} // namespace ROCKSDB_NAMESPACE
+5
View File
@@ -1171,6 +1171,8 @@ extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_flushjobinfo_smallest_seqno(const rocksdb_flushjobinfo_t*);
extern ROCKSDB_LIBRARY_API void rocksdb_reset_status(
rocksdb_status_ptr_t* status_ptr);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info);
/* Compaction job info */
extern ROCKSDB_LIBRARY_API void rocksdb_compactionjobinfo_status(
@@ -1221,6 +1223,9 @@ extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_base_input_level(
const rocksdb_subcompactionjobinfo_t*);
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_output_level(
const rocksdb_subcompactionjobinfo_t*);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_subcompactionjobinfo_compaction_reason(
const rocksdb_subcompactionjobinfo_t* info);
/* External file ingestion info */
extern ROCKSDB_LIBRARY_API const char*
+141 -9
View File
@@ -18,16 +18,148 @@ namespace ROCKSDB_NAMESPACE {
enum CompressionType : unsigned char {
// NOTE: do not change the values of existing entries, as these are
// part of the persistent format on disk.
kNoCompression = 0x0,
kSnappyCompression = 0x1,
kZlibCompression = 0x2,
kBZip2Compression = 0x3,
kLZ4Compression = 0x4,
kLZ4HCCompression = 0x5,
kXpressCompression = 0x6,
kZSTD = 0x7,
kNoCompression = 0x00,
kSnappyCompression = 0x01,
kZlibCompression = 0x02,
kBZip2Compression = 0x03,
kLZ4Compression = 0x04,
kLZ4HCCompression = 0x05,
kXpressCompression = 0x06,
kZSTD = 0x07,
kLastBuiltinCompression = kZSTD,
// TODO: add enum values for user custom compression types
// Reserved for future use: up to 0x7F
// For use by user custom CompressionManagers
kCustomCompression80 = 0x80,
kFirstCustomCompression = kCustomCompression80,
kCustomCompression81 = 0x81,
kCustomCompression82 = 0x82,
kCustomCompression83 = 0x83,
kCustomCompression84 = 0x84,
kCustomCompression85 = 0x85,
kCustomCompression86 = 0x86,
kCustomCompression87 = 0x87,
kCustomCompression88 = 0x88,
kCustomCompression89 = 0x89,
kCustomCompression8A = 0x8A,
kCustomCompression8B = 0x8B,
kCustomCompression8C = 0x8C,
kCustomCompression8D = 0x8D,
kCustomCompression8E = 0x8E,
kCustomCompression8F = 0x8F,
kCustomCompression90 = 0x90,
kCustomCompression91 = 0x91,
kCustomCompression92 = 0x92,
kCustomCompression93 = 0x93,
kCustomCompression94 = 0x94,
kCustomCompression95 = 0x95,
kCustomCompression96 = 0x96,
kCustomCompression97 = 0x97,
kCustomCompression98 = 0x98,
kCustomCompression99 = 0x99,
kCustomCompression9A = 0x9A,
kCustomCompression9B = 0x9B,
kCustomCompression9C = 0x9C,
kCustomCompression9D = 0x9D,
kCustomCompression9E = 0x9E,
kCustomCompression9F = 0x9F,
kCustomCompressionA0 = 0xA0,
kCustomCompressionA1 = 0xA1,
kCustomCompressionA2 = 0xA2,
kCustomCompressionA3 = 0xA3,
kCustomCompressionA4 = 0xA4,
kCustomCompressionA5 = 0xA5,
kCustomCompressionA6 = 0xA6,
kCustomCompressionA7 = 0xA7,
kCustomCompressionA8 = 0xA8,
kCustomCompressionA9 = 0xA9,
kCustomCompressionAA = 0xAA,
kCustomCompressionAB = 0xAB,
kCustomCompressionAC = 0xAC,
kCustomCompressionAD = 0xAD,
kCustomCompressionAE = 0xAE,
kCustomCompressionAF = 0xAF,
kCustomCompressionB0 = 0xB0,
kCustomCompressionB1 = 0xB1,
kCustomCompressionB2 = 0xB2,
kCustomCompressionB3 = 0xB3,
kCustomCompressionB4 = 0xB4,
kCustomCompressionB5 = 0xB5,
kCustomCompressionB6 = 0xB6,
kCustomCompressionB7 = 0xB7,
kCustomCompressionB8 = 0xB8,
kCustomCompressionB9 = 0xB9,
kCustomCompressionBA = 0xBA,
kCustomCompressionBB = 0xBB,
kCustomCompressionBC = 0xBC,
kCustomCompressionBD = 0xBD,
kCustomCompressionBE = 0xBE,
kCustomCompressionBF = 0xBF,
kCustomCompressionC0 = 0xC0,
kCustomCompressionC1 = 0xC1,
kCustomCompressionC2 = 0xC2,
kCustomCompressionC3 = 0xC3,
kCustomCompressionC4 = 0xC4,
kCustomCompressionC5 = 0xC5,
kCustomCompressionC6 = 0xC6,
kCustomCompressionC7 = 0xC7,
kCustomCompressionC8 = 0xC8,
kCustomCompressionC9 = 0xC9,
kCustomCompressionCA = 0xCA,
kCustomCompressionCB = 0xCB,
kCustomCompressionCC = 0xCC,
kCustomCompressionCD = 0xCD,
kCustomCompressionCE = 0xCE,
kCustomCompressionCF = 0xCF,
kCustomCompressionD0 = 0xD0,
kCustomCompressionD1 = 0xD1,
kCustomCompressionD2 = 0xD2,
kCustomCompressionD3 = 0xD3,
kCustomCompressionD4 = 0xD4,
kCustomCompressionD5 = 0xD5,
kCustomCompressionD6 = 0xD6,
kCustomCompressionD7 = 0xD7,
kCustomCompressionD8 = 0xD8,
kCustomCompressionD9 = 0xD9,
kCustomCompressionDA = 0xDA,
kCustomCompressionDB = 0xDB,
kCustomCompressionDC = 0xDC,
kCustomCompressionDD = 0xDD,
kCustomCompressionDE = 0xDE,
kCustomCompressionDF = 0xDF,
kCustomCompressionE0 = 0xE0,
kCustomCompressionE1 = 0xE1,
kCustomCompressionE2 = 0xE2,
kCustomCompressionE3 = 0xE3,
kCustomCompressionE4 = 0xE4,
kCustomCompressionE5 = 0xE5,
kCustomCompressionE6 = 0xE6,
kCustomCompressionE7 = 0xE7,
kCustomCompressionE8 = 0xE8,
kCustomCompressionE9 = 0xE9,
kCustomCompressionEA = 0xEA,
kCustomCompressionEB = 0xEB,
kCustomCompressionEC = 0xEC,
kCustomCompressionED = 0xED,
kCustomCompressionEE = 0xEE,
kCustomCompressionEF = 0xEF,
kCustomCompressionF0 = 0xF0,
kCustomCompressionF1 = 0xF1,
kCustomCompressionF2 = 0xF2,
kCustomCompressionF3 = 0xF3,
kCustomCompressionF4 = 0xF4,
kCustomCompressionF5 = 0xF5,
kCustomCompressionF6 = 0xF6,
kCustomCompressionF7 = 0xF7,
kCustomCompressionF8 = 0xF8,
kCustomCompressionF9 = 0xF9,
kCustomCompressionFA = 0xFA,
kCustomCompressionFB = 0xFB,
kCustomCompressionFC = 0xFC,
kCustomCompressionFD = 0xFD,
kCustomCompressionFE = 0xFE,
kLastCustomCompression = kCustomCompressionFE,
// kDisableCompressionOption is used to disable some compression options.
kDisableCompressionOption = 0xff,
+25 -1
View File
@@ -57,6 +57,7 @@ struct WaitForCompactOptions;
class Env;
class EventListener;
class FileSystem;
class MultiScan;
class Replayer;
class StatsHistoryIterator;
class TraceReader;
@@ -1092,7 +1093,30 @@ class DB {
// Get an iterator that scans multiple key ranges. The scan ranges should
// be in increasing order of start key. See multi_scan_iterator.h for more
// details.
// details. For optimal performance, ensure that either all entries in
// scan_opts specify the range limit, or none of them do.
//
// NOTE: iterate_upper_bound in ReadOptions will be ignored. Instead, the
// range.limit in ScanOptions is consulted to determine the upper bound key,
// if specified.
//
// Example usage -
// std::vector<ScanOptions> scans{{.start = Slice("bar")},
// {.start = Slice("foo")}};
// std::unique_ptr<MultiScan> iter.reset(
// db->NewMultiScan());
// try {
// for (auto scan : *iter) {
// for (auto it : scan) {
// // Do something with key - it.first
// // Do something with value - it.second
// }
// }
// } catch (MultiScanException& ex) {
// // Check ex.status()
// } catch (std::logic_error& ex) {
// // Check ex.what()
// }
virtual std::unique_ptr<MultiScan> NewMultiScan(
const ReadOptions& /*options*/, ColumnFamilyHandle* /*column_family*/,
const std::vector<ScanOptions>& /*scan_opts*/) {
+10
View File
@@ -866,6 +866,13 @@ class RandomAccessFile {
"RandomAccessFile::InvalidateCache not supported.");
}
// The default implementation returns "not supported" so that user
// implementations of FSRandomAccessFile do not need to immediately implement
// this function.
virtual Status GetFileSize(uint64_t* /*result*/) {
return Status::NotSupported("RandomAccessFile::GetFileSize not supported.");
}
// If you're adding methods here, remember to add them to
// RandomAccessFileWrapper too.
};
@@ -1750,6 +1757,9 @@ class RandomAccessFileWrapper : public RandomAccessFile {
Status InvalidateCache(size_t offset, size_t length) override {
return target_->InvalidateCache(offset, length);
}
Status GetFileSize(uint64_t* file_size) override {
return target_->GetFileSize(file_size);
}
private:
RandomAccessFile* target_;
+9
View File
@@ -240,6 +240,15 @@ class EncryptedRandomAccessFile : public FSRandomAccessFile {
size_t GetRequiredBufferAlignment() const override;
IOStatus InvalidateCache(size_t offset, size_t length) override;
// Intentionally leave GetFileSize not overridden here, so that it inherits
// the default implementation from its parent class, which is Not Supported.
//
// As GetFileSize API is not required to be implemented yet, we use encrypted
// file system in unit test to validate the rest of the system could continue
// working with the Not Supported behavior.
//
// IOStatus GetFileSize(uint64_t* /*result*/) override;
};
class EncryptedWritableFile : public FSWritableFile {
+1 -1
View File
@@ -269,7 +269,7 @@ class ExternalTableFactory : public Customizable {
// Allocate a TableFactory that wraps around an ExternalTableFactory. Use this
// to allocate and set in ColumnFamilyOptions::table_factory.
std::shared_ptr<TableFactory> NewExternalTableFactory(
std::unique_ptr<TableFactory> NewExternalTableFactory(
std::shared_ptr<ExternalTableFactory> inner_factory);
} // namespace ROCKSDB_NAMESPACE
+2 -1
View File
@@ -80,7 +80,8 @@ class FileChecksumGenFactory : public Customizable {
const ConfigOptions& options, const std::string& value,
std::shared_ptr<FileChecksumGenFactory>* result);
// Create a new FileChecksumGenerator.
// Create a new FileChecksumGenerator. Recommended to return nullptr if the
// requested function name is not recognized.
virtual std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
const FileChecksumGenContext& context) = 0;
+12
View File
@@ -1051,6 +1051,14 @@ class FSRandomAccessFile {
// open.
virtual Temperature GetTemperature() const { return Temperature::kUnknown; }
// Get the file size on an open-for-reading file without re-seeking the file's
// path in the filesystem. The default implementation returns "not supported"
// so that user implementations of FSRandomAccessFile do not need to
// immediately implement this function.
virtual IOStatus GetFileSize(uint64_t* /*result*/) {
return IOStatus::NotSupported("GetFileSize Not Supported");
}
// If you're adding methods here, remember to add them to
// RandomAccessFileWrapper too.
};
@@ -1772,6 +1780,10 @@ class FSRandomAccessFileWrapper : public FSRandomAccessFile {
return target_->GetTemperature();
}
virtual IOStatus GetFileSize(uint64_t* result) override {
return target_->GetFileSize(result);
}
private:
std::unique_ptr<FSRandomAccessFile> guard_;
FSRandomAccessFile* target_;
+12 -8
View File
@@ -95,14 +95,18 @@ class Iterator : public IteratorBase {
return Slice();
}
// RocksDB Internal - DO NOT USE
// Prepare the iterator to scan the ranges specified in scan_opts. The
// upper bound and other table specific limits may be specified. This will
// typically be followed by Seeks to the start keys in the order they're
// specified in scan_opts. If the user does a Seek to some other target key,
// the iterator should disregard the scan_opts from that point onwards and
// behave like a normal iterator. Its the user's responsibility to again
// call Prepare().
// Prepare the iterator to scan the ranges specified in scan_opts. This
// includes prefetching relevant blocks from disk. The upper bound and
// other table specific limits should be specified for each
// scan for best results. If an upper bound is not specified, Prepare may
// skip prefetching as it cannot accurately determine how much to prefetch.
//
// Prepare should typically be followed by Seeks to the start keys in the
// order they're specified in scan_opts. If the user does a Seek to some
// other target key, the iterator should disregard the scan_opts from that
// point onwards and behave like a normal iterator. Its the user's
// responsibility to again call Prepare().
//
// If Prepare() is called, it overrides the iterate_upper_bound in
// ReadOptions
virtual void Prepare(const std::vector<ScanOptions>& /*scan_opts*/) {}
+13
View File
@@ -162,6 +162,12 @@ class MemTableRep {
return true;
}
// Only used after concurrent memtable inserts.
// This function will be called by each writer after all writes are done
// through InsertConcurrently().
// This is used by VectorRep to do batched writes for concurrent inserts.
virtual void BatchPostProcess() {}
// Returns true iff an entry that compares equal to key is in the collection.
virtual bool Contains(const char* key) const = 0;
@@ -397,6 +403,11 @@ class SkipListFactory : public MemTableRepFactory {
// the vector is sorted. This is useful for workloads where iteration is very
// rare and writes are generally not issued after reads begin.
//
// Concurrent inserts are supported by buffering writes in thread-local vectors
// for each write batch. To optimize performance for concurrent inserts, it is
// recommended to perform batched writes, and enable unordered_write (refer to
// the option comment for its impact on read consistency).
//
// Parameters:
// count: Passed to the constructor of the underlying std::vector of each
// VectorRep. On initialization, the underlying array will be at least count
@@ -418,6 +429,8 @@ class VectorRepFactory : public MemTableRepFactory {
MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator&, Allocator*,
const SliceTransform*,
Logger* logger) override;
bool IsInsertConcurrentlySupported() const override { return true; }
};
// This class contains a fixed array of buckets, each
+29 -28
View File
@@ -5,6 +5,7 @@
#pragma once
#include "rocksdb/db.h"
#include "rocksdb/iterator.h"
#include "rocksdb/options.h"
@@ -72,6 +73,8 @@ class Scan {
explicit Scan(Iterator* db_iter) : db_iter_(db_iter) {}
void Reset(Iterator* db_iter) { db_iter_ = db_iter; }
ScanIterator begin() { return ScanIterator(db_iter_); }
std::nullptr_t end() { return nullptr; }
@@ -149,9 +152,9 @@ class Scan {
// A Status exception is thrown if there is an error.
class MultiScan {
public:
MultiScan(const std::vector<ScanOptions>& scan_opts,
std::unique_ptr<Iterator>&& db_iter)
: scan_opts_(scan_opts), db_iter_(std::move(db_iter)) {}
MultiScan(const ReadOptions& read_options,
const std::vector<ScanOptions>& scan_opts, DB* db,
ColumnFamilyHandle* cfh);
explicit MultiScan(std::unique_ptr<Iterator>&& db_iter)
: db_iter_(std::move(db_iter)) {}
@@ -168,9 +171,17 @@ class MultiScan {
using difference_type = int;
using iterator_category = std::input_iterator_tag;
MultiScanIterator(const std::vector<ScanOptions>& scan_opts,
Iterator* db_iter)
: scan_opts_(scan_opts), idx_(0), db_iter_(db_iter), scan_(db_iter_) {
MultiScanIterator(const std::vector<ScanOptions>& scan_opts, DB* db,
ColumnFamilyHandle* cfh, ReadOptions& read_options,
Slice* upper_bound, std::unique_ptr<Iterator>& db_iter)
: scan_opts_(scan_opts),
db_(db),
cfh_(cfh),
read_options_(read_options),
upper_bound_(upper_bound),
idx_(0),
db_iter_(db_iter),
scan_(db_iter_.get()) {
if (scan_opts_.empty()) {
throw std::logic_error("Zero scans in multi-scan");
}
@@ -181,28 +192,9 @@ class MultiScan {
}
}
explicit MultiScanIterator(const std::vector<ScanOptions>& scan_opts)
: scan_opts_(scan_opts),
idx_(scan_opts_.size()),
db_iter_(nullptr),
scan_(nullptr) {}
~MultiScanIterator() { assert(status_.ok()); }
MultiScanIterator& operator++() {
if (idx_ >= scan_opts_.size()) {
throw std::logic_error("Index out of range");
}
idx_++;
if (idx_ < scan_opts_.size()) {
db_iter_->Seek(*scan_opts_[idx_].range.start);
status_ = db_iter_->status();
if (!status_.ok()) {
throw MultiScanException(status_);
}
}
return *this;
}
MultiScanIterator& operator++();
bool operator==(std::nullptr_t /*other*/) const {
return idx_ >= scan_opts_.size();
@@ -217,20 +209,29 @@ class MultiScan {
private:
const std::vector<ScanOptions>& scan_opts_;
DB* db_;
ColumnFamilyHandle* cfh_;
ReadOptions& read_options_;
Slice* upper_bound_;
size_t idx_;
Iterator* db_iter_;
std::unique_ptr<Iterator>& db_iter_;
Status status_;
Scan scan_;
};
MultiScanIterator begin() {
return MultiScanIterator(scan_opts_, db_iter_.get());
return MultiScanIterator(scan_opts_, db_, cfh_, read_options_,
&upper_bound_, db_iter_);
}
std::nullptr_t end() { return nullptr; }
private:
ReadOptions read_options_;
const std::vector<ScanOptions> scan_opts_;
DB* db_;
ColumnFamilyHandle* cfh_;
Slice upper_bound_;
std::unique_ptr<Iterator> db_iter_;
};
+26 -6
View File
@@ -57,6 +57,7 @@ class Statistics;
class InternalKeyComparator;
class WalFilter;
class FileSystem;
class UserDefinedIndexFactory;
struct Options;
struct DbPath;
@@ -608,6 +609,13 @@ struct DBOptions {
// checksums. True also enters a read-only mode when a DB write fails;
// see DB::Resume().
//
// When set to true, the DB will fail to open if any SST files fail to open
// e.g. due to incorrect file size or corrupted footer.
//
// When set to false, when there are files corrupted, the DB will still be
// opened, and the healthy ones could still be accessed, while corrupted one
// will not
//
// As most workloads value data correctness over availability, this option
// is on by default. Note that the name of this old option is potentially
// misleading, and other options and operations go further in proactive
@@ -1297,12 +1305,13 @@ struct DBOptions {
// Default: false
bool skip_stats_update_on_db_open = false;
// If true, then DB::Open() will not fetch and check sizes of all sst files.
// This may significantly speed up startup if there are many sst files,
// especially when using non-default Env with expensive GetFileSize().
// We'll still check that all required sst files exist.
// If paranoid_checks is false, this option is ignored, and sst files are
// not checked at all.
// This option is deprecated and marked as no-op. Kept for backward
// compatibility until usage is fully removed.
// File size check will be performed through a thread
// pool during DB Open, when max_open_files is set to -1.
// Therefore, the concern of DB Open slowness is eliminated.
// Note that when max_open_files is not set to -1, only a subset of files will
// be opened and checked during DB Open.
//
// Default: false
bool skip_checking_sst_file_sizes_on_db_open = false;
@@ -2061,6 +2070,17 @@ struct ReadOptions {
// Default: false
bool auto_refresh_iterator_with_snapshot = false;
// EXPERIMENTAL
//
// Specify an alternate index to use in the SST files instead of the native
// block based table index. The table_factory used for the column family
// must support building/reading this index.
//
// Currently, only forward scans are supported. For forward scans, only Seek()
// is supported. SeekToFirst() is not supported. If the caller wishes to scan
// from start to end, the native index must be used.
const UserDefinedIndexFactory* table_index_factory = nullptr;
// *** END options only relevant to iterators or scans ***
// *** BEGIN options for RocksDB internal use only ***
+13
View File
@@ -44,6 +44,7 @@ class TableReader;
class WritableFileWriter;
struct ConfigOptions;
struct EnvOptions;
class UserDefinedIndexFactory;
// Types of checksums to use for checking integrity of logical blocks within
// files. All checksums currently use 32 bits of checking power (1 in 4B
@@ -492,8 +493,16 @@ struct BlockBasedTableOptions {
// Because filters only impact performance and are not data-critical, an
// SST file can be opened and used without filters if (a) the filter
// policy name or schema is unrecognized, or (b) filter_policy is nullptr.
// See filter_policy regarding filters.
std::shared_ptr<const FilterPolicy> filter_policy = nullptr;
// EXPERIMENTAL
//
// If non-nullptr, use the specified factory to build user-defined index.
// This allows users to define their own index format and build the index
// during table building.
std::shared_ptr<UserDefinedIndexFactory> user_defined_index_factory = nullptr;
// If true, place whole keys in the filter (not just prefixes).
// This must generally be true for gets to be efficient.
bool whole_key_filtering = true;
@@ -561,6 +570,10 @@ struct BlockBasedTableOptions {
// misplaced within or between files is as likely to fail checksum
// verification as random corruption. Also checksum-protects SST footer.
// Can be read by RocksDB versions >= 8.6.0.
// 7 -- Support for custom compression algorithms with a CompressionManager
// using a non-built-in CompatibilityName(). See `compression_manager` in
// ColumnFamilyOptions. Also changes the format of TableProperties field
// `compression_name`. Can be read by RocksDB versions >= 10.4.0.
//
// Using the default setting of format_version is strongly recommended, so
// that available enhancements are adopted eventually and automatically. The
+19 -2
View File
@@ -109,6 +109,10 @@ class TablePropertiesCollector {
// table.
// @params key the user key that is inserted into the table.
// @params value the value that is inserted into the table.
// @params file_size the current file size. For BlockBasedTable, this
// includes all the data blocks written so far, upto but not including
// the current block being built. With parallel compression, data
// blocks are written async so it depends on the compression progress.
virtual Status AddUserKey(const Slice& key, const Slice& value,
EntryType /*type*/, SequenceNumber /*seq*/,
uint64_t /*file_size*/) {
@@ -143,7 +147,7 @@ class TablePropertiesCollector {
// The name of the properties collector can be used for debugging purpose.
virtual const char* Name() const = 0;
// EXPERIMENTAL Return whether the output file should be further compacted
// Return whether the output file should be further compacted
virtual bool NeedCompact() const { return false; }
// For internal use only.
@@ -344,7 +348,20 @@ struct TableProperties {
// {collector_name[1]},{collector_name[2]},{collector_name[3]} ..
std::string property_collectors_names;
// The compression algo used to compress the SST files.
// Identifies the compression algorithm or schema used in the file.
// Specifically:
// * For format_version < 7, it is one of several names for built-in
// compression types. Because of how some previous versions of RocksDB
// behave, this must be set to "ZSTD" if any blocks are compressed
// with zstd and must NOT be set to "NoCompression" if any blocks are
// compressed.
// * For format_version >= 7, the format is
// <compatibility_name>;<hex-coded compression types>;<future use>
// where <compatibility_name> is the CompatibilityName() of the
// CompressionManager used for the file, or empty if compression was
// disabled; <hex-coded compression types> represents a sorted set of
// CompressionType values used in the file other than kNoCompression, each
// as 2-digit hex, e.g. 04 for LZ$, 07 for ZSTD, etc.
std::string compression_name;
// Compression options used to compress the SST files.
+3
View File
@@ -69,6 +69,7 @@ class ToolHooks {
virtual Status Open(const Options& options,
const blob_db::BlobDBOptions& bdb_options,
const std::string& dbname, blob_db::BlobDB** blob_db) = 0;
virtual void Exit(int status) = 0;
};
class DefaultHooks : public ToolHooks {
@@ -117,6 +118,8 @@ class DefaultHooks : public ToolHooks {
const blob_db::BlobDBOptions& bdb_options,
const std::string& dbname,
blob_db::BlobDB** blob_db) override;
virtual void Exit(int status) override { exit(status); }
};
extern DefaultHooks defaultHooks;
+142
View File
@@ -0,0 +1,142 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// 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).
//
// *****************************************************************
// EXPERIMENTAL - subject to change while under development
// *****************************************************************
#pragma once
#include <string>
#include "rocksdb/advanced_iterator.h"
#include "rocksdb/customizable.h"
#include "rocksdb/options.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
namespace ROCKSDB_NAMESPACE {
// Prefix for user-defined index block names
inline const std::string kUserDefinedIndexPrefix =
"rocksdb.user_defined_index.";
// This is a public API for user-defined index builders.
// It allows users to define their own index format and build custom
// indexes during table building. Currently, only a monolithic index
// block is supported (no partitioned index).
// The interface for building user-defined index.
class UserDefinedIndexBuilder {
public:
// Right now, we only support Puts. In the future, we may support merges,
// deletions etc.
enum ValueType {
kValue,
kTypeMax,
};
// File offset and size of the data block
struct BlockHandle {
uint64_t offset;
uint64_t size;
};
virtual ~UserDefinedIndexBuilder() = default;
// Add a new index entry to index block. The key for the new index entry
// should be >= last_key_in_current_block and < first_key_in_next_block.
// The previous index entry key and the new index entry key cover
// all the keys in the data block associated with the new index entry.
//
// Called before the OnKeyAdded() call for first_key_in_next_block.
// @last_key_in_current_block: The last key in the current data block
// @first_key_in_next_block: it will be nullptr if the entry being added is
// the last one in the table
// @block_handle: offset/size of the data block referenced by this index
// entry. This should be stored along with the index entry
// key
// @separator_scratch: a scratch buffer to back a computed separator between
// those, as needed. May be modified on each call.
// @return: the key or separator stored in the index, which could be
// last_key_in_current_block or a computed separator backed by
// separator_scratch.
virtual Slice AddIndexEntry(const Slice& last_key_in_current_block,
const Slice* first_key_in_next_block,
const BlockHandle& block_handle,
std::string* separator_scratch) = 0;
// This method will be called whenever a key is added. The subclasses may
// override OnKeyAdded() if they need to collect additional information.
// The type argument indicates whether the value is a full value or partial.
// At the moment, only full values are supported.
virtual void OnKeyAdded(const Slice& /*key*/, ValueType /*type*/,
const Slice& /*value*/) {}
// Finish building the index.
// Returns a Status and the serialized index contents.
// The memory backing the contents should not be freed until this builder
// object is destructed.
virtual Status Finish(Slice* index_contents) = 0;
};
// The interface for iterating the user defined index. This will be
// instantiated and used by a scan to iterate through the index entries
// covered by the scan.
class UserDefinedIndexIterator {
public:
virtual ~UserDefinedIndexIterator() = default;
// Prepare the iterator for a series of scans. The iterator should use
// this as an opportunity to do any prefetching and buffering of results.
virtual void Prepare(const ScanOptions scan_opts[], size_t num_opts) = 0;
// Given the target key, position the index iterator at the index entry
// with the smallest key >= target. The result must be updated with the
// index key, and the bound_check_result. The bound_check_result should
// be set to kOutOfBound if no block satisfies the target key and
// termination criteria, kInbound if the data block is definitely fully
// within bounds, or kUnknown if the data block could be partially
// within bounds.
virtual Status SeekAndGetResult(const Slice& target,
IterateResult* result) = 0;
// Advance to the next index entry. The result must be populated similar
// to SeekAndGetResult.
virtual Status NextAndGetResult(IterateResult* result) = 0;
// Return the BlockHandle in the current index entry
virtual UserDefinedIndexBuilder::BlockHandle value() = 0;
};
// A reader interface for the user defined index
class UserDefinedIndexReader {
public:
virtual ~UserDefinedIndexReader() = default;
// Allocate an iterator that will be used by RocksDB to perform scans
virtual std::unique_ptr<UserDefinedIndexIterator> NewIterator(
const ReadOptions& read_options) = 0;
// The memory usage of the index, including the size of the raw contents and
// any other heap data structures allocated by the reader
virtual size_t ApproximateMemoryUsage() const = 0;
};
// Factory for creating user-defined index builders.
class UserDefinedIndexFactory : public Customizable {
public:
virtual ~UserDefinedIndexFactory() = default;
// Create a new builder for user-defined index.
virtual UserDefinedIndexBuilder* NewBuilder() const = 0;
// Create a new user defined index reader given the contents of the index
// block
virtual std::unique_ptr<UserDefinedIndexReader> NewReader(
Slice& index_block) const = 0;
};
} // namespace ROCKSDB_NAMESPACE
+2
View File
@@ -63,6 +63,8 @@ class DBWithTTL : public StackableDB {
virtual void SetTtl(ColumnFamilyHandle* h, int32_t ttl) = 0;
virtual Status GetTtl(ColumnFamilyHandle* h, int32_t* ttl) = 0;
protected:
explicit DBWithTTL(DB* db) : StackableDB(db) {}
};
@@ -23,15 +23,20 @@ class CompactOnDeletionCollectorFactory
// A factory of a table property collector that marks a SST
// file as need-compaction when it observe at least "D" deletion
// entries in any "N" consecutive entries, or the ratio of tombstone
// entries >= deletion_ratio.
// entries >= deletion_ratio for the entire file.
//
// @param sliding_window_size "N"
// @param deletion_trigger "D"
// @param deletion_ratio, if <= 0 or > 1, disable triggering compaction
// based on deletion ratio.
// @param min_file_size, a file needs to be at least this size to be marked
// for compaction. See comments above
// TablePropertiesCollector::AddUserKey() for limitations/inaccuracies on
// the file size.
CompactOnDeletionCollectorFactory(size_t sliding_window_size,
size_t deletion_trigger,
double deletion_ratio);
double deletion_ratio,
uint64_t min_file_size = 0);
~CompactOnDeletionCollectorFactory() override {}
@@ -59,6 +64,12 @@ class CompactOnDeletionCollectorFactory
}
double GetDeletionRatio() const { return deletion_ratio_.load(); }
uint64_t GetMinFileSize() const { return min_file_size_.load(); }
void SetMinFileSize(uint64_t min_file_size) {
min_file_size_.store(min_file_size);
}
static const char* kClassName() { return "CompactOnDeletionCollector"; }
const char* Name() const override { return kClassName(); }
@@ -68,6 +79,7 @@ class CompactOnDeletionCollectorFactory
std::atomic<size_t> sliding_window_size_;
std::atomic<size_t> deletion_trigger_;
std::atomic<double> deletion_ratio_;
std::atomic<uint64_t> min_file_size_;
};
// Creates a factory of a table property collector that marks a SST
@@ -85,7 +97,8 @@ class CompactOnDeletionCollectorFactory
std::shared_ptr<CompactOnDeletionCollectorFactory>
NewCompactOnDeletionCollectorFactory(size_t sliding_window_size,
size_t deletion_trigger,
double deletion_ratio = 0);
double deletion_ratio = 0,
uint64_t min_file_size = 0);
// A factory of a table property collector that marks a SST file as
// need-compaction when for the tiering use case, it observes, among all the
+2 -2
View File
@@ -396,9 +396,9 @@ struct TransactionOptions {
// due to too many memtables.
// Note that the ingestion relies on the transaction's underlying index,
// (WriteBatchWithIndex), so updates that are added to the transaction
// without indexing (e.g. added directly to the transaction underlying
// without indexing (i.e. added directly to the transaction underlying
// write batch through Transaction::GetWriteBatch()->GetWriteBatch())
// are not supported. They will not be applied to the DB.
// are not supported, and the optimization will not apply in that case.
//
// NOTE: since WBWI keep track of the most recent update per key, a Put
// followed by a SingleDelete will be written to DB as a SingleDelete. This
@@ -379,6 +379,8 @@ class WriteBatchWithIndex : public WriteBatchBase {
};
const std::unordered_map<uint32_t, CFStat>& GetCFStats() const;
// The total number of operations issued into this WBWI.
size_t GetWBWIOpCount() const;
bool GetOverwriteKey() const;
private:
+1 -1
View File
@@ -12,7 +12,7 @@
// 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 4
#define ROCKSDB_MINOR 6
#define ROCKSDB_PATCH 0
// Do not use these. We made the mistake of declaring macros starting with
@@ -30,6 +30,14 @@ public class NativeLibraryLoader {
private static final String tempFilePrefix = "librocksdbjni";
private static final String tempFileSuffix = Environment.getJniLibraryExtension();
/**
* If you set the System Property ROCKS_JAVA_DEBUG_NLL can be to true
* messages about attempts to load the native library will be printed
* to std out.
*/
private static boolean DEBUG_LOADING =
"true".equals(System.getProperty("ROCKS_JAVA_DEBUG_NLL", "false"));
/**
* Get a reference to the NativeLibraryLoader
*
@@ -55,7 +63,7 @@ public class NativeLibraryLoader {
*
* @throws java.io.IOException if a filesystem operation fails.
*/
@SuppressWarnings("PMD.EmptyCatchBlock")
@SuppressWarnings({"PMD.EmptyCatchBlock", "PMD.SystemPrintln"})
public synchronized void loadLibrary(final String tmpDir) throws IOException {
try {
// try dynamic library
@@ -63,6 +71,9 @@ public class NativeLibraryLoader {
return;
} catch (final UnsatisfiedLinkError ule) {
// ignore - try from static library
if (DEBUG_LOADING) {
System.out.println("Unable to load shared dynamic library: " + sharedLibraryName);
}
}
try {
@@ -71,6 +82,9 @@ public class NativeLibraryLoader {
return;
} catch (final UnsatisfiedLinkError ule) {
// ignore - then try static library fallback or from jar
if (DEBUG_LOADING) {
System.out.println("Unable to load shared static library: " + jniLibraryName);
}
}
if (fallbackJniLibraryName != null) {
@@ -80,6 +94,10 @@ public class NativeLibraryLoader {
return;
} catch (final UnsatisfiedLinkError ule) {
// ignore - then try from jar
if (DEBUG_LOADING) {
System.out.println(
"Unable to load shared static fallback library: " + fallbackJniLibraryName);
}
}
}
@@ -137,18 +155,23 @@ public class NativeLibraryLoader {
}
}
@SuppressWarnings({"PMD.UseProperClassLoader", "PMD.UseTryWithResources"})
@SuppressWarnings({"PMD.UseProperClassLoader", "PMD.UseTryWithResources", "PMD.SystemPrintln"})
File loadLibraryFromJarToTemp(final String tmpDir) throws IOException {
try (InputStream is = getClass().getClassLoader().getResourceAsStream(jniLibraryFileName)) {
if (is != null) {
final File temp = createTemp(tmpDir, jniLibraryFileName);
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
return temp;
} else {
if (DEBUG_LOADING) {
System.out.println("Unable to find: " + jniLibraryFileName + " on the classpath");
}
}
}
if (fallbackJniLibraryFileName == null) {
throw new RuntimeException(fallbackJniLibraryFileName + " was not found inside JAR.");
throw new RuntimeException(
jniLibraryFileName + " was not found inside JAR, and there is no fallback.");
}
try (InputStream is =
@@ -157,10 +180,16 @@ public class NativeLibraryLoader {
final File temp = createTemp(tmpDir, fallbackJniLibraryFileName);
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
return temp;
} else {
if (DEBUG_LOADING) {
System.out.println(
"Unable to find fallback: " + fallbackJniLibraryFileName + " on the classpath");
}
}
}
throw new RuntimeException(jniLibraryFileName + " was not found inside JAR.");
throw new RuntimeException("Neither " + jniLibraryFileName + " or " + fallbackJniLibraryFileName
+ " were found inside the JAR, and there is no fallback.");
}
/**
+22 -12
View File
@@ -84,13 +84,7 @@ public class RocksDB extends RocksObject {
return;
}
while (libraryLoaded.get() == LibraryState.LOADING) {
try {
Thread.sleep(10);
} catch(final InterruptedException e) {
//ignore
}
}
waitForLibraryToBeLoaded();
}
/**
@@ -146,12 +140,28 @@ public class RocksDB extends RocksObject {
return;
}
while (libraryLoaded.get() == LibraryState.LOADING) {
try {
Thread.sleep(10);
} catch(final InterruptedException e) {
//ignore
waitForLibraryToBeLoaded();
}
private static void waitForLibraryToBeLoaded() {
final long wait = 10; // Time to wait before re-checking if another thread loaded the library
final long timeout =
10 * 1000; // Maximum time to wait for another thread to load the library (10 seconds)
long waited = 0;
try {
while (libraryLoaded.get() == LibraryState.LOADING) {
Thread.sleep(wait);
waited += wait;
if (waited >= timeout) {
throw new RuntimeException(
"Exceeded timeout whilst trying to load the RocksDB shared library");
}
}
} catch (final InterruptedException e) {
// restore interrupted status
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted whilst trying to load the RocksDB shared library", e);
}
}
+49 -8
View File
@@ -30,6 +30,8 @@ class VectorRep : public MemTableRep {
// collection.
void Insert(KeyHandle handle) override;
void InsertConcurrently(KeyHandle handle) override;
// Returns true iff an entry that compares equal to key is in the collection.
bool Contains(const char* key) const override;
@@ -40,6 +42,8 @@ class VectorRep : public MemTableRep {
void Get(const LookupKey& k, void* callback_args,
bool (*callback_func)(void* arg, const char* entry)) override;
void BatchPostProcess() override;
~VectorRep() override = default;
class Iterator : public MemTableRep::Iterator {
@@ -100,19 +104,40 @@ class VectorRep : public MemTableRep {
private:
friend class Iterator;
ALIGN_AS(CACHE_LINE_SIZE) RelaxedAtomic<size_t> bucket_size_;
using Bucket = std::vector<const char*>;
std::shared_ptr<Bucket> bucket_;
mutable port::RWMutex rwlock_;
bool immutable_;
bool sorted_;
const KeyComparator& compare_;
// Thread-local vector to buffer concurrent writes.
using TlBucket = std::vector<const char*>;
ThreadLocalPtr tl_writes_;
static void DeleteTlBucket(void* ptr) {
auto* v = static_cast<TlBucket*>(ptr);
delete v;
}
};
void VectorRep::Insert(KeyHandle handle) {
auto* key = static_cast<char*>(handle);
WriteLock l(&rwlock_);
assert(!immutable_);
bucket_->push_back(key);
{
WriteLock l(&rwlock_);
assert(!immutable_);
bucket_->push_back(key);
}
bucket_size_.FetchAddRelaxed(1);
}
void VectorRep::InsertConcurrently(KeyHandle handle) {
auto* v = static_cast<TlBucket*>(tl_writes_.Get());
if (!v) {
v = new TlBucket();
tl_writes_.Reset(v);
}
v->push_back(static_cast<char*>(handle));
}
// Returns true iff an entry that compares equal to key is in the collection.
@@ -127,19 +152,35 @@ void VectorRep::MarkReadOnly() {
}
size_t VectorRep::ApproximateMemoryUsage() {
return sizeof(bucket_) + sizeof(*bucket_) +
bucket_->size() *
sizeof(
std::remove_reference<decltype(*bucket_)>::type::value_type);
return bucket_size_.LoadRelaxed() *
sizeof(std::remove_reference<decltype(*bucket_)>::type::value_type);
}
void VectorRep::BatchPostProcess() {
auto* v = static_cast<TlBucket*>(tl_writes_.Get());
if (v) {
{
WriteLock l(&rwlock_);
assert(!immutable_);
for (auto& key : *v) {
bucket_->push_back(key);
}
}
bucket_size_.FetchAddRelaxed(v->size());
delete v;
tl_writes_.Reset(nullptr);
}
}
VectorRep::VectorRep(const KeyComparator& compare, Allocator* allocator,
size_t count)
: MemTableRep(allocator),
bucket_size_(0),
bucket_(new Bucket()),
immutable_(false),
sorted_(false),
compare_(compare) {
compare_(compare),
tl_writes_(DeleteTlBucket) {
bucket_.get()->reserve(count);
}
+4 -3
View File
@@ -272,7 +272,8 @@ Status ConfigurableHelper::ConfigureOptions(
if (config_options.ignore_unknown_options) {
s = Status::OK();
} else if (s.ok() && unused == nullptr && !remaining.empty()) {
s = Status::NotFound("Could not find option: ", remaining.begin()->first);
s = Status::NotFound("Extra option not recognized",
remaining.begin()->first);
}
return s;
}
@@ -369,7 +370,7 @@ Status ConfigurableHelper::ConfigureSingleOption(
const auto opt_info =
FindOption(configurable, opt_name, &elem_name, &opt_ptr);
if (opt_info == nullptr) {
return Status::NotFound("Could not find option: ", name);
return Status::NotFound("Could not find option", name);
} else {
return ConfigureOption(config_options, configurable, *opt_info, opt_name,
elem_name, value, opt_ptr);
@@ -465,7 +466,7 @@ Status ConfigurableHelper::ConfigureOption(
return configurable.ParseOption(config_options, opt_info, name, value,
opt_ptr);
} else {
return Status::NotFound("Could not find option: ", name);
return Status::NotFound("Unknown how to configure option", name);
}
}
+128 -1
View File
@@ -386,6 +386,133 @@ std::unordered_map<std::string, CompressionType>
{"kLZ4HCCompression", kLZ4HCCompression},
{"kXpressCompression", kXpressCompression},
{"kZSTD", kZSTD},
{"kCustomCompression80", kCustomCompression80},
{"kCustomCompression81", kCustomCompression81},
{"kCustomCompression82", kCustomCompression82},
{"kCustomCompression83", kCustomCompression83},
{"kCustomCompression84", kCustomCompression84},
{"kCustomCompression85", kCustomCompression85},
{"kCustomCompression86", kCustomCompression86},
{"kCustomCompression87", kCustomCompression87},
{"kCustomCompression88", kCustomCompression88},
{"kCustomCompression89", kCustomCompression89},
{"kCustomCompression8A", kCustomCompression8A},
{"kCustomCompression8B", kCustomCompression8B},
{"kCustomCompression8C", kCustomCompression8C},
{"kCustomCompression8D", kCustomCompression8D},
{"kCustomCompression8E", kCustomCompression8E},
{"kCustomCompression8F", kCustomCompression8F},
{"kCustomCompression90", kCustomCompression90},
{"kCustomCompression91", kCustomCompression91},
{"kCustomCompression92", kCustomCompression92},
{"kCustomCompression93", kCustomCompression93},
{"kCustomCompression94", kCustomCompression94},
{"kCustomCompression95", kCustomCompression95},
{"kCustomCompression96", kCustomCompression96},
{"kCustomCompression97", kCustomCompression97},
{"kCustomCompression98", kCustomCompression98},
{"kCustomCompression99", kCustomCompression99},
{"kCustomCompression9A", kCustomCompression9A},
{"kCustomCompression9B", kCustomCompression9B},
{"kCustomCompression9C", kCustomCompression9C},
{"kCustomCompression9D", kCustomCompression9D},
{"kCustomCompression9E", kCustomCompression9E},
{"kCustomCompression9F", kCustomCompression9F},
{"kCustomCompressionA0", kCustomCompressionA0},
{"kCustomCompressionA1", kCustomCompressionA1},
{"kCustomCompressionA2", kCustomCompressionA2},
{"kCustomCompressionA3", kCustomCompressionA3},
{"kCustomCompressionA4", kCustomCompressionA4},
{"kCustomCompressionA5", kCustomCompressionA5},
{"kCustomCompressionA6", kCustomCompressionA6},
{"kCustomCompressionA7", kCustomCompressionA7},
{"kCustomCompressionA8", kCustomCompressionA8},
{"kCustomCompressionA9", kCustomCompressionA9},
{"kCustomCompressionAA", kCustomCompressionAA},
{"kCustomCompressionAB", kCustomCompressionAB},
{"kCustomCompressionAC", kCustomCompressionAC},
{"kCustomCompressionAD", kCustomCompressionAD},
{"kCustomCompressionAE", kCustomCompressionAE},
{"kCustomCompressionAF", kCustomCompressionAF},
{"kCustomCompressionB0", kCustomCompressionB0},
{"kCustomCompressionB1", kCustomCompressionB1},
{"kCustomCompressionB2", kCustomCompressionB2},
{"kCustomCompressionB3", kCustomCompressionB3},
{"kCustomCompressionB4", kCustomCompressionB4},
{"kCustomCompressionB5", kCustomCompressionB5},
{"kCustomCompressionB6", kCustomCompressionB6},
{"kCustomCompressionB7", kCustomCompressionB7},
{"kCustomCompressionB8", kCustomCompressionB8},
{"kCustomCompressionB9", kCustomCompressionB9},
{"kCustomCompressionBA", kCustomCompressionBA},
{"kCustomCompressionBB", kCustomCompressionBB},
{"kCustomCompressionBC", kCustomCompressionBC},
{"kCustomCompressionBD", kCustomCompressionBD},
{"kCustomCompressionBE", kCustomCompressionBE},
{"kCustomCompressionBF", kCustomCompressionBF},
{"kCustomCompressionC0", kCustomCompressionC0},
{"kCustomCompressionC1", kCustomCompressionC1},
{"kCustomCompressionC2", kCustomCompressionC2},
{"kCustomCompressionC3", kCustomCompressionC3},
{"kCustomCompressionC4", kCustomCompressionC4},
{"kCustomCompressionC5", kCustomCompressionC5},
{"kCustomCompressionC6", kCustomCompressionC6},
{"kCustomCompressionC7", kCustomCompressionC7},
{"kCustomCompressionC8", kCustomCompressionC8},
{"kCustomCompressionC9", kCustomCompressionC9},
{"kCustomCompressionCA", kCustomCompressionCA},
{"kCustomCompressionCB", kCustomCompressionCB},
{"kCustomCompressionCC", kCustomCompressionCC},
{"kCustomCompressionCD", kCustomCompressionCD},
{"kCustomCompressionCE", kCustomCompressionCE},
{"kCustomCompressionCF", kCustomCompressionCF},
{"kCustomCompressionD0", kCustomCompressionD0},
{"kCustomCompressionD1", kCustomCompressionD1},
{"kCustomCompressionD2", kCustomCompressionD2},
{"kCustomCompressionD3", kCustomCompressionD3},
{"kCustomCompressionD4", kCustomCompressionD4},
{"kCustomCompressionD5", kCustomCompressionD5},
{"kCustomCompressionD6", kCustomCompressionD6},
{"kCustomCompressionD7", kCustomCompressionD7},
{"kCustomCompressionD8", kCustomCompressionD8},
{"kCustomCompressionD9", kCustomCompressionD9},
{"kCustomCompressionDA", kCustomCompressionDA},
{"kCustomCompressionDB", kCustomCompressionDB},
{"kCustomCompressionDC", kCustomCompressionDC},
{"kCustomCompressionDD", kCustomCompressionDD},
{"kCustomCompressionDE", kCustomCompressionDE},
{"kCustomCompressionDF", kCustomCompressionDF},
{"kCustomCompressionE0", kCustomCompressionE0},
{"kCustomCompressionE1", kCustomCompressionE1},
{"kCustomCompressionE2", kCustomCompressionE2},
{"kCustomCompressionE3", kCustomCompressionE3},
{"kCustomCompressionE4", kCustomCompressionE4},
{"kCustomCompressionE5", kCustomCompressionE5},
{"kCustomCompressionE6", kCustomCompressionE6},
{"kCustomCompressionE7", kCustomCompressionE7},
{"kCustomCompressionE8", kCustomCompressionE8},
{"kCustomCompressionE9", kCustomCompressionE9},
{"kCustomCompressionEA", kCustomCompressionEA},
{"kCustomCompressionEB", kCustomCompressionEB},
{"kCustomCompressionEC", kCustomCompressionEC},
{"kCustomCompressionED", kCustomCompressionED},
{"kCustomCompressionEE", kCustomCompressionEE},
{"kCustomCompressionEF", kCustomCompressionEF},
{"kCustomCompressionF0", kCustomCompressionF0},
{"kCustomCompressionF1", kCustomCompressionF1},
{"kCustomCompressionF2", kCustomCompressionF2},
{"kCustomCompressionF3", kCustomCompressionF3},
{"kCustomCompressionF4", kCustomCompressionF4},
{"kCustomCompressionF5", kCustomCompressionF5},
{"kCustomCompressionF6", kCustomCompressionF6},
{"kCustomCompressionF7", kCustomCompressionF7},
{"kCustomCompressionF8", kCustomCompressionF8},
{"kCustomCompressionF9", kCustomCompressionF9},
{"kCustomCompressionFA", kCustomCompressionFA},
{"kCustomCompressionFB", kCustomCompressionFB},
{"kCustomCompressionFC", kCustomCompressionFC},
{"kCustomCompressionFD", kCustomCompressionFD},
{"kCustomCompressionFE", kCustomCompressionFE},
{"kDisableCompressionOption", kDisableCompressionOption}};
const std::vector<CompressionType>& GetSupportedCompressions() {
@@ -910,7 +1037,7 @@ Status OptionTypeInfo::Parse(const ConfigOptions& config_options,
: value;
if (opt_ptr == nullptr) {
return Status::NotFound("Could not find option", opt_name);
return Status::NotFound("Nullptr option", opt_name);
} else if (parse_func_ != nullptr) {
ConfigOptions copy = config_options;
copy.invoke_prepare_options = false;
+3
View File
@@ -72,6 +72,9 @@ std::unique_ptr<Configurable> CFOptionsAsConfigurable(
Status StringToMap(const std::string& opts_str,
std::unordered_map<std::string, std::string>* opts_map);
Status GetStringFromCompressionType(std::string* compression_str,
CompressionType compression_type);
struct OptionsHelper {
static const std::string kCFOptionsName /*= "ColumnFamilyOptions"*/;
static const std::string kDBOptionsName /*= "DBOptions" */;
+7 -2
View File
@@ -129,6 +129,8 @@ TEST_F(OptionsSettableTest, BlockBasedTableOptionsAllFieldsSettable) {
sizeof(CacheUsageOptions)},
{offsetof(struct BlockBasedTableOptions, filter_policy),
sizeof(std::shared_ptr<const FilterPolicy>)},
{offsetof(struct BlockBasedTableOptions, user_defined_index_factory),
sizeof(std::shared_ptr<UserDefinedIndexFactory>)},
};
// In this test, we catch a new option of BlockBasedTableOptions that is not
@@ -703,8 +705,11 @@ TEST_F(OptionsSettableTest, ColumnFamilyOptionsAllFieldsSettable) {
new_options->compaction_options_fifo.file_temperature_age_thresholds[0]
.age,
12345);
ASSERT_EQ(new_options->compression_manager,
GetBuiltinCompressionManager(/*compression_format_version*/ 2));
// TODO: try to enhance ObjectLibrary to support singletons
// ASSERT_EQ(new_options->compression_manager,
// GetBuiltinV2CompressionManager());
ASSERT_STREQ(new_options->compression_manager->Name(),
GetBuiltinV2CompressionManager()->Name());
ColumnFamilyOptions rnd_filled_options = *new_options;
Loaded 100 of 181 files, more files were not shown because too many files have changed in this diff. Show more