Compare commits

...
40 Commits
Author SHA1 Message Date
Siying Dong 1178bf775f Improve comments on options.writable_file_max_buffer_size
Comments of options.writable_file_max_buffer_size mentioned Windows, which is confusing. Remove it.
2021-11-04 14:00:24 -07:00
Hui Xiao 1ababeb76a Deallocate payload of BlockBasedTableBuilder::Rep::FilterBlockBuilder earlier for Full/PartitionedFilter (#9070)
Summary:
Note: This PR is the 1st part of a bigger PR stack (https://github.com/facebook/rocksdb/pull/9073).

Context:
Previously, the payload (i.e, filter data) within `BlockBasedTableBuilder::Rep::FilterBlockBuilder` object is not deallocated until `BlockBasedTableBuilder` is deallocated, despite it is no longer useful after its related `filter_content` being written.
- Transferred the payload (i.e, the filter data) out of `BlockBasedTableBuilder::Rep::FilterBlockBuilder` object
- For PartitionedFilter:
   - Unified `filters` and `filter_gc` lists into one `std::deque<FilterEntry> filters` by adding a new field `last_filter_entry_key` and storing the `std::unique_ptr filter_data` with the `Slice filter` in the same entry
   - Reset `last_filter_data` in the case where `filters` is empty, which should be as by then we would've finish using all the `Slice filter`
- Deallocated the payload by going out of scope as soon as we're done with using the `filter_content` associated with the payload
- This is an internal interface change at the level of `FilterBlockBuilder::Finish()`, which leads to touching the inherited interface in `BlockBasedFilterBlockBuilder`. But for that, the payload transferring is ignored.

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

Test Plan: - The main focus is to catch segment fault error during `FilterBlockBuilder::Finish()` and `BlockBasedTableBuilder::Finish()` and interface mismatch. Relying on existing CI tests is enough as `assert(false)` was temporarily added to verify the new logic of transferring ownership indeed run

Reviewed By: pdillinger

Differential Revision: D31884933

Pulled By: hx235

fbshipit-source-id: f73ecfbea13788d4fc058013ace27230110b52f4
2021-11-04 13:35:38 -07:00
Hui Xiao a64c8ca7a8 Sanitize negative request bytes in GenericRateLimiter::Request and clarify API (#9112)
Summary:
Context:
Surprisingly, there isn't any sanitization against negative `int64_t bytes` in `GenericRateLimiter::Request(int64_t bytes, const Env::IOPriority pri, Statistics* stats)`. A negative `bytes` can be passed in and incorrectly increases `available_bytes_` by subtracting the negative `bytes` from `available_bytes_`, such as  [here](https://github.com/facebook/rocksdb/blob/main/util/rate_limiter.cc#L138) and [here](https://github.com/facebook/rocksdb/blob/main/util/rate_limiter.cc#L283), which are incorrect behaviors.
- Sanitized negative request bytes by rounding it up to 0
- Added notes to public and internal API

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

Test Plan: - Rely on existing tests

Reviewed By: ajkr

Differential Revision: D32085364

Pulled By: hx235

fbshipit-source-id: b1b6066b2dd5ffc7bcbfb07069ca65a33578251b
2021-11-04 10:11:53 -07:00
Peter Dillinger dfedc74d82 Some checksum code refactoring (#9113)
Summary:
To prepare for adding checksum to footer and "context aware"
checksums. This also brings closely related code much closer together.

Recently added `BlockBasedTableBuilder::ComputeBlockTrailer` for testing
is made obsolete in the refactoring, as testing the checksums can happen
at a lower level of abstraction.

Also now checking for unrecognized checksum type on reading footer,
rather than later on use.

Also removed an obsolete function delcaration.

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

Test Plan:
existing tests worked before refactoring to remove
`ComputeBlockTrailer`. And then refactored+improved tests using it.

Reviewed By: mrambacher

Differential Revision: D32090149

Pulled By: pdillinger

fbshipit-source-id: 2879da683c1498ea85a3b70dace9b6d9f6b47b6e
2021-11-04 09:09:34 -07:00
Adam Retter 312d9c47b4 Re-enable 390x+cmake* Travis jobs (#9110)
Summary:
Revert "Temporarily disable s390x+cmake* Travis jobs (https://github.com/facebook/rocksdb/issues/9095)"
This reverts commit f2d11b3fdc.

I have now uploaded the CMake deb for s390x provided by jonathan-albrecht-ibm to our S3 bucket.

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

Reviewed By: ajkr

Differential Revision: D32082903

Pulled By: riversand963

fbshipit-source-id: b7243d19fc133e665a8654e3b528c4f53d5b11d1
2021-11-03 20:30:15 -07:00
Yanqin Jin 9b53f14a35 Fixed a bug in CompactionIterator when write-preared transaction is used (#9060)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/9060

RocksDB bottommost level compaction may zero out an internal key's sequence if
the key's sequence is in the earliest_snapshot.
In write-prepared transaction, checking the visibility of a certain sequence in
a specific released snapshot may return a "snapshot released" result.
Therefore, it is possible, after a certain sequence of events, a PUT has its
sequence zeroed out, but a subsequent SingleDelete of the same key will still
be output with its original sequence. This violates the ascending order of
keys and leads to incorrect result.

The solution is to use an extra variable `last_key_seq_zeroed_` to track the
information about visibility in earliest snapshot. With this variable, we can
know for sure that a SingleDelete is in the earliest snapshot even if the said
snapshot is released during compaction before processing the SD.

Reviewed By: ltamasi

Differential Revision: D31813016

fbshipit-source-id: d8cff59d6f34e0bdf282614034aaea99be9174e1
2021-11-03 15:55:00 -07:00
Jack Feng 56810142a5 Fixing child jobs of rocksdb for duplo deprecation (#9117)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/9117

This fixes the loosely formatted json child job spec. I.e., trailing commas

Reviewed By: jay-zhuang

Differential Revision: D32119606

fbshipit-source-id: 0ef571ccbef0e638df18f17288611d3b5774f129
2021-11-03 14:56:26 -07:00
Jay Zhuang 29102641dd Skip directory fsync for filesystem btrfs (#8903)
Summary:
Directory fsync might be expensive on btrfs and it may not be needed.
Here are 4 directory fsync cases:
1. creating a new file: dir-fsync is not needed on btrfs, as long as the
   new file itself is synced.
2. renaming a file: dir-fsync is not needed if the renamed file is
   synced. So an API `FsyncAfterFileRename(filename, ...)` is provided
   to sync the file on btrfs. By default, it just calls dir-fsync.
3. deleting files: dir-fsync is forced by set
   `IOOptions.force_dir_fsync = true`
4. renaming multiple files (like backup and checkpoint): dir-fsync is
   forced, the same as above.

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

Test Plan: run tests on btrfs and non btrfs

Reviewed By: ajkr

Differential Revision: D30885059

Pulled By: jay-zhuang

fbshipit-source-id: dd2730b31580b0bcaedffc318a762d7dbf25de4a
2021-11-03 12:21:27 -07:00
Levi Tamasi 081722780b Refactor the detailed consistency checks and the SST saving logic in VersionBuilder (#9099)
Summary:
The patch refactors the parts of `VersionBuilder` that deal with SST file
comparisons. Specifically, it makes the following changes:
* Turns `NewestFirstBySeqNo` and `BySmallestKey` from free-standing
functions into function objects. Note: `BySmallestKey` has a pointer to the
`InternalKeyComparator`, while `NewestFirstBySeqNo` is completely
stateless.
* Eliminates the wrapper `FileComparator`, which was essentially an
unnecessary DIY virtual function call mechanism.
* Refactors `CheckConsistencyDetails` and `SaveSSTFilesTo` using helper
function templates that take comparator/checker function objects. Using
static polymorphism eliminates the need to make runtime decisions about
which comparator to use.
* Extends some error messages returned by the consistency checks and
makes them more uniform.
* Removes some incomplete/redundant consistency checks from `VersionBuilder`
and `FilePicker`.
* Improves const correctness in several places.

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

Test Plan: `make check`

Reviewed By: riversand963

Differential Revision: D32027503

Pulled By: ltamasi

fbshipit-source-id: 621326ae41f4f55f7ad6a91abbd6e666d5c7857c
2021-11-03 11:52:47 -07:00
Peter Dillinger 2b60621f16 Don't call OnTableFileCreated with OK for empty+deleted file (#9118)
Summary:
EventListener::OnTableFileCreated was previously called with OK
status and file_size==0 in cases of no SST file contents written
(because there was no content to add) and the empty file deleted before
calling the listener. This could lead to a stress test assertion failure
added in https://github.com/facebook/rocksdb/issues/9054.

This changes the status to Aborted, to align with the API doc:
"... if the file is successfully created. Now it will also be called on
failure case. User can check info.status to see if it succeeded or not."
For internal purposes, this case is considered "success" but for
listener purposes, no SST file is (successfully) created.

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

Test Plan: test case added + existing db_stress

Reviewed By: ajkr, riversand963

Differential Revision: D32120232

Pulled By: pdillinger

fbshipit-source-id: a804e2e0a52598018d3b182da97804d402ffcdfa
2021-11-03 08:43:27 -07:00
Peter Dillinger 21f8a57f2a Fix TSAN report on MemPurge test (#9115)
Summary:
TSAN reported data race on count variables in MemPurgeBasic
test. This suggests the test could fail if mempurges were slow enough
that they don't complete before the count variables being checked, but
injecting a long sleep into MemPurge (outside DB mutex) confirms that
blocked writes ensure enough mempurges/flushes happen to make the test
pass. All the possible different values on testing should be OK to make
the test pass.

So this change makes the variables atomic so that up-to-date value is
always read and TSAN report suppressed. I have also used `.exchange(0)`
to make the checking less stateful by "popping off" all the accumulated
counts.

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

Test Plan: updated test, watch for any flakiness

Reviewed By: riversand963

Differential Revision: D32114432

Pulled By: pdillinger

fbshipit-source-id: c985609d39896a0d8f69ebc87b221e688609bdd8
2021-11-02 21:54:29 -07:00
Andrew Kryczka 67a7b74b7f Clarify setting CompressionOptions::max_dict_bytes > 0 will charge block cache (#9119)
Summary:
Add it to the API comment.

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

Reviewed By: hx235

Differential Revision: D32124238

Pulled By: ajkr

fbshipit-source-id: d1f82037417d883f2000f2d62995a7708dda77c6
2021-11-02 21:43:50 -07:00
Peter Dillinger 82afa01815 Some API clarifications (#9080)
Summary:
* Clarify that RocksDB is not exception safe on many of our callback
and extension interfaces
* Clarify FSRandomAccessFile::MultiRead implementations must accept
non-sorted inputs (see https://github.com/facebook/rocksdb/issues/8953)
* Clarify ConcurrentTaskLimiter and SstFileManager are not (currently)
extensible interfaces
* Mark WriteBufferManager as `final`, so it is then clearly not a
callback interface, even though it smells like one
* Clarify TablePropertiesCollector Status returns are mostly ignored

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

Test Plan: comments only (except WriteBufferManager final)

Reviewed By: ajkr

Differential Revision: D31968782

Pulled By: pdillinger

fbshipit-source-id: 11b648ce3ce3c5e5bdc02d2eafc7ea4b864bd1d2
2021-11-02 20:30:07 -07:00
mrambacher f72c834eab Make FileSystem a Customizable Class (#8649)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/8649

Reviewed By: zhichao-cao

Differential Revision: D32036059

Pulled By: mrambacher

fbshipit-source-id: 4f1e7557ecac52eb849b83ae02b8d7d232112295
2021-11-02 09:07:11 -07:00
Levi Tamasi cfc57f55b5 Mention PR 9100 in HISTORY.md (#9111)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/9111

Reviewed By: riversand963

Differential Revision: D32076310

Pulled By: ltamasi

fbshipit-source-id: 81e30a02ded87c0f1a42985db42e80b62235ba11
2021-11-01 15:22:32 -07:00
Alan Paxton ec9082d698 Regression tests for tickets fixed by previous change. (#9019)
Summary:
closes https://github.com/facebook/rocksdb/issues/5891
closes https://github.com/facebook/rocksdb/issues/2001

Java BytewiseComparator is now unsigned compliant, consistent with the default C++ comparator, which has always been thus. Consequently 2 tickets reporting the previous broken state can be closed.

 This test confirms that the following issues were in fact resolved
 by a change made between 6.2.2 and 6.22.1,
 to wit https://github.com/facebook/rocksdb/commit/7242dae7
which as part of its effect, changed the Java bytewise comparators.

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

Reviewed By: pdillinger

Differential Revision: D31610910

Pulled By: mrambacher

fbshipit-source-id: 664230f1377a1aa270136edd63eea2c206b907e9
2021-11-01 15:06:47 -07:00
Hui Xiao 560fe70233 Add new API CacheReservationManager::GetDummyEntrySize() (#9072)
Summary:
Note: it might conflict with another CRM related PR https://github.com/facebook/rocksdb/pull/9071 and so will merge after that's merged.

Context:
As `CacheReservationManager` being used by more memory users, it is convenient to retrieve the dummy entry size for `CacheReservationManager` instead of hard-coding `256 * 1024` in writing tests. Plus it allows more flexibility to change our implementation on dummy entry size.

A follow-up PR is needed to replace those hard-coded dummy entry size value in `db_test2.cc`, `db_write_buffer_manager_test.cc`, `write_buffer_manager_test.cc`, `table_test.cc` and the ones introduced in https://github.com/facebook/rocksdb/pull/9072#issue-1034326069.
- Exposed the private static constexpr `kDummyEntrySize` through public static `CacheReservationManager::GetDummyEntrySize()`

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

Test Plan:
- Passing new tests
- Passing existing tests

Reviewed By: ajkr

Differential Revision: D32043684

Pulled By: hx235

fbshipit-source-id: ddefc6921c052adab6a2cda2394eb26da3076a50
2021-11-01 14:46:09 -07:00
sdong a2b9be42b6 Try to start TTL earlier with kMinOverlappingRatio is used (#8749)
Summary:
Right now, when options.ttl is set, compactions are triggered around the time when TTL is reached. This might cause extra compactions which are often bursty. This commit tries to mitigate it by picking those files earlier in normal compaction picking process. This is only implemented using kMinOverlappingRatio with Leveled compaction as it is the default value and it is more complicated to change other styles.

When a file is aged more than ttl/2, RocksDB starts to boost the compaction priority of files in normal compaction picking process, and hope by the time TTL is reached, very few extra compaction is needed.

In order for this to work, another change is made: during a compaction, if an output level file is older than ttl/2, cut output files based on original boundary (if it is not in the last level). This is to make sure that after an old file is moved to the next level, and new data is merged from the upper level, the new data falling into this range isn't reset with old timestamp. Without this change, in many cases, most files from one level will keep having old timestamp, even if they have newer data and we stuck in it.

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

Test Plan: Add a unit test to test the boosting logic. Will add a unit test to test it end-to-end.

Reviewed By: jay-zhuang

Differential Revision: D30735261

fbshipit-source-id: 503c2d89250b22911eb99e72b379be154de3428e
2021-11-01 14:36:31 -07:00
hx235 a5ec5e3ea0 Minor improvement to #8428 (Account for dictionary-building buffer in global memory limit) (#9032)
Summary:
Summary/Context:
- Renamed `cache_rev_mng` to `compression_dict_buffer_cache_res_mgr`
   - It is to distinguish with other potential `cache_res_mgr` in `BlockBasedTableBuilder` and to use correct short-hand for the words "reservation", "manager"
- Added `table_options.block_cache == nullptr` in additional to `table_options.no_block_cache == true` to be conditions where we don't create a `CacheReservationManager`
   - Theoretically `table_options.no_block_cache == true` is equivalent to `table_options.block_cache == nullptr` by API. But since segment fault will be generated by passing `nullptr` into `CacheReservationManager`'s constructor, it does not hurt to directly verify  `table_options.block_cache != nullptr` before passing in
- Renamed `is_cache_full` to `exceeds_global_block_cache_limit`
   - It is to hide implementation detail of cache reservation and to emphasize on the concept/design intent of caping memory within global block cache limit

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

Test Plan: - Passing existing tests

Reviewed By: ajkr

Differential Revision: D32005807

Pulled By: hx235

fbshipit-source-id: 619fd17bb924199de3db5924d8ab7dae53b1efa2
2021-11-01 14:28:09 -07:00
leipeng 230c98f3ce fix histogram NUM_FILES_IN_SINGLE_COMPACTION (#9026)
Summary:
currently histogram `NUM_FILES_IN_SINGLE_COMPACTION` just counted files in first level of compaction input, this fix counts files in all levels of compaction input.

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

Reviewed By: ajkr

Differential Revision: D31668241

Pulled By: jay-zhuang

fbshipit-source-id: c02f6c4a5df9fbf0b7510036594811152e8738af
2021-11-01 12:57:27 -07:00
Levi Tamasi b1c27a52d2 Add a consistency check that prevents the overflow of garbage in blob files (#9100)
Summary:
The number or total size of garbage blobs in any given blob file can
never exceed the number or total size of all blobs in the file. (This
would be a similar error to e.g. attempting to remove from the LSM tree
an SST file that has already been removed.) The patch builds on
https://github.com/facebook/rocksdb/issues/9085 and adds a
consistency check to `VersionBuilder` that prevents the above from
happening.

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

Test Plan: `make check`

Reviewed By: riversand963

Differential Revision: D32048982

Pulled By: ltamasi

fbshipit-source-id: 6f7e0793bf534ad04c3359cc0f696b8e4e5ef81c
2021-11-01 12:32:14 -07:00
Alan Paxton 73e6b89fad Java wrapper for blob_gc_force_threshold as blobGarbageCollectionForceThreshold (#9109)
Summary:
Extra option added as a supplement to https://github.com/facebook/rocksdb/pull/8999

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

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

Reviewed By: mrambacher

Differential Revision: D32065039

Pulled By: ltamasi

fbshipit-source-id: 6c484050a30fe0523850a8a3c95dc85b0a501362
2021-11-01 11:59:10 -07:00
leipeng 2b70224f82 remove bad extra RecordTick(stats_, WRITE_WITH_WAL) (#9064)
Summary:
This PR fix wrong ticker `WRITE_WITH_WAL`.

`RecordTick(WRITE_WITH_WAL)` will be called later in `WriteToWAL` and `ConcurrentWriteToWAL`.

Fixes:
1. Delete these two extra `RecordTick(WRITE_WITH_WAL)`
2. Fix corresponding test case

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

Reviewed By: ajkr

Differential Revision: D31944459

Pulled By: riversand963

fbshipit-source-id: f1aa8d2a4320456bc357bc5b0902032f7dcad086
2021-11-01 11:43:14 -07:00
Yanqin Jin 0e12b1d691 Update buckify scripts (#9104)
Summary:
https://github.com/facebook/rocksdb/commit/49af999954c0c130fefdb5f4bafc919c18341521
updates RocksDB buckifier script directly via fbcode. We need to make
sure that the following command run in RocksDB repo generate the same
TARGETS file.

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

Test Plan:
```
$python buckifier/buckify_rocksdb.py
```
Verify that TARGETS file does not have uncommitted changes.

Reviewed By: jay-zhuang

Differential Revision: D32055387

Pulled By: riversand963

fbshipit-source-id: 19cf1b8145095b6df625958458189680e543e3ba
2021-11-01 10:11:18 -07:00
leipeng 01bd86ad35 InternalStats::DumpCFMapStat: fix sum.w_amp (#9065)
Summary:
sum `w_amp` will be a very large number`(bytes_written + bytes_written_blob)` when there is no any flush and ingest.

This PR set sum `w_amp` to zero if there is no any flush and ingest, this is conform to per-level `w_amp` computation.

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

Reviewed By: ajkr

Differential Revision: D31943994

Pulled By: riversand963

fbshipit-source-id: acbef5e331debebfad09e0e0d8d0885ebbc00609
2021-10-31 23:11:43 -07:00
Yanqin Jin d263505417 Avoid div-by-zero error in db_stress (#9086)
Summary:
If a column family has 0 levels, then existing `TestCompactFiles(...)` may hit
divide-by-zero. To fix, return early if the cf is empty.

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

Test Plan: TBD

Reviewed By: ajkr

Differential Revision: D31986799

Pulled By: riversand963

fbshipit-source-id: 48f7dfb2b2b47cfc1315cb71ca80eb230d947f17
2021-10-31 22:16:03 -07:00
Yanqin Jin 8e59a1dc9a Attempt to deflake ListenerTest.MultiCF (#9084)
Summary:
EventListenerTest.MultiCF uses TestFlushListener which has members
flushed_dbs_ and flushed_column_family_names_ that are not protected by
locks. This implicitly indicates that we need to ensure the methods
accessing these data structures in a single threaded way. In other
tests, e.g. MultiDBMultiListeners, we use TEST_WaitForFlushMemtable() to
wait until all memtables of a given column family are flushed, hence no
pending flush threads will concurrently call OnFlushCompleted() and
cause data race for flushed_dbs_. To fix a test failure, we should do
the same for MultiCF.

Example data race stack traces reported by TSAN
```
Read of size 8 at 0x7b6000002840 by main thread:
    #0 std::vector<rocksdb::DB*, std::allocator<rocksdb::DB*> >::size() const /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:655:40
    https://github.com/facebook/rocksdb/issues/1 rocksdb::EventListenerTest_MultiCF_Test::TestBody() /home/circleci/project/db/listener_test.cc:380:7

Previous write of size 8 at 0x7b6000002840 by thread T2:
    #0 void std::vector<rocksdb::DB*, std::allocator<rocksdb::DB*> >::_M_emplace_back_aux<rocksdb::DB* const&>(rocksdb::DB* const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/vector.tcc:442:26
    https://github.com/facebook/rocksdb/issues/1 std::vector<rocksdb::DB*, std::allocator<rocksdb::DB*> >::push_back(rocksdb::DB* const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:923:4
    https://github.com/facebook/rocksdb/issues/2 rocksdb::TestFlushListener::OnFlushCompleted(rocksdb::DB*, rocksdb::FlushJobInfo const&) /home/circleci/project/db/listener_test.cc:255:18
```

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

Test Plan: ./listener_test --gtest_filter=EventListenerTest.MultiCF

Reviewed By: jay-zhuang

Differential Revision: D31952259

Pulled By: riversand963

fbshipit-source-id: 94a7f29e4e9466ead42418944eb2247fc32bd499
2021-10-31 22:12:15 -07:00
Yanqin Jin 8f4f302316 Attempt to deflake DBFlushTest.FireOnFlushCompletedAfterCommittedResult (#9083)
Summary:
DBFlushTest.FireOnFlushCompletedAfterCommittedResult uses test sync
points to coordinate interleaving of different threads. Before this PR,
the test writes some data to memtable, triggers a manual flush, and
triggers a second manual flush after a first bg flush thread starts
executing. Though unlikely, it is possible for the second bg flush
thread to run faster than the first bg flush thread and deques flush
queue first. In this case, the original test will fail.
The fix is to wait until the first bg flush thread deques the flush
queue before triggering second manual flush.

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

Test Plan: ./db_flush_test --gtest_filter=DBFlushTest.FireOnFlushCompletedAfterCommittedResult

Reviewed By: jay-zhuang

Differential Revision: D31951239

Pulled By: riversand963

fbshipit-source-id: f32d7cdabe6ad6808fd18e54e663936dc0a9edb4
2021-10-31 22:08:48 -07:00
CodemodService Bot 49af999954 internal_repo_rocksdb/repo
Reviewed By: DrMarcII

Differential Revision: D32033741

fbshipit-source-id: af12d9d72f109a4a2837cb64e02fa0dbc9175711
2021-10-29 19:34:39 -07:00
Levi Tamasi 44d04582cb Aggregate blob file related changes in VersionBuilder as VersionEdits are applied (#9085)
Summary:
The current VersionBuilder code on mainline keeps track of blob file related
changes ("delta") induced by a series of `VersionEdit`s in the form of
`BlobFileMetaDataDelta` objects. Specifically, `BlobFileMetaDataDelta`
contains the amount of additional garbage generated by compactions, as well
as the set of newly linked/unlinked SSTs. This is very handy for detecting trivial moves,
since in that case the newly linked and unlinked SSTs cancel each other out.
However, this representation does not allow us to easily tell whether a certain
blob file is obsolete after applying a set of `VersionEdit`s or not. In order to
solve this issue, the patch introduces `MutableBlobFileMetaData`, which, in addition
to the delta, also contains the materialized state after applying a set of version edits
(i.e. the total amount of garbage and the resulting set of linked SSTs). This will
enable us to add further consistency checks and to improve certain pieces of
functionality where knowing up front which blob files get obsoleted is beneficial.
(Note: this patch is just the refactoring part; I plan to create separate PRs for
the enhancements.)

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

Test Plan: Ran `make check` and the stress tests in BlobDB mode.

Reviewed By: riversand963

Differential Revision: D31980867

Pulled By: ltamasi

fbshipit-source-id: cc4286778b10900af720423d6b772c77f28a93e3
2021-10-29 17:47:02 -07:00
Yanqin Jin fdf2a0d7eb Fix a compaction bug for write-prepared txn (#9061)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/9061

In write-prepared txn, checking a sequence's visibility in a released (old)
snapshot may return "Snapshot released". Suppose we have two snapshots:

```
earliest_snap < earliest_write_conflict_snap
```

If we release `earliest_write_conflict_snap` but keep `earliest_snap` during
bottommost level compaction, then it is possible that certain sequence of
events can lead to a PUT being seq-zeroed followed by a SingleDelete of the
same key. This violates the ascending order of keys, and will cause data
inconsistency.

Reviewed By: ltamasi

Differential Revision: D31813017

fbshipit-source-id: dc68ba2541d1228489b93cf3edda5f37ed06f285
2021-10-29 15:23:17 -07:00
Jonathan Albrecht f2d11b3fdc Temporarily disable s390x+cmake* Travis jobs (#9095)
Summary:
Temporarily disable s390x+cmake* jobs until a cmake-3.14.5-Linux-s390x.deb can be installed to https://rocksdb-deps.s3-us-west-2.amazonaws.com.

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

Reviewed By: akankshamahajan15

Differential Revision: D32025417

Pulled By: riversand963

fbshipit-source-id: eefb9737937987c7d9273482a89e4d2266cd5375
2021-10-29 11:06:50 -07:00
Peter Dillinger 92e2399669 Fix EnvLibrados and add to CI (#9088)
Summary:
This feature was not part of any common or CI build, so no
surprise it broke. Now we can at least ensure compilation. I don't know
how to run the test successfully (missing config file) so it is bypassed
for now.

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

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

Test Plan: CI

Reviewed By: mrambacher

Differential Revision: D32009467

Pulled By: pdillinger

fbshipit-source-id: 3e0d1e5fde7f0ece703d48a81479e1cc7392c25c
2021-10-29 08:19:03 -07:00
Peter Dillinger a7d4bea43a Implement XXH3 block checksum type (#9069)
Summary:
XXH3 - latest hash function that is extremely fast on large
data, easily faster than crc32c on most any x86_64 hardware. In
integrating this hash function, I have handled the compression type byte
in a non-standard way to avoid using the streaming API (extra data
movement and active code size because of hash function complexity). This
approach got a thumbs-up from Yann Collet.

Existing functionality change:
* reject bad ChecksumType in options with InvalidArgument

This change split off from https://github.com/facebook/rocksdb/issues/9058 because context-aware checksum is
likely to be handled through different configuration than ChecksumType.

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

Test Plan:
tests updated, and substantially expanded. Unit tests now check
that we don't accidentally change the values generated by the checksum
algorithms ("schema test") and that we properly handle
invalid/unrecognized checksum types in options or in file footer.

DBTestBase::ChangeOptions (etc.) updated from two to one configuration
changing from default CRC32c ChecksumType. The point of this test code
is to detect possible interactions among features, and the likelihood of
some bad interaction being detected by including configurations other
than XXH3 and CRC32c--and then not detected by stress/crash test--is
extremely low.

Stress/crash test also updated (manual run long enough to see it accepts
new checksum type). db_bench also updated for microbenchmarking
checksums.

 ### Performance microbenchmark (PORTABLE=0 DEBUG_LEVEL=0, Broadwell processor)

./db_bench -benchmarks=crc32c,xxhash,xxhash64,xxh3,crc32c,xxhash,xxhash64,xxh3,crc32c,xxhash,xxhash64,xxh3
crc32c       :       0.200 micros/op 5005220 ops/sec; 19551.6 MB/s (4096 per op)
xxhash       :       0.807 micros/op 1238408 ops/sec; 4837.5 MB/s (4096 per op)
xxhash64     :       0.421 micros/op 2376514 ops/sec; 9283.3 MB/s (4096 per op)
xxh3         :       0.171 micros/op 5858391 ops/sec; 22884.3 MB/s (4096 per op)
crc32c       :       0.206 micros/op 4859566 ops/sec; 18982.7 MB/s (4096 per op)
xxhash       :       0.793 micros/op 1260850 ops/sec; 4925.2 MB/s (4096 per op)
xxhash64     :       0.410 micros/op 2439182 ops/sec; 9528.1 MB/s (4096 per op)
xxh3         :       0.161 micros/op 6202872 ops/sec; 24230.0 MB/s (4096 per op)
crc32c       :       0.203 micros/op 4924686 ops/sec; 19237.1 MB/s (4096 per op)
xxhash       :       0.839 micros/op 1192388 ops/sec; 4657.8 MB/s (4096 per op)
xxhash64     :       0.424 micros/op 2357391 ops/sec; 9208.6 MB/s (4096 per op)
xxh3         :       0.162 micros/op 6182678 ops/sec; 24151.1 MB/s (4096 per op)

As you can see, especially once warmed up, xxh3 is fastest.

 ### Performance macrobenchmark (PORTABLE=0 DEBUG_LEVEL=0, Broadwell processor)

Test

    for I in `seq 1 50`; do for CHK in 0 1 2 3 4; do TEST_TMPDIR=/dev/shm/rocksdb$CHK ./db_bench -benchmarks=fillseq -memtablerep=vector -allow_concurrent_memtable_write=false -num=30000000 -checksum_type=$CHK 2>&1 | grep 'micros/op' | tee -a results-$CHK & done; wait; done

Results (ops/sec)

    for FILE in results*; do echo -n "$FILE "; awk '{ s += $5; c++; } END { print 1.0 * s / c; }' < $FILE; done

results-0 252118 # kNoChecksum
results-1 251588 # kCRC32c
results-2 251863 # kxxHash
results-3 252016 # kxxHash64
results-4 252038 # kXXH3

Reviewed By: mrambacher

Differential Revision: D31905249

Pulled By: pdillinger

fbshipit-source-id: cb9b998ebe2523fc7c400eedf62124a78bf4b4d1
2021-10-28 22:15:17 -07:00
Andrew Kryczka f24c39ab3d Prevent corruption with parallel manual compactions and change_level == true (#9077)
Summary:
The bug can impact the following scenario. There must be two `CompactRange()`s, call them A and B. Compaction A must have `change_level=true`. Compactions A and B must run in parallel, and new data must be added while they run as well.

Now, on to the details of the race condition. Compaction A must reach the refitting phase while B's next step is to trivial move new data (i.e., data that has been inserted behind A) down to the same level that A's refit targets (`CompactRangeOptions::target_level`). B must be unregistered  (i.e., has not yet called `AddManualCompaction()` for the current `RunManualCompaction()`) while A invokes `DisableManualCompaction()`s to prepare for refitting. In the old code, B could still proceed to register a manual compaction, while A had disabled manual compaction.

The next part of the race condition is B picks and schedules a trivial move while A has released the lock in refitting phase in order to persist the LSM state change (i.e., the log phase of `LogAndApply()`). That way, B does not see the refitted data when picking a trivial-move compaction. So it is susceptible to picking one that overlaps.

Finally, B executes the picked trivial-move compaction. Trivial-move compactions are special in that they never check whether manual compaction is disabled. So the picked compaction causing overlap ends up being applied, leading to LSM corruption if `force_consistency_checks=false`, or entering read-only mode with `Status::Corruption` if `force_consistency_checks=true` (the default).

The fix is just to prevent B from registering itself in `RunManualCompaction()` while manual compactions are disabled, consequently preventing any trivial move or other compaction from being picked/scheduled.

Thanks to siying for finding the bug.

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

Test Plan: The test does not go all the way in exposing the bug because it requires a compaction to be picked/scheduled while logging LSM state change for RefitLevel(). But the fix is to make such a compaction not picked/scheduled in the first place, so any repro of that scenario would end up hanging RefitLevel() logging. So instead I just verified no such compaction is registered in the scenario where `RefitLevel()` disables manual compactions.

Reviewed By: siying

Differential Revision: D31921908

Pulled By: ajkr

fbshipit-source-id: 9bb5d0e847ad428211227f40830c685c209fbecb
2021-10-27 23:08:56 -07:00
Peter Dillinger 5bf9a7d5ee Clarify caching behavior for index and filter partitions (#9068)
Summary:
Somewhat confusingly, index and filter partition blocks are
never owned by table readers, even with
cache_index_and_filter_blocks=false. They still go into block cache
(possibly pinned by table reader) if there is a block cache. If no block
cache, they are only loaded transiently on demand.

This PR primarily clarifies the options APIs and some internal code
comments.

Also, this closes a hypothetical data corruption vulnerability where
some but not all index partitions are pinned. I haven't been able to
reproduce a case where it can happen (the failure seems to propagate
to abort table open) but it's worth patching nonetheless.

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

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

Test Plan:
existing tests :-/  I could cover the new code using sync
points, but then I'd have to very carefully relax my `assert(false)`

Reviewed By: ajkr

Differential Revision: D31898284

Pulled By: pdillinger

fbshipit-source-id: f2511a7d3a36bc04b627935d8e6cfea6422f98be
2021-10-27 17:23:04 -07:00
Calin Culianu 82846f41d3 Fix incorrect order of comments in win_thread.cc (#9033)
Summary:
The comments in the `#endif` section at the end of the file were in the
wrong order.

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

Reviewed By: mrambacher

Differential Revision: D31935856

Pulled By: ajkr

fbshipit-source-id: 24aca039993d6e27022cfe8d6434e90f2934c87c
2021-10-27 13:25:01 -07:00
Peter Dillinger 4ec31dc8ac Make format-diff.sh locale-independent (#9079)
Summary:
Force POSIX locale for calls to 'git remote' that might have
locale-dependent formatting, as shown in https://github.com/facebook/rocksdb/issues/8731 comment

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

Test Plan:
manual (haven't tried on a machine with non-english default
locale)

Reviewed By: ltamasi

Differential Revision: D31943092

Pulled By: pdillinger

fbshipit-source-id: 7dbe5915824f39f73b412cc3d1a86a2521cf76c1
2021-10-27 12:26:36 -07:00
myasuka dc00e4b120 Introduce allowStall option for write buffer manager constructor (#9076)
Summary:
https://github.com/facebook/rocksdb/pull/7898 enable write buffer manager to stall write when memory_usage exceeds buffer_size, this is really useful for container running case to limit the memory usage. However, this feature is not visiable for rocksJava yet.

This PR targets to introduce this feature for rocksJava.

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

Reviewed By: akankshamahajan15

Differential Revision: D31931092

Pulled By: anand1976

fbshipit-source-id: 5531c16a87598663a02368c07b5e13a503164578
2021-10-26 12:09:54 -07:00
Jonathan Albrecht e970248602 Add support for building on s390x platform (#8962)
Summary:
This PR adds support for building on s390x including updating travis CI. It uses the previous work in https://github.com/facebook/rocksdb/pull/6168 and adds some more changes to get all current tests (make check and jni tests) to pass. The tests were run with snappy, lz4, bzip2 and zstd all compiled in.

There are a few pieces still needed to get the travis build working that I don't think I can do. adamretter is this something you could help with?

1. A prebuilt https://rocksdb-deps.s3-us-west-2.amazonaws.com/cmake/cmake-3.14.5-Linux-s390x.deb package
2. A https://hub.docker.com/r/evolvedbinary/rocksjava s390x image

Not sure if there is more required for travis. Happy to help in any way I can.

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

Reviewed By: mrambacher

Differential Revision: D31802198

Pulled By: pdillinger

fbshipit-source-id: 683511466fa6b505f85ba5a9964a268c6151f0c2
2021-10-22 10:13:15 -07:00
156 changed files with 3886 additions and 1270 deletions

No files matched your search

+12 -4
View File
@@ -98,6 +98,13 @@ commands:
command: |
sudo apt-get update -y && sudo apt-get install -y libbenchmark-dev
install-librados:
steps:
- run:
name: Install librados
command: |
sudo apt-get update -y && sudo apt-get install -y librados-dev
upgrade-cmake:
steps:
- run:
@@ -171,14 +178,15 @@ jobs:
- run: make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
- post-steps
build-linux-mem-env:
build-linux-mem-env-librados:
machine:
image: ubuntu-1604:202104-01
resource_class: 2xlarge
steps:
- pre-steps
- install-gflags
- run: MEM_ENV=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
- install-librados
- run: MEM_ENV=1 ROCKSDB_USE_LIBRADOS=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
- post-steps
build-linux-encrypted-env:
@@ -708,9 +716,9 @@ workflows:
jobs:
- build-linux-cmake
- build-linux-cmake-ubuntu-20
build-linux-mem-env:
build-linux-mem-env-librados:
jobs:
- build-linux-mem-env
- build-linux-mem-env-librados
build-linux-encrypted-env:
jobs:
- build-linux-encrypted-env
+51
View File
@@ -5,6 +5,7 @@ os:
arch:
- arm64
- ppc64le
- s390x
compiler:
- clang
- gcc
@@ -59,6 +60,12 @@ matrix:
- os: linux
arch: ppc64le
env: JOB_NAME=make-gcc4.8
- os: linux
arch: s390x
env: JOB_NAME=cmake-mingw
- os: linux
arch: s390x
env: JOB_NAME=make-gcc4.8
- os: linux
compiler: clang
- if: type = pull_request AND commit_message !~ /FULL_CI/
@@ -73,6 +80,10 @@ matrix:
os: linux
arch: ppc64le
env: TEST_GROUP=1
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: TEST_GROUP=1
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -81,6 +92,10 @@ matrix:
os: linux
arch: ppc64le
env: TEST_GROUP=2
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: TEST_GROUP=2
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -89,6 +104,10 @@ matrix:
os: linux
arch: ppc64le
env: TEST_GROUP=3
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: TEST_GROUP=3
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -97,6 +116,10 @@ matrix:
os: linux
arch: ppc64le
env: TEST_GROUP=4
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: TEST_GROUP=4
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -109,6 +132,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=java_test
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
os: linux
arch: s390x
env: JOB_NAME=java_test
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -117,6 +144,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=lite_build
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: JOB_NAME=lite_build
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -125,6 +156,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=examples
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: JOB_NAME=examples
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -133,6 +168,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=cmake-gcc8
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: JOB_NAME=cmake-gcc8
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -141,6 +180,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=cmake-gcc9
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: JOB_NAME=cmake-gcc9
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -149,6 +192,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=cmake-gcc9-c++20
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: JOB_NAME=cmake-gcc9-c++20
- if: type = pull_request AND commit_message !~ /FULL_CI/
os : linux
arch: arm64
@@ -157,6 +204,10 @@ matrix:
os: linux
arch: ppc64le
env: JOB_NAME=status_checked
- if: type = pull_request AND commit_message !~ /FULL_CI/
os: linux
arch: s390x
env: JOB_NAME=status_checked
install:
- if [ "${JOB_NAME}" == cmake-gcc8 ]; then
+13 -1
View File
@@ -267,6 +267,13 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|AARCH64")
endif(HAS_ARMV8_CRC)
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|AARCH64")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
CHECK_C_COMPILER_FLAG("-march=native" HAS_S390X_MARCH_NATIVE)
if(HAS_S390X_MARCH_NATIVE)
message(STATUS " HAS_S390X_MARCH_NATIVE yes")
endif(HAS_S390X_MARCH_NATIVE)
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
option(PORTABLE "build a portable binary" OFF)
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
@@ -292,12 +299,17 @@ if(PORTABLE)
if(FORCE_AVX2)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mbmi -mlzcnt")
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
endif()
endif()
else()
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
else()
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x" AND NOT HAS_S390X_MARCH_NATIVE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
endif()
+29
View File
@@ -1,4 +1,33 @@
# Rocksdb Change Log
## Unreleased
### New Features
* Added new ChecksumType kXXH3 which is faster than kCRC32c on almost all x86\_64 hardware.
* Added a new online consistency check for BlobDB which validates that the number/total size of garbage blobs does not exceed the number/total size of all blobs in any given blob file.
### Bug Fixes
* Prevent a `CompactRange()` with `CompactRangeOptions::change_level == true` from possibly causing corruption to the LSM state (overlapping files within a level) when run in parallel with another manual compaction. Note that setting `force_consistency_checks == true` (the default) would cause the DB to enter read-only mode in this scenario and return `Status::Corruption`, rather than committing any corruption.
* Fixed a bug in CompactionIterator when write-prepared transaction is used. A released earliest write conflict snapshot may cause assertion failure in dbg mode and unexpected key in opt mode.
* Fix ticker WRITE_WITH_WAL("rocksdb.write.wal"), this bug is caused by a bad extra `RecordTick(stats_, WRITE_WITH_WAL)` (at 2 place), this fix remove the extra `RecordTick`s and fix the corresponding test case.
* EventListener::OnTableFileCreated was previously called with OK status and file_size==0 in cases of no SST file contents written (because there was no content to add) and the empty file deleted before calling the listener. Now the status is Aborted.
* Fixed a bug in CompactionIterator when write-preared transaction is used. Releasing earliest_snapshot during compaction may cause a SingleDelete to be output after a PUT of the same user key whose seq has been zeroed.
* Added input sanitization on negative bytes passed into `GenericRateLimiter::Request`.
### Behavior Changes
* `NUM_FILES_IN_SINGLE_COMPACTION` was only counting the first input level files, now it's including all input files.
### Public Interface Change
* When options.ttl is used with leveled compaction with compactinon priority kMinOverlappingRatio, files exceeding half of TTL value will be prioritized more, so that by the time TTL is reached, fewer extra compactions will be scheduled to clear them up. At the same time, when compacting files with data older than half of TTL, output files may be cut off based on those files' boundaries, in order for the early TTL compaction to work properly.
### Public API change
* Made FileSystem extend the Customizable class and added a CreateFromString method. Implementations need to be registered with the ObjectRegistry and to implement a Name() method in order to be created via this method.
* Clarified in API comments that RocksDB is not exception safe for callbacks and custom extensions. An exception propagating into RocksDB can lead to undefined behavior, including data loss, unreported corruption, deadlocks, and more.
* Marked `WriteBufferManager` as `final` because it is not intended for extension.
* Add API `FSDirectory::FsyncWithDirOptions()`, which provides extra information like directory fsync reason in `DirFsyncOptions`. File system like btrfs is using that to skip directory fsync for creating a new file, or when renaming a file, fsync the target file instead of the directory, which improves the `DB::Open()` speed by ~20%.
* `DB::Open()` is not going be blocked by obsolete file purge if `DBOptions::avoid_unnecessary_blocking_io` is set to true.
### Performance Improvements
* Released some memory related to filter construction earlier in `BlockBasedTableBuilder` for `FullFilter` and `PartitionedFilter` case (#9070)
## 6.26.0 (2021-10-20)
### Bug Fixes
* Fixes a bug in directed IO mode when calling MultiGet() for blobs in the same blob file. The bug is caused by not sorting the blob read requests by file offsets.
+6 -2
View File
@@ -222,6 +222,7 @@ am__v_AR_1 =
ifdef ROCKSDB_USE_LIBRADOS
LIB_SOURCES += utilities/env_librados.cc
TEST_MAIN_SOURCES += utilities/env_librados_test.cc
LDFLAGS += -lrados
endif
@@ -1925,6 +1926,9 @@ clipping_iterator_test: $(OBJ_DIR)/db/compaction/clipping_iterator_test.o $(TEST
ribbon_bench: $(OBJ_DIR)/microbench/ribbon_bench.o $(LIBRARY)
$(AM_LINK)
db_basic_bench: $(OBJ_DIR)/microbench/db_basic_bench.o $(LIBRARY)
$(AM_LINK)
cache_reservation_manager_test: $(OBJ_DIR)/cache/cache_reservation_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
#-------------------------------------------------
@@ -1997,7 +2001,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
ifeq ($(PLATFORM), OS_SOLARIS)
ARCH := $(shell isainfo -b)
else ifeq ($(PLATFORM), OS_OPENBSD)
ifneq (,$(filter amd64 ppc64 ppc64le arm64 aarch64 sparc64, $(MACHINE)))
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 sparc64, $(MACHINE)))
ARCH := 64
else
ARCH := 32
@@ -2017,7 +2021,7 @@ ifneq ($(origin JNI_LIBC), undefined)
JNI_LIBC_POSTFIX = -$(JNI_LIBC)
endif
ifneq (,$(filter ppc% arm64 aarch64 sparc64, $(MACHINE)))
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64, $(MACHINE)))
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
else
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
+36 -19
View File
@@ -31,7 +31,11 @@ ROCKSDB_EXTERNAL_DEPS = [
ROCKSDB_OS_DEPS_0 = [
(
"linux",
["third-party//numa:numa", "third-party//liburing:uring", "third-party//tbb:tbb"],
[
"third-party//numa:numa",
"third-party//liburing:uring",
"third-party//tbb:tbb",
],
),
(
"macos",
@@ -69,7 +73,13 @@ ROCKSDB_OS_PREPROCESSOR_FLAGS_0 = [
),
(
"windows",
[ "-DOS_WIN", "-DWIN32", "-D_MBCS", "-DWIN64", "-DNOMINMAX" ]
[
"-DOS_WIN",
"-DWIN32",
"-D_MBCS",
"-DWIN64",
"-DNOMINMAX",
],
),
]
@@ -445,13 +455,14 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = False,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [],
external_deps = ROCKSDB_EXTERNAL_DEPS,
link_whole = False,
)
cpp_library(
@@ -771,13 +782,14 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = True,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [],
external_deps = ROCKSDB_EXTERNAL_DEPS,
link_whole = True,
)
cpp_library(
@@ -795,15 +807,16 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = False,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [":rocksdb_lib"],
external_deps = ROCKSDB_EXTERNAL_DEPS + [
("googletest", None, "gtest"),
],
link_whole = False,
)
cpp_library(
@@ -818,13 +831,14 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = False,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [":rocksdb_lib"],
external_deps = ROCKSDB_EXTERNAL_DEPS,
link_whole = False,
)
cpp_library(
@@ -833,13 +847,14 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = False,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [":rocksdb_lib"],
external_deps = ROCKSDB_EXTERNAL_DEPS,
link_whole = False,
)
cpp_library(
@@ -863,10 +878,11 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = ROCKSDB_LIB_DEPS,
external_deps = ROCKSDB_EXTERNAL_DEPS,
)
@@ -875,10 +891,10 @@ cpp_binary(
name = "c_test_bin",
srcs = ["db/c_test.c"],
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
deps = [":rocksdb_test_lib"],
) if not is_opt_mode else None
@@ -897,13 +913,14 @@ cpp_library(
auto_headers = AutoHeaders.RECURSIVE_GLOB,
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = False,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [":rocksdb_test_lib"],
external_deps = ROCKSDB_EXTERNAL_DEPS,
link_whole = False,
)
# [test_name, test_src, test_type, extra_deps, extra_compiler_flags]
@@ -2220,10 +2237,10 @@ ROCKS_TESTS = [
name = test_name,
srcs = [test_cc],
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
deps = [":rocksdb_test_lib"] + extra_deps,
external_deps = ROCKSDB_EXTERNAL_DEPS + [
("googletest", None, "gtest"),
+2 -2
View File
@@ -84,10 +84,10 @@ cpp_binary(
name = "c_test_bin",
srcs = ["db/c_test.c"],
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
deps = [":rocksdb_test_lib"],
) if not is_opt_mode else None
+19 -7
View File
@@ -38,7 +38,11 @@ ROCKSDB_EXTERNAL_DEPS = [
ROCKSDB_OS_DEPS_0 = [
(
"linux",
["third-party//numa:numa", "third-party//liburing:uring", "third-party//tbb:tbb"],
[
"third-party//numa:numa",
"third-party//liburing:uring",
"third-party//tbb:tbb",
],
),
(
"macos",
@@ -76,7 +80,13 @@ ROCKSDB_OS_PREPROCESSOR_FLAGS_0 = [
),
(
"windows",
[ "-DOS_WIN", "-DWIN32", "-D_MBCS", "-DWIN64", "-DNOMINMAX" ]
[
"-DOS_WIN",
"-DWIN32",
"-D_MBCS",
"-DWIN64",
"-DNOMINMAX",
],
),
]
@@ -144,13 +154,14 @@ cpp_library(
{headers_attr_prefix}headers = {headers},
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
link_whole = {link_whole},
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = [{deps}],
external_deps = ROCKSDB_EXTERNAL_DEPS{extra_external_deps},
link_whole = {link_whole},
)
"""
@@ -161,10 +172,11 @@ cpp_library(
{headers_attr_prefix}headers = {headers},
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
os_deps = ROCKSDB_OS_DEPS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
unexported_deps_by_default = False,
deps = ROCKSDB_LIB_DEPS,
external_deps = ROCKSDB_EXTERNAL_DEPS,
)
@@ -205,10 +217,10 @@ ROCKS_TESTS = [
name = test_name,
srcs = [test_cc],
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
include_paths = ROCKSDB_INCLUDE_PATHS,
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
deps = [":rocksdb_test_lib"] + extra_deps,
external_deps = ROCKSDB_EXTERNAL_DEPS + [
("googletest", None, "gtest"),
+12 -2
View File
@@ -638,13 +638,19 @@ if test "0$PORTABLE" -eq 0; then
# Tune for this POWER processor, treating '+' models as base models
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
elif test -n "`echo $TARGET_ARCHITECTURE | grep -e^arm -e^aarch64`"; then
# TODO: Handle this with approprite options.
COMMON_FLAGS="$COMMON_FLAGS"
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
COMMON_FLAGS="$COMMON_FLAGS"
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ \
-fsyntax-only -march=native - -o /dev/null 2>/dev/null; then
COMMON_FLAGS="$COMMON_FLAGS -march=native "
else
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
fi
COMMON_FLAGS="$COMMON_FLAGS"
elif [ "$TARGET_OS" == "IOS" ]; then
COMMON_FLAGS="$COMMON_FLAGS"
elif [ "$TARGET_OS" == "AIX" ] || [ "$TARGET_OS" == "SunOS" ]; then
@@ -661,6 +667,10 @@ else
TRY_SSE_ETC="1"
fi
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
fi
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
# For portability compile for macOS 10.12 (2016) or newer
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.12"
+2 -2
View File
@@ -127,11 +127,11 @@ uncommitted_code=`git diff HEAD`
if [ -z "$uncommitted_code" ]
then
# Attempt to get name of facebook/rocksdb.git remote.
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(LC_ALL=POSIX LANG=POSIX git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
# Fall back on 'origin' if that fails
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin
# Use main branch from that remote
[ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/$(git remote show $FORMAT_REMOTE | sed -n '/HEAD branch/s/.*: //p')"
[ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/$(LC_ALL=POSIX LANG=POSIX git remote show $FORMAT_REMOTE | sed -n '/HEAD branch/s/.*: //p')"
# Get the common ancestor with that remote branch. Everything after that
# common ancestor would be considered the contents of a pull request, so
# should be relevant for formatting fixes.
+49 -49
View File
@@ -27,7 +27,7 @@ if [ ! -z $EMAIL ]; then
'type':'email',
'triggers': [ '$TRIGGER' ],
'emails':['$EMAIL']
},"
}"
fi
CREATE_TASK=
@@ -38,8 +38,8 @@ if [ ! -z $ONCALL ]; then
'triggers':[ 'fail' ],
'priority':0,
'subscribers':[ '$SUBSCRIBER' ],
'tags':[ 'rocksdb', 'ci' ],
},"
'tags':[ 'rocksdb', 'ci' ]
}"
fi
# For now, create the tasks using only the dedicated task creation tool.
@@ -48,7 +48,7 @@ CREATE_TASK=
REPORT=
if [[ ! -z $REPORT_EMAIL || ! -z $CREATE_TASK ]]; then
REPORT="'report': [
$REPORT_EMAIL
$REPORT_EMAIL,
$CREATE_TASK
]"
fi
@@ -79,9 +79,9 @@ UPLOAD_DB_DIR="
{
'name':'rocksdb_db_dir',
'paths': ['rocksdb_db.tar.gz'],
'bundle': false,
},
],
'bundle': false
}
]
}"
# We will eventually set the RATIO to 1, but we want do this
@@ -141,8 +141,8 @@ DISABLE_COMMANDS="[
'name':'Job disabled. Please contact test owner',
'shell':'exit 1',
'user':'root'
},
],
}
]
}
]"
@@ -161,7 +161,7 @@ UNIT_TEST_COMMANDS="[
'shell':'cd $WORKING_DIR; $SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -184,7 +184,7 @@ UNIT_TEST_NON_SHM_COMMANDS="[
'shell':'cd $WORKING_DIR; $NON_SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -205,7 +205,7 @@ RELEASE_BUILD_COMMANDS="[
'shell':'cd $WORKING_DIR; make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -226,7 +226,7 @@ UNIT_TEST_COMMANDS_481="[
'shell':'cd $WORKING_DIR; $SHM $GCC_481 $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -247,7 +247,7 @@ RELEASE_BUILD_COMMANDS_481="[
'shell':'cd $WORKING_DIR; $GCC_481 make $PARALLEL_j release || $CONTRUN_NAME=release_gcc481 $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -268,7 +268,7 @@ CLANG_UNIT_TEST_COMMANDS="[
'shell':'cd $WORKING_DIR; $CLANG $SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -289,7 +289,7 @@ CLANG_RELEASE_BUILD_COMMANDS="[
'shell':'cd $WORKING_DIR; $CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -310,7 +310,7 @@ CLANG_ANALYZE_COMMANDS="[
'shell':'cd $WORKING_DIR; $CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -331,7 +331,7 @@ CODE_COV_COMMANDS="[
'shell':'cd $WORKING_DIR; $SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -352,7 +352,7 @@ UNITY_COMMANDS="[
'shell':'cd $WORKING_DIR; $SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -373,7 +373,7 @@ LITE_BUILD_COMMANDS="[
'shell':'cd $WORKING_DIR; $SKIP_FORMAT_CHECKS make J=1 LITE=1 all check || $CONTRUN_NAME=lite $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -403,7 +403,7 @@ STRESS_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -433,7 +433,7 @@ BLACKBOX_STRESS_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -463,7 +463,7 @@ WHITEBOX_STRESS_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -493,7 +493,7 @@ STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -523,7 +523,7 @@ STRESS_CRASH_TEST_WITH_TXN_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -553,7 +553,7 @@ STRESS_CRASH_TEST_WITH_TS_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -621,7 +621,7 @@ ASAN_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -645,7 +645,7 @@ ASAN_BLACKBOX_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -669,7 +669,7 @@ ASAN_WHITEBOX_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -693,7 +693,7 @@ ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -717,7 +717,7 @@ ASAN_CRASH_TEST_WITH_TXN_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -762,7 +762,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -786,7 +786,7 @@ UBSAN_BLACKBOX_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -810,7 +810,7 @@ UBSAN_WHITEBOX_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -834,7 +834,7 @@ UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -858,7 +858,7 @@ UBSAN_CRASH_TEST_WITH_TXN_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -881,7 +881,7 @@ VALGRIND_TEST_COMMANDS="[
'shell':'cd $WORKING_DIR; $SHM $DEBUG make $PARALLELISM valgrind_test || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -904,7 +904,7 @@ TSAN_UNIT_TEST_COMMANDS="[
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -928,7 +928,7 @@ TSAN_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -952,7 +952,7 @@ TSAN_BLACKBOX_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -976,7 +976,7 @@ TSAN_WHITEBOX_CRASH_TEST_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -1000,7 +1000,7 @@ TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -1024,7 +1024,7 @@ TSAN_CRASH_TEST_WITH_TXN_COMMANDS="[
'user':'root',
$PARSER
},
$UPLOAD_DB_DIR,
$UPLOAD_DB_DIR
],
$REPORT
}
@@ -1041,7 +1041,7 @@ run_format_compatible()
mkdir /dev/shm/rocksdb
export https_proxy="fwdproxy:8080"
tools/check_format_compatible.sh
}
@@ -1057,7 +1057,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
'shell':'cd $WORKING_DIR; build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -1092,7 +1092,7 @@ NO_COMPRESSION_COMMANDS="[
'shell':'cd $WORKING_DIR; build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -1152,7 +1152,7 @@ REGRESSION_COMMANDS="[
'shell':'cd $WORKING_DIR; build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -1173,7 +1173,7 @@ JAVA_BUILD_TEST_COMMANDS="[
'shell':'cd $WORKING_DIR; $SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
@@ -1207,7 +1207,7 @@ FBCODE_STRESS_CRASH_TEST_COMMANDS="[
'shell':'cd $WORKING_DIR; mkdir /dev/shm/rocksdb_fbcode_crash_test && TEST_TMPDIR=\$(mktemp -d --tmpdir=/dev/shm/rocksdb_fbcode_crash_test) python3 rocksdb/src/tools/db_crashtest.py --stress_cmd=buck-out/dbg/gen/rocks/tools/rocks_db_stress -secondary_cache_uri=\"$SECONDARY_CACHE_URI\" --env_uri=$ENV_URI $EXTRA_DB_STRESS_ARGS -logtostderr=false $TEST_TYPE || $CONTRUN_NAME=db_stress_fbcode $TASK_CREATION_TOOL',
'user':'root',
$PARSER
},
}
],
$REPORT
}
+3 -1
View File
@@ -75,6 +75,8 @@ class CacheReservationManager {
Status UpdateCacheReservation(std::size_t new_memory_used);
std::size_t GetTotalReservedCacheSize();
static constexpr std::size_t GetDummyEntrySize() { return kSizeDummyEntry; }
private:
static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
// The key will be longer than keys for blocks in SST files so they won't
@@ -95,4 +97,4 @@ class CacheReservationManager {
// The non-prefix part will be updated according to the ID to use.
char cache_key_[kCacheKeyPrefixSize + kMaxVarint64Length];
};
} // namespace ROCKSDB_NAMESPACE
} // namespace ROCKSDB_NAMESPACE
+9 -5
View File
@@ -25,7 +25,8 @@ class CacheReservationManagerTest : public ::testing::Test {
static constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
static constexpr int kNumShardBits = 0; // 2^0 shard
static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
static constexpr std::size_t kSizeDummyEntry =
CacheReservationManager::GetDummyEntrySize();
static const std::size_t kCacheKeyPrefixSize =
BlockBasedTable::kMaxCacheKeyPrefixSize + kMaxVarint64Length;
static constexpr std::size_t kMetaDataChargeOverhead = 10000;
@@ -143,7 +144,8 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
IncreaseCacheReservationOnFullCache) {
constexpr std::size_t kOneMegabyte = 1024 * 1024;
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
constexpr std::size_t kSizeDummyEntry =
CacheReservationManager::GetDummyEntrySize();
constexpr std::size_t kMetaDataChargeOverhead = 10000;
LRUCacheOptions lo;
@@ -307,7 +309,8 @@ TEST_F(CacheReservationManagerTest,
TEST(CacheReservationManagerWithDelayedDecreaseTest,
DecreaseCacheReservationWithDelayedDecrease) {
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
constexpr std::size_t kSizeDummyEntry =
CacheReservationManager::GetDummyEntrySize();
constexpr std::size_t kMetaDataChargeOverhead = 10000;
LRUCacheOptions lo;
@@ -379,7 +382,8 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
TEST(CacheReservationManagerDestructorTest,
ReleaseRemainingDummyEntriesOnDestruction) {
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
constexpr std::size_t kSizeDummyEntry =
CacheReservationManager::GetDummyEntrySize();
constexpr std::size_t kMetaDataChargeOverhead = 10000;
LRUCacheOptions lo;
@@ -408,4 +412,4 @@ TEST(CacheReservationManagerDestructorTest,
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
}
+6 -1
View File
@@ -380,14 +380,19 @@ Status BuildTable(
}
}
Status status_for_listener = s;
if (meta->fd.GetFileSize() == 0) {
fname = "(nil)";
if (s.ok()) {
status_for_listener = Status::Aborted("Empty SST file not kept");
}
}
// Output to event logger and fire events.
EventHelpers::LogAndNotifyTableFileCreationFinished(
event_logger, ioptions.listeners, dbname, tboptions.column_family_name,
fname, job_id, meta->fd, meta->oldest_blob_file_number, tp,
tboptions.reason, s, file_checksum, file_checksum_func_name);
tboptions.reason, status_for_listener, file_checksum,
file_checksum_func_name);
return s;
}
+10 -1
View File
@@ -612,10 +612,19 @@ bool Compaction::DoesInputReferenceBlobFiles() const {
return false;
}
uint64_t Compaction::MinInputFileOldestAncesterTime() const {
uint64_t Compaction::MinInputFileOldestAncesterTime(
const InternalKey* start, const InternalKey* end) const {
uint64_t min_oldest_ancester_time = port::kMaxUint64;
const InternalKeyComparator& icmp =
column_family_data()->internal_comparator();
for (const auto& level_files : inputs_) {
for (const auto& file : level_files.files) {
if (start != nullptr && icmp.Compare(file->largest, *start) < 0) {
continue;
}
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
continue;
}
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
if (oldest_ancester_time != 0) {
min_oldest_ancester_time =
+4 -1
View File
@@ -304,7 +304,10 @@ class Compaction {
uint32_t max_subcompactions() const { return max_subcompactions_; }
uint64_t MinInputFileOldestAncesterTime() const;
// start and end are sub compact range. Null if no boundary.
// This is used to filter out some input files' ancester's time range.
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
const InternalKey* end) const;
// Called by DBImpl::NotifyOnCompactionCompleted to make sure number of
// compaction begin and compaction completion callbacks match.
+46 -40
View File
@@ -433,6 +433,8 @@ void CompactionIterator::NextFromInput() {
has_outputted_key_ = false;
last_key_seq_zeroed_ = false;
current_key_committed_ = KeyCommitted(ikey_.sequence);
// Apply the compaction filter to the first committed version of the user
@@ -520,6 +522,25 @@ void CompactionIterator::NextFromInput() {
// 2) We've already returned a record in this snapshot -OR-
// there are no earlier earliest_write_conflict_snapshot.
//
// A note about 2) above:
// we try to determine whether there is any earlier write conflict
// checking snapshot by calling DefinitelyInSnapshot() with seq and
// earliest_write_conflict_snapshot as arguments. For write-prepared
// and write-unprepared transactions, if earliest_write_conflict_snapshot
// is evicted from WritePreparedTxnDB::commit_cache, then
// DefinitelyInSnapshot(seq, earliest_write_conflict_snapshot) returns
// false, even if the seq is actually visible within
// earliest_write_conflict_snapshot. Consequently, CompactionIterator
// may try to zero out its sequence number, thus hitting assertion error
// in debug mode or cause incorrect DBIter return result.
// We observe that earliest_write_conflict_snapshot >= earliest_snapshot,
// and the seq zeroing logic depends on
// DefinitelyInSnapshot(seq, earliest_snapshot). Therefore, if we cannot
// determine whether seq is **definitely** in
// earliest_write_conflict_snapshot, then we can additionally check if
// seq is definitely in earliest_snapshot. If the latter holds, then the
// former holds too.
//
// Rule 1 is needed for SingleDelete correctness. Rule 2 is needed to
// allow Transactions to do write-conflict checking (if we compacted away
// all keys, then we wouldn't know that a write happened in this
@@ -575,10 +596,16 @@ void CompactionIterator::NextFromInput() {
TEST_SYNC_POINT_CALLBACK(
"CompactionIterator::NextFromInput:SingleDelete:1",
const_cast<Compaction*>(c));
// Check whether the next key belongs to the same snapshot as the
// SingleDelete.
if (prev_snapshot == 0 ||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot)) {
if (last_key_seq_zeroed_) {
++iter_stats_.num_record_drop_hidden;
++iter_stats_.num_record_drop_obsolete;
assert(bottommost_level_);
AdvanceInputIter();
} else if (prev_snapshot == 0 ||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot)) {
// Check whether the next key belongs to the same snapshot as the
// SingleDelete.
TEST_SYNC_POINT_CALLBACK(
"CompactionIterator::NextFromInput:SingleDelete:2", nullptr);
if (next_ikey.type == kTypeSingleDeletion) {
@@ -598,7 +625,10 @@ void CompactionIterator::NextFromInput() {
valid_ = true;
} else if (has_outputted_key_ ||
DefinitelyInSnapshot(ikey_.sequence,
earliest_write_conflict_snapshot_)) {
earliest_write_conflict_snapshot_) ||
(earliest_snapshot_ < earliest_write_conflict_snapshot_ &&
DefinitelyInSnapshot(ikey_.sequence,
earliest_snapshot_))) {
// Found a matching value, we can drop the single delete and the
// value. It is safe to drop both records since we've already
// outputted a key in this snapshot, or there is no earlier
@@ -639,6 +669,9 @@ void CompactionIterator::NextFromInput() {
// We hit the next snapshot without hitting a put, so the iterator
// returns the single delete.
valid_ = true;
TEST_SYNC_POINT_CALLBACK(
"CompactionIterator::NextFromInput:SingleDelete:3",
const_cast<Compaction*>(c));
}
} else {
// We are at the end of the input, could not parse the next key, or hit
@@ -661,6 +694,11 @@ void CompactionIterator::NextFromInput() {
if (!bottommost_level_) {
++iter_stats_.num_optimized_del_drop_obsolete;
}
} else if (last_key_seq_zeroed_) {
// Skip.
++iter_stats_.num_record_drop_hidden;
++iter_stats_.num_record_drop_obsolete;
assert(bottommost_level_);
} else {
// Output SingleDelete
valid_ = true;
@@ -1016,6 +1054,9 @@ void CompactionIterator::PrepareOutput() {
ikey_.type);
}
ikey_.sequence = 0;
last_key_seq_zeroed_ = true;
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput:ZeroingSeq",
&ikey_);
if (!timestamp_size_) {
current_key_.UpdateInternalKey(0, ikey_.type);
} else if (full_history_ts_low_ && cmp_with_history_ts_low_ < 0) {
@@ -1084,41 +1125,6 @@ inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
(ikey_.sequence < preserve_deletes_seqnum_);
}
bool CompactionIterator::IsInCurrentEarliestSnapshot(SequenceNumber sequence) {
assert(snapshot_checker_ != nullptr);
bool pre_condition = (earliest_snapshot_ == kMaxSequenceNumber ||
(earliest_snapshot_iter_ != snapshots_->end() &&
*earliest_snapshot_iter_ == earliest_snapshot_));
assert(pre_condition);
if (!pre_condition) {
ROCKS_LOG_FATAL(info_log_,
"Pre-Condition is not hold in IsInEarliestSnapshot");
}
auto in_snapshot =
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
while (UNLIKELY(in_snapshot == SnapshotCheckerResult::kSnapshotReleased)) {
// Avoid the the current earliest_snapshot_ being return as
// earliest visible snapshot for the next value. So if a value's sequence
// is zero-ed out by PrepareOutput(), the next value will be compact out.
released_snapshots_.insert(earliest_snapshot_);
earliest_snapshot_iter_++;
if (earliest_snapshot_iter_ == snapshots_->end()) {
earliest_snapshot_ = kMaxSequenceNumber;
} else {
earliest_snapshot_ = *earliest_snapshot_iter_;
}
in_snapshot =
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
}
assert(in_snapshot != SnapshotCheckerResult::kSnapshotReleased);
if (in_snapshot == SnapshotCheckerResult::kSnapshotReleased) {
ROCKS_LOG_FATAL(info_log_,
"Unexpected released snapshot in IsInEarliestSnapshot");
}
return in_snapshot == SnapshotCheckerResult::kInSnapshot;
}
uint64_t CompactionIterator::ComputeBlobGarbageCollectionCutoffFileNumber(
const CompactionProxy* compaction) {
if (!compaction) {
+4 -10
View File
@@ -271,14 +271,10 @@ class CompactionIterator {
SnapshotCheckerResult::kInSnapshot;
}
bool IsInCurrentEarliestSnapshot(SequenceNumber sequence);
bool DefinitelyInSnapshot(SequenceNumber seq, SequenceNumber snapshot);
bool DefinitelyNotInSnapshot(SequenceNumber seq, SequenceNumber snapshot);
bool InCurrentEarliestSnapshot(SequenceNumber seq);
// Extract user-defined timestamp from user key if possible and compare it
// with *full_history_ts_low_ if applicable.
inline void UpdateTimestampAndCompareWithFullHistoryLow() {
@@ -405,6 +401,10 @@ class CompactionIterator {
const int level_;
// True if the previous internal key (same user key)'s sequence number has
// just been zeroed out during bottommost compaction.
bool last_key_seq_zeroed_{false};
void AdvanceInputIter() { input_.Next(); }
void SkipUntil(const Slice& skip_until) { input_.Seek(skip_until); }
@@ -439,10 +439,4 @@ inline bool CompactionIterator::DefinitelyNotInSnapshot(
SnapshotCheckerResult::kNotInSnapshot)));
}
inline bool CompactionIterator::InCurrentEarliestSnapshot(SequenceNumber seq) {
return ((seq) <= earliest_snapshot_ &&
(snapshot_checker_ == nullptr ||
LIKELY(IsInCurrentEarliestSnapshot(seq))));
}
} // namespace ROCKSDB_NAMESPACE
+119 -5
View File
@@ -170,6 +170,13 @@ struct CompactionJob::SubcompactionState {
}
}
// Some identified files with old oldest ancester time and the range should be
// isolated out so that the output file(s) in that range can be merged down
// for TTL and clear the timestamps for the range.
std::vector<FileMetaData*> files_to_cut_for_ttl;
int cur_files_to_cut_for_ttl = -1;
int next_files_to_cut_for_ttl = 0;
uint64_t current_output_file_size = 0;
// State during the subcompaction
@@ -212,6 +219,8 @@ struct CompactionJob::SubcompactionState {
return Status::OK();
}
void FillFilesToCutForTtl();
// Returns true iff we should stop building the current output
// before processing "internal_key".
bool ShouldStopBefore(const Slice& internal_key, uint64_t curr_file_size) {
@@ -244,6 +253,40 @@ struct CompactionJob::SubcompactionState {
return true;
}
if (!files_to_cut_for_ttl.empty()) {
if (cur_files_to_cut_for_ttl != -1) {
// Previous key is inside the range of a file
if (icmp->Compare(internal_key,
files_to_cut_for_ttl[cur_files_to_cut_for_ttl]
->largest.Encode()) > 0) {
next_files_to_cut_for_ttl = cur_files_to_cut_for_ttl + 1;
cur_files_to_cut_for_ttl = -1;
return true;
}
} else {
// Look for the key position
while (next_files_to_cut_for_ttl <
static_cast<int>(files_to_cut_for_ttl.size())) {
if (icmp->Compare(internal_key,
files_to_cut_for_ttl[next_files_to_cut_for_ttl]
->smallest.Encode()) >= 0) {
if (icmp->Compare(internal_key,
files_to_cut_for_ttl[next_files_to_cut_for_ttl]
->largest.Encode()) <= 0) {
// With in the current file
cur_files_to_cut_for_ttl = next_files_to_cut_for_ttl;
return true;
}
// Beyond the current file
next_files_to_cut_for_ttl++;
} else {
// Still fall into the gap
break;
}
}
}
}
return false;
}
@@ -256,6 +299,46 @@ struct CompactionJob::SubcompactionState {
}
};
void CompactionJob::SubcompactionState::FillFilesToCutForTtl() {
if (compaction->immutable_options()->compaction_style !=
CompactionStyle::kCompactionStyleLevel ||
compaction->immutable_options()->compaction_pri !=
CompactionPri::kMinOverlappingRatio ||
compaction->mutable_cf_options()->ttl == 0 ||
compaction->num_input_levels() < 2 || compaction->bottommost_level()) {
return;
}
// We define new file with oldest ancestor time to be younger than 1/4 TTL,
// and an old one to be older than 1/2 TTL time.
int64_t temp_current_time;
auto get_time_status = compaction->immutable_options()->clock->GetCurrentTime(
&temp_current_time);
if (!get_time_status.ok()) {
return;
}
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
if (current_time < compaction->mutable_cf_options()->ttl) {
return;
}
uint64_t old_age_thres =
current_time - compaction->mutable_cf_options()->ttl / 2;
const std::vector<FileMetaData*>& olevel =
*(compaction->inputs(compaction->num_input_levels() - 1));
for (FileMetaData* file : olevel) {
// Worth filtering out by start and end?
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
// We put old files if they are not too small to prevent a flood
// of small files.
if (oldest_ancester_time < old_age_thres &&
file->fd.GetFileSize() >
compaction->mutable_cf_options()->target_file_size_base / 2) {
files_to_cut_for_ttl.push_back(file);
}
}
}
// Maintains state for the entire compaction
struct CompactionJob::CompactionState {
Compaction* const compaction;
@@ -680,12 +763,16 @@ Status CompactionJob::Run() {
constexpr IODebugContext* dbg = nullptr;
if (output_directory_) {
io_s = output_directory_->Fsync(IOOptions(), dbg);
io_s = output_directory_->FsyncWithDirOptions(
IOOptions(), dbg,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
}
if (io_s.ok() && wrote_new_blob_files && blob_output_directory_ &&
blob_output_directory_ != output_directory_) {
io_s = blob_output_directory_->Fsync(IOOptions(), dbg);
io_s = blob_output_directory_->FsyncWithDirOptions(
IOOptions(), dbg,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
}
}
if (io_status_.ok()) {
@@ -1291,6 +1378,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
auto c_iter = sub_compact->c_iter.get();
c_iter->SeekToFirst();
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
sub_compact->FillFilesToCutForTtl();
// ShouldStopBefore() maintains state based on keys processed so far. The
// compaction loop always calls it on the "next" key, thus won't tell it the
// first key. So we do that here.
@@ -1739,7 +1827,6 @@ Status CompactionJob::FinishCompactionOutputFile(
meta->UpdateBoundariesForRange(smallest_candidate, largest_candidate,
tombstone.seq_,
cfd->internal_comparator());
// The smallest key in a file is used for range tombstone truncation, so
// it cannot have a seqnum of 0 (unless the smallest data key in a file
// has a seqnum of 0). Otherwise, the truncated tombstone may expose
@@ -1763,6 +1850,23 @@ Status CompactionJob::FinishCompactionOutputFile(
if (s.ok()) {
meta->fd.file_size = current_bytes;
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
// With accurate smallest and largest key, we can get a slightly more
// accurate oldest ancester time.
// This makes oldest ancester time in manifest more accurate than in
// table properties. Not sure how to resolve it.
if (meta->smallest.size() > 0 && meta->largest.size() > 0) {
uint64_t refined_oldest_ancester_time;
Slice new_smallest = meta->smallest.user_key();
Slice new_largest = meta->largest.user_key();
if (!new_largest.empty() && !new_smallest.empty()) {
refined_oldest_ancester_time =
sub_compact->compaction->MinInputFileOldestAncesterTime(
&(meta->smallest), &(meta->largest));
if (refined_oldest_ancester_time != port::kMaxUint64) {
meta->oldest_ancester_time = refined_oldest_ancester_time;
}
}
}
}
sub_compact->current_output()->finished = true;
sub_compact->total_bytes += current_bytes;
@@ -2033,8 +2137,17 @@ Status CompactionJob::OpenCompactionOutputFile(
get_time_status.ToString().c_str());
}
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
InternalKey tmp_start, tmp_end;
if (sub_compact->start != nullptr) {
tmp_start.SetMinPossibleForUserKey(*(sub_compact->start));
}
if (sub_compact->end != nullptr) {
tmp_end.SetMinPossibleForUserKey(*(sub_compact->end));
}
uint64_t oldest_ancester_time =
sub_compact->compaction->MinInputFileOldestAncesterTime();
sub_compact->compaction->MinInputFileOldestAncesterTime(
(sub_compact->start != nullptr) ? &tmp_start : nullptr,
(sub_compact->end != nullptr) ? &tmp_end : nullptr);
if (oldest_ancester_time == port::kMaxUint64) {
oldest_ancester_time = current_time;
}
@@ -2333,7 +2446,8 @@ Status CompactionServiceCompactionJob::Run() {
constexpr IODebugContext* dbg = nullptr;
if (output_directory_) {
io_s = output_directory_->Fsync(IOOptions(), dbg);
io_s = output_directory_->FsyncWithDirOptions(IOOptions(), dbg,
DirFsyncOptions());
}
}
if (io_status_.ok()) {
+63 -3
View File
@@ -3,15 +3,15 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include <limits>
#include <string>
#include <utility>
#include "db/compaction/compaction.h"
#include "db/compaction/compaction_picker_fifo.h"
#include "db/compaction/compaction_picker_level.h"
#include "db/compaction/compaction_picker_universal.h"
#include "db/compaction/file_pri.h"
#include "test_util/testharness.h"
#include "test_util/testutil.h"
#include "util/string_util.h"
@@ -149,7 +149,7 @@ class CompactionPickerTest : public testing::Test {
vstorage_ = std::move(temp_vstorage_);
}
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
vstorage_->UpdateFilesByCompactionPri(ioptions_, mutable_cf_options_);
vstorage_->UpdateNumNonEmptyLevels();
vstorage_->GenerateFileIndexer();
vstorage_->GenerateLevelFilesBrief();
@@ -1673,6 +1673,66 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys11) {
ASSERT_EQ(7U, compaction->input(1, 0)->fd.GetNumber());
}
TEST_F(CompactionPickerTest, FileTtlBooster) {
// Set TTL to 2048
// TTL boosting for all levels starts at 1024,
// Whole TTL range is 2048 * 31 / 32 - 1024 = 1984 - 1024 = 960.
// From second last level (L5), range starts at
// 1024 + 480, 1024 + 240, 1024 + 120 (which is L3).
// Boosting step 124 / 16 = 7.75 -> 7
//
const uint64_t kCurrentTime = 1000000;
FileMetaData meta;
{
FileTtlBooster booster(kCurrentTime, 2048, 7, 3);
// Not triggering if the file is younger than ttl/2
meta.oldest_ancester_time = kCurrentTime - 1023;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancester_time = kCurrentTime - 1024;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancester_time = kCurrentTime + 10;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
// Within one boosting step
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 6);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
// One boosting step
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 7);
ASSERT_EQ(2, booster.GetBoostScore(&meta));
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 8);
ASSERT_EQ(2, booster.GetBoostScore(&meta));
// Multiple boosting steps
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 30);
ASSERT_EQ(5, booster.GetBoostScore(&meta));
// Very high boosting steps
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 700);
ASSERT_EQ(101, booster.GetBoostScore(&meta));
}
{
// Test second last level
FileTtlBooster booster(kCurrentTime, 2048, 7, 5);
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
ASSERT_EQ(3, booster.GetBoostScore(&meta));
}
{
// Test last level
FileTtlBooster booster(kCurrentTime, 2048, 7, 6);
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
ASSERT_EQ(1, booster.GetBoostScore(&meta));
meta.oldest_ancester_time = kCurrentTime - 3000;
ASSERT_EQ(1, booster.GetBoostScore(&meta));
}
}
TEST_F(CompactionPickerTest, NotScheduleL1IfL0WithHigherPri1) {
NewVersionStorage(6, kCompactionStyleLevel);
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
#pragma once
#include <algorithm>
#include "db/version_edit.h"
namespace ROCKSDB_NAMESPACE {
// We boost files that are closer to TTL limit. This boosting could be
// through FileMetaData.compensated_file_size but this compensated size
// is widely used as something similar to file size so dramatically boost
// the value might cause unintended consequences.
//
// This boosting algorithm can go very fancy, but here we use a simple
// formula which can satisify:
// (1) Different levels are triggered slightly differently to avoid
// too many cascading cases
// (2) Files in the same level get boosting more when TTL gets closer.
//
// Don't do any boosting before TTL has past by half. This is to make
// sure lower write amp for most of the case. And all levels should be
// fully boosted when total TTL compaction threshold triggers.
// Differientiate boosting ranges of each level by 1/2. This will make
// range for each level exponentially increasing. We could do it by
// having them to be equal, or go even fancier. We can adjust it after
// we observe the behavior in production.
// The threshold starting boosting:
// +------------------------------------------------------------------ +
// ^ ^ ^ ^ ^ ^
// Age 0 ... | | second last level thresold
// | |
// | third last level
// |
// forth last level
//
// We arbitrarily set with 0 when a file is aged boost_age_start and
// grow linearly. The ratio is arbitrarily set so that when the next level
// starts to boost, the previous level's boosting amount is 16.
class FileTtlBooster {
public:
FileTtlBooster(uint64_t current_time, uint64_t ttl, int num_non_empty_levels,
int level)
: current_time_(current_time) {
if (ttl == 0 || level == 0 || level >= num_non_empty_levels - 1) {
enabled_ = false;
boost_age_start_ = 0;
boost_step_ = 1;
} else {
enabled_ = true;
uint64_t all_boost_start_age = ttl / 2;
uint64_t all_boost_age_range = (ttl / 32) * 31 - all_boost_start_age;
uint64_t boost_age_range =
all_boost_age_range >> (num_non_empty_levels - level - 1);
boost_age_start_ = all_boost_start_age + boost_age_range;
const uint64_t kBoostRatio = 16;
// prevent 0 value to avoid divide 0 error.
boost_step_ = std::max(boost_age_range / kBoostRatio, uint64_t{1});
}
}
uint64_t GetBoostScore(FileMetaData* f) {
if (!enabled_) {
return 1;
}
uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime();
if (oldest_ancester_time >= current_time_) {
return 1;
}
uint64_t age = current_time_ - oldest_ancester_time;
if (age > boost_age_start_) {
// Use integer just for convenience.
// We could make all file_to_order double if we want.
// Technically this can overflow if users override timing and
// give a very high current time. Ignore the case for simplicity.
// Boosting is addition to current value, so +1. This will effectively
// make boosting to kick in after the first boost_step_ is reached.
return (age - boost_age_start_) / boost_step_ + 1;
}
return 1;
}
private:
bool enabled_;
uint64_t current_time_;
uint64_t boost_age_start_;
uint64_t boost_step_;
};
} // namespace ROCKSDB_NAMESPACE
+17 -7
View File
@@ -10,6 +10,7 @@
#include <cstring>
#include "db/db_test_util.h"
#include "options/options_helper.h"
#include "port/stack_trace.h"
#include "rocksdb/flush_block_policy.h"
#include "rocksdb/merge_operator.h"
@@ -974,13 +975,14 @@ TEST_F(DBBasicTest, MultiGetEmpty) {
TEST_F(DBBasicTest, ChecksumTest) {
BlockBasedTableOptions table_options;
Options options = CurrentOptions();
// change when new checksum type added
int max_checksum = static_cast<int>(kxxHash64);
const int kNumPerFile = 2;
const auto algs = GetSupportedChecksums();
const int algs_size = static_cast<int>(algs.size());
// generate one table with each type of checksum
for (int i = 0; i <= max_checksum; ++i) {
table_options.checksum = static_cast<ChecksumType>(i);
for (int i = 0; i < algs_size; ++i) {
table_options.checksum = algs[i];
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Reopen(options);
for (int j = 0; j < kNumPerFile; ++j) {
@@ -990,15 +992,20 @@ TEST_F(DBBasicTest, ChecksumTest) {
}
// with each valid checksum type setting...
for (int i = 0; i <= max_checksum; ++i) {
table_options.checksum = static_cast<ChecksumType>(i);
for (int i = 0; i < algs_size; ++i) {
table_options.checksum = algs[i];
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Reopen(options);
// verify every type of checksum (should be regardless of that setting)
for (int j = 0; j < (max_checksum + 1) * kNumPerFile; ++j) {
for (int j = 0; j < algs_size * kNumPerFile; ++j) {
ASSERT_EQ(Key(j), Get(Key(j)));
}
}
// Now test invalid checksum type
table_options.checksum = static_cast<ChecksumType>(123);
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
ASSERT_TRUE(TryReopen(options).IsInvalidArgument());
}
// On Windows you can have either memory mapped file or a file
@@ -3289,6 +3296,9 @@ class DeadlineFS : public FileSystemWrapper {
ignore_deadline_(false),
error_on_delay_(error_on_delay) {}
static const char* kClassName() { return "DeadlineFileSystem"; }
const char* Name() const override { return kClassName(); }
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& opts,
std::unique_ptr<FSRandomAccessFile>* result,
+137
View File
@@ -4302,6 +4302,67 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, LevelTtlBooster) {
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 3;
const int kValueSize = 1000;
Options options = CurrentOptions();
options.ttl = 10 * 60 * 60; // 10 hours
options.periodic_compaction_seconds = 480 * 60 * 60; // very long
options.level0_file_num_compaction_trigger = 2;
options.max_bytes_for_level_base = 5 * uint64_t{kNumKeysPerFile * kValueSize};
options.max_open_files = -1; // needed for both periodic and ttl compactions
options.compaction_pri = CompactionPri::kMinOverlappingRatio;
env_->SetMockSleep();
options.env = env_;
// NOTE: Presumed unnecessary and removed: resetting mock time in env
DestroyAndReopen(options);
Random rnd(301);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(2);
ASSERT_EQ("0,0,3", FilesPerLevel());
// Create some files for L1
for (int i = 0; i < 2; i++) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(Put(Key(2 * j + i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
ASSERT_EQ("0,1,3", FilesPerLevel());
// Make the new L0 files qualify TTL boosting and generate one more to trigger
// L1 -> L2 compaction. Old files will be picked even if their priority is
// lower without boosting.
env_->MockSleepForSeconds(8 * 60 * 60);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(Put(Key(kNumKeysPerFile * 2 + 2 * j + i),
rnd.RandomString(kValueSize * 2)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
ASSERT_EQ("0,1,2", FilesPerLevel());
ASSERT_GT(SizeAtLevel(1), kNumKeysPerFile * 4 * kValueSize);
}
TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
class TestCompactionFilter : public CompactionFilter {
const char* Name() const override { return "TestCompactionFilter"; }
@@ -6863,6 +6924,82 @@ TEST_F(DBCompactionTest,
ASSERT_TRUE(callback_completed);
}
TEST_F(DBCompactionTest, ChangeLevelConflictsWithManual) {
Options options = CurrentOptions();
options.num_levels = 3;
Reopen(options);
// Setup an LSM with L2 populated.
Random rnd(301);
ASSERT_OK(Put(Key(0), rnd.RandomString(990)));
ASSERT_OK(Put(Key(1), rnd.RandomString(990)));
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,0,1", FilesPerLevel(0));
// The background thread will refit L2->L1 while the foreground thread will
// attempt to run a compaction on new data. The following dependencies
// ensure the background manual compaction's refitting phase disables manual
// compaction immediately before the foreground manual compaction can register
// itself. Manual compaction is kept disabled until the foreground manual
// checks for the failure once.
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
// Only do Put()s for foreground CompactRange() once the background
// CompactRange() has reached the refitting phase.
{
"DBImpl::CompactRange:BeforeRefit:1",
"DBCompactionTest::ChangeLevelConflictsWithManual:"
"PreForegroundCompactRange",
},
// Right before we register the manual compaction, proceed with
// the refitting phase so manual compactions are disabled. Stay in
// the refitting phase with manual compactions disabled until it is
// noticed.
{
"DBImpl::RunManualCompaction:0",
"DBImpl::CompactRange:BeforeRefit:2",
},
{
"DBImpl::CompactRange:PreRefitLevel",
"DBImpl::RunManualCompaction:1",
},
{
"DBImpl::RunManualCompaction:PausedAtStart",
"DBImpl::CompactRange:PostRefitLevel",
},
// If compaction somehow were scheduled, let's let it run after reenabling
// manual compactions. This dependency is not expected to be hit but is
// here for speculatively coercing future bugs.
{
"DBImpl::CompactRange:PostRefitLevel:ManualCompactionEnabled",
"BackgroundCallCompaction:0",
},
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ROCKSDB_NAMESPACE::port::Thread refit_level_thread([&] {
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 1;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
});
TEST_SYNC_POINT(
"DBCompactionTest::ChangeLevelConflictsWithManual:"
"PreForegroundCompactRange");
ASSERT_OK(Put(Key(0), rnd.RandomString(990)));
ASSERT_OK(Put(Key(1), rnd.RandomString(990)));
ASSERT_TRUE(dbfull()
->CompactRange(CompactRangeOptions(), nullptr, nullptr)
.IsIncomplete());
refit_level_thread.join();
}
#endif // !defined(ROCKSDB_LITE)
} // namespace ROCKSDB_NAMESPACE
+20 -22
View File
@@ -783,8 +783,8 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
options.listeners.emplace_back(listener);
#endif // !ROCKSDB_LITE
ASSERT_OK(TryReopen(options));
uint32_t mempurge_count = 0;
uint32_t sst_count = 0;
std::atomic<uint32_t> mempurge_count{0};
std::atomic<uint32_t> sst_count{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::FlushJob:MemPurgeSuccessful",
[&](void* /*arg*/) { mempurge_count++; });
@@ -859,10 +859,8 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
// Check that there was no SST files created during flush.
const uint32_t EXPECTED_SST_COUNT = 0;
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
const uint32_t mempurge_count_record = mempurge_count;
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
// Insertion of of K-V pairs, no overwrites.
for (size_t i = 0; i < NUM_REPEAT; i++) {
@@ -893,9 +891,9 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
}
// Assert that at least one flush to storage has been performed
ASSERT_GT(sst_count, EXPECTED_SST_COUNT);
EXPECT_GT(sst_count.exchange(0), EXPECTED_SST_COUNT);
// (which will consequently increase the number of mempurges recorded too).
ASSERT_GE(mempurge_count, mempurge_count_record);
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
// Assert that there is no data corruption, even with
// a flush to storage.
@@ -935,8 +933,8 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
ASSERT_OK(TryReopen(options));
uint32_t mempurge_count = 0;
uint32_t sst_count = 0;
std::atomic<uint32_t> mempurge_count{0};
std::atomic<uint32_t> sst_count{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::FlushJob:MemPurgeSuccessful",
[&](void* /*arg*/) { mempurge_count++; });
@@ -1024,8 +1022,8 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
// Check that there was no SST files created during flush.
const uint32_t EXPECTED_SST_COUNT = 0;
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
// Additional test for the iterator+memPurge.
ASSERT_OK(Put(KEY2, p_v2));
@@ -1142,8 +1140,8 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
ASSERT_OK(TryReopen(options));
uint32_t mempurge_count = 0;
uint32_t sst_count = 0;
std::atomic<uint32_t> mempurge_count{0};
std::atomic<uint32_t> sst_count{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::FlushJob:MemPurgeSuccessful",
[&](void* /*arg*/) { mempurge_count++; });
@@ -1189,8 +1187,8 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
// Check that there was no SST files created during flush.
const uint32_t EXPECTED_SST_COUNT = 0;
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
// Verify that the ConditionalUpdateCompactionFilter
// updated the values of KEY2 and KEY3, and not KEY4 and KEY5.
@@ -1233,8 +1231,8 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
ASSERT_OK(Put(0, "bar", "v2"));
ASSERT_OK(Put(1, "bar", "v2"));
ASSERT_OK(Put(1, "foo", "v3"));
uint32_t mempurge_count = 0;
uint32_t sst_count = 0;
std::atomic<uint32_t> mempurge_count{0};
std::atomic<uint32_t> sst_count{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::FlushJob:MemPurgeSuccessful",
[&](void* /*arg*/) { mempurge_count++; });
@@ -1330,10 +1328,10 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
// Check that there was no SST files created during flush.
const uint32_t EXPECTED_SST_COUNT = 0;
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
if (options.experimental_mempurge_threshold ==
std::numeric_limits<double>::max()) {
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
}
ReopenWithColumnFamilies({"default", "pikachu"}, options);
@@ -1359,7 +1357,7 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
ASSERT_OK(Put(1, RNDKEY, RNDVALUE));
}
// ASsert than there was at least one flush to storage.
EXPECT_GT(sst_count, EXPECTED_SST_COUNT);
EXPECT_GT(sst_count.exchange(0), EXPECTED_SST_COUNT);
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_EQ("v4", Get(1, "foo"));
ASSERT_EQ("v2", Get(1, "bar"));
@@ -1534,7 +1532,7 @@ TEST_F(DBFlushTest, FireOnFlushCompletedAfterCommittedResult) {
std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCallFlush:start",
{{"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables",
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitFirst"},
{"DBImpl::FlushMemTableToOutputFile:Finish",
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitSecond"}});
+11 -1
View File
@@ -1389,7 +1389,9 @@ Status DBImpl::SyncWAL() {
IOStatusCheck(io_s);
}
if (status.ok() && need_log_dir_sync) {
status = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
status = directories_.GetWalDir()->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
}
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
@@ -4288,6 +4290,14 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
if (s.ok()) {
// Retry if the file name happen to conflict with an existing one.
s = GetEnv()->RenameFile(file_name, options_file_name);
std::unique_ptr<FSDirectory> dir_obj;
if (s.ok()) {
s = fs_->NewDirectory(GetName(), IOOptions(), &dir_obj, nullptr);
}
if (s.ok()) {
s = dir_obj->FsyncWithDirOptions(IOOptions(), nullptr,
DirFsyncOptions(options_file_name));
}
}
if (s.ok()) {
InstrumentedMutexLock l(&mutex_);
+3
View File
@@ -974,6 +974,9 @@ class DBImpl : public DB {
// is only for the special test of CancelledCompactions
Status TEST_WaitForCompact(bool waitUnscheduled = false);
// Wait for any background purge
Status TEST_WaitForPurge();
// Get the background error status
Status TEST_GetBGError();
+26 -4
View File
@@ -120,7 +120,9 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
}
}
if (io_s.ok()) {
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
}
mutex_.Lock();
@@ -532,7 +534,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
// Sync on all distinct output directories.
for (auto dir : distinct_output_dirs) {
if (dir != nullptr) {
Status error_status = dir->Fsync(IOOptions(), nullptr);
Status error_status = dir->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
if (!error_status.ok()) {
s = error_status;
break;
@@ -1110,6 +1114,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
assert(temp_s.ok());
}
EnableManualCompaction();
TEST_SYNC_POINT(
"DBImpl::CompactRange:PostRefitLevel:ManualCompactionEnabled");
}
LogFlush(immutable_db_options_.info_log);
@@ -1743,6 +1749,17 @@ Status DBImpl::RunManualCompaction(
TEST_SYNC_POINT("DBImpl::RunManualCompaction:1");
InstrumentedMutexLock l(&mutex_);
if (manual_compaction_paused_ > 0) {
// Does not make sense to `AddManualCompaction()` in this scenario since
// `DisableManualCompaction()` just waited for the manual compaction queue
// to drain. So return immediately.
TEST_SYNC_POINT("DBImpl::RunManualCompaction:PausedAtStart");
manual.status =
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
manual.done = true;
return manual.status;
}
// When a manual compaction arrives, temporarily disable scheduling of
// non-manual compactions and wait until the number of scheduled compaction
// jobs drops to zero. This used to be needed to ensure that this manual
@@ -3063,8 +3080,12 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
status = Status::CompactionTooLarge();
} else {
// update statistics
RecordInHistogram(stats_, NUM_FILES_IN_SINGLE_COMPACTION,
c->inputs(0)->size());
size_t num_files = 0;
for (auto& each_level : *c->inputs()) {
num_files += each_level.files.size();
}
RecordInHistogram(stats_, NUM_FILES_IN_SINGLE_COMPACTION, num_files);
// There are three things that can change compaction score:
// 1) When flush or compaction finish. This case is covered by
// InstallSuperVersionAndScheduleWork
@@ -3384,6 +3405,7 @@ bool DBImpl::HasPendingManualCompaction() {
}
void DBImpl::AddManualCompaction(DBImpl::ManualCompactionState* m) {
assert(manual_compaction_paused_ == 0);
manual_compaction_dequeue_.push_back(m);
}
+8
View File
@@ -184,6 +184,14 @@ Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
return error_handler_.GetBGError();
}
Status DBImpl::TEST_WaitForPurge() {
InstrumentedMutexLock l(&mutex_);
while (bg_purge_scheduled_ && error_handler_.GetBGError().ok()) {
bg_cv_.Wait();
}
return error_handler_.GetBGError();
}
Status DBImpl::TEST_GetBGError() {
InstrumentedMutexLock l(&mutex_);
return error_handler_.GetBGError();
+7 -1
View File
@@ -655,7 +655,13 @@ void DBImpl::DeleteObsoleteFiles() {
mutex_.Unlock();
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
bool defer_purge = immutable_db_options_.avoid_unnecessary_blocking_io;
PurgeObsoleteFiles(job_context, defer_purge);
if (defer_purge) {
mutex_.Lock();
SchedulePurge();
mutex_.Unlock();
}
}
job_context.Clean();
mutex_.Lock();
+2 -4
View File
@@ -1694,10 +1694,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
if (impl->two_write_queues_) {
impl->log_write_mutex_.Unlock();
}
impl->DeleteObsoleteFiles();
s = impl->directories_.GetDbDir()->Fsync(IOOptions(), nullptr);
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFilesAndSyncDir");
}
if (s.ok()) {
// In WritePrepared there could be gap in sequence numbers. This breaks
@@ -1770,6 +1766,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
*dbptr = impl;
impl->opened_successfully_ = true;
impl->DeleteObsoleteFiles();
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFiles");
impl->MaybeScheduleFlushOrCompaction();
} else {
persist_options_status.PermitUncheckedError();
+3 -7
View File
@@ -156,11 +156,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
PERF_TIMER_GUARD(write_pre_and_post_process_time);
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
disable_memtable, batch_cnt, pre_release_callback);
if (!write_options.disableWAL) {
RecordTick(stats_, WRITE_WITH_WAL);
}
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
write_thread_.JoinBatchGroup(&w);
@@ -681,7 +676,6 @@ Status DBImpl::WriteImplWALOnly(
PERF_TIMER_GUARD(write_pre_and_post_process_time);
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
disable_memtable, sub_batch_cnt, pre_release_callback);
RecordTick(stats_, WRITE_WITH_WAL);
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
write_thread->JoinBatchGroup(&w);
@@ -1148,7 +1142,9 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
// We only sync WAL directory the first time WAL syncing is
// requested, so that in case users never turn on WAL sync,
// we can avoid the disk I/O in the write code path.
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
}
}
+3 -2
View File
@@ -233,7 +233,7 @@ void VerifySimilar(uint64_t a, uint64_t b, double bias) {
void VerifyTableProperties(
const TableProperties& base_tp, const TableProperties& new_tp,
double filter_size_bias = CACHE_LINE_SIZE >= 256 ? 0.15 : 0.1,
double filter_size_bias = CACHE_LINE_SIZE >= 256 ? 0.18 : 0.1,
double index_size_bias = 0.1, double data_size_bias = 0.1,
double num_data_blocks_bias = 0.05) {
VerifySimilar(base_tp.data_size, new_tp.data_size, data_size_bias);
@@ -627,7 +627,8 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
value_is_delta_encoded);
// Gives larger bias here as index block size, filter block size,
// and data block size become much harder to estimate in this test.
VerifyTableProperties(expected_tp, tp, 0.5, 0.4, 0.4, 0.25);
VerifyTableProperties(expected_tp, tp, CACHE_LINE_SIZE >= 256 ? 0.6 : 0.5,
0.4, 0.4, 0.25);
}
}
}
+1 -1
View File
@@ -690,7 +690,7 @@ TEST_F(DBSecondaryTest, SwitchToNewManifestDuringOpen) {
SyncPoint::GetInstance()->LoadDependency(
{{"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:0",
"VersionSet::ProcessManifestWrites:BeforeNewManifest"},
{"DBImpl::Open:AfterDeleteFilesAndSyncDir",
{"DBImpl::Open:AfterDeleteFiles",
"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:"
"1"}});
SyncPoint::GetInstance()->EnableProcessing();
+2 -6
View File
@@ -475,12 +475,8 @@ Options DBTestBase::GetOptions(
case kInfiniteMaxOpenFiles:
options.max_open_files = -1;
break;
case kxxHashChecksum: {
table_options.checksum = kxxHash;
break;
}
case kxxHash64Checksum: {
table_options.checksum = kxxHash64;
case kXXH3Checksum: {
table_options.checksum = kXXH3;
break;
}
case kFIFOCompaction: {
+1 -2
View File
@@ -854,7 +854,7 @@ class DBTestBase : public testing::Test {
kUniversalCompactionMultiLevel = 20,
kCompressedBlockCache = 21,
kInfiniteMaxOpenFiles = 22,
kxxHashChecksum = 23,
kXXH3Checksum = 23,
kFIFOCompaction = 24,
kOptimizeFiltersForHits = 25,
kRowCache = 26,
@@ -869,7 +869,6 @@ class DBTestBase : public testing::Test {
kBlockBasedTableWithPartitionedIndexFormat4,
kPartitionedFilterWithNewTableReaderForCompactions,
kUniversalSubcompactions,
kxxHash64Checksum,
kUnorderedWrite,
// This must be the last line
kEnd,
+49 -6
View File
@@ -117,9 +117,15 @@ class DeleteFileTest : public DBTestBase {
manifest_cnt += (type == kDescriptorFile);
}
}
ASSERT_EQ(required_log, log_cnt);
ASSERT_EQ(required_sst, sst_cnt);
ASSERT_EQ(required_manifest, manifest_cnt);
if (required_log >= 0) {
ASSERT_EQ(required_log, log_cnt);
}
if (required_sst >= 0) {
ASSERT_EQ(required_sst, sst_cnt);
}
if (required_manifest >= 0) {
ASSERT_EQ(required_manifest, manifest_cnt);
}
}
static void DoSleep(void* arg) {
@@ -264,6 +270,41 @@ TEST_F(DeleteFileTest, BackgroundPurgeIteratorTest) {
CheckFileTypeCounts(dbname_, 0, 1, 1);
}
TEST_F(DeleteFileTest, PurgeDuringOpen) {
Options options = CurrentOptions();
CheckFileTypeCounts(dbname_, -1, 0, -1);
Close();
std::unique_ptr<WritableFile> file;
ASSERT_OK(options.env->NewWritableFile(dbname_ + "/000002.sst", &file,
EnvOptions()));
ASSERT_OK(file->Close());
CheckFileTypeCounts(dbname_, -1, 1, -1);
options.avoid_unnecessary_blocking_io = false;
options.create_if_missing = false;
Reopen(options);
CheckFileTypeCounts(dbname_, -1, 0, -1);
Close();
// test background purge
options.avoid_unnecessary_blocking_io = true;
options.create_if_missing = false;
ASSERT_OK(options.env->NewWritableFile(dbname_ + "/000002.sst", &file,
EnvOptions()));
ASSERT_OK(file->Close());
CheckFileTypeCounts(dbname_, -1, 1, -1);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->LoadDependency(
{{"DeleteFileTest::PurgeDuringOpen:1", "DBImpl::BGWorkPurge:start"}});
SyncPoint::GetInstance()->EnableProcessing();
Reopen(options);
// the obsolete file is not deleted until the background purge job is ran
CheckFileTypeCounts(dbname_, -1, 1, -1);
TEST_SYNC_POINT("DeleteFileTest::PurgeDuringOpen:1");
ASSERT_OK(dbfull()->TEST_WaitForPurge());
CheckFileTypeCounts(dbname_, -1, 0, -1);
}
TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
Options options = CurrentOptions();
SetOptions(&options);
@@ -310,6 +351,11 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
do_test(false);
}
options.avoid_unnecessary_blocking_io = true;
options.create_if_missing = false;
Reopen(options);
ASSERT_OK(dbfull()->TEST_WaitForPurge());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->LoadDependency(
@@ -317,9 +363,6 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
"DBImpl::BGWorkPurge:start"}});
SyncPoint::GetInstance()->EnableProcessing();
options.avoid_unnecessary_blocking_io = true;
options.create_if_missing = false;
Reopen(options);
{
SCOPED_TRACE("avoid_unnecessary_blocking_io = true");
do_test(true);
+3 -1
View File
@@ -168,7 +168,9 @@ Status ExternalSstFileIngestionJob::Prepare(
TEST_SYNC_POINT("ExternalSstFileIngestionJob::BeforeSyncDir");
if (status.ok()) {
for (auto path_id : ingestion_path_ids) {
status = directories_->GetDataDir(path_id)->Fsync(IOOptions(), nullptr);
status = directories_->GetDataDir(path_id)->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
if (!status.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
"Failed to sync directory %" ROCKSDB_PRIszt
+3 -3
View File
@@ -10,6 +10,7 @@
#include "db/db_test_util.h"
#include "db/dbformat.h"
#include "file/filename.h"
#include "options/options_helper.h"
#include "port/port.h"
#include "port/stack_trace.h"
#include "rocksdb/sst_file_reader.h"
@@ -2383,10 +2384,9 @@ TEST_F(ExternalSSTFileTest, IngestFileWrittenWithCompressionDictionary) {
// Very slow, not worth the cost to run regularly
TEST_F(ExternalSSTFileTest, DISABLED_HugeBlockChecksum) {
int max_checksum = static_cast<int>(kxxHash64);
for (int i = 0; i <= max_checksum; ++i) {
for (auto t : GetSupportedChecksums()) {
BlockBasedTableOptions table_options;
table_options.checksum = static_cast<ChecksumType>(i);
table_options.checksum = t;
Options options = CurrentOptions();
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
+3 -1
View File
@@ -937,7 +937,9 @@ Status FlushJob::WriteLevel0Table() {
meta_.marked_for_compaction ? " (needs compaction)" : "");
if (s.ok() && output_file_directory_ != nullptr && sync_output_directory_) {
s = output_file_directory_->Fsync(IOOptions(), nullptr);
s = output_file_directory_->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
}
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table", &mems_);
db_mutex_->Lock();
+5 -3
View File
@@ -1519,9 +1519,11 @@ void InternalStats::DumpCFMapStats(
}
}
// Cumulative summary
double w_amp = (compaction_stats_sum->bytes_written +
compaction_stats_sum->bytes_written_blob) /
static_cast<double>(curr_ingest + 1);
double w_amp = (0 == curr_ingest)
? 0.0
: (compaction_stats_sum->bytes_written +
compaction_stats_sum->bytes_written_blob) /
static_cast<double>(curr_ingest);
// Stats summary across levels
std::map<LevelStatType, double> sum_stats;
PrepareLevelStats(&sum_stats, total_files, total_files_being_compacted,
+13
View File
@@ -377,6 +377,7 @@ TEST_F(EventListenerTest, MultiCF) {
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
for (int i = 1; i < 8; ++i) {
ASSERT_OK(Flush(i));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_EQ(listener->flushed_dbs_.size(), i);
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
}
@@ -771,6 +772,7 @@ class TableFileCreationListener : public EventListener {
} else {
if (idx >= 0) {
failure_[idx]++;
last_failure_ = info.status;
}
}
}
@@ -778,6 +780,7 @@ class TableFileCreationListener : public EventListener {
int started_[2];
int finished_[2];
int failure_[2];
Status last_failure_;
};
TEST_F(EventListenerTest, TableFileCreationListenersTest) {
@@ -800,6 +803,7 @@ TEST_F(EventListenerTest, TableFileCreationListenersTest) {
test_env->SetStatus(Status::NotSupported("not supported"));
ASSERT_NOK(Flush());
listener->CheckAndResetCounters(1, 1, 1, 0, 0, 0);
ASSERT_TRUE(listener->last_failure_.IsNotSupported());
test_env->SetStatus(Status::OK());
Reopen(options);
@@ -816,6 +820,14 @@ TEST_F(EventListenerTest, TableFileCreationListenersTest) {
ASSERT_OK(dbfull()->TEST_WaitForCompact());
listener->CheckAndResetCounters(0, 0, 0, 1, 1, 0);
// Verify that an empty table file that is immediately deleted gives Aborted
// status to listener.
ASSERT_OK(Put("baz", "z"));
ASSERT_OK(SingleDelete("baz"));
ASSERT_OK(Flush());
listener->CheckAndResetCounters(1, 1, 1, 0, 0, 0);
ASSERT_TRUE(listener->last_failure_.IsAborted());
ASSERT_OK(Put("foo", "aaa3"));
ASSERT_OK(Put("bar", "bbb3"));
ASSERT_OK(Flush());
@@ -824,6 +836,7 @@ TEST_F(EventListenerTest, TableFileCreationListenersTest) {
dbfull()->CompactRange(CompactRangeOptions(), &kRangeStart, &kRangeEnd));
ASSERT_NOK(dbfull()->TEST_WaitForCompact());
listener->CheckAndResetCounters(1, 1, 0, 1, 1, 1);
ASSERT_TRUE(listener->last_failure_.IsNotSupported());
Close();
}
+413 -291
View File
@@ -34,68 +34,62 @@
namespace ROCKSDB_NAMESPACE {
bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b) {
if (a->fd.largest_seqno != b->fd.largest_seqno) {
return a->fd.largest_seqno > b->fd.largest_seqno;
}
if (a->fd.smallest_seqno != b->fd.smallest_seqno) {
return a->fd.smallest_seqno > b->fd.smallest_seqno;
}
// Break ties by file number
return a->fd.GetNumber() > b->fd.GetNumber();
}
namespace {
bool BySmallestKey(FileMetaData* a, FileMetaData* b,
const InternalKeyComparator* cmp) {
int r = cmp->Compare(a->smallest, b->smallest);
if (r != 0) {
return (r < 0);
}
// Break ties by file number
return (a->fd.GetNumber() < b->fd.GetNumber());
}
} // namespace
class VersionBuilder::Rep {
private:
// Helper to sort files_ in v
// kLevel0 -- NewestFirstBySeqNo
// kLevelNon0 -- BySmallestKey
struct FileComparator {
enum SortMethod { kLevel0 = 0, kLevelNon0 = 1, } sort_method;
const InternalKeyComparator* internal_comparator;
class NewestFirstBySeqNo {
public:
bool operator()(const FileMetaData* lhs, const FileMetaData* rhs) const {
assert(lhs);
assert(rhs);
FileComparator() : internal_comparator(nullptr) {}
bool operator()(FileMetaData* f1, FileMetaData* f2) const {
switch (sort_method) {
case kLevel0:
return NewestFirstBySeqNo(f1, f2);
case kLevelNon0:
return BySmallestKey(f1, f2, internal_comparator);
if (lhs->fd.largest_seqno != rhs->fd.largest_seqno) {
return lhs->fd.largest_seqno > rhs->fd.largest_seqno;
}
assert(false);
return false;
if (lhs->fd.smallest_seqno != rhs->fd.smallest_seqno) {
return lhs->fd.smallest_seqno > rhs->fd.smallest_seqno;
}
// Break ties by file number
return lhs->fd.GetNumber() > rhs->fd.GetNumber();
}
};
class BySmallestKey {
public:
explicit BySmallestKey(const InternalKeyComparator* cmp) : cmp_(cmp) {}
bool operator()(const FileMetaData* lhs, const FileMetaData* rhs) const {
assert(lhs);
assert(rhs);
assert(cmp_);
const int r = cmp_->Compare(lhs->smallest, rhs->smallest);
if (r != 0) {
return (r < 0);
}
// Break ties by file number
return (lhs->fd.GetNumber() < rhs->fd.GetNumber());
}
private:
const InternalKeyComparator* cmp_;
};
struct LevelState {
std::unordered_set<uint64_t> deleted_files;
// Map from file number to file meta data.
std::unordered_map<uint64_t, FileMetaData*> added_files;
};
// A class that represents the accumulated changes (like additional garbage or
// newly linked/unlinked SST files) for a given blob file after applying a
// series of VersionEdits.
class BlobFileMetaDataDelta {
public:
bool IsEmpty() const {
return !shared_meta_ && !additional_garbage_count_ &&
!additional_garbage_bytes_ && newly_linked_ssts_.empty() &&
newly_unlinked_ssts_.empty();
}
std::shared_ptr<SharedBlobFileMetaData> GetSharedMeta() const {
return shared_meta_;
return !additional_garbage_count_ && !additional_garbage_bytes_ &&
newly_linked_ssts_.empty() && newly_unlinked_ssts_.empty();
}
uint64_t GetAdditionalGarbageCount() const {
@@ -114,13 +108,6 @@ class VersionBuilder::Rep {
return newly_unlinked_ssts_;
}
void SetSharedMeta(std::shared_ptr<SharedBlobFileMetaData> shared_meta) {
assert(!shared_meta_);
assert(shared_meta);
shared_meta_ = std::move(shared_meta);
}
void AddGarbage(uint64_t count, uint64_t bytes) {
additional_garbage_count_ += count;
additional_garbage_bytes_ += bytes;
@@ -159,13 +146,86 @@ class VersionBuilder::Rep {
}
private:
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
uint64_t additional_garbage_count_ = 0;
uint64_t additional_garbage_bytes_ = 0;
std::unordered_set<uint64_t> newly_linked_ssts_;
std::unordered_set<uint64_t> newly_unlinked_ssts_;
};
// A class that represents the state of a blob file after applying a series of
// VersionEdits. In addition to the resulting state, it also contains the
// delta (see BlobFileMetaDataDelta above). The resulting state can be used to
// identify obsolete blob files, while the delta makes it possible to
// efficiently detect trivial moves.
class MutableBlobFileMetaData {
public:
// To be used for brand new blob files
explicit MutableBlobFileMetaData(
std::shared_ptr<SharedBlobFileMetaData>&& shared_meta)
: shared_meta_(std::move(shared_meta)) {}
// To be used for pre-existing blob files
explicit MutableBlobFileMetaData(
const std::shared_ptr<BlobFileMetaData>& meta)
: shared_meta_(meta->GetSharedMeta()),
linked_ssts_(meta->GetLinkedSsts()),
garbage_blob_count_(meta->GetGarbageBlobCount()),
garbage_blob_bytes_(meta->GetGarbageBlobBytes()) {}
const std::shared_ptr<SharedBlobFileMetaData>& GetSharedMeta() const {
return shared_meta_;
}
bool HasDelta() const { return !delta_.IsEmpty(); }
const std::unordered_set<uint64_t>& GetLinkedSsts() const {
return linked_ssts_;
}
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; }
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; }
bool AddGarbage(uint64_t count, uint64_t bytes) {
assert(shared_meta_);
if (garbage_blob_count_ + count > shared_meta_->GetTotalBlobCount() ||
garbage_blob_bytes_ + bytes > shared_meta_->GetTotalBlobBytes()) {
return false;
}
delta_.AddGarbage(count, bytes);
garbage_blob_count_ += count;
garbage_blob_bytes_ += bytes;
return true;
}
void LinkSst(uint64_t sst_file_number) {
delta_.LinkSst(sst_file_number);
assert(linked_ssts_.find(sst_file_number) == linked_ssts_.end());
linked_ssts_.emplace(sst_file_number);
}
void UnlinkSst(uint64_t sst_file_number) {
delta_.UnlinkSst(sst_file_number);
assert(linked_ssts_.find(sst_file_number) != linked_ssts_.end());
linked_ssts_.erase(sst_file_number);
}
private:
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
// Accumulated changes
BlobFileMetaDataDelta delta_;
// Resulting state after applying the changes
BlobFileMetaData::LinkedSsts linked_ssts_;
uint64_t garbage_blob_count_ = 0;
uint64_t garbage_blob_bytes_ = 0;
};
const FileOptions& file_options_;
const ImmutableCFOptions* const ioptions_;
TableCache* table_cache_;
@@ -183,11 +243,12 @@ class VersionBuilder::Rep {
bool has_invalid_levels_;
// Current levels of table files affected by additions/deletions.
std::unordered_map<uint64_t, int> table_file_levels_;
FileComparator level_zero_cmp_;
FileComparator level_nonzero_cmp_;
NewestFirstBySeqNo level_zero_cmp_;
BySmallestKey level_nonzero_cmp_;
// Metadata delta for all blob files affected by the series of version edits.
std::map<uint64_t, BlobFileMetaDataDelta> blob_file_meta_deltas_;
// Mutable metadata objects for all blob files affected by the series of
// version edits.
std::map<uint64_t, MutableBlobFileMetaData> mutable_blob_file_metas_;
public:
Rep(const FileOptions& file_options, const ImmutableCFOptions* ioptions,
@@ -199,14 +260,11 @@ class VersionBuilder::Rep {
base_vstorage_(base_vstorage),
version_set_(version_set),
num_levels_(base_vstorage->num_levels()),
has_invalid_levels_(false) {
has_invalid_levels_(false),
level_nonzero_cmp_(base_vstorage_->InternalComparator()) {
assert(ioptions_);
levels_ = new LevelState[num_levels_];
level_zero_cmp_.sort_method = FileComparator::kLevel0;
level_nonzero_cmp_.sort_method = FileComparator::kLevelNon0;
level_nonzero_cmp_.internal_comparator =
base_vstorage_->InternalComparator();
}
~Rep() {
@@ -232,29 +290,10 @@ class VersionBuilder::Rep {
}
}
bool IsBlobFileInVersion(uint64_t blob_file_number) const {
auto delta_it = blob_file_meta_deltas_.find(blob_file_number);
if (delta_it != blob_file_meta_deltas_.end()) {
if (delta_it->second.GetSharedMeta()) {
return true;
}
}
assert(base_vstorage_);
const auto& base_blob_files = base_vstorage_->GetBlobFiles();
auto base_it = base_blob_files.find(blob_file_number);
if (base_it != base_blob_files.end()) {
assert(base_it->second);
assert(base_it->second->GetSharedMeta());
return true;
}
return false;
}
// Mapping used for checking the consistency of links between SST files and
// blob files. It is built using the forward links (table file -> blob file),
// and is subsequently compared with the inverse mapping stored in the
// BlobFileMetaData objects.
using ExpectedLinkedSsts =
std::unordered_map<uint64_t, BlobFileMetaData::LinkedSsts>;
@@ -270,91 +309,155 @@ class VersionBuilder::Rep {
(*expected_linked_ssts)[blob_file_number].emplace(table_file_number);
}
Status CheckConsistencyDetails(VersionStorageInfo* vstorage) {
// Make sure the files are sorted correctly and that the links between
// table files and blob files are consistent. The latter is checked using
// the following mapping, which is built using the forward links
// (table file -> blob file), and is subsequently compared with the inverse
// mapping stored in the BlobFileMetaData objects.
template <class Checker>
Status CheckConsistencyDetailsForLevel(
const VersionStorageInfo* vstorage, int level, Checker checker,
const std::string& sync_point,
ExpectedLinkedSsts* expected_linked_ssts) const {
#ifdef NDEBUG
(void)sync_point;
#endif
assert(vstorage);
assert(level >= 0 && level < num_levels_);
assert(expected_linked_ssts);
const auto& level_files = vstorage->LevelFiles(level);
if (level_files.empty()) {
return Status::OK();
}
assert(level_files[0]);
UpdateExpectedLinkedSsts(level_files[0]->fd.GetNumber(),
level_files[0]->oldest_blob_file_number,
expected_linked_ssts);
for (size_t i = 1; i < level_files.size(); ++i) {
assert(level_files[i]);
UpdateExpectedLinkedSsts(level_files[i]->fd.GetNumber(),
level_files[i]->oldest_blob_file_number,
expected_linked_ssts);
auto lhs = level_files[i - 1];
auto rhs = level_files[i];
#ifndef NDEBUG
auto pair = std::make_pair(&lhs, &rhs);
TEST_SYNC_POINT_CALLBACK(sync_point, &pair);
#endif
const Status s = checker(lhs, rhs);
if (!s.ok()) {
return s;
}
}
return Status::OK();
}
// Make sure table files are sorted correctly and that the links between
// table files and blob files are consistent.
Status CheckConsistencyDetails(const VersionStorageInfo* vstorage) const {
assert(vstorage);
ExpectedLinkedSsts expected_linked_ssts;
for (int level = 0; level < num_levels_; level++) {
auto& level_files = vstorage->LevelFiles(level);
if (num_levels_ > 0) {
// Check L0
{
auto l0_checker = [this](const FileMetaData* lhs,
const FileMetaData* rhs) {
assert(lhs);
assert(rhs);
if (level_files.empty()) {
continue;
if (!level_zero_cmp_(lhs, rhs)) {
std::ostringstream oss;
oss << "L0 files are not sorted properly: files #"
<< lhs->fd.GetNumber() << ", #" << rhs->fd.GetNumber();
return Status::Corruption("VersionBuilder", oss.str());
}
if (rhs->fd.smallest_seqno == rhs->fd.largest_seqno) {
// This is an external file that we ingested
const SequenceNumber external_file_seqno = rhs->fd.smallest_seqno;
if (!(external_file_seqno < lhs->fd.largest_seqno ||
external_file_seqno == 0)) {
std::ostringstream oss;
oss << "L0 file #" << lhs->fd.GetNumber() << " with seqno "
<< lhs->fd.smallest_seqno << ' ' << lhs->fd.largest_seqno
<< " vs. file #" << rhs->fd.GetNumber()
<< " with global_seqno " << external_file_seqno;
return Status::Corruption("VersionBuilder", oss.str());
}
} else if (lhs->fd.smallest_seqno <= rhs->fd.smallest_seqno) {
std::ostringstream oss;
oss << "L0 file #" << lhs->fd.GetNumber() << " with seqno "
<< lhs->fd.smallest_seqno << ' ' << lhs->fd.largest_seqno
<< " vs. file #" << rhs->fd.GetNumber() << " with seqno "
<< rhs->fd.smallest_seqno << ' ' << rhs->fd.largest_seqno;
return Status::Corruption("VersionBuilder", oss.str());
}
return Status::OK();
};
const Status s = CheckConsistencyDetailsForLevel(
vstorage, /* level */ 0, l0_checker,
"VersionBuilder::CheckConsistency0", &expected_linked_ssts);
if (!s.ok()) {
return s;
}
}
assert(level_files[0]);
UpdateExpectedLinkedSsts(level_files[0]->fd.GetNumber(),
level_files[0]->oldest_blob_file_number,
&expected_linked_ssts);
for (size_t i = 1; i < level_files.size(); i++) {
assert(level_files[i]);
UpdateExpectedLinkedSsts(level_files[i]->fd.GetNumber(),
level_files[i]->oldest_blob_file_number,
&expected_linked_ssts);
// Check L1 and up
const InternalKeyComparator* const icmp = vstorage->InternalComparator();
assert(icmp);
auto f1 = level_files[i - 1];
auto f2 = level_files[i];
if (level == 0) {
#ifndef NDEBUG
auto pair = std::make_pair(&f1, &f2);
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency0", &pair);
#endif
if (!level_zero_cmp_(f1, f2)) {
return Status::Corruption("L0 files are not sorted properly");
for (int level = 1; level < num_levels_; ++level) {
auto checker = [this, level, icmp](const FileMetaData* lhs,
const FileMetaData* rhs) {
assert(lhs);
assert(rhs);
if (!level_nonzero_cmp_(lhs, rhs)) {
std::ostringstream oss;
oss << 'L' << level << " files are not sorted properly: files #"
<< lhs->fd.GetNumber() << ", #" << rhs->fd.GetNumber();
return Status::Corruption("VersionBuilder", oss.str());
}
if (f2->fd.smallest_seqno == f2->fd.largest_seqno) {
// This is an external file that we ingested
SequenceNumber external_file_seqno = f2->fd.smallest_seqno;
if (!(external_file_seqno < f1->fd.largest_seqno ||
external_file_seqno == 0)) {
return Status::Corruption(
"L0 file with seqno " + ToString(f1->fd.smallest_seqno) +
" " + ToString(f1->fd.largest_seqno) +
" vs. file with global_seqno" +
ToString(external_file_seqno) + " with fileNumber " +
ToString(f1->fd.GetNumber()));
}
} else if (f1->fd.smallest_seqno <= f2->fd.smallest_seqno) {
return Status::Corruption("L0 files seqno " +
ToString(f1->fd.smallest_seqno) + " " +
ToString(f1->fd.largest_seqno) + " " +
ToString(f1->fd.GetNumber()) + " vs. " +
ToString(f2->fd.smallest_seqno) + " " +
ToString(f2->fd.largest_seqno) + " " +
ToString(f2->fd.GetNumber()));
}
} else {
#ifndef NDEBUG
auto pair = std::make_pair(&f1, &f2);
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency1", &pair);
#endif
if (!level_nonzero_cmp_(f1, f2)) {
return Status::Corruption(
"L" + ToString(level) +
" files are not sorted properly: files #" +
ToString(f1->fd.GetNumber()) + ", #" +
ToString(f2->fd.GetNumber()));
// Make sure there is no overlap in level
if (icmp->Compare(lhs->largest, rhs->smallest) >= 0) {
std::ostringstream oss;
oss << 'L' << level << " has overlapping ranges: file #"
<< lhs->fd.GetNumber()
<< " largest key: " << lhs->largest.DebugString(true)
<< " vs. file #" << rhs->fd.GetNumber()
<< " smallest key: " << rhs->smallest.DebugString(true);
return Status::Corruption("VersionBuilder", oss.str());
}
// Make sure there is no overlap in levels > 0
if (vstorage->InternalComparator()->Compare(f1->largest,
f2->smallest) >= 0) {
return Status::Corruption(
"L" + ToString(level) + " have overlapping ranges: file #" +
ToString(f1->fd.GetNumber()) +
" largest key: " + (f1->largest).DebugString(true) +
" vs. file #" + ToString(f2->fd.GetNumber()) +
" smallest key: " + (f2->smallest).DebugString(true));
}
return Status::OK();
};
const Status s = CheckConsistencyDetailsForLevel(
vstorage, level, checker, "VersionBuilder::CheckConsistency1",
&expected_linked_ssts);
if (!s.ok()) {
return s;
}
}
}
// Make sure that all blob files in the version have non-garbage data.
// Make sure that all blob files in the version have non-garbage data and
// the links between them and the table files are consistent.
const auto& blob_files = vstorage->GetBlobFiles();
for (const auto& pair : blob_files) {
const uint64_t blob_file_number = pair.first;
@@ -386,7 +489,9 @@ class VersionBuilder::Rep {
return ret_s;
}
Status CheckConsistency(VersionStorageInfo* vstorage) {
Status CheckConsistency(const VersionStorageInfo* vstorage) const {
assert(vstorage);
// Always run consistency checks in debug build
#ifdef NDEBUG
if (!vstorage->force_consistency_checks()) {
@@ -426,6 +531,49 @@ class VersionBuilder::Rep {
return true;
}
bool IsBlobFileInVersion(uint64_t blob_file_number) const {
auto mutable_it = mutable_blob_file_metas_.find(blob_file_number);
if (mutable_it != mutable_blob_file_metas_.end()) {
return true;
}
assert(base_vstorage_);
const auto& base_blob_files = base_vstorage_->GetBlobFiles();
auto base_it = base_blob_files.find(blob_file_number);
if (base_it != base_blob_files.end()) {
return true;
}
return false;
}
MutableBlobFileMetaData* GetOrCreateMutableBlobFileMetaData(
uint64_t blob_file_number) {
auto mutable_it = mutable_blob_file_metas_.find(blob_file_number);
if (mutable_it != mutable_blob_file_metas_.end()) {
return &mutable_it->second;
}
assert(base_vstorage_);
const auto& base_blob_files = base_vstorage_->GetBlobFiles();
auto base_it = base_blob_files.find(blob_file_number);
if (base_it != base_blob_files.end()) {
assert(base_it->second);
mutable_it = mutable_blob_file_metas_
.emplace(blob_file_number,
MutableBlobFileMetaData(base_it->second))
.first;
return &mutable_it->second;
}
return nullptr;
}
Status ApplyBlobFileAddition(const BlobFileAddition& blob_file_addition) {
const uint64_t blob_file_number = blob_file_addition.GetBlobFileNumber();
@@ -460,8 +608,8 @@ class VersionBuilder::Rep {
blob_file_addition.GetChecksumMethod(),
blob_file_addition.GetChecksumValue(), deleter);
blob_file_meta_deltas_[blob_file_number].SetSharedMeta(
std::move(shared_meta));
mutable_blob_file_metas_.emplace(
blob_file_number, MutableBlobFileMetaData(std::move(shared_meta)));
return Status::OK();
}
@@ -469,16 +617,22 @@ class VersionBuilder::Rep {
Status ApplyBlobFileGarbage(const BlobFileGarbage& blob_file_garbage) {
const uint64_t blob_file_number = blob_file_garbage.GetBlobFileNumber();
if (!IsBlobFileInVersion(blob_file_number)) {
MutableBlobFileMetaData* const mutable_meta =
GetOrCreateMutableBlobFileMetaData(blob_file_number);
if (!mutable_meta) {
std::ostringstream oss;
oss << "Blob file #" << blob_file_number << " not found";
return Status::Corruption("VersionBuilder", oss.str());
}
blob_file_meta_deltas_[blob_file_number].AddGarbage(
blob_file_garbage.GetGarbageBlobCount(),
blob_file_garbage.GetGarbageBlobBytes());
if (!mutable_meta->AddGarbage(blob_file_garbage.GetGarbageBlobCount(),
blob_file_garbage.GetGarbageBlobBytes())) {
std::ostringstream oss;
oss << "Garbage overflow for blob file #" << blob_file_number;
return Status::Corruption("VersionBuilder", oss.str());
}
return Status::OK();
}
@@ -573,9 +727,12 @@ class VersionBuilder::Rep {
const uint64_t blob_file_number =
GetOldestBlobFileNumberForTableFile(level, file_number);
if (blob_file_number != kInvalidBlobFileNumber &&
IsBlobFileInVersion(blob_file_number)) {
blob_file_meta_deltas_[blob_file_number].UnlinkSst(file_number);
if (blob_file_number != kInvalidBlobFileNumber) {
MutableBlobFileMetaData* const mutable_meta =
GetOrCreateMutableBlobFileMetaData(blob_file_number);
if (mutable_meta) {
mutable_meta->UnlinkSst(file_number);
}
}
auto& level_state = levels_[level];
@@ -640,9 +797,12 @@ class VersionBuilder::Rep {
const uint64_t blob_file_number = f->oldest_blob_file_number;
if (blob_file_number != kInvalidBlobFileNumber &&
IsBlobFileInVersion(blob_file_number)) {
blob_file_meta_deltas_[blob_file_number].LinkSst(file_number);
if (blob_file_number != kInvalidBlobFileNumber) {
MutableBlobFileMetaData* const mutable_meta =
GetOrCreateMutableBlobFileMetaData(blob_file_number);
if (mutable_meta) {
mutable_meta->LinkSst(file_number);
}
}
table_file_levels_[file_number] = level;
@@ -651,7 +811,7 @@ class VersionBuilder::Rep {
}
// Apply all of the edits in *edit to the current state.
Status Apply(VersionEdit* edit) {
Status Apply(const VersionEdit* edit) {
{
const Status s = CheckConsistency(base_vstorage_);
if (!s.ok()) {
@@ -704,65 +864,11 @@ class VersionBuilder::Rep {
return Status::OK();
}
static BlobFileMetaData::LinkedSsts ApplyLinkedSstChanges(
const BlobFileMetaData::LinkedSsts& base,
const std::unordered_set<uint64_t>& newly_linked,
const std::unordered_set<uint64_t>& newly_unlinked) {
BlobFileMetaData::LinkedSsts result(base);
for (uint64_t sst_file_number : newly_unlinked) {
assert(result.find(sst_file_number) != result.end());
result.erase(sst_file_number);
}
for (uint64_t sst_file_number : newly_linked) {
assert(result.find(sst_file_number) == result.end());
result.emplace(sst_file_number);
}
return result;
}
static std::shared_ptr<BlobFileMetaData> CreateMetaDataForNewBlobFile(
const BlobFileMetaDataDelta& delta) {
auto shared_meta = delta.GetSharedMeta();
assert(shared_meta);
assert(delta.GetNewlyUnlinkedSsts().empty());
auto meta = BlobFileMetaData::Create(
std::move(shared_meta), delta.GetNewlyLinkedSsts(),
delta.GetAdditionalGarbageCount(), delta.GetAdditionalGarbageBytes());
return meta;
}
static std::shared_ptr<BlobFileMetaData>
GetOrCreateMetaDataForExistingBlobFile(
const std::shared_ptr<BlobFileMetaData>& base_meta,
const BlobFileMetaDataDelta& delta) {
assert(base_meta);
assert(!delta.GetSharedMeta());
if (delta.IsEmpty()) {
return base_meta;
}
auto shared_meta = base_meta->GetSharedMeta();
assert(shared_meta);
auto linked_ssts = ApplyLinkedSstChanges(base_meta->GetLinkedSsts(),
delta.GetNewlyLinkedSsts(),
delta.GetNewlyUnlinkedSsts());
auto meta = BlobFileMetaData::Create(
std::move(shared_meta), std::move(linked_ssts),
base_meta->GetGarbageBlobCount() + delta.GetAdditionalGarbageCount(),
base_meta->GetGarbageBlobBytes() + delta.GetAdditionalGarbageBytes());
return meta;
static std::shared_ptr<BlobFileMetaData> CreateBlobFileMetaData(
const MutableBlobFileMetaData& mutable_meta) {
return BlobFileMetaData::Create(
mutable_meta.GetSharedMeta(), mutable_meta.GetLinkedSsts(),
mutable_meta.GetGarbageBlobCount(), mutable_meta.GetGarbageBlobBytes());
}
// Add the blob file specified by meta to *vstorage if it is determined to
@@ -799,39 +905,52 @@ class VersionBuilder::Rep {
auto base_it = base_blob_files.begin();
const auto base_it_end = base_blob_files.end();
auto delta_it = blob_file_meta_deltas_.begin();
const auto delta_it_end = blob_file_meta_deltas_.end();
auto mutable_it = mutable_blob_file_metas_.begin();
const auto mutable_it_end = mutable_blob_file_metas_.end();
while (base_it != base_it_end && delta_it != delta_it_end) {
while (base_it != base_it_end && mutable_it != mutable_it_end) {
const uint64_t base_blob_file_number = base_it->first;
const uint64_t delta_blob_file_number = delta_it->first;
const uint64_t mutable_blob_file_number = mutable_it->first;
if (base_blob_file_number < delta_blob_file_number) {
if (base_blob_file_number < mutable_blob_file_number) {
const auto& base_meta = base_it->second;
AddBlobFileIfNeeded(vstorage, base_meta, &found_first_non_empty);
++base_it;
} else if (delta_blob_file_number < base_blob_file_number) {
const auto& delta = delta_it->second;
} else if (mutable_blob_file_number < base_blob_file_number) {
const auto& mutable_meta = mutable_it->second;
auto meta = CreateMetaDataForNewBlobFile(delta);
auto meta = CreateBlobFileMetaData(mutable_meta);
AddBlobFileIfNeeded(vstorage, meta, &found_first_non_empty);
++delta_it;
++mutable_it;
} else {
assert(base_blob_file_number == delta_blob_file_number);
assert(base_blob_file_number == mutable_blob_file_number);
const auto& base_meta = base_it->second;
const auto& delta = delta_it->second;
const auto& mutable_meta = mutable_it->second;
auto meta = GetOrCreateMetaDataForExistingBlobFile(base_meta, delta);
assert(base_meta);
assert(base_meta->GetSharedMeta() == mutable_meta.GetSharedMeta());
AddBlobFileIfNeeded(vstorage, meta, &found_first_non_empty);
if (!mutable_meta.HasDelta()) {
assert(base_meta->GetGarbageBlobCount() ==
mutable_meta.GetGarbageBlobCount());
assert(base_meta->GetGarbageBlobBytes() ==
mutable_meta.GetGarbageBlobBytes());
assert(base_meta->GetLinkedSsts() == mutable_meta.GetLinkedSsts());
AddBlobFileIfNeeded(vstorage, base_meta, &found_first_non_empty);
} else {
auto meta = CreateBlobFileMetaData(mutable_meta);
AddBlobFileIfNeeded(vstorage, meta, &found_first_non_empty);
}
++base_it;
++delta_it;
++mutable_it;
}
}
@@ -843,18 +962,19 @@ class VersionBuilder::Rep {
++base_it;
}
while (delta_it != delta_it_end) {
const auto& delta = delta_it->second;
while (mutable_it != mutable_it_end) {
const auto& mutable_meta = mutable_it->second;
auto meta = CreateMetaDataForNewBlobFile(delta);
auto meta = CreateBlobFileMetaData(mutable_meta);
AddBlobFileIfNeeded(vstorage, meta, &found_first_non_empty);
++delta_it;
++mutable_it;
}
}
void MaybeAddFile(VersionStorageInfo* vstorage, int level, FileMetaData* f) {
void MaybeAddFile(VersionStorageInfo* vstorage, int level,
FileMetaData* f) const {
const uint64_t file_number = f->fd.GetNumber();
const auto& level_state = levels_[level];
@@ -879,52 +999,52 @@ class VersionBuilder::Rep {
}
}
void SaveSSTFilesTo(VersionStorageInfo* vstorage) {
for (int level = 0; level < num_levels_; level++) {
const auto& cmp = (level == 0) ? level_zero_cmp_ : level_nonzero_cmp_;
// Merge the set of added files with the set of pre-existing files.
// Drop any deleted files. Store the result in *v.
const auto& base_files = base_vstorage_->LevelFiles(level);
const auto& unordered_added_files = levels_[level].added_files;
vstorage->Reserve(level,
base_files.size() + unordered_added_files.size());
template <class Cmp>
void SaveSSTFilesTo(VersionStorageInfo* vstorage, int level, Cmp cmp) const {
// Merge the set of added files with the set of pre-existing files.
// Drop any deleted files. Store the result in *vstorage.
const auto& base_files = base_vstorage_->LevelFiles(level);
const auto& unordered_added_files = levels_[level].added_files;
vstorage->Reserve(level, base_files.size() + unordered_added_files.size());
// Sort added files for the level.
std::vector<FileMetaData*> added_files;
added_files.reserve(unordered_added_files.size());
for (const auto& pair : unordered_added_files) {
added_files.push_back(pair.second);
}
std::sort(added_files.begin(), added_files.end(), cmp);
// Sort added files for the level.
std::vector<FileMetaData*> added_files;
added_files.reserve(unordered_added_files.size());
for (const auto& pair : unordered_added_files) {
added_files.push_back(pair.second);
}
std::sort(added_files.begin(), added_files.end(), cmp);
#ifndef NDEBUG
FileMetaData* prev_added_file = nullptr;
for (const auto& added : added_files) {
if (level > 0 && prev_added_file != nullptr) {
assert(base_vstorage_->InternalComparator()->Compare(
prev_added_file->smallest, added->smallest) <= 0);
}
prev_added_file = added;
}
#endif
auto base_iter = base_files.begin();
auto base_end = base_files.end();
auto added_iter = added_files.begin();
auto added_end = added_files.end();
while (added_iter != added_end || base_iter != base_end) {
if (base_iter == base_end ||
(added_iter != added_end && cmp(*added_iter, *base_iter))) {
MaybeAddFile(vstorage, level, *added_iter++);
} else {
MaybeAddFile(vstorage, level, *base_iter++);
}
auto base_iter = base_files.begin();
auto base_end = base_files.end();
auto added_iter = added_files.begin();
auto added_end = added_files.end();
while (added_iter != added_end || base_iter != base_end) {
if (base_iter == base_end ||
(added_iter != added_end && cmp(*added_iter, *base_iter))) {
MaybeAddFile(vstorage, level, *added_iter++);
} else {
MaybeAddFile(vstorage, level, *base_iter++);
}
}
}
void SaveSSTFilesTo(VersionStorageInfo* vstorage) const {
assert(vstorage);
if (!num_levels_) {
return;
}
SaveSSTFilesTo(vstorage, /* level */ 0, level_zero_cmp_);
for (int level = 1; level < num_levels_; ++level) {
SaveSSTFilesTo(vstorage, level, level_nonzero_cmp_);
}
}
// Save the current state in *vstorage.
Status SaveTo(VersionStorageInfo* vstorage) {
Status SaveTo(VersionStorageInfo* vstorage) const {
Status s = CheckConsistency(base_vstorage_);
if (!s.ok()) {
return s;
@@ -1061,9 +1181,11 @@ bool VersionBuilder::CheckConsistencyForNumLevels() {
return rep_->CheckConsistencyForNumLevels();
}
Status VersionBuilder::Apply(VersionEdit* edit) { return rep_->Apply(edit); }
Status VersionBuilder::Apply(const VersionEdit* edit) {
return rep_->Apply(edit);
}
Status VersionBuilder::SaveTo(VersionStorageInfo* vstorage) {
Status VersionBuilder::SaveTo(VersionStorageInfo* vstorage) const {
return rep_->SaveTo(vstorage);
}
+2 -3
View File
@@ -37,8 +37,8 @@ class VersionBuilder {
~VersionBuilder();
bool CheckConsistencyForNumLevels();
Status Apply(VersionEdit* edit);
Status SaveTo(VersionStorageInfo* vstorage);
Status Apply(const VersionEdit* edit);
Status SaveTo(VersionStorageInfo* vstorage) const;
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache,
bool is_initial_load,
@@ -66,5 +66,4 @@ class BaseReferencedVersionBuilder {
Version* version_;
};
extern bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b);
} // namespace ROCKSDB_NAMESPACE
+64 -1
View File
@@ -149,7 +149,7 @@ class VersionBuilderTest : public testing::Test {
}
void UpdateVersionStorageInfo() {
vstorage_.UpdateFilesByCompactionPri(ioptions_.compaction_pri);
vstorage_.UpdateFilesByCompactionPri(ioptions_, mutable_cf_options_);
vstorage_.UpdateNumNonEmptyLevels();
vstorage_.GenerateFileIndexer();
vstorage_.GenerateLevelFilesBrief();
@@ -923,6 +923,69 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileNotFound) {
ASSERT_TRUE(std::strstr(s.getState(), "Blob file #1234 not found"));
}
TEST_F(VersionBuilderTest, BlobFileGarbageOverflow) {
// Test that VersionEdits that would result in the count/total size of garbage
// exceeding the count/total size of all blobs are rejected.
EnvOptions env_options;
constexpr TableCache* table_cache = nullptr;
constexpr VersionSet* version_set = nullptr;
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
version_set);
VersionEdit addition;
constexpr uint64_t blob_file_number = 1234;
constexpr uint64_t total_blob_count = 5678;
constexpr uint64_t total_blob_bytes = 999999;
constexpr char checksum_method[] = "SHA1";
constexpr char checksum_value[] =
"\xbd\xb7\xf3\x4a\x59\xdf\xa1\x59\x2c\xe7\xf5\x2e\x99\xf9\x8c\x57\x0c\x52"
"\x5c\xbd";
addition.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
checksum_method, checksum_value);
// Add dummy table file to ensure the blob file is referenced.
constexpr uint64_t table_file_number = 1;
AddDummyFileToEdit(&addition, table_file_number, blob_file_number);
ASSERT_OK(builder.Apply(&addition));
{
// Garbage blob count overflow
constexpr uint64_t garbage_blob_count = 5679;
constexpr uint64_t garbage_blob_bytes = 999999;
VersionEdit garbage;
garbage.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
garbage_blob_bytes);
const Status s = builder.Apply(&garbage);
ASSERT_TRUE(s.IsCorruption());
ASSERT_TRUE(
std::strstr(s.getState(), "Garbage overflow for blob file #1234"));
}
{
// Garbage blob bytes overflow
constexpr uint64_t garbage_blob_count = 5678;
constexpr uint64_t garbage_blob_bytes = 1000000;
VersionEdit garbage;
garbage.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
garbage_blob_bytes);
const Status s = builder.Apply(&garbage);
ASSERT_TRUE(s.IsCorruption());
ASSERT_TRUE(
std::strstr(s.getState(), "Garbage overflow for blob file #1234"));
}
}
TEST_F(VersionBuilderTest, SaveBlobFilesTo) {
// Add three blob files to base version.
for (uint64_t i = 3; i >= 1; --i) {
+34 -48
View File
@@ -20,12 +20,13 @@
#include <unordered_map>
#include <vector>
#include "compaction/compaction.h"
#include "db/blob/blob_fetcher.h"
#include "db/blob/blob_file_cache.h"
#include "db/blob/blob_file_reader.h"
#include "db/blob/blob_index.h"
#include "db/blob/blob_log_format.h"
#include "db/compaction/compaction.h"
#include "db/compaction/file_pri.h"
#include "db/internal_stats.h"
#include "db/log_reader.h"
#include "db/log_writer.h"
@@ -119,10 +120,9 @@ Status OverlapWithIterator(const Comparator* ucmp,
// are MergeInProgress).
class FilePicker {
public:
FilePicker(std::vector<FileMetaData*>* files, const Slice& user_key,
const Slice& ikey, autovector<LevelFilesBrief>* file_levels,
unsigned int num_levels, FileIndexer* file_indexer,
const Comparator* user_comparator,
FilePicker(const Slice& user_key, const Slice& ikey,
autovector<LevelFilesBrief>* file_levels, unsigned int num_levels,
FileIndexer* file_indexer, const Comparator* user_comparator,
const InternalKeyComparator* internal_comparator)
: num_levels_(num_levels),
curr_level_(static_cast<unsigned int>(-1)),
@@ -130,9 +130,6 @@ class FilePicker {
hit_file_level_(static_cast<unsigned int>(-1)),
search_left_bound_(0),
search_right_bound_(FileIndexer::kLevelMaxIndex),
#ifndef NDEBUG
files_(files),
#endif
level_files_brief_(file_levels),
is_hit_file_last_in_level_(false),
curr_file_level_(nullptr),
@@ -141,9 +138,6 @@ class FilePicker {
file_indexer_(file_indexer),
user_comparator_(user_comparator),
internal_comparator_(internal_comparator) {
#ifdef NDEBUG
(void)files;
#endif
// Setup member variables to search first level.
search_ended_ = !PrepareNextLevel();
if (!search_ended_) {
@@ -213,23 +207,7 @@ class FilePicker {
}
}
}
#ifndef NDEBUG
// Sanity check to make sure that the files are correctly sorted
if (prev_file_) {
if (curr_level_ != 0) {
int comp_sign = internal_comparator_->Compare(
prev_file_->largest_key, f->smallest_key);
assert(comp_sign < 0);
} else {
// level == 0, the current file cannot be newer than the previous
// one. Use compressed data structure, has no attribute seqNo
assert(curr_index_in_curr_level_ > 0);
assert(!NewestFirstBySeqNo(files_[0][curr_index_in_curr_level_],
files_[0][curr_index_in_curr_level_-1]));
}
}
prev_file_ = f;
#endif
returned_file_level_ = curr_level_;
if (curr_level_ > 0 && cmp_largest < 0) {
// No more files to search in this level.
@@ -261,9 +239,6 @@ class FilePicker {
unsigned int hit_file_level_;
int32_t search_left_bound_;
int32_t search_right_bound_;
#ifndef NDEBUG
std::vector<FileMetaData*>* files_;
#endif
autovector<LevelFilesBrief>* level_files_brief_;
bool search_ended_;
bool is_hit_file_last_in_level_;
@@ -275,9 +250,6 @@ class FilePicker {
FileIndexer* file_indexer_;
const Comparator* user_comparator_;
const InternalKeyComparator* internal_comparator_;
#ifndef NDEBUG
FdWithKeyRange* prev_file_;
#endif
// Setup local variables to search next level.
// Returns false if there are no more levels to search.
@@ -347,9 +319,7 @@ class FilePicker {
}
start_index_in_curr_level_ = start_index;
curr_index_in_curr_level_ = start_index;
#ifndef NDEBUG
prev_file_ = nullptr;
#endif
return true;
}
// curr_level_ = num_levels_. So, no more levels to search.
@@ -2021,10 +1991,10 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
pinned_iters_mgr.StartPinning();
}
FilePicker fp(
storage_info_.files_, user_key, ikey, &storage_info_.level_files_brief_,
storage_info_.num_non_empty_levels_, &storage_info_.file_indexer_,
user_comparator(), internal_comparator());
FilePicker fp(user_key, ikey, &storage_info_.level_files_brief_,
storage_info_.num_non_empty_levels_,
&storage_info_.file_indexer_, user_comparator(),
internal_comparator());
FdWithKeyRange* f = fp.GetNextFile();
while (f != nullptr) {
@@ -2426,7 +2396,8 @@ void Version::PrepareApply(
UpdateAccumulatedStats(update_stats);
storage_info_.UpdateNumNonEmptyLevels();
storage_info_.CalculateBaseBytes(*cfd_->ioptions(), mutable_cf_options);
storage_info_.UpdateFilesByCompactionPri(cfd_->ioptions()->compaction_pri);
storage_info_.UpdateFilesByCompactionPri(*cfd_->ioptions(),
mutable_cf_options);
storage_info_.GenerateFileIndexer();
storage_info_.GenerateLevelFilesBrief();
storage_info_.GenerateLevel0NonOverlapping();
@@ -3138,11 +3109,22 @@ namespace {
// Sort `temp` based on ratio of overlapping size over file size
void SortFileByOverlappingRatio(
const InternalKeyComparator& icmp, const std::vector<FileMetaData*>& files,
const std::vector<FileMetaData*>& next_level_files,
const std::vector<FileMetaData*>& next_level_files, SystemClock* clock,
int level, int num_non_empty_levels, uint64_t ttl,
std::vector<Fsize>* temp) {
std::unordered_map<uint64_t, uint64_t> file_to_order;
auto next_level_it = next_level_files.begin();
int64_t curr_time;
Status status = clock->GetCurrentTime(&curr_time);
if (!status.ok()) {
// If we can't get time, disable TTL.
ttl = 0;
}
FileTtlBooster ttl_booster(static_cast<uint64_t>(curr_time), ttl,
num_non_empty_levels, level);
for (auto& file : files) {
uint64_t overlapping_bytes = 0;
// Skip files in next level that is smaller than current file
@@ -3162,9 +3144,12 @@ void SortFileByOverlappingRatio(
next_level_it++;
}
uint64_t ttl_boost_score = (ttl > 0) ? ttl_booster.GetBoostScore(file) : 1;
assert(ttl_boost_score > 0);
assert(file->compensated_file_size != 0);
file_to_order[file->fd.GetNumber()] =
overlapping_bytes * 1024u / file->compensated_file_size;
file_to_order[file->fd.GetNumber()] = overlapping_bytes * 1024U /
file->compensated_file_size /
ttl_boost_score;
}
std::sort(temp->begin(), temp->end(),
@@ -3176,7 +3161,7 @@ void SortFileByOverlappingRatio(
} // namespace
void VersionStorageInfo::UpdateFilesByCompactionPri(
CompactionPri compaction_pri) {
const ImmutableOptions& ioptions, const MutableCFOptions& options) {
if (compaction_style_ == kCompactionStyleNone ||
compaction_style_ == kCompactionStyleFIFO ||
compaction_style_ == kCompactionStyleUniversal) {
@@ -3201,7 +3186,7 @@ void VersionStorageInfo::UpdateFilesByCompactionPri(
if (num > temp.size()) {
num = temp.size();
}
switch (compaction_pri) {
switch (ioptions.compaction_pri) {
case kByCompensatedSize:
std::partial_sort(temp.begin(), temp.begin() + num, temp.end(),
CompareCompensatedSizeDescending);
@@ -3222,7 +3207,8 @@ void VersionStorageInfo::UpdateFilesByCompactionPri(
break;
case kMinOverlappingRatio:
SortFileByOverlappingRatio(*internal_comparator_, files_[level],
files_[level + 1], &temp);
files_[level + 1], ioptions.clock, level,
num_non_empty_levels_, options.ttl, &temp);
break;
default:
assert(false);
+3 -2
View File
@@ -197,7 +197,8 @@ class VersionStorageInfo {
// Sort all files for this version based on their file size and
// record results in files_by_compaction_pri_. The largest files are listed
// first.
void UpdateFilesByCompactionPri(CompactionPri compaction_pri);
void UpdateFilesByCompactionPri(const ImmutableOptions& immutable_options,
const MutableCFOptions& mutable_cf_options);
void GenerateLevel0NonOverlapping();
bool level0_non_overlapping() const {
@@ -490,7 +491,7 @@ class VersionStorageInfo {
next_file_to_compact_by_size_[level] = 0;
}
const InternalKeyComparator* InternalComparator() {
const InternalKeyComparator* InternalComparator() const {
return internal_comparator_;
}
+3 -3
View File
@@ -177,7 +177,7 @@ class VersionStorageInfoTestBase : public testing::Test {
void Finalize() {
vstorage_.UpdateNumNonEmptyLevels();
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
vstorage_.UpdateFilesByCompactionPri(ioptions_.compaction_pri);
vstorage_.UpdateFilesByCompactionPri(ioptions_, mutable_cf_options_);
vstorage_.GenerateFileIndexer();
vstorage_.GenerateLevelFilesBrief();
vstorage_.GenerateLevel0NonOverlapping();
@@ -2083,7 +2083,7 @@ class VersionSetAtomicGroupTest : public VersionSetTestBase,
});
SyncPoint::GetInstance()->SetCallBack(
"VersionEditHandlerBase::Iterate:Finish", [&](void* arg) {
num_recovered_edits_ = *reinterpret_cast<int*>(arg);
num_recovered_edits_ = *reinterpret_cast<size_t*>(arg);
});
SyncPoint::GetInstance()->SetCallBack(
"AtomicGroupReadBuffer::AddEdit:AtomicGroup",
@@ -2123,7 +2123,7 @@ class VersionSetAtomicGroupTest : public VersionSetTestBase,
bool first_in_atomic_group_ = false;
bool last_in_atomic_group_ = false;
int num_edits_in_atomic_group_ = 0;
int num_recovered_edits_ = 0;
size_t num_recovered_edits_ = 0;
VersionEdit corrupted_edit_;
VersionEdit edit_with_incorrect_group_size_;
std::unique_ptr<log::Writer> log_writer_;
+4
View File
@@ -1812,6 +1812,10 @@ void StressTest::TestCompactFiles(ThreadState* thread,
ROCKSDB_NAMESPACE::ColumnFamilyMetaData cf_meta_data;
db_->GetColumnFamilyMetaData(column_family, &cf_meta_data);
if (cf_meta_data.levels.empty()) {
return;
}
// Randomly compact up to three consecutive files from a level
const int kMaxRetry = 3;
for (int attempt = 0; attempt < kMaxRetry; ++attempt) {
+1 -1
View File
@@ -272,7 +272,7 @@ class CompositeDirectoryWrapper : public Directory {
Status Fsync() override {
IOOptions io_opts;
IODebugContext dbg;
return target_->Fsync(io_opts, &dbg);
return target_->FsyncWithDirOptions(io_opts, &dbg, DirFsyncOptions());
}
size_t GetUniqueId(char* id, size_t max_size) const override {
return target_->GetUniqueId(id, max_size);
Vendored
+2 -1
View File
@@ -324,7 +324,8 @@ class LegacyFileSystemWrapper : public FileSystem {
explicit LegacyFileSystemWrapper(Env* t) : target_(t) {}
~LegacyFileSystemWrapper() override {}
const char* Name() const override { return "Legacy File System"; }
static const char* kClassName() { return "LegacyFileSystem"; }
const char* Name() const override { return kClassName(); }
// Return the target to which this Env forwards all calls
Env* target() const { return target_; }
+73 -54
View File
@@ -13,20 +13,35 @@
#include "env/composite_env_wrapper.h"
#include "env/fs_remap.h"
#include "rocksdb/utilities/options_type.h"
#include "util/string_util.h" // errnoStr
namespace ROCKSDB_NAMESPACE {
namespace {
class ChrootFileSystem : public RemapFileSystem {
public:
ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
const std::string& chroot_dir)
: RemapFileSystem(base) {
static std::unordered_map<std::string, OptionTypeInfo> chroot_fs_type_info = {
{"chroot_dir", {0, OptionType::kString}}};
} // namespace
ChrootFileSystem::ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
const std::string& chroot_dir)
: RemapFileSystem(base), chroot_dir_(chroot_dir) {
RegisterOptions("chroot_dir", &chroot_dir_, &chroot_fs_type_info);
}
Status ChrootFileSystem::PrepareOptions(const ConfigOptions& options) {
Status s = FileSystemWrapper::PrepareOptions(options);
if (!s.ok()) {
return s;
} else if (chroot_dir_.empty()) {
s = Status::InvalidArgument("ChRootFileSystem requires a chroot dir");
} else {
s = target_->FileExists(chroot_dir_, IOOptions(), nullptr);
}
if (s.ok()) {
#if defined(OS_AIX)
char resolvedName[PATH_MAX];
char* real_chroot_dir = realpath(chroot_dir.c_str(), resolvedName);
char* real_chroot_dir = realpath(chroot_dir_.c_str(), resolvedName);
#else
char* real_chroot_dir = realpath(chroot_dir.c_str(), nullptr);
char* real_chroot_dir = realpath(chroot_dir_.c_str(), nullptr);
#endif
// chroot_dir must exist so realpath() returns non-nullptr.
assert(real_chroot_dir != nullptr);
@@ -35,32 +50,32 @@ class ChrootFileSystem : public RemapFileSystem {
free(real_chroot_dir);
#endif
}
return s;
}
const char* Name() const override { return "ChrootFS"; }
IOStatus ChrootFileSystem::GetTestDirectory(const IOOptions& options,
std::string* path,
IODebugContext* dbg) {
// Adapted from PosixEnv's implementation since it doesn't provide a way to
// create directory in the chroot.
char buf[256];
snprintf(buf, sizeof(buf), "/rocksdbtest-%d", static_cast<int>(geteuid()));
*path = buf;
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override {
// Adapted from PosixEnv's implementation since it doesn't provide a way to
// create directory in the chroot.
char buf[256];
snprintf(buf, sizeof(buf), "/rocksdbtest-%d", static_cast<int>(geteuid()));
*path = buf;
// Directory may already exist, so ignore return
return CreateDirIfMissing(*path, options, dbg);
}
// Directory may already exist, so ignore return
return CreateDirIfMissing(*path, options, dbg);
}
protected:
// Returns status and expanded absolute path including the chroot directory.
// Checks whether the provided path breaks out of the chroot. If it returns
// non-OK status, the returned path should not be used.
std::pair<IOStatus, std::string> EncodePath(
const std::string& path) override {
if (path.empty() || path[0] != '/') {
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
}
std::pair<IOStatus, std::string> res;
res.second = chroot_dir_ + path;
std::pair<IOStatus, std::string> ChrootFileSystem::EncodePath(
const std::string& path) {
if (path.empty() || path[0] != '/') {
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
}
std::pair<IOStatus, std::string> res;
res.second = chroot_dir_ + path;
#if defined(OS_AIX)
char resolvedName[PATH_MAX];
char* normalized_path = realpath(res.second.c_str(), resolvedName);
@@ -81,47 +96,51 @@ class ChrootFileSystem : public RemapFileSystem {
free(normalized_path);
#endif
return res;
}
}
// Similar to EncodePath() except assumes the basename in the path hasn't been
// created yet.
std::pair<IOStatus, std::string> EncodePathWithNewBasename(
const std::string& path) override {
if (path.empty() || path[0] != '/') {
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
}
// Basename may be followed by trailing slashes
size_t final_idx = path.find_last_not_of('/');
if (final_idx == std::string::npos) {
// It's only slashes so no basename to extract
return EncodePath(path);
}
// Pull off the basename temporarily since realname(3) (used by
// EncodePath()) requires a path that exists
size_t base_sep = path.rfind('/', final_idx);
auto status_and_enc_path = EncodePath(path.substr(0, base_sep + 1));
status_and_enc_path.second.append(path.substr(base_sep + 1));
return status_and_enc_path;
std::pair<IOStatus, std::string> ChrootFileSystem::EncodePathWithNewBasename(
const std::string& path) {
if (path.empty() || path[0] != '/') {
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
}
// Basename may be followed by trailing slashes
size_t final_idx = path.find_last_not_of('/');
if (final_idx == std::string::npos) {
// It's only slashes so no basename to extract
return EncodePath(path);
}
private:
std::string chroot_dir_;
};
} // namespace
// Pull off the basename temporarily since realname(3) (used by
// EncodePath()) requires a path that exists
size_t base_sep = path.rfind('/', final_idx);
auto status_and_enc_path = EncodePath(path.substr(0, base_sep + 1));
status_and_enc_path.second.append(path.substr(base_sep + 1));
return status_and_enc_path;
}
std::shared_ptr<FileSystem> NewChrootFileSystem(
const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir) {
return std::make_shared<ChrootFileSystem>(base, chroot_dir);
auto chroot_fs = std::make_shared<ChrootFileSystem>(base, chroot_dir);
Status s = chroot_fs->PrepareOptions(ConfigOptions());
if (s.ok()) {
return chroot_fs;
} else {
return nullptr;
}
}
Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir) {
if (!base_env->FileExists(chroot_dir).ok()) {
return nullptr;
}
std::shared_ptr<FileSystem> chroot_fs =
NewChrootFileSystem(base_env->GetFileSystem(), chroot_dir);
return new CompositeEnvWrapper(base_env, chroot_fs);
auto chroot_fs = NewChrootFileSystem(base_env->GetFileSystem(), chroot_dir);
if (chroot_fs != nullptr) {
return new CompositeEnvWrapper(base_env, chroot_fs);
} else {
return nullptr;
}
}
} // namespace ROCKSDB_NAMESPACE
+31 -1
View File
@@ -9,9 +9,37 @@
#include <string>
#include "rocksdb/env.h"
#include "env/fs_remap.h"
#include "rocksdb/file_system.h"
namespace ROCKSDB_NAMESPACE {
class ChrootFileSystem : public RemapFileSystem {
public:
ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
const std::string& chroot_dir);
static const char* kClassName() { return "ChrootFS"; }
const char* Name() const override { return kClassName(); }
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override;
Status PrepareOptions(const ConfigOptions& options) override;
protected:
// Returns status and expanded absolute path including the chroot directory.
// Checks whether the provided path breaks out of the chroot. If it returns
// non-OK status, the returned path should not be used.
std::pair<IOStatus, std::string> EncodePath(const std::string& path) override;
// Similar to EncodePath() except assumes the basename in the path hasn't been
// created yet.
std::pair<IOStatus, std::string> EncodePathWithNewBasename(
const std::string& path) override;
private:
std::string chroot_dir_;
};
// Returns an Env that translates paths such that the root directory appears to
// be chroot_dir. chroot_dir should refer to an existing directory.
@@ -19,6 +47,8 @@ namespace ROCKSDB_NAMESPACE {
// This class has not been fully analyzed for providing strong security
// guarantees.
Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir);
std::shared_ptr<FileSystem> NewChrootFileSystem(
const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir);
} // namespace ROCKSDB_NAMESPACE
+30 -2
View File
@@ -438,11 +438,20 @@ IOStatus EncryptedRandomRWFile::Close(const IOOptions& options,
}
namespace {
static std::unordered_map<std::string, OptionTypeInfo> encrypted_fs_type_info =
{
{"provider",
OptionTypeInfo::AsCustomSharedPtr<EncryptionProvider>(
0 /* No offset, whole struct*/, OptionVerificationType::kByName,
OptionTypeFlags::kNone)},
};
// EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption
// to files stored on disk.
class EncryptedFileSystemImpl : public EncryptedFileSystem {
public:
const char* Name() const override { return "EncryptedFS"; }
const char* Name() const override {
return EncryptedFileSystem::kClassName();
}
// Returns the raw encryption provider that should be used to write the input
// encrypted file. If there is no such provider, NotFound is returned.
IOStatus GetWritableProvider(const std::string& /*fname*/,
@@ -664,6 +673,7 @@ class EncryptedFileSystemImpl : public EncryptedFileSystem {
const std::shared_ptr<EncryptionProvider>& provider)
: EncryptedFileSystem(base) {
provider_ = provider;
RegisterOptions("EncryptionProvider", &provider_, &encrypted_fs_type_info);
}
Status AddCipher(const std::string& descriptor, const char* cipher,
@@ -910,10 +920,28 @@ class EncryptedFileSystemImpl : public EncryptedFileSystem {
};
} // namespace
Status NewEncryptedFileSystemImpl(
const std::shared_ptr<FileSystem>& base,
const std::shared_ptr<EncryptionProvider>& provider,
std::unique_ptr<FileSystem>* result) {
result->reset(new EncryptedFileSystemImpl(base, provider));
return Status::OK();
}
std::shared_ptr<FileSystem> NewEncryptedFS(
const std::shared_ptr<FileSystem>& base,
const std::shared_ptr<EncryptionProvider>& provider) {
return std::make_shared<EncryptedFileSystemImpl>(base, provider);
std::unique_ptr<FileSystem> efs;
Status s = NewEncryptedFileSystemImpl(base, provider, &efs);
if (s.ok()) {
s = efs->PrepareOptions(ConfigOptions());
}
if (s.ok()) {
std::shared_ptr<FileSystem> result(efs.release());
return result;
} else {
return nullptr;
}
}
// Returns an Env that encrypts data when stored on disk and decrypts data when
// read from disk.
+6
View File
@@ -105,6 +105,12 @@ class CTREncryptionProvider : public EncryptionProvider {
uint64_t initialCounter, const Slice& iv, const Slice& prefix,
std::unique_ptr<BlockAccessCipherStream>* result);
};
Status NewEncryptedFileSystemImpl(
const std::shared_ptr<FileSystem>& base_fs,
const std::shared_ptr<EncryptionProvider>& provider,
std::unique_ptr<FileSystem>* fs);
} // namespace ROCKSDB_NAMESPACE
#endif // !defined(ROCKSDB_LITE)
+161 -10
View File
@@ -39,6 +39,7 @@
#include "env/emulated_clock.h"
#include "env/env_chroot.h"
#include "env/env_encryption_ctr.h"
#include "env/fs_readonly.h"
#include "env/unique_id_gen.h"
#include "logging/log_buffer.h"
#include "logging/logging.h"
@@ -59,6 +60,7 @@
#include "util/mutexlock.h"
#include "util/random.h"
#include "util/string_util.h"
#include "utilities/env_timed.h"
#include "utilities/fault_injection_env.h"
#include "utilities/fault_injection_fs.h"
@@ -2233,14 +2235,17 @@ INSTANTIATE_TEST_CASE_P(DefaultEnvWithDirectIO, EnvPosixTestWithParam,
#endif // !defined(ROCKSDB_LITE)
#if !defined(ROCKSDB_LITE) && !defined(OS_WIN)
static std::unique_ptr<Env> chroot_env(
NewChrootEnv(Env::Default(), test::TmpDir(Env::Default())));
INSTANTIATE_TEST_CASE_P(
ChrootEnvWithoutDirectIO, EnvPosixTestWithParam,
::testing::Values(std::pair<Env*, bool>(chroot_env.get(), false)));
INSTANTIATE_TEST_CASE_P(
ChrootEnvWithDirectIO, EnvPosixTestWithParam,
::testing::Values(std::pair<Env*, bool>(chroot_env.get(), true)));
static Env* GetChrootEnv() {
static std::unique_ptr<Env> chroot_env(
NewChrootEnv(Env::Default(), test::TmpDir(Env::Default())));
return chroot_env.get();
}
INSTANTIATE_TEST_CASE_P(ChrootEnvWithoutDirectIO, EnvPosixTestWithParam,
::testing::Values(std::pair<Env*, bool>(GetChrootEnv(),
false)));
INSTANTIATE_TEST_CASE_P(ChrootEnvWithDirectIO, EnvPosixTestWithParam,
::testing::Values(std::pair<Env*, bool>(GetChrootEnv(),
true)));
#endif // !defined(ROCKSDB_LITE) && !defined(OS_WIN)
class EnvFSTestWithParam
@@ -2492,7 +2497,6 @@ TEST_F(CreateEnvTest, CreateMockSystemClock) {
guard->reset(new MockSystemClock(nullptr));
return guard->get();
});
ASSERT_OK(SystemClock::CreateFromString(
config_options_, EmulatedSystemClock::kClassName(), &mock));
ASSERT_NE(mock, nullptr);
@@ -2518,6 +2522,154 @@ TEST_F(CreateEnvTest, CreateMockSystemClock) {
ASSERT_OK(SystemClock::CreateFromString(
config_options_, EmulatedSystemClock::kClassName(), &mock));
}
TEST_F(CreateEnvTest, CreateReadOnlyFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
ASSERT_OK(FileSystem::CreateFromString(
config_options_, ReadOnlyFileSystem::kClassName(), &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), ReadOnlyFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
std::string("id=") + ReadOnlyFileSystem::kClassName() +
"; target=" + TimedFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
opts_str = fs->ToString(config_options_);
ASSERT_STREQ(fs->Name(), ReadOnlyFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
TEST_F(CreateEnvTest, CreateTimedFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
ASSERT_OK(FileSystem::CreateFromString(config_options_,
TimedFileSystem::kClassName(), &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
std::string("id=") + TimedFileSystem::kClassName() +
"; target=" + ReadOnlyFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
opts_str = fs->ToString(config_options_);
ASSERT_STREQ(fs->Name(), TimedFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), ReadOnlyFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
#ifndef OS_WIN
TEST_F(CreateEnvTest, CreateChrootFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
auto tmp_dir = test::TmpDir(Env::Default());
// The Chroot FileSystem has a required "chroot_dir" option.
ASSERT_NOK(FileSystem::CreateFromString(config_options_,
ChrootFileSystem::kClassName(), &fs));
// ChrootFileSystem fails with an invalid directory
ASSERT_NOK(FileSystem::CreateFromString(
config_options_,
std::string("chroot_dir=/No/Such/Directory; id=") +
ChrootFileSystem::kClassName(),
&fs));
std::string chroot_opts = std::string("chroot_dir=") + tmp_dir +
std::string("; id=") +
ChrootFileSystem::kClassName();
// Create a valid ChrootFileSystem with an inner Default
ASSERT_OK(FileSystem::CreateFromString(config_options_, chroot_opts, &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), ChrootFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
// Create a valid ChrootFileSystem with an inner TimedFileSystem
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
chroot_opts + "; target=" + TimedFileSystem::kClassName(), &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), ChrootFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
opts_str = fs->ToString(config_options_);
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
// Create a TimedFileSystem with an inner ChrootFileSystem
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
"target={" + chroot_opts + "}; id=" + TimedFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), TimedFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), ChrootFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
opts_str = fs->ToString(config_options_);
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
#endif // OS_WIN
TEST_F(CreateEnvTest, CreateEncryptedFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
std::string base_opts =
std::string("provider=1://test; id=") + EncryptedFileSystem::kClassName();
// The EncryptedFileSystem requires a "provider" option.
ASSERT_NOK(FileSystem::CreateFromString(
config_options_, EncryptedFileSystem::kClassName(), &fs));
ASSERT_OK(FileSystem::CreateFromString(config_options_, base_opts, &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), EncryptedFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
ASSERT_OK(FileSystem::CreateFromString(
config_options_, base_opts + "; target=" + TimedFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), EncryptedFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
opts_str = fs->ToString(config_options_);
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
#endif // ROCKSDB_LITE
namespace {
@@ -2742,7 +2894,6 @@ TEST_F(EnvTest, FailureToCreateLockFile) {
// Clean up
ASSERT_OK(DestroyDir(env, dir));
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+115 -9
View File
@@ -6,9 +6,17 @@
#include "rocksdb/file_system.h"
#include "env/composite_env_wrapper.h"
#include "env/env_chroot.h"
#include "env/env_encryption_ctr.h"
#include "env/fs_readonly.h"
#include "env/mock_env.h"
#include "options/db_options.h"
#include "rocksdb/convenience.h"
#include "rocksdb/utilities/customizable_util.h"
#include "rocksdb/utilities/object_registry.h"
#include "rocksdb/utilities/options_type.h"
#include "util/string_util.h"
#include "utilities/env_timed.h"
namespace ROCKSDB_NAMESPACE {
@@ -21,19 +29,63 @@ Status FileSystem::Load(const std::string& value,
return CreateFromString(ConfigOptions(), value, result);
}
#ifndef ROCKSDB_LITE
static int RegisterBuiltinFileSystems(ObjectLibrary& library,
const std::string& /*arg*/) {
library.Register<FileSystem>(
TimedFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new TimedFileSystem(nullptr));
return guard->get();
});
library.Register<FileSystem>(
ReadOnlyFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new ReadOnlyFileSystem(nullptr));
return guard->get();
});
library.Register<FileSystem>(
EncryptedFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* errmsg) {
Status s = NewEncryptedFileSystemImpl(nullptr, nullptr, guard);
if (!s.ok()) {
*errmsg = s.ToString();
}
return guard->get();
});
#ifndef OS_WIN
library.Register<FileSystem>(
ChrootFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new ChrootFileSystem(nullptr, ""));
return guard->get();
});
#endif // OS_WIN
size_t num_types;
return static_cast<int>(library.GetFactoryCount(&num_types));
}
#endif // ROCKSDB_LITE
Status FileSystem::CreateFromString(const ConfigOptions& config_options,
const std::string& value,
std::shared_ptr<FileSystem>* result) {
Status s;
auto default_fs = FileSystem::Default();
if (default_fs->IsInstanceOf(value)) {
*result = default_fs;
return Status::OK();
} else {
#ifndef ROCKSDB_LITE
(void)config_options;
s = ObjectRegistry::NewInstance()->NewSharedObject<FileSystem>(value, result);
#else
(void)config_options;
(void)result;
s = Status::NotSupported("Cannot load FileSystem in LITE mode", value);
#endif
return s;
static std::once_flag once;
std::call_once(once, [&]() {
RegisterBuiltinFileSystems(*(ObjectLibrary::Default().get()), "");
});
#endif // ROCKSDB_LITE
return LoadSharedObject<FileSystem>(config_options, value, nullptr, result);
}
}
IOStatus FileSystem::ReuseWritableFile(const std::string& fname,
@@ -147,4 +199,58 @@ IOStatus ReadFileToString(FileSystem* fs, const std::string& fname,
return s;
}
namespace {
static std::unordered_map<std::string, OptionTypeInfo> fs_wrapper_type_info = {
#ifndef ROCKSDB_LITE
{"target",
OptionTypeInfo::AsCustomSharedPtr<FileSystem>(
0, OptionVerificationType::kByName, OptionTypeFlags::kDontSerialize)},
#endif // ROCKSDB_LITE
};
} // namespace
FileSystemWrapper::FileSystemWrapper(const std::shared_ptr<FileSystem>& t)
: target_(t) {
RegisterOptions("", &target_, &fs_wrapper_type_info);
}
Status FileSystemWrapper::PrepareOptions(const ConfigOptions& options) {
if (target_ == nullptr) {
target_ = FileSystem::Default();
}
return FileSystem::PrepareOptions(options);
}
#ifndef ROCKSDB_LITE
std::string FileSystemWrapper::SerializeOptions(
const ConfigOptions& config_options, const std::string& header) const {
auto parent = FileSystem::SerializeOptions(config_options, "");
if (config_options.IsShallow() || target_ == nullptr ||
target_->IsInstanceOf(FileSystem::kDefaultName())) {
return parent;
} else {
std::string result = header;
if (!StartsWith(parent, OptionTypeInfo::kIdPropName())) {
result.append(OptionTypeInfo::kIdPropName()).append("=");
}
result.append(parent);
if (!EndsWith(result, config_options.delimiter)) {
result.append(config_options.delimiter);
}
result.append("target=").append(target_->ToString(config_options));
return result;
}
}
#endif // ROCKSDB_LITE
DirFsyncOptions::DirFsyncOptions() { reason = kDefault; }
DirFsyncOptions::DirFsyncOptions(std::string file_renamed_new_name) {
reason = kFileRenamed;
renamed_new_name = file_renamed_new_name;
}
DirFsyncOptions::DirFsyncOptions(FsyncReason fsync_reason) {
assert(fsync_reason != kFileRenamed);
reason = fsync_reason;
}
} // namespace ROCKSDB_NAMESPACE
+3
View File
@@ -27,6 +27,9 @@ class FileSystemTracingWrapper : public FileSystemWrapper {
~FileSystemTracingWrapper() override {}
static const char* kClassName() { return "FileSystemTracing"; }
const char* Name() const override { return kClassName(); }
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* result,
+11 -7
View File
@@ -141,7 +141,9 @@ class PosixFileSystem : public FileSystem {
public:
PosixFileSystem();
const char* Name() const override { return "Posix File System"; }
static const char* kClassName() { return "PosixFileSystem"; }
const char* Name() const override { return kClassName(); }
const char* NickName() const override { return kDefaultName(); }
~PosixFileSystem() override {}
@@ -618,7 +620,8 @@ class PosixFileSystem : public FileSystem {
return IOStatus::NotFound();
default:
assert(err == EIO || err == ENOMEM);
return IOStatus::IOError("Unexpected error(" + ToString(err) +
return IOStatus::IOError("Unexpected error(" +
ROCKSDB_NAMESPACE::ToString(err) +
") accessing file `" + fname + "' ");
}
}
@@ -819,11 +822,12 @@ class PosixFileSystem : public FileSystem {
errno = ENOLCK;
// Note that the thread ID printed is the same one as the one in
// posix logger, but posix logger prints it hex format.
return IOError("lock hold by current process, acquire time " +
ToString(prev_info.acquire_time) +
" acquiring thread " +
ToString(prev_info.acquiring_thread),
fname, errno);
return IOError(
"lock hold by current process, acquire time " +
ROCKSDB_NAMESPACE::ToString(prev_info.acquire_time) +
" acquiring thread " +
ROCKSDB_NAMESPACE::ToString(prev_info.acquiring_thread),
fname, errno);
}
IOStatus result = IOStatus::OK();
+3
View File
@@ -26,6 +26,9 @@ class ReadOnlyFileSystem : public FileSystemWrapper {
explicit ReadOnlyFileSystem(const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {}
static const char* kClassName() { return "ReadOnlyFileSystem"; }
const char* Name() const override { return kClassName(); }
IOStatus NewWritableFile(const std::string& /*fname*/,
const FileOptions& /*options*/,
std::unique_ptr<FSWritableFile>* /*result*/,
+8
View File
@@ -39,6 +39,14 @@ class RemapFileSystem : public FileSystemWrapper {
public:
// Left abstract:
// const char* Name() const override { ... }
static const char* kClassName() { return "RemapFileSystem"; }
bool IsInstanceOf(const std::string& id) const override {
if (id == kClassName()) {
return true;
} else {
return FileSystemWrapper::IsInstanceOf(id);
}
}
Status RegisterDbPaths(const std::vector<std::string>& paths) override;
+48 -4
View File
@@ -1534,17 +1534,61 @@ PosixMemoryMappedFileBuffer::~PosixMemoryMappedFileBuffer() {
/*
* PosixDirectory
*/
#if !defined(BTRFS_SUPER_MAGIC)
// The magic number for BTRFS is fixed, if it's not defined, define it here
#define BTRFS_SUPER_MAGIC 0x9123683E
#endif
PosixDirectory::PosixDirectory(int fd) : fd_(fd) {
is_btrfs_ = false;
#ifdef OS_LINUX
struct statfs buf;
int ret = fstatfs(fd, &buf);
is_btrfs_ = (ret == 0 && buf.f_type == BTRFS_SUPER_MAGIC);
#endif
}
PosixDirectory::~PosixDirectory() { close(fd_); }
IOStatus PosixDirectory::Fsync(const IOOptions& /*opts*/,
IODebugContext* /*dbg*/) {
IOStatus PosixDirectory::Fsync(const IOOptions& opts, IODebugContext* dbg) {
return FsyncWithDirOptions(opts, dbg, DirFsyncOptions());
}
IOStatus PosixDirectory::FsyncWithDirOptions(
const IOOptions& /*opts*/, IODebugContext* /*dbg*/,
const DirFsyncOptions& dir_fsync_options) {
IOStatus s = IOStatus::OK();
#ifndef OS_AIX
if (is_btrfs_) {
// skip dir fsync for new file creation, which is not needed for btrfs
if (dir_fsync_options.reason == DirFsyncOptions::kNewFileSynced) {
return s;
}
// skip dir fsync for renaming file, only need to sync new file
if (dir_fsync_options.reason == DirFsyncOptions::kFileRenamed) {
std::string new_name = dir_fsync_options.renamed_new_name;
assert(!new_name.empty());
int fd;
do {
IOSTATS_TIMER_GUARD(open_nanos);
fd = open(new_name.c_str(), O_RDONLY);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
s = IOError("While open renaming file", new_name, errno);
} else if (fsync(fd) < 0) {
s = IOError("While fsync renaming file", new_name, errno);
}
if (close(fd) < 0) {
s = IOError("While closing file after fsync", new_name, errno);
}
return s;
}
// fallback to dir-fsync for kDefault, kDirRenamed and kFileDeleted
}
if (fsync(fd_) == -1) {
return IOError("While fsync", "a directory", errno);
s = IOError("While fsync", "a directory", errno);
}
#endif
return IOStatus::OK();
return s;
}
} // namespace ROCKSDB_NAMESPACE
#endif
+6 -1
View File
@@ -391,12 +391,17 @@ struct PosixMemoryMappedFileBuffer : public MemoryMappedFileBuffer {
class PosixDirectory : public FSDirectory {
public:
explicit PosixDirectory(int fd) : fd_(fd) {}
explicit PosixDirectory(int fd);
~PosixDirectory();
virtual IOStatus Fsync(const IOOptions& opts, IODebugContext* dbg) override;
virtual IOStatus FsyncWithDirOptions(
const IOOptions&, IODebugContext*,
const DirFsyncOptions& dir_fsync_options) override;
private:
int fd_;
bool is_btrfs_;
};
} // namespace ROCKSDB_NAMESPACE
+27 -120
View File
@@ -565,132 +565,39 @@ class TestMemLogger : public Logger {
}
size_t GetLogFileSize() const override { return log_size_; }
};
} // namespace
class MockFileSystem : public FileSystem {
public:
explicit MockFileSystem(const std::shared_ptr<SystemClock>& clock,
bool supports_direct_io = true)
: system_clock_(clock), supports_direct_io_(supports_direct_io) {
clock_ = system_clock_.get();
MockFileSystem::MockFileSystem(const std::shared_ptr<SystemClock>& clock,
bool supports_direct_io)
: system_clock_(clock), supports_direct_io_(supports_direct_io) {
clock_ = system_clock_.get();
}
MockFileSystem::~MockFileSystem() {
for (auto i = file_map_.begin(); i != file_map_.end(); ++i) {
i->second->Unref();
}
~MockFileSystem() override {
for (auto i = file_map_.begin(); i != file_map_.end(); ++i) {
i->second->Unref();
}
}
IOStatus MockFileSystem::GetAbsolutePath(const std::string& db_path,
const IOOptions& /*options*/,
std::string* output_path,
IODebugContext* /*dbg*/) {
*output_path = NormalizeMockPath(db_path);
if (output_path->at(0) != '/') {
return IOStatus::NotSupported("GetAbsolutePath");
} else {
return IOStatus::OK();
}
}
const char* Name() const override { return "Memory"; }
IOStatus NewSequentialFile(const std::string& f, const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomAccessFile(const std::string& f,
const FileOptions& file_opts,
std::unique_ptr<FSRandomAccessFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomRWFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override;
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewWritableFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus ReopenWritableFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewDirectory(const std::string& /*name*/, const IOOptions& io_opts,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override;
IOStatus FileExists(const std::string& fname, const IOOptions& /*io_opts*/,
IODebugContext* /*dbg*/) override;
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override;
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus Truncate(const std::string& fname, size_t size,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override;
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override;
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override;
IOStatus RenameFile(const std::string& src, const std::string& target,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus LinkFile(const std::string& /*src*/, const std::string& /*target*/,
const IOOptions& /*options*/,
IODebugContext* /*dbg*/) override;
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override;
IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override;
IOStatus NewLogger(const std::string& fname, const IOOptions& io_opts,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override;
// Get full directory name for this db.
IOStatus GetAbsolutePath(const std::string& db_path,
const IOOptions& /*options*/,
std::string* output_path,
IODebugContext* /*dbg*/) override {
*output_path = NormalizeMockPath(db_path);
if (output_path->at(0) != '/') {
return IOStatus::NotSupported("GetAbsolutePath");
} else {
return IOStatus::OK();
}
}
IOStatus IsDirectory(const std::string& /*path*/,
const IOOptions& /*options*/, bool* /*is_dir*/,
IODebugContext* /*dgb*/) override {
return IOStatus::NotSupported("IsDirectory");
std::string MockFileSystem::NormalizeMockPath(const std::string& path) {
std::string p = NormalizePath(path);
if (p.back() == kFilePathSeparator && p.size() > 1) {
p.pop_back();
}
return p;
}
Status CorruptBuffer(const std::string& fname);
private:
bool RenameFileInternal(const std::string& src, const std::string& dest);
void DeleteFileInternal(const std::string& fname);
bool GetChildrenInternal(const std::string& fname,
std::vector<std::string>* results);
std::string NormalizeMockPath(const std::string& path) {
std::string p = NormalizePath(path);
if (p.back() == kFilePathSeparator && p.size() > 1) {
p.pop_back();
}
return p;
}
private:
// Map from filenames to MemFile objects, representing a simple file system.
port::Mutex mutex_;
std::map<std::string, MemFile*> file_map_; // Protected by mutex_.
std::shared_ptr<SystemClock> system_clock_;
SystemClock* clock_;
bool supports_direct_io_;
};
} // Anonymous namespace
// Partial implementation of the FileSystem interface.
IOStatus MockFileSystem::NewSequentialFile(
const std::string& fname, const FileOptions& file_opts,
+106
View File
@@ -14,11 +14,117 @@
#include <vector>
#include "env/composite_env_wrapper.h"
#include "port/port.h"
#include "rocksdb/env.h"
#include "rocksdb/status.h"
#include "rocksdb/system_clock.h"
namespace ROCKSDB_NAMESPACE {
class MemFile;
class MockFileSystem : public FileSystem {
public:
explicit MockFileSystem(const std::shared_ptr<SystemClock>& clock,
bool supports_direct_io = true);
~MockFileSystem() override;
static const char* kClassName() { return "MemoryFileSystem"; }
const char* Name() const override { return kClassName(); }
IOStatus NewSequentialFile(const std::string& f, const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomAccessFile(const std::string& f,
const FileOptions& file_opts,
std::unique_ptr<FSRandomAccessFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomRWFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override;
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewWritableFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus ReopenWritableFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewDirectory(const std::string& /*name*/, const IOOptions& io_opts,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override;
IOStatus FileExists(const std::string& fname, const IOOptions& /*io_opts*/,
IODebugContext* /*dbg*/) override;
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override;
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus Truncate(const std::string& fname, size_t size,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override;
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override;
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override;
IOStatus RenameFile(const std::string& src, const std::string& target,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus LinkFile(const std::string& /*src*/, const std::string& /*target*/,
const IOOptions& /*options*/,
IODebugContext* /*dbg*/) override;
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override;
IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override;
IOStatus NewLogger(const std::string& fname, const IOOptions& io_opts,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override;
// Get full directory name for this db.
IOStatus GetAbsolutePath(const std::string& db_path,
const IOOptions& /*options*/,
std::string* output_path,
IODebugContext* /*dbg*/) override;
IOStatus IsDirectory(const std::string& /*path*/,
const IOOptions& /*options*/, bool* /*is_dir*/,
IODebugContext* /*dgb*/) override {
return IOStatus::NotSupported("IsDirectory");
}
Status CorruptBuffer(const std::string& fname);
private:
bool RenameFileInternal(const std::string& src, const std::string& dest);
void DeleteFileInternal(const std::string& fname);
bool GetChildrenInternal(const std::string& fname,
std::vector<std::string>* results);
std::string NormalizeMockPath(const std::string& path);
private:
// Map from filenames to MemFile objects, representing a simple file system.
port::Mutex mutex_;
std::map<std::string, MemFile*> file_map_; // Protected by mutex_.
std::shared_ptr<SystemClock> system_clock_;
SystemClock* clock_;
bool supports_direct_io_;
};
class MockEnv : public CompositeEnvWrapper {
public:
static MockEnv* Create(Env* base);
+3 -1
View File
@@ -357,7 +357,9 @@ Status DeleteScheduler::DeleteTrashFile(const std::string& path_in_trash,
s = fs_->NewDirectory(dir_to_sync, IOOptions(), &dir_obj, nullptr);
}
if (s.ok()) {
s = dir_obj->Fsync(IOOptions(), nullptr);
s = dir_obj->FsyncWithDirOptions(
IOOptions(), nullptr,
DirFsyncOptions(DirFsyncOptions::FsyncReason::kFileDeleted));
TEST_SYNC_POINT_CALLBACK(
"DeleteScheduler::DeleteTrashFile::AfterSyncDir",
reinterpret_cast<void*>(const_cast<std::string*>(&dir_to_sync)));
+13 -2
View File
@@ -405,7 +405,8 @@ IOStatus SetCurrentFile(FileSystem* fs, const std::string& dbname,
}
if (s.ok()) {
if (directory_to_fsync != nullptr) {
s = directory_to_fsync->Fsync(IOOptions(), nullptr);
s = directory_to_fsync->FsyncWithDirOptions(
IOOptions(), nullptr, DirFsyncOptions(CurrentFileName(dbname)));
}
} else {
fs->DeleteFile(tmp, IOOptions(), nullptr)
@@ -428,9 +429,19 @@ Status SetIdentityFile(Env* env, const std::string& dbname,
assert(!id.empty());
// Reserve the filename dbname/000000.dbtmp for the temporary identity file
std::string tmp = TempFileName(dbname, 0);
std::string identify_file_name = IdentityFileName(dbname);
Status s = WriteStringToFile(env, id, tmp, true);
if (s.ok()) {
s = env->RenameFile(tmp, IdentityFileName(dbname));
s = env->RenameFile(tmp, identify_file_name);
}
std::unique_ptr<FSDirectory> dir_obj;
if (s.ok()) {
s = env->GetFileSystem()->NewDirectory(dbname, IOOptions(), &dir_obj,
nullptr);
}
if (s.ok()) {
s = dir_obj->FsyncWithDirOptions(IOOptions(), nullptr,
DirFsyncOptions(identify_file_name));
}
if (!s.ok()) {
env->DeleteFile(tmp).PermitUncheckedError();
+3
View File
@@ -39,6 +39,9 @@ class MockFS : public FileSystemWrapper {
bool support_prefetch)
: FileSystemWrapper(wrapped), support_prefetch_(support_prefetch) {}
static const char* kClassName() { return "MockFS"; }
const char* Name() const override { return kClassName(); }
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& opts,
std::unique_ptr<FSRandomAccessFile>* result,
+17 -8
View File
@@ -105,9 +105,14 @@ struct CompressionOptions {
//
// When compression dictionary is disabled, we compress and write each block
// before buffering data for the next one. When compression dictionary is
// enabled, we buffer all SST file data in-memory so we can sample it, as data
// enabled, we buffer SST file data in-memory so we can sample it, as data
// can only be compressed and written after the dictionary has been finalized.
// So users of this feature may see increased memory usage.
//
// The amount of data buffered can be limited by `max_dict_buffer_bytes`. This
// buffered memory is charged to the block cache when there is a block cache.
// If block cache insertion fails with `Status::Incomplete` (i.e., it is
// full), we finalize the dictionary with whatever data we have and then stop
// buffering.
//
// Default: 0.
uint32_t max_dict_bytes;
@@ -307,35 +312,39 @@ struct AdvancedColumnFamilyOptions {
// delta_value - Delta value to be merged with the existing_value.
// Stored in transaction logs.
// merged_value - Set when delta is applied on the previous value.
//
// Applicable only when inplace_update_support is true,
// this callback function is called at the time of updating the memtable
// as part of a Put operation, lets say Put(key, delta_value). It allows the
// 'delta_value' specified as part of the Put operation to be merged with
// an 'existing_value' of the key in the database.
//
// If the merged value is smaller in size that the 'existing_value',
// then this function can update the 'existing_value' buffer inplace and
// the corresponding 'existing_value'_size pointer, if it wishes to.
// The callback should return UpdateStatus::UPDATED_INPLACE.
// In this case. (In this case, the snapshot-semantics of the rocksdb
// Iterator is not atomic anymore).
//
// If the merged value is larger in size than the 'existing_value' or the
// application does not wish to modify the 'existing_value' buffer inplace,
// then the merged value should be returned via *merge_value. It is set by
// merging the 'existing_value' and the Put 'delta_value'. The callback should
// return UpdateStatus::UPDATED in this case. This merged value will be added
// to the memtable.
//
// If merging fails or the application does not wish to take any action,
// then the callback should return UpdateStatus::UPDATE_FAILED.
//
// Please remember that the original call from the application is Put(key,
// delta_value). So the transaction log (if enabled) will still contain (key,
// delta_value). The 'merged_value' is not stored in the transaction log.
// Hence the inplace_callback function should be consistent across db reopens.
//
// RocksDB callbacks are NOT exception-safe. A callback completing with an
// exception can lead to undefined behavior in RocksDB, including data loss,
// unreported corruption, deadlocks, and more.
//
// Default: nullptr
UpdateStatus (*inplace_callback)(char* existing_value,
uint32_t* existing_value_size,
+4
View File
@@ -170,6 +170,10 @@ class Cache {
// The SizeCallback takes a void* pointer to the object and returns the size
// of the persistable data. It can be used by the secondary cache to allocate
// memory if needed.
//
// RocksDB callbacks are NOT exception-safe. A callback completing with an
// exception can lead to undefined behavior in RocksDB, including data loss,
// unreported corruption, deadlocks, and more.
using SizeCallback = size_t (*)(void* obj);
// The SaveToCallback takes a void* object pointer and saves the persistable
+8 -1
View File
@@ -24,7 +24,10 @@ class SliceTransform;
// CompactionFilter allows an application to modify/delete a key-value during
// table file creation.
//
// 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 CompactionFilter : public Customizable {
public:
enum ValueType {
@@ -219,6 +222,10 @@ class CompactionFilter : public Customizable {
// `CompactionFilter` according to `ShouldFilterTableFileCreation()`. This
// allows the application to know about the different ongoing threads of work
// and makes it unnecessary for `CompactionFilter` to provide thread-safety.
//
// 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 CompactionFilterFactory : public Customizable {
public:
virtual ~CompactionFilterFactory() {}
+4
View File
@@ -21,6 +21,10 @@ class Slice;
// used as keys in an sstable or a database. A Comparator implementation
// must be thread-safe since rocksdb may invoke its methods concurrently
// from multiple threads.
//
// 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 Comparator : public Customizable {
public:
Comparator() : timestamp_size_(0) {}
@@ -17,6 +17,8 @@
namespace ROCKSDB_NAMESPACE {
// This is NOT an extensible interface but a public interface for result of
// NewConcurrentTaskLimiter. Any derived classes must be RocksDB internal.
class ConcurrentTaskLimiter {
public:
virtual ~ConcurrentTaskLimiter() {}
+7
View File
@@ -145,6 +145,9 @@ struct EnvOptions {
RateLimiter* rate_limiter = nullptr;
};
// 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 Env {
public:
struct FileAttributes {
@@ -1150,6 +1153,10 @@ enum InfoLogLevel : unsigned char {
};
// An interface for writing log messages.
//
// 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 Logger {
public:
size_t kDoNotSupportGetLogFileSize = (std::numeric_limits<size_t>::max)();
+19
View File
@@ -24,6 +24,9 @@ struct ConfigOptions;
// read from disk.
Env* NewEncryptedEnv(Env* base_env,
const std::shared_ptr<EncryptionProvider>& provider);
std::shared_ptr<FileSystem> NewEncryptedFS(
const std::shared_ptr<FileSystem>& base_fs,
const std::shared_ptr<EncryptionProvider>& provider);
// BlockAccessCipherStream is the base class for any cipher stream that
// supports random access at block level (without requiring data from other
@@ -59,6 +62,10 @@ class BlockAccessCipherStream {
};
// BlockCipher
//
// 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 BlockCipher : public Customizable {
public:
virtual ~BlockCipher(){};
@@ -102,6 +109,10 @@ class BlockCipher : public Customizable {
// The encryption provider is used to create a cipher stream for a specific
// file. The returned cipher stream will be used for actual
// encryption/decryption actions.
//
// 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 EncryptionProvider : public Customizable {
public:
virtual ~EncryptionProvider(){};
@@ -440,6 +451,14 @@ class EncryptedFileSystem : public FileSystemWrapper {
// otherwise
virtual Status AddCipher(const std::string& descriptor, const char* cipher,
size_t len, bool for_write) = 0;
static const char* kClassName() { return "EncryptedFileSystem"; }
bool IsInstanceOf(const std::string& name) const override {
if (name == kClassName()) {
return true;
} else {
return FileSystemWrapper::IsInstanceOf(name);
}
}
};
} // namespace ROCKSDB_NAMESPACE
+12
View File
@@ -43,6 +43,10 @@ struct FileChecksumGenContext {
// * Finalize is called at most once during the life of the object
// * All calls to Update come before Finalize
// * All calls to GetChecksum come after Finalize
//
// 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 FileChecksumGenerator {
public:
virtual ~FileChecksumGenerator() {}
@@ -64,6 +68,10 @@ class FileChecksumGenerator {
};
// Create the FileChecksumGenerator object for each SST file.
//
// 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 FileChecksumGenFactory : public Customizable {
public:
virtual ~FileChecksumGenFactory() {}
@@ -86,6 +94,10 @@ class FileChecksumGenFactory : public Customizable {
// the checksum information of all valid SST file of a DB instance. It can
// also be used to store the checksum information of a list of SST files to
// be ingested.
//
// 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 FileChecksumList {
public:
virtual ~FileChecksumList() {}
+66 -13
View File
@@ -28,6 +28,7 @@
#include <unordered_map>
#include <vector>
#include "rocksdb/customizable.h"
#include "rocksdb/env.h"
#include "rocksdb/io_status.h"
#include "rocksdb/options.h"
@@ -99,7 +100,36 @@ struct IOOptions {
// such as NewRandomAccessFile and NewWritableFile.
std::unordered_map<std::string, std::string> property_bag;
IOOptions() : timeout(0), prio(IOPriority::kIOLow), type(IOType::kUnknown) {}
// Force directory fsync, some file systems like btrfs may skip directory
// fsync, set this to force the fsync
bool force_dir_fsync;
IOOptions() : IOOptions(false) {}
explicit IOOptions(bool force_dir_fsync_)
: timeout(std::chrono::microseconds::zero()),
prio(IOPriority::kIOLow),
type(IOType::kUnknown),
force_dir_fsync(force_dir_fsync_) {}
};
struct DirFsyncOptions {
enum FsyncReason : uint8_t {
kNewFileSynced,
kFileRenamed,
kDirRenamed,
kFileDeleted,
kDefault,
} reason;
std::string renamed_new_name; // for kFileRenamed
// add other options for other FsyncReason
DirFsyncOptions();
explicit DirFsyncOptions(std::string file_renamed_new_name);
explicit DirFsyncOptions(FsyncReason fsync_reason);
};
// File scope options that control how a file is opened/created and accessed
@@ -207,7 +237,11 @@ struct IODebugContext {
// retryable.
// NewCompositeEnv can be used to create an Env with a custom FileSystem for
// DBOptions::env.
class FileSystem {
//
// 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 FileSystem : public Customizable {
public:
FileSystem();
@@ -216,9 +250,8 @@ class FileSystem {
virtual ~FileSystem();
virtual const char* Name() const = 0;
static const char* Type() { return "FileSystem"; }
static const char* kDefaultName() { return "DefaultFileSystem"; }
// Loads the FileSystem specified by the input value into the result
// The CreateFromString alternative should be used; this method may be
@@ -730,10 +763,11 @@ class FSRandomAccessFile {
// Read a bunch of blocks as described by reqs. The blocks can
// optionally be read in parallel. This is a synchronous call, i.e it
// should return after all reads have completed. The reads will be
// non-overlapping. If the function return Status is not ok, status of
// individual requests will be ignored and return status will be assumed
// for all read requests. The function return status is only meant for any
// any errors that occur before even processing specific read requests
// non-overlapping but can be in any order. If the function return Status
// is not ok, status of individual requests will be ignored and return
// status will be assumed for all read requests. The function return status
// is only meant for errors that occur before processing individual read
// requests.
virtual IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs,
const IOOptions& options, IODebugContext* dbg) {
assert(reqs != nullptr);
@@ -1106,6 +1140,15 @@ class FSDirectory {
// Fsync directory. Can be called concurrently from multiple threads.
virtual IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) = 0;
// FsyncWithDirOptions after renaming a file. Depends on the filesystem, it
// may fsync directory or just the renaming file (e.g. btrfs). By default, it
// just calls directory fsync.
virtual IOStatus FsyncWithDirOptions(
const IOOptions& options, IODebugContext* dbg,
const DirFsyncOptions& /*dir_fsync_options*/) {
return Fsync(options, dbg);
}
virtual size_t GetUniqueId(char* /*id*/, size_t /*max_size*/) const {
return 0;
}
@@ -1147,12 +1190,9 @@ class FSDirectory {
class FileSystemWrapper : public FileSystem {
public:
// Initialize an EnvWrapper that delegates all calls to *t
explicit FileSystemWrapper(const std::shared_ptr<FileSystem>& t)
: target_(t) {}
explicit FileSystemWrapper(const std::shared_ptr<FileSystem>& t);
~FileSystemWrapper() override {}
const char* Name() const override { return target_->Name(); }
// Return the target to which this Env forwards all calls
FileSystem* target() const { return target_.get(); }
@@ -1343,7 +1383,13 @@ class FileSystemWrapper : public FileSystem {
return target_->IsDirectory(path, options, is_dir, dbg);
}
private:
const Customizable* Inner() const override { return target_.get(); }
Status PrepareOptions(const ConfigOptions& options) override;
#ifndef ROCKSDB_LITE
std::string SerializeOptions(const ConfigOptions& config_options,
const std::string& header) const override;
#endif // ROCKSDB_LITE
protected:
std::shared_ptr<FileSystem> target_;
};
@@ -1615,6 +1661,13 @@ class FSDirectoryWrapper : public FSDirectory {
IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) override {
return target_->Fsync(options, dbg);
}
IOStatus FsyncWithDirOptions(
const IOOptions& options, IODebugContext* dbg,
const DirFsyncOptions& dir_fsync_options) override {
return target_->FsyncWithDirOptions(options, dbg, dir_fsync_options);
}
size_t GetUniqueId(char* id, size_t max_size) const override {
return target_->GetUniqueId(id, max_size);
}
+5 -1
View File
@@ -18,7 +18,11 @@ struct ConfigOptions;
struct Options;
// FlushBlockPolicy provides a configurable way to determine when to flush a
// block in the block based tables,
// block in the block based tables.
//
// 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 FlushBlockPolicy {
public:
// Keep track of the key/value sequences and return the boolean value to
+4
View File
@@ -476,6 +476,10 @@ struct ExternalFileIngestionInfo {
// the current thread holding any DB mutex. This is to prevent potential
// deadlock and performance issue when using EventListener callback
// in a complex way.
//
// [Exceptions] 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 EventListener : public Customizable {
public:
static const char* Type() { return "EventListener"; }
+3
View File
@@ -44,6 +44,9 @@ class Logger;
//
// Refer to rocksdb-merge wiki for more details and example implementations.
//
// 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 MergeOperator : public Customizable {
public:
virtual ~MergeOperator() {}
+4 -1
View File
@@ -396,6 +396,9 @@ struct CompactionServiceJobInfo {
priority(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 CompactionService : public Customizable {
public:
static const char* Type() { return "CompactionService"; }
@@ -931,7 +934,7 @@ struct DBOptions {
size_t random_access_max_buffer_size = 1024 * 1024;
// This is the maximum buffer size that is used by WritableFileWriter.
// On Windows, we need to maintain an aligned buffer for writes.
// With direct IO, we need to maintain an aligned buffer for writes.
// We allow the buffer to grow until it's size hits the limit in buffered
// IO and fix the buffer size when using direct IO to ensure alignment of
// write requests if the logical sector size is unusual
+5 -1
View File
@@ -15,6 +15,9 @@
namespace ROCKSDB_NAMESPACE {
// 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 RateLimiter {
public:
enum class OpType {
@@ -63,7 +66,8 @@ class RateLimiter {
// Requests token to read or write bytes and potentially updates statistics.
//
// If this request can not be satisfied, the call is blocked. Caller is
// responsible to make sure bytes <= GetSingleBurstBytes().
// responsible to make sure bytes <= GetSingleBurstBytes()
// and bytes >= 0.
virtual void Request(const int64_t bytes, const Env::IOPriority pri,
Statistics* stats, OpType op_type) {
if (IsRateLimited(op_type)) {
+4
View File
@@ -43,6 +43,10 @@ class SecondaryCacheResultHandle {
//
// Cache interface for caching blocks on a secondary tier (which can include
// non-volatile media, or alternate forms of caching such as compressed data)
//
// 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 SecondaryCache : public Customizable {
public:
virtual ~SecondaryCache() {}
+8 -6
View File
@@ -25,12 +25,14 @@ namespace ROCKSDB_NAMESPACE {
class Slice;
struct ConfigOptions;
/*
* A SliceTransform is a generic pluggable way of transforming one string
* to another. Its primary use-case is in configuring rocksdb
* to store prefix blooms by setting prefix_extractor in
* ColumnFamilyOptions.
*/
// A SliceTransform is a generic pluggable way of transforming one string
// to another. Its primary use-case is in configuring rocksdb
// to store prefix blooms by setting prefix_extractor in
// ColumnFamilyOptions.
//
// 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 SliceTransform : public Customizable {
public:
virtual ~SliceTransform(){};
+2 -1
View File
@@ -21,7 +21,8 @@ class Logger;
// SstFileManager is used to track SST and blob files in the DB and control
// their deletion rate. All SstFileManager public functions are thread-safe.
// SstFileManager is not extensible.
// SstFileManager is NOT an extensible interface but a public interface for
// result of NewSstFileManager. Any derived classes must be RocksDB internal.
class SstFileManager {
public:
virtual ~SstFileManager() {}
+3
View File
@@ -78,6 +78,9 @@ class SstPartitioner {
};
};
// 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 SstPartitionerFactory : public Customizable {
public:
virtual ~SstPartitionerFactory() {}
+4
View File
@@ -573,6 +573,10 @@ enum StatsLevel : uint8_t {
// options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
// HistogramData hist;
// options.statistics->histogramData(FLUSH_TIME, &hist);
//
// 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 Statistics : public Customizable {
public:
virtual ~Statistics() {}
+9 -4
View File
@@ -49,6 +49,7 @@ enum ChecksumType : char {
kCRC32c = 0x1,
kxxHash = 0x2,
kxxHash64 = 0x3,
kXXH3 = 0x4, // Supported since RocksDB 6.27
};
// `PinningTier` is used to specify which tier of block-based tables should
@@ -117,9 +118,10 @@ struct BlockBasedTableOptions {
// caching as they should now apply to range tombstone and compression
// dictionary meta-blocks, in addition to index and filter meta-blocks.
//
// Indicating if we'd put index/filter blocks to the block cache.
// If not specified, each "table reader" object will pre-load index/filter
// block during table initialization.
// Whether to put index/filter blocks in the block cache. When false,
// each "table reader" object will pre-load index/filter blocks during
// table initialization. Index and filter partition blocks always use
// block cache regardless of this option.
bool cache_index_and_filter_blocks = false;
// If cache_index_and_filter_blocks is enabled, cache index and filter
@@ -190,6 +192,8 @@ struct BlockBasedTableOptions {
kHashSearch = 0x01,
// A two-level index implementation. Both levels are binary search indexes.
// Second level index blocks ("partitions") use block cache even when
// cache_index_and_filter_blocks=false.
kTwoLevelIndexSearch = 0x02,
// Like kBinarySearch, but index also contains first key of each block.
@@ -285,7 +289,8 @@ struct BlockBasedTableOptions {
// well.
// TODO(myabandeh): remove the note above once the limitation is lifted
// Use partitioned full filters for each SST file. This option is
// incompatible with block-based filters.
// incompatible with block-based filters. Filter partition blocks use
// block cache even when cache_index_and_filter_blocks=false.
bool partition_filters = false;
// Option to generate Bloom/Ribbon filters that minimize memory
+12 -1
View File
@@ -80,7 +80,14 @@ extern const std::string kRangeDelBlock;
// a collection of callback functions that will be invoked during table
// building. It is constructed with TablePropertiesCollectorFactory. The methods
// don't need to be thread-safe, as we will create exactly one
// TablePropertiesCollector object per table and then call it sequentially
// TablePropertiesCollector object per table and then call it sequentially.
//
// Statuses from these callbacks are currently logged when not OK, but
// otherwise ignored by RocksDB.
//
// 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 TablePropertiesCollector {
public:
virtual ~TablePropertiesCollector() {}
@@ -133,6 +140,10 @@ class TablePropertiesCollector {
// Constructs TablePropertiesCollector. Internals create a new
// TablePropertiesCollector for each new table
//
// 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 TablePropertiesCollectorFactory : public Customizable {
public:
struct Context {
@@ -262,6 +262,10 @@ struct CreateBackupOptions {
bool flush_before_backup = false;
// Callback for reporting progress, based on callback_trigger_interval_size.
//
// RocksDB callbacks are NOT exception-safe. A callback completing with an
// exception can lead to undefined behavior in RocksDB, including data loss,
// unreported corruption, deadlocks, and more.
std::function<void()> progress_callback = []() {};
// If false, background_thread_cpu_priority is ignored.
+6 -7
View File
@@ -76,7 +76,8 @@ class EnvLibrados : public EnvWrapper {
// Store in *result the names of the children of the specified directory.
// The names are relative to "dir".
// Original contents of *results are dropped.
Status GetChildren(const std::string& dir, std::vector<std::string>* result);
Status GetChildren(const std::string& dir,
std::vector<std::string>* result) override;
// Delete the named file.
Status DeleteFile(const std::string& fname) override;
@@ -116,18 +117,16 @@ class EnvLibrados : public EnvWrapper {
// to go away.
//
// May create the named file if it does not already exist.
Status LockFile(const std::string& fname, FileLock** lock);
Status LockFile(const std::string& fname, FileLock** lock) override;
// Release the lock acquired by a previous successful call to LockFile.
// REQUIRES: lock was returned by a successful LockFile() call
// REQUIRES: lock has not already been unlocked.
Status UnlockFile(FileLock* lock);
Status UnlockFile(FileLock* lock) override;
// Get full directory name for this db.
Status GetAbsolutePath(const std::string& db_path, std::string* output_path);
// Generate unique id
std::string GenerateUniqueId();
Status GetAbsolutePath(const std::string& db_path,
std::string* output_path) override;
// Get default EnvLibrados
static EnvLibrados* Default();
+4
View File
@@ -19,6 +19,10 @@ struct ConfigOptions;
// WALFilter allows an application to inspect write-ahead-log (WAL)
// records or modify their processing on recovery.
// Please see the details below.
//
// 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 WalFilter : public Customizable {
public:
static const char* Type() { return "WalFilter"; }
+3 -3
View File
@@ -23,8 +23,8 @@
namespace ROCKSDB_NAMESPACE {
class CacheReservationManager;
// Interface to block and signal DB instances.
// Each DB instance contains ptr to StallInterface.
// Interface to block and signal DB instances, intended for RocksDB
// internal use only. Each DB instance contains ptr to StallInterface.
class StallInterface {
public:
virtual ~StallInterface() {}
@@ -34,7 +34,7 @@ class StallInterface {
virtual void Signal() = 0;
};
class WriteBufferManager {
class WriteBufferManager final {
public:
// Parameters:
// _buffer_size: _buffer_size = 0 indicates no limit. Memory won't be capped.
+1
View File
@@ -112,6 +112,7 @@ JAVA_TESTS = \
org.rocksdb.BlobOptionsTest\
org.rocksdb.BlockBasedTableConfigTest\
org.rocksdb.BuiltinComparatorTest\
org.rocksdb.BytewiseComparatorRegressionTest\
org.rocksdb.util.BytewiseComparatorTest\
org.rocksdb.util.BytewiseComparatorIntTest\
org.rocksdb.CheckPointTest\
+51
View File
@@ -3837,6 +3837,30 @@ jdouble Java_org_rocksdb_Options_blobGarbageCollectionAgeCutoff(JNIEnv*,
return static_cast<jdouble>(opts->blob_garbage_collection_age_cutoff);
}
/*
* Class: org_rocksdb_Options
* Method: setBlobGarbageCollectionForceThreshold
* Signature: (JD)V
*/
void Java_org_rocksdb_Options_setBlobGarbageCollectionForceThreshold(
JNIEnv*, jobject, jlong jhandle,
jdouble jblob_garbage_collection_force_threshold) {
auto* opts = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
opts->blob_garbage_collection_force_threshold =
static_cast<double>(jblob_garbage_collection_force_threshold);
}
/*
* Class: org_rocksdb_Options
* Method: blobGarbageCollectionForceThreshold
* Signature: (J)D
*/
jdouble Java_org_rocksdb_Options_blobGarbageCollectionForceThreshold(
JNIEnv*, jobject, jlong jhandle) {
auto* opts = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
return static_cast<jdouble>(opts->blob_garbage_collection_force_threshold);
}
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::ColumnFamilyOptions
@@ -5545,6 +5569,33 @@ jdouble Java_org_rocksdb_ColumnFamilyOptions_blobGarbageCollectionAgeCutoff(
return static_cast<jdouble>(opts->blob_garbage_collection_age_cutoff);
}
/*
* Class: org_rocksdb_ColumnFamilyOptions
* Method: setBlobGarbageCollectionForceThreshold
* Signature: (JD)V
*/
void Java_org_rocksdb_ColumnFamilyOptions_setBlobGarbageCollectionForceThreshold(
JNIEnv*, jobject, jlong jhandle,
jdouble jblob_garbage_collection_force_threshold) {
auto* opts =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle);
opts->blob_garbage_collection_force_threshold =
static_cast<double>(jblob_garbage_collection_force_threshold);
}
/*
* Class: org_rocksdb_ColumnFamilyOptions
* Method: blobGarbageCollectionAgeCutoff
* Signature: (J)D
*/
jdouble
Java_org_rocksdb_ColumnFamilyOptions_blobGarbageCollectionForceThreshold(
JNIEnv*, jobject, jlong jhandle) {
auto* opts =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle);
return static_cast<jdouble>(opts->blob_garbage_collection_force_threshold);
}
/////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DBOptions
+4 -3
View File
@@ -16,14 +16,15 @@
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_WriteBufferManager_newWriteBufferManager(
JNIEnv* /*env*/, jclass /*jclazz*/, jlong jbuffer_size, jlong jcache_handle) {
JNIEnv* /*env*/, jclass /*jclazz*/, jlong jbuffer_size, jlong jcache_handle,
jboolean allow_stall) {
auto* cache_ptr =
reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::Cache>*>(
jcache_handle);
auto* write_buffer_manager =
new std::shared_ptr<ROCKSDB_NAMESPACE::WriteBufferManager>(
std::make_shared<ROCKSDB_NAMESPACE::WriteBufferManager>(jbuffer_size,
*cache_ptr));
std::make_shared<ROCKSDB_NAMESPACE::WriteBufferManager>(
jbuffer_size, *cache_ptr, allow_stall));
return reinterpret_cast<jlong>(write_buffer_manager);
}
@@ -687,6 +687,31 @@ public interface AdvancedMutableColumnFamilyOptionsInterface<
*/
double blobGarbageCollectionAgeCutoff();
/**
* If the ratio of garbage in the oldest blob files exceeds this threshold,
* targeted compactions are scheduled in order to force garbage collecting
* the blob files in question, assuming they are all eligible based on the
* value of {@link #blobGarbageCollectionAgeCutoff} above. This option is
* currently only supported with leveled compactions.
*
* Note that {@link #enableBlobGarbageCollection} has to be set in order for this
* option to have any effect.
*
* Default: 1.0
*
* Dynamically changeable through the SetOptions() API
*
* @param blobGarbageCollectionForceThreshold new value for the threshold
* @return the reference to the current options
*/
T setBlobGarbageCollectionForceThreshold(double blobGarbageCollectionForceThreshold);
/**
* Get the current value for the {@link #blobGarbageCollectionForceThreshold}
* @return the current threshold at which garbage collection of blobs is forced
*/
double blobGarbageCollectionForceThreshold();
//
// END options for blobs (integrated BlobDB)
//
@@ -1164,6 +1164,39 @@ public class ColumnFamilyOptions extends RocksObject
return blobGarbageCollectionAgeCutoff(nativeHandle_);
}
/**
* If the ratio of garbage in the oldest blob files exceeds this threshold,
* targeted compactions are scheduled in order to force garbage collecting
* the blob files in question, assuming they are all eligible based on the
* value of {@link #blobGarbageCollectionAgeCutoff} above. This option is
* currently only supported with leveled compactions.
*
* Note that {@link #enableBlobGarbageCollection} has to be set in order for this
* option to have any effect.
*
* Default: 1.0
*
* Dynamically changeable through the SetOptions() API
*
* @param blobGarbageCollectionForceThreshold new value for the threshold
* @return the reference to the current options
*/
@Override
public ColumnFamilyOptions setBlobGarbageCollectionForceThreshold(
final double blobGarbageCollectionForceThreshold) {
setBlobGarbageCollectionForceThreshold(nativeHandle_, blobGarbageCollectionForceThreshold);
return this;
}
/**
* Get the current value for the {@link #blobGarbageCollectionForceThreshold}
* @return the current threshold at which garbage collection of blobs is forced
*/
@Override
public double blobGarbageCollectionForceThreshold() {
return blobGarbageCollectionForceThreshold(nativeHandle_);
}
//
// END options for blobs (integrated BlobDB)
//
@@ -1358,6 +1391,9 @@ public class ColumnFamilyOptions extends RocksObject
private native void setBlobGarbageCollectionAgeCutoff(
final long nativeHandle_, final double blobGarbageCollectionAgeCutoff);
private native double blobGarbageCollectionAgeCutoff(final long nativeHandle_);
private native void setBlobGarbageCollectionForceThreshold(
final long nativeHandle_, final double blobGarbageCollectionForceThreshold);
private native double blobGarbageCollectionForceThreshold(final long nativeHandle_);
// instance variables
// NOTE: If you add new member variables, please update the copy constructor above!
@@ -119,7 +119,8 @@ public class MutableColumnFamilyOptions
blob_file_size(ValueType.LONG),
blob_compression_type(ValueType.ENUM),
enable_blob_garbage_collection(ValueType.BOOLEAN),
blob_garbage_collection_age_cutoff(ValueType.DOUBLE);
blob_garbage_collection_age_cutoff(ValueType.DOUBLE),
blob_garbage_collection_force_threshold(ValueType.DOUBLE);
private final ValueType valueType;
BlobOption(final ValueType valueType) {
@@ -546,5 +547,17 @@ public class MutableColumnFamilyOptions
public double blobGarbageCollectionAgeCutoff() {
return getDouble(BlobOption.blob_garbage_collection_age_cutoff);
}
@Override
public MutableColumnFamilyOptionsBuilder setBlobGarbageCollectionForceThreshold(
final double blobGarbageCollectionForceThreshold) {
return setDouble(
BlobOption.blob_garbage_collection_force_threshold, blobGarbageCollectionForceThreshold);
}
@Override
public double blobGarbageCollectionForceThreshold() {
return getDouble(BlobOption.blob_garbage_collection_force_threshold);
}
}
}
+17 -2
View File
@@ -2077,7 +2077,7 @@ public class Options extends RocksObject
}
@Override
public Options setBlobGarbageCollectionAgeCutoff(double blobGarbageCollectionAgeCutoff) {
public Options setBlobGarbageCollectionAgeCutoff(final double blobGarbageCollectionAgeCutoff) {
setBlobGarbageCollectionAgeCutoff(nativeHandle_, blobGarbageCollectionAgeCutoff);
return this;
}
@@ -2087,6 +2087,18 @@ public class Options extends RocksObject
return blobGarbageCollectionAgeCutoff(nativeHandle_);
}
@Override
public Options setBlobGarbageCollectionForceThreshold(
final double blobGarbageCollectionForceThreshold) {
setBlobGarbageCollectionForceThreshold(nativeHandle_, blobGarbageCollectionForceThreshold);
return this;
}
@Override
public double blobGarbageCollectionForceThreshold() {
return blobGarbageCollectionForceThreshold(nativeHandle_);
}
//
// END options for blobs (integrated BlobDB)
//
@@ -2517,8 +2529,11 @@ public class Options extends RocksObject
final long nativeHandle_, final boolean enableBlobGarbageCollection);
private native boolean enableBlobGarbageCollection(final long nativeHandle_);
private native void setBlobGarbageCollectionAgeCutoff(
final long nativeHandle_, double blobGarbageCollectionAgeCutoff);
final long nativeHandle_, final double blobGarbageCollectionAgeCutoff);
private native double blobGarbageCollectionAgeCutoff(final long nativeHandle_);
private native void setBlobGarbageCollectionForceThreshold(
final long nativeHandle_, final double blobGarbageCollectionForceThreshold);
private native double blobGarbageCollectionForceThreshold(final long nativeHandle_);
// instance variables
// NOTE: If you add new member variables, please update the copy constructor above!
@@ -22,12 +22,29 @@ public class WriteBufferManager extends RocksObject {
*
* @param bufferSizeBytes buffer size(in bytes) to use for native write_buffer_manager
* @param cache cache whose memory should be bounded by this write buffer manager
* @param allowStall if set true, it will enable stalling of writes when memory_usage() exceeds
* buffer_size.
* It will wait for flush to complete and memory usage to drop down.
*/
public WriteBufferManager(final long bufferSizeBytes, final Cache cache){
super(newWriteBufferManager(bufferSizeBytes, cache.nativeHandle_));
public WriteBufferManager(
final long bufferSizeBytes, final Cache cache, final boolean allowStall) {
super(newWriteBufferManager(bufferSizeBytes, cache.nativeHandle_, allowStall));
this.allowStall_ = allowStall;
}
private native static long newWriteBufferManager(final long bufferSizeBytes, final long cacheHandle);
public WriteBufferManager(final long bufferSizeBytes, final Cache cache){
this(bufferSizeBytes, cache, false);
}
public boolean allowStall() {
return allowStall_;
}
private native static long newWriteBufferManager(
final long bufferSizeBytes, final long cacheHandle, final boolean allowStall);
@Override
protected native void disposeInternal(final long handle);
private boolean allowStall_;
}
Loaded 100 of 156 files, more files were not shown because too many files have changed in this diff. Show more