Compare commits

..
10 Commits
Author SHA1 Message Date
Yu Zhang b5cde68b8a Update HISTORY for 9.7.2 2024-10-14 10:27:00 -07:00
Yu Zhang d9787264a8 Update version.h 2024-10-08 11:42:30 -07:00
Yu Zhang 2fef013616 Fix a bug for surfacing write unix time (#13057)
Summary:
The write unix time from non L0 files are not surfaced properly because the level's wrapper iterator doesn't have a `write_unix_time` implementation that delegates to the corresponding file. The unit test didn't catch this because it incorrectly destroy the old db and reopen to check write time, instead of just reopen and check. This fix also include a change to support ldb's scan command to get write time for easier debugging.

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

Test Plan: Updated unit tests

Reviewed By: pdillinger

Differential Revision: D64015107

Pulled By: jowlyzhang

fbshipit-source-id: 244474f78a034f80c9235eea2aa8a0f4e54dff59
2024-10-08 11:41:38 -07:00
anand76 a245672710 Update HISTORY and version for 9.7.1 2024-09-26 23:54:52 -07:00
Peter Dillinger 786ac6a0e9 Bug fix and test BuildDBOptions (#13038)
Summary:
The following DBOptions were not being propagated through BuildDBOptions, which could at least lead to settings being lost through `GetOptionsFromString()`, possibly elsewhere as well:
* background_close_inactive_wals
* write_dbid_to_manifest
* write_identity_file
* prefix_seek_opt_in_only

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

Test Plan:
This problem was not being caught by
OptionsSettableTest.DBOptionsAllFieldsSettable when the option was omitted from both options_helper.cc and options_settable_test.cc. I have added to the test to catch future instances (and the updated test was how I found three of the four missing options).

The same kind of bug seems to be caught by
ColumnFamilyOptionsAllFieldsSettable, and AFAIK analogous code does not exist for BlockBasedTableOptions.

Reviewed By: ltamasi

Differential Revision: D63483779

Pulled By: pdillinger

fbshipit-source-id: a5d5f6e434174bacb8e5d251b767e81e62b7225a
2024-09-26 15:24:46 -07:00
anand76 ade981b5b4 More accurate accounting of compressed cache memory (#13032)
Summary:
When an item is inserted into the compressed secondary cache, this PR calculates the charge using the malloc_usable_size of the allocated memory, as well as the unique pointer allocation.

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

Test Plan: New unit test

Reviewed By: pdillinger

Differential Revision: D63418493

Pulled By: anand1976

fbshipit-source-id: 1db2835af6867442bb8cf6d9bf412e120ddd3824
2024-09-26 08:52:17 -07:00
anand76 84432c6580 Respect lowest_used_cache_tier option for compressed blocks (#13030)
Summary:
If the lowest_used_cache_tier DB option is set to kVolatileTier, skip insertion of compressed blocks into the secondary cache. Previously, these were always inserted into the secondary cache via the InsertSaved() method, leading to pollution of the secondary cache with blocks that would never be read.

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

Test Plan: Add a new unit test

Reviewed By: pdillinger

Differential Revision: D63329841

Pulled By: anand1976

fbshipit-source-id: 14d2fce2ed309401d9ad4d2e7c356218b6673f7b
2024-09-26 08:52:05 -07:00
Jay Huh 56bc1a0e83 Remove unnecessary semi-colon (#13034)
Summary:
As title

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

Reviewed By: ltamasi

Differential Revision: D63413712

Pulled By: jaykorean

fbshipit-source-id: 0070761b0d9de1f50fe0baf235643d36aeb9f7f5
2024-09-25 15:38:37 -07:00
Jay Huh 9af0d6eafa More info in CompactionServiceJobInfo and CompactionJobStats (#13029)
Summary:
Add the following to the `CompactionServiceJobInfo`
- compaction_reason
- is_full_compaction
- is_manual_compaction
- bottommost_level

Added `is_remote_compaction` to the `CompactionJobStats` and set initial values to avoid UB for uninitialized values.

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

Test Plan:
```
./compaction_service_test --gtest_filter="*CompactionInfo*"
```

Reviewed By: anand1976

Differential Revision: D63322878

Pulled By: jaykorean

fbshipit-source-id: f02a66ca45e660b9d354a43837d8ec6beb7621fb
2024-09-25 11:43:29 -07:00
Levi Tamasi a617c14d2a Update HISTORY for the 9.7 release 2024-09-20 17:09:41 -07:00
180 changed files with 2289 additions and 7724 deletions

No files matched your search

+1 -1
View File
@@ -32,7 +32,7 @@
# 3. cmake ..
# 4. make -j
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.10)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
include(ReadVersion)
+5 -40
View File
@@ -1,50 +1,15 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 9.8.4 (11/18/2024)
### Behavior Changes
* When Remote Compaction is enabled, do not purge OPTIONS file immediately by DeleteObsoleteOptionsFiles() after SetOptions(). Rely on PurgeObsoleteFiles() to clean up obsolete OPTIONS file after each compaction.
## 9.8.3 (11/12/2024)
## 9.7.2 (10/08/2024)
### Bug Fixes
* Fix missing cases of corruption retry during DB open and read API processing.
## 9.8.2 (11/06/2024)
### Public API Changes
* Added a new API `Transaction::GetAttributeGroupIterator` that can be used to create a multi-column-family attribute group iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
### Behavior Changes
* `BaseDeltaIterator` now honors the read option `allow_unprepared_value`.
* Fix a bug for surfacing write unix time: `Iterator::GetProperty("rocksdb.iterator.write-time")` for non-L0 files.
## 9.7.1 (09/26/2024)
### Bug Fixes
* `BaseDeltaIterator` now calls `PrepareValue` on the base iterator in case it has been created with the `allow_unprepared_value` read option set. Earlier, such base iterators could lead to incorrect values being exposed from `BaseDeltaIterator`.
* Fix a bug for replaying WALs for WriteCommitted transaction DB when its user-defined timestamps setting is toggled on/off between DB sessions.
## 9.8.1 (10/31/2024)
### Bug Fixes
* Fix a leak of obsolete blob files left open until DB::Close(). This bug was introduced in version 9.4.0.
## 9.8.0 (10/25/2024)
### New Features
* All non-`block_cache` options in `BlockBasedTableOptions` are now mutable with `DB::SetOptions()`. See also Bug Fixes below.
* When using iterators with BlobDB, it is now possible to load large values on an on-demand basis, i.e. only if they are actually needed by the application. This can save I/O in use cases where the values associated with certain keys are not needed. For more details, see the new read option `allow_unprepared_value` and the iterator API `PrepareValue`.
* Add a new file ingestion option `IngestExternalFileOptions::fill_cache` to support not adding blocks from ingested files into block cache during file ingestion.
* The option `allow_unprepared_value` is now also supported for multi-column-family iterators (i.e. `CoalescingIterator` and `AttributeGroupIterator`).
* When a file with just one range deletion (standalone range deletion file) is ingested via bulk loading, it will be marked for compaction. During compaction, this type of files can be used to directly filter out some input files that are not protected by any snapshots and completely deleted by the standalone range deletion file.
### Behavior Changes
* During file ingestion, overlapping files level assignment are done in multiple batches, so that they can potentially be assigned to lower levels other than always land on L0.
* OPTIONS file to be loaded by remote worker is now preserved so that it does not get purged by the primary host. A similar technique as how we are preserving new SST files from getting purged is used for this. min_options_file_numbers_ is tracked like pending_outputs_ is tracked.
* Trim readahead_size during scans so data blocks containing keys that are not in the same prefix as the seek key in `Seek()` are not prefetched when `ReadOptions::auto_readahead_size=true` (default value) and `ReadOptions::prefix_same_as_start = true`
* Assigning levels for external files are done in the same way for universal compaction and leveled compaction. The old behavior tends to assign files to L0 while the new behavior will assign the files to the lowest level possible.
### Bug Fixes
* Fix a longstanding race condition in SetOptions for `block_based_table_factory` options. The fix has some subtle behavior changes because of copying and replacing the TableFactory on a change with SetOptions, including requiring an Iterator::Refresh() for an existing Iterator to use the latest options.
* Fix under counting of allocated memory in the compressed secondary cache due to looking at the compressed block size rather than the actual memory allocated, which could be larger due to internal fragmentation.
* `GetApproximateMemTableStats()` could return disastrously bad estimates 5-25% of the time. The function has been re-engineered to return much better estimates with similar CPU cost.
* Skip insertion of compressed blocks in the secondary cache if the lowest_used_cache_tier DB option is kVolatileTier.
* Fix an issue in level compaction where a small CF with small compaction debt can cause the DB to allow parallel compactions. (#13054)
* Several DB option settings could be lost through `GetOptionsFromString()`, possibly elsewhere as well. Affected options, now fixed:`background_close_inactive_wals`, `write_dbid_to_manifest`, `write_identity_file`, `prefix_seek_opt_in_only`
* Fix under counting of allocated memory in the compressed secondary cache due to looking at the compressed block size rather than the actual memory allocated, which could be larger due to internal fragmentation.
* Skip insertion of compressed blocks in the secondary cache if the lowest_used_cache_tier DB option is kVolatileTier.
## 9.7.0 (09/20/2024)
### New Features
+3 -2
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
try:
from builtins import str
@@ -131,7 +132,7 @@ def generate_targets(repo_path, deps_map):
if len(sys.argv) >= 2:
# Heuristically quote and canonicalize whitespace for inclusion
# in how the file was generated.
extra_argv = " '{}'".format(" ".join(sys.argv[1].split()))
extra_argv = " '{0}'".format(" ".join(sys.argv[1].split()))
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, extra_argv)
@@ -212,7 +213,7 @@ def generate_targets(repo_path, deps_map):
for src in src_mk.get("MICROBENCH_SOURCES", []):
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
TARGETS.add_binary(name, [src], [], extra_bench_libs=True)
print(f"Extra dependencies:\n{json.dumps(deps_map)}")
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
# Dictionary test executable name -> relative source file path
test_source_map = {}
+1
View File
@@ -1,4 +1,5 @@
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
try:
from builtins import object, str
+1
View File
@@ -1,4 +1,5 @@
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import absolute_import, division, print_function, unicode_literals
rocksdb_target_header_template = """# This file \100generated by:
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
+1
View File
@@ -2,6 +2,7 @@
"""
This module keeps commonly used components.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
try:
from builtins import object
+5 -4
View File
@@ -25,6 +25,7 @@
#
# The solution is to move the include out of the #ifdef.
from __future__ import print_function
import argparse
import re
@@ -61,7 +62,7 @@ def expand_include(
included.add(include_path)
with open(include_path) as f:
print(f'#line 1 "{include_path}"', file=source_out)
print('#line 1 "{}"'.format(include_path), file=source_out)
process_file(
f, include_path, source_out, header_out, include_paths, public_include_paths
)
@@ -117,7 +118,7 @@ def process_file(
)
if expanded:
print(f'#line {line + 1} "{abs_path}"', file=source_out)
print('#line {} "{}"'.format(line + 1, abs_path), file=source_out)
elif text != "#pragma once\n":
source_out.write(text)
@@ -156,8 +157,8 @@ def main():
with open(filename) as f, open(args.source_out, "w") as source_out, open(
args.header_out, "w"
) as header_out:
print(f'#line 1 "{filename}"', file=source_out)
print(f'#include "{header_out.name}"', file=source_out)
print('#line 1 "{}"'.format(filename), file=source_out)
print('#include "{}"'.format(header_out.name), file=source_out)
process_file(
f, abs_path, source_out, header_out, include_paths, public_include_paths
)
+2 -2
View File
@@ -102,7 +102,7 @@ class BenchmarkUtils:
class ResultParser:
def __init__(self, field=r"(\w|[+-:.%])+", intrafield=r"(\s)+", separator="\t"):
def __init__(self, field="(\w|[+-:.%])+", intrafield="(\s)+", separator="\t"):
self.field = re.compile(field)
self.intra = re.compile(intrafield)
self.sep = re.compile(separator)
@@ -159,7 +159,7 @@ class ResultParser:
def load_report_from_tsv(filename: str):
file = open(filename)
file = open(filename, "r")
contents = file.readlines()
file.close()
parser = ResultParser()
+11 -10
View File
@@ -9,6 +9,7 @@
- Prints those error messages to stdout
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import re
import sys
@@ -42,7 +43,7 @@ class GTestErrorParser(ErrorParserBase):
return None
gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line)
if gtest_fail_match:
return "{} failed: {}".format(self._last_gtest_name, gtest_fail_match.group(1))
return "%s failed: %s" % (self._last_gtest_name, gtest_fail_match.group(1))
return None
@@ -65,52 +66,52 @@ class CompilerErrorParser(MatchErrorParser):
# format (link error):
# '<filename>:<line #>: error: <error msg>'
# The below regex catches both
super().__init__(r"\S+:\d+: error:")
super(CompilerErrorParser, self).__init__(r"\S+:\d+: error:")
class ScanBuildErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"scan-build: \d+ bugs found.$")
super(ScanBuildErrorParser, self).__init__(r"scan-build: \d+ bugs found.$")
class DbCrashErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"\*\*\*.*\^$|TEST FAILED.")
super(DbCrashErrorParser, self).__init__(r"\*\*\*.*\^$|TEST FAILED.")
class WriteStressErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(
super(WriteStressErrorParser, self).__init__(
r"ERROR: write_stress died with exitcode=\d+"
)
class AsanErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"==\d+==ERROR: AddressSanitizer:")
super(AsanErrorParser, self).__init__(r"==\d+==ERROR: AddressSanitizer:")
class UbsanErrorParser(MatchErrorParser):
def __init__(self):
# format: '<filename>:<line #>:<column #>: runtime error: <error msg>'
super().__init__(r"\S+:\d+:\d+: runtime error:")
super(UbsanErrorParser, self).__init__(r"\S+:\d+:\d+: runtime error:")
class ValgrindErrorParser(MatchErrorParser):
def __init__(self):
# just grab the summary, valgrind doesn't clearly distinguish errors
# from other log messages.
super().__init__(r"==\d+== ERROR SUMMARY:")
super(ValgrindErrorParser, self).__init__(r"==\d+== ERROR SUMMARY:")
class CompatErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"==== .*[Ee]rror.* ====$")
super(CompatErrorParser, self).__init__(r"==== .*[Ee]rror.* ====$")
class TsanErrorParser(MatchErrorParser):
def __init__(self):
super().__init__(r"WARNING: ThreadSanitizer:")
super(TsanErrorParser, self).__init__(r"WARNING: ThreadSanitizer:")
_TEST_NAME_TO_PARSERS = {
+1 -3
View File
@@ -133,9 +133,7 @@ Status Cache::CreateFromString(const ConfigOptions& config_options,
std::shared_ptr<Cache>* result) {
Status status;
std::shared_ptr<Cache> cache;
if (StartsWith(value, "null")) {
cache = nullptr;
} else if (value.find("://") == std::string::npos) {
if (value.find("://") == std::string::npos) {
if (value.find('=') == std::string::npos) {
cache = NewLRUCache(ParseSizeT(value));
} else {
+4 -3
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
from __future__ import print_function
import optparse
import re
@@ -108,11 +109,11 @@ def report_coverage():
# Check if we need to display coverage info for interested files.
if len(interested_files):
per_file_coverage = {
fname: per_file_coverage[fname]
per_file_coverage = dict(
(fname, per_file_coverage[fname])
for fname in interested_files
if fname in per_file_coverage
}
)
# If we only interested in several files, it makes no sense to report
# the total_coverage
total_coverage = None
-2
View File
@@ -83,8 +83,6 @@ class ArenaWrappedDBIter : public Iterator {
Status Refresh() override;
Status Refresh(const Snapshot*) override;
bool PrepareValue() override { return db_iter_->PrepareValue(); }
void Init(Env* env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
+9 -34
View File
@@ -13,11 +13,14 @@ namespace ROCKSDB_NAMESPACE {
class AttributeGroupIteratorImpl : public AttributeGroupIterator {
public:
AttributeGroupIteratorImpl(
const ReadOptions& read_options, const Comparator* comparator,
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
cfh_iter_pairs)
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
ResetFunc(this), PopulateFunc(this)) {}
const Comparator* comparator,
const std::vector<ColumnFamilyHandle*>& column_families,
const std::vector<Iterator*>& child_iterators)
: impl_(
comparator, column_families, child_iterators, [this]() { Reset(); },
[this](const autovector<MultiCfIteratorInfo>& items) {
AddToAttributeGroups(items);
}) {}
~AttributeGroupIteratorImpl() override {}
// No copy allowed
@@ -42,36 +45,8 @@ class AttributeGroupIteratorImpl : public AttributeGroupIterator {
void Reset() { attribute_groups_.clear(); }
bool PrepareValue() override { return impl_.PrepareValue(); }
private:
class ResetFunc {
public:
explicit ResetFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
void operator()() const {
assert(iter_);
iter_->Reset();
}
private:
AttributeGroupIteratorImpl* iter_;
};
class PopulateFunc {
public:
explicit PopulateFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
assert(iter_);
iter_->AddToAttributeGroups(items);
}
private:
AttributeGroupIteratorImpl* iter_;
};
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
MultiCfIteratorImpl impl_;
IteratorAttributeGroups attribute_groups_;
void AddToAttributeGroups(const autovector<MultiCfIteratorInfo>& items);
};
-10
View File
@@ -42,7 +42,6 @@ Status BlobFileCache::GetBlobFileReader(
assert(blob_file_reader);
assert(blob_file_reader->IsEmpty());
// NOTE: sharing same Cache with table_cache
const Slice key = GetSliceForKey(&blob_file_number);
assert(cache_);
@@ -99,13 +98,4 @@ Status BlobFileCache::GetBlobFileReader(
return Status::OK();
}
void BlobFileCache::Evict(uint64_t blob_file_number) {
// NOTE: sharing same Cache with table_cache
const Slice key = GetSliceForKey(&blob_file_number);
assert(cache_);
cache_.get()->Erase(key);
}
} // namespace ROCKSDB_NAMESPACE
-9
View File
@@ -36,15 +36,6 @@ class BlobFileCache {
uint64_t blob_file_number,
CacheHandleGuard<BlobFileReader>* blob_file_reader);
// Called when a blob file is obsolete to ensure it is removed from the cache
// to avoid effectively leaking the open file and assicated memory
void Evict(uint64_t blob_file_number);
// Used to identify cache entries for blob files (not normally useful)
static const Cache::CacheItemHelper* GetHelper() {
return CacheInterface::GetBasicHelper();
}
private:
using CacheInterface =
BasicTypedCacheInterface<BlobFileReader, CacheEntryRole::kMisc>;
+6 -7
View File
@@ -20,24 +20,23 @@
namespace ROCKSDB_NAMESPACE {
BlobSource::BlobSource(const ImmutableOptions& immutable_options,
const MutableCFOptions& mutable_cf_options,
BlobSource::BlobSource(const ImmutableOptions* immutable_options,
const std::string& db_id,
const std::string& db_session_id,
BlobFileCache* blob_file_cache)
: db_id_(db_id),
db_session_id_(db_session_id),
statistics_(immutable_options.statistics.get()),
statistics_(immutable_options->statistics.get()),
blob_file_cache_(blob_file_cache),
blob_cache_(immutable_options.blob_cache),
lowest_used_cache_tier_(immutable_options.lowest_used_cache_tier) {
blob_cache_(immutable_options->blob_cache),
lowest_used_cache_tier_(immutable_options->lowest_used_cache_tier) {
auto bbto =
mutable_cf_options.table_factory->GetOptions<BlockBasedTableOptions>();
immutable_options->table_factory->GetOptions<BlockBasedTableOptions>();
if (bbto &&
bbto->cache_usage_options.options_overrides.at(CacheEntryRole::kBlobCache)
.charged == CacheEntryRoleOptions::Decision::kEnabled) {
blob_cache_ = SharedCacheInterface{std::make_shared<ChargedCache>(
immutable_options.blob_cache, bbto->block_cache)};
immutable_options->blob_cache, bbto->block_cache)};
}
}
+1 -5
View File
@@ -21,7 +21,6 @@
namespace ROCKSDB_NAMESPACE {
struct ImmutableOptions;
struct MutableCFOptions;
class Status;
class FilePrefetchBuffer;
class Slice;
@@ -32,10 +31,7 @@ class Slice;
// storage with minimal cost.
class BlobSource {
public:
// NOTE: db_id, db_session_id, and blob_file_cache are saved by reference or
// pointer.
BlobSource(const ImmutableOptions& immutable_options,
const MutableCFOptions& mutable_cf_options,
BlobSource(const ImmutableOptions* immutable_options,
const std::string& db_id, const std::string& db_session_id,
BlobFileCache* blob_file_cache);
+14 -22
View File
@@ -148,7 +148,6 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -194,8 +193,8 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -465,7 +464,6 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -500,8 +498,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, nullptr /*HistogramImpl*/, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -591,7 +589,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -647,8 +644,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -785,7 +782,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -831,8 +827,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
backing_cache.get(), &immutable_options, &file_options,
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ReadOptions read_options;
read_options.verify_checksums = true;
@@ -1109,7 +1105,6 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr uint32_t column_family_id = 1;
constexpr bool has_ttl = false;
@@ -1142,8 +1137,8 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
backing_cache.get(), &immutable_options, &file_options, column_family_id,
blob_file_read_hist, nullptr /*IOTracer*/));
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
CacheHandleGuard<BlobFileReader> file_reader;
ReadOptions read_options;
@@ -1410,7 +1405,6 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr ExpirationRange expiration_range;
@@ -1432,8 +1426,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
backing_cache.get(), &immutable_options, &file_options,
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ConcurrentCacheReservationManager* cache_res_mgr =
static_cast<ChargedCache*>(blob_source.GetBlobCache())
@@ -1525,8 +1519,6 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
DestroyAndReopen(options_);
ImmutableOptions immutable_options(options_);
MutableCFOptions mutable_cf_options(options_);
constexpr size_t blob_size = 24 << 10; // 24KB
for (size_t i = 0; i < kNumBlobs; ++i) {
blob_file_size_ -= blobs_[i].size(); // old blob size
@@ -1554,8 +1546,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
backing_cache.get(), &immutable_options, &file_options,
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
db_session_id_, blob_file_cache.get());
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
blob_file_cache.get());
ConcurrentCacheReservationManager* cache_res_mgr =
static_cast<ChargedCache*>(blob_source.GetBlobCache())
-149
View File
@@ -374,115 +374,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCachePinning) {
}
}
TEST_F(DBBlobBasicTest, IterateBlobsAllowUnpreparedValue) {
Options options = GetDefaultOptions();
options.enable_blob_files = true;
Reopen(options);
constexpr size_t num_blobs = 5;
std::vector<std::string> keys;
std::vector<std::string> blobs;
for (size_t i = 0; i < num_blobs; ++i) {
keys.emplace_back("key" + std::to_string(i));
blobs.emplace_back("blob" + std::to_string(i));
ASSERT_OK(Put(keys[i], blobs[i]));
}
ASSERT_OK(Flush());
ReadOptions read_options;
read_options.allow_unprepared_value = true;
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
{
size_t i = 0;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_EQ(iter->value(), blobs[i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
{
size_t i = 0;
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
{
size_t i = 1;
for (iter->Seek(keys[i]); iter->Valid(); iter->Next()) {
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[i]);
ASSERT_EQ(iter->value(), blobs[i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
{
size_t i = 1;
for (iter->SeekForPrev(keys[num_blobs - 1 - i]); iter->Valid();
iter->Prev()) {
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
ASSERT_OK(iter->status());
++i;
}
ASSERT_OK(iter->status());
ASSERT_EQ(i, num_blobs);
}
}
TEST_F(DBBlobBasicTest, MultiGetBlobs) {
constexpr size_t min_blob_size = 6;
@@ -1764,46 +1655,6 @@ TEST_P(DBBlobBasicIOErrorTest, CompactionFilterReadBlob_IOError) {
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBBlobBasicIOErrorTest, IterateBlobsAllowUnpreparedValue_IOError) {
Options options;
options.env = fault_injection_env_.get();
options.enable_blob_files = true;
Reopen(options);
constexpr char key[] = "key";
constexpr char blob_value[] = "blob_value";
ASSERT_OK(Put(key, blob_value));
ASSERT_OK(Flush());
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
fault_injection_env_->SetFilesystemActive(false,
Status::IOError(sync_point_));
});
SyncPoint::GetInstance()->EnableProcessing();
ReadOptions read_options;
read_options.allow_unprepared_value = true;
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
iter->SeekToFirst();
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key(), key);
ASSERT_TRUE(iter->value().empty());
ASSERT_OK(iter->status());
ASSERT_FALSE(iter->PrepareValue());
ASSERT_FALSE(iter->Valid());
ASSERT_TRUE(iter->status().IsIOError());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBBlobBasicTest, WarmCacheWithBlobsDuringFlush) {
Options options = GetDefaultOptions();
+24 -4
View File
@@ -53,7 +53,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
assert((tboptions.column_family_id ==
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
tboptions.column_family_name.empty());
return tboptions.moptions.table_factory->NewTableBuilder(tboptions, file);
return tboptions.ioptions.table_factory->NewTableBuilder(tboptions, file);
}
Status BuildTable(
@@ -206,6 +206,10 @@ Status BuildTable(
/*compaction=*/nullptr, compaction_filter.get(),
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
const size_t ts_sz = ucmp->timestamp_size();
const bool logical_strip_timestamp =
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
SequenceNumber smallest_preferred_seqno = kMaxSequenceNumber;
std::string key_after_flush_buf;
std::string value_buf;
@@ -218,6 +222,16 @@ Status BuildTable(
Slice key_after_flush = key_after_flush_buf;
Slice value_after_flush = value;
// If user defined timestamps will be stripped from user key after flush,
// the in memory version of the key act logically the same as one with a
// minimum timestamp. We update the timestamp here so file boundary and
// output validator, block builder all see the effect of the stripping.
if (logical_strip_timestamp) {
key_after_flush_buf.clear();
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
key_after_flush = key_after_flush_buf;
}
if (ikey.type == kTypeValuePreferredSeqno) {
auto [unpacked_value, unix_write_time] =
ParsePackedValueWithWriteTime(value);
@@ -277,7 +291,11 @@ Status BuildTable(
Slice last_tombstone_start_user_key{};
for (range_del_it->SeekToFirst(); range_del_it->Valid();
range_del_it->Next()) {
auto tombstone = range_del_it->Tombstone();
// When user timestamp should not be persisted, we logically strip a
// range tombstone's start and end key's timestamp (replace it with min
// timestamp) before passing them along to table builder and to update
// file boundaries.
auto tombstone = range_del_it->Tombstone(logical_strip_timestamp);
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
builder->Add(kv.first.Encode(), kv.second);
InternalKey tombstone_end = tombstone.SerializeEndKey();
@@ -420,7 +438,8 @@ Status BuildTable(
// the goal is to cache it here for further user reads.
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
tboptions.read_options, file_options, tboptions.internal_comparator,
*meta, nullptr /* range_del_agg */, mutable_cf_options, nullptr,
*meta, nullptr /* range_del_agg */,
mutable_cf_options.prefix_extractor, nullptr,
(internal_stats == nullptr) ? nullptr
: internal_stats->GetFileReadHist(0),
TableReaderCaller::kFlush, /*arena=*/nullptr,
@@ -428,7 +447,8 @@ Status BuildTable(
MaxFileSizeForL0MetaPin(mutable_cf_options),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key*/ nullptr,
/*allow_unprepared_value*/ false));
/*allow_unprepared_value*/ false,
mutable_cf_options.block_protection_bytes_per_key));
s = it->status();
if (s.ok() && paranoid_file_checks) {
OutputValidator file_validator(tboptions.internal_comparator,
+9 -35
View File
@@ -12,12 +12,14 @@ namespace ROCKSDB_NAMESPACE {
// EXPERIMENTAL
class CoalescingIterator : public Iterator {
public:
CoalescingIterator(
const ReadOptions& read_options, const Comparator* comparator,
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
cfh_iter_pairs)
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
ResetFunc(this), PopulateFunc(this)) {}
CoalescingIterator(const Comparator* comparator,
const std::vector<ColumnFamilyHandle*>& column_families,
const std::vector<Iterator*>& child_iterators)
: impl_(
comparator, column_families, child_iterators, [this]() { Reset(); },
[this](const autovector<MultiCfIteratorInfo>& items) {
Coalesce(items);
}) {}
~CoalescingIterator() override {}
// No copy allowed
@@ -48,36 +50,8 @@ class CoalescingIterator : public Iterator {
wide_columns_.clear();
}
bool PrepareValue() override { return impl_.PrepareValue(); }
private:
class ResetFunc {
public:
explicit ResetFunc(CoalescingIterator* iter) : iter_(iter) {}
void operator()() const {
assert(iter_);
iter_->Reset();
}
private:
CoalescingIterator* iter_;
};
class PopulateFunc {
public:
explicit PopulateFunc(CoalescingIterator* iter) : iter_(iter) {}
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
assert(iter_);
iter_->Coalesce(items);
}
private:
CoalescingIterator* iter_;
};
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
MultiCfIteratorImpl impl_;
Slice value_;
WideColumns wide_columns_;
+6 -12
View File
@@ -595,8 +595,8 @@ ColumnFamilyData::ColumnFamilyData(
blob_file_cache_.reset(
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
internal_stats_->GetBlobFileReadHist(), io_tracer));
blob_source_.reset(new BlobSource(ioptions_, mutable_cf_options_, db_id,
db_session_id, blob_file_cache_.get()));
blob_source_.reset(new BlobSource(ioptions(), db_id, db_session_id,
blob_file_cache_.get()));
if (ioptions_.compaction_style == kCompactionStyleLevel) {
compaction_picker_.reset(
@@ -901,11 +901,7 @@ uint64_t GetPendingCompactionBytesForCompactionSpeedup(
return slowdown_threshold;
}
// Prevent a small CF from triggering parallel compactions for other CFs.
// Require compaction debt to be more than a full L0 to Lbase compaction.
const uint64_t kMinDebtSize = 2 * mutable_cf_options.max_bytes_for_level_base;
uint64_t size_threshold =
std::max(bottommost_files_size / kBottommostSizeDivisor, kMinDebtSize);
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
return std::min(size_threshold, slowdown_threshold);
}
@@ -1176,12 +1172,10 @@ bool ColumnFamilyData::NeedsCompaction() const {
Compaction* ColumnFamilyData::PickCompaction(
const MutableCFOptions& mutable_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer) {
const MutableDBOptions& mutable_db_options, LogBuffer* log_buffer) {
auto* result = compaction_picker_->PickCompaction(
GetName(), mutable_options, mutable_db_options, existing_snapshots,
snapshot_checker, current_->storage_info(), log_buffer);
GetName(), mutable_options, mutable_db_options, current_->storage_info(),
log_buffer);
if (result != nullptr) {
result->FinalizeInputInfo(current_);
}
+6 -10
View File
@@ -16,7 +16,6 @@
#include "cache/cache_reservation_manager.h"
#include "db/memtable_list.h"
#include "db/snapshot_checker.h"
#include "db/table_cache.h"
#include "db/table_properties_collector.h"
#include "db/write_batch_internal.h"
@@ -386,9 +385,9 @@ class ColumnFamilyData {
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
// REQUIRE: DB mutex held
void SetMemtable(MemTable* new_mem) {
new_mem->SetID(++last_memtable_id_);
uint64_t memtable_id = last_memtable_id_.fetch_add(1) + 1;
new_mem->SetID(memtable_id);
mem_ = new_mem;
}
@@ -402,18 +401,15 @@ class ColumnFamilyData {
SequenceNumber earliest_seq);
TableCache* table_cache() const { return table_cache_.get(); }
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
BlobSource* blob_source() const { return blob_source_.get(); }
// See documentation in compaction_picker.h
// REQUIRES: DB mutex held
bool NeedsCompaction() const;
// REQUIRES: DB mutex held
Compaction* PickCompaction(
const MutableCFOptions& mutable_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer);
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
const MutableDBOptions& mutable_db_options,
LogBuffer* log_buffer);
// Check if the passed range overlap with any running compactions.
// REQUIRES: DB mutex held
@@ -673,7 +669,7 @@ class ColumnFamilyData {
bool allow_2pc_;
// Memtable id to track flush.
uint64_t last_memtable_id_;
std::atomic<uint64_t> last_memtable_id_;
// Directories corresponding to cf_paths.
std::vector<std::shared_ptr<FSDirectory>> data_dirs_;
+7 -98
View File
@@ -3012,25 +3012,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
ASSERT_OK(db_->Flush(FlushOptions()));
{
// 1MB debt is way bigger than bottommost data so definitely triggers
// speedup.
VersionStorageInfo* vstorage = cfd->current()->storage_info();
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
// Eight bytes is way smaller than bottommost data so definitely does not
// trigger speedup.
vstorage->TEST_set_estimated_compaction_needed_bytes(8, dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
// 1MB is much larger than bottommost level size. However, since it's too
// small in terms of absolute size, it does not trigger parallel compaction
// in this case (see GetPendingCompactionBytesForCompactionSpeedup()).
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
vstorage->TEST_set_estimated_compaction_needed_bytes(
2 * mutable_cf_options.max_bytes_for_level_base, dbmu);
RecalculateWriteStallConditions(cfd, mutable_cf_options);
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
}
}
@@ -3876,91 +3870,6 @@ TEST_F(ManualFlushSkipRetainUDTTest, ManualFlush) {
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, FlushRemovesStaleEntries) {
column_family_options_.max_write_buffer_number = 4;
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
ColumnFamilyData* cfd =
static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
for (int version = 0; version < 100; version++) {
if (version == 50) {
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_SwitchMemtable(cfd));
}
ASSERT_OK(
Put(0, "foo", EncodeAsUint64(version), "v" + std::to_string(version)));
}
ASSERT_OK(Flush(0));
TablePropertiesCollection tables_properties;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
ASSERT_EQ(1, tables_properties.size());
std::shared_ptr<const TableProperties> table_properties =
tables_properties.begin()->second;
ASSERT_EQ(1, table_properties->num_entries);
ASSERT_EQ(0, table_properties->num_deletions);
ASSERT_EQ(0, table_properties->num_range_deletions);
CheckEffectiveCutoffTime(100);
CheckAutomaticFlushRetainUDT(101);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, RangeDeletionFlushRemovesStaleEntries) {
column_family_options_.max_write_buffer_number = 4;
Open();
// TODO(yuzhangyu): a non 0 full history ts low is needed for this garbage
// collection to kick in. This doesn't work well for the very first flush of
// the column family. Not a big issue, but would be nice to improve this.
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(9)));
for (int i = 10; i < 100; i++) {
ASSERT_OK(Put(0, "foo" + std::to_string(i), EncodeAsUint64(i),
"val" + std::to_string(i)));
if (i % 2 == 1) {
ASSERT_OK(db_->DeleteRange(WriteOptions(), "foo" + std::to_string(i - 1),
"foo" + std::to_string(i), EncodeAsUint64(i)));
}
}
ASSERT_OK(Flush(0));
CheckEffectiveCutoffTime(100);
std::string read_ts = EncodeAsUint64(100);
std::string min_ts = EncodeAsUint64(0);
ReadOptions ropts;
Slice read_ts_slice = read_ts;
std::string value;
ropts.timestamp = &read_ts_slice;
{
Iterator* iter = db_->NewIterator(ropts);
iter->SeekToFirst();
int i = 11;
while (iter->Valid()) {
ASSERT_TRUE(iter->Valid());
ASSERT_EQ("foo" + std::to_string(i), iter->key());
ASSERT_EQ("val" + std::to_string(i), iter->value());
ASSERT_EQ(min_ts, iter->timestamp());
iter->Next();
i += 2;
}
ASSERT_OK(iter->status());
delete iter;
}
TablePropertiesCollection tables_properties;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
ASSERT_EQ(1, tables_properties.size());
std::shared_ptr<const TableProperties> table_properties =
tables_properties.begin()->second;
// 45 point data + 45 range deletions. 45 obsolete point data are garbage
// collected.
ASSERT_EQ(90, table_properties->num_entries);
ASSERT_EQ(45, table_properties->num_deletions);
ASSERT_EQ(45, table_properties->num_range_deletions);
Close();
}
TEST_F(ManualFlushSkipRetainUDTTest, ManualCompaction) {
Open();
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
+12 -99
View File
@@ -283,10 +283,9 @@ Compaction::Compaction(
uint32_t _output_path_id, CompressionType _compression,
CompressionOptions _compression_opts, Temperature _output_temperature,
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
std::optional<SequenceNumber> _earliest_snapshot,
const SnapshotChecker* _snapshot_checker, bool _manual_compaction,
const std::string& _trim_ts, double _score, bool _deletion_compaction,
bool l0_files_might_overlap, CompactionReason _compaction_reason,
bool _manual_compaction, const std::string& _trim_ts, double _score,
bool _deletion_compaction, bool l0_files_might_overlap,
CompactionReason _compaction_reason,
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
double _blob_garbage_collection_age_cutoff)
: input_vstorage_(vstorage),
@@ -308,8 +307,6 @@ Compaction::Compaction(
l0_files_might_overlap_(l0_files_might_overlap),
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
grandparents_(std::move(_grandparents)),
earliest_snapshot_(_earliest_snapshot),
snapshot_checker_(_snapshot_checker),
score_(_score),
bottommost_level_(
// For simplicity, we don't support the concept of "bottommost level"
@@ -370,13 +367,9 @@ Compaction::Compaction(
// setup input_levels_
{
input_levels_.resize(num_input_levels());
if (earliest_snapshot_.has_value()) {
FilterInputsForCompactionIterator();
} else {
for (size_t which = 0; which < num_input_levels(); which++) {
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
&arena_);
}
for (size_t which = 0; which < num_input_levels(); which++) {
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
&arena_);
}
}
@@ -752,10 +745,8 @@ void Compaction::ResetNextCompactionIndex() {
}
namespace {
int InputSummary(const std::vector<FileMetaData*>& files,
const std::vector<bool>& files_filtered, char* output,
int InputSummary(const std::vector<FileMetaData*>& files, char* output,
int len) {
assert(files_filtered.empty() || (files.size() == files_filtered.size()));
*output = '\0';
int write = 0;
for (size_t i = 0; i < files.size(); i++) {
@@ -763,14 +754,8 @@ int InputSummary(const std::vector<FileMetaData*>& files,
int ret;
char sztxt[16];
AppendHumanBytes(files.at(i)->fd.GetFileSize(), sztxt, 16);
if (files_filtered.empty()) {
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
files.at(i)->fd.GetNumber(), sztxt);
} else {
ret = snprintf(output + write, sz, "%" PRIu64 "(%s filtered:%s) ",
files.at(i)->fd.GetNumber(), sztxt,
files_filtered.at(i) ? "true" : "false");
}
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
files.at(i)->fd.GetNumber(), sztxt);
if (ret < 0 || ret >= sz) {
break;
}
@@ -796,15 +781,8 @@ void Compaction::Summary(char* output, int len) {
return;
}
}
assert(non_start_level_input_files_filtered_.empty() ||
non_start_level_input_files_filtered_.size() == inputs_.size() - 1);
write += InputSummary(
inputs_[level_iter].files,
(level_iter == 0 || non_start_level_input_files_filtered_.empty())
? std::vector<bool>{}
: non_start_level_input_files_filtered_[level_iter - 1],
output + write, len - write);
write +=
InputSummary(inputs_[level_iter].files, output + write, len - write);
if (write < 0 || write >= len) {
return;
}
@@ -887,7 +865,7 @@ bool Compaction::ShouldFormSubcompactions() const {
return false;
}
if (mutable_cf_options_.table_factory->Name() ==
if (cfd_->ioptions()->table_factory->Name() ==
TableFactory::kPlainTableName()) {
return false;
}
@@ -1013,69 +991,4 @@ int Compaction::EvaluatePenultimateLevel(
return penultimate_level;
}
void Compaction::FilterInputsForCompactionIterator() {
assert(earliest_snapshot_.has_value());
// cfd_ is not populated at Compaction construction time, get it from
// VersionStorageInfo instead.
assert(input_vstorage_);
const auto* ucmp = input_vstorage_->user_comparator();
assert(ucmp);
// Simply comparing file boundaries when user-defined timestamp is defined
// is not as safe because we need to also compare timestamp to know for
// sure. Although entries with higher timestamp is also supposed to have
// higher sequence number for the same user key (without timestamp).
assert(ucmp->timestamp_size() == 0);
size_t num_input_levels = inputs_.size();
// TODO(yuzhangyu): filtering of older L0 file by new L0 file is not
// supported yet.
FileMetaData* rangedel_candidate = inputs_[0].level == 0
? inputs_[0].files.back()
: inputs_[0].files.front();
assert(rangedel_candidate);
if (!rangedel_candidate->FileIsStandAloneRangeTombstone() ||
!DataIsDefinitelyInSnapshot(rangedel_candidate->fd.smallest_seqno,
earliest_snapshot_.value(),
snapshot_checker_)) {
for (size_t level = 0; level < num_input_levels; level++) {
DoGenerateLevelFilesBrief(&input_levels_[level], inputs_[level].files,
&arena_);
}
return;
}
Slice rangedel_start_ukey = rangedel_candidate->smallest.user_key();
Slice rangedel_end_ukey = rangedel_candidate->largest.user_key();
SequenceNumber rangedel_seqno = rangedel_candidate->fd.smallest_seqno;
std::vector<std::vector<FileMetaData*>> non_start_level_input_files;
non_start_level_input_files.reserve(num_input_levels - 1);
non_start_level_input_files_filtered_.reserve(num_input_levels - 1);
for (size_t level = 1; level < num_input_levels; level++) {
non_start_level_input_files.emplace_back();
non_start_level_input_files_filtered_.emplace_back();
for (FileMetaData* file : inputs_[level].files) {
non_start_level_input_files_filtered_.back().push_back(false);
// When range data and point data has the same sequence number, point
// data wins. Range deletion end key is exclusive, so check it's bigger
// than file right boundary user key.
if (rangedel_seqno > file->fd.largest_seqno &&
ucmp->CompareWithoutTimestamp(rangedel_start_ukey,
file->smallest.user_key()) <= 0 &&
ucmp->CompareWithoutTimestamp(rangedel_end_ukey,
file->largest.user_key()) > 0) {
non_start_level_input_files_filtered_.back().back() = true;
} else {
non_start_level_input_files.back().push_back(file);
}
}
}
DoGenerateLevelFilesBrief(&input_levels_[0], inputs_[0].files, &arena_);
assert(non_start_level_input_files.size() == num_input_levels - 1);
for (size_t level = 1; level < num_input_levels; level++) {
DoGenerateLevelFilesBrief(&input_levels_[level],
non_start_level_input_files[level - 1], &arena_);
}
}
} // namespace ROCKSDB_NAMESPACE
+1 -27
View File
@@ -8,8 +8,6 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#pragma once
#include "db/snapshot_checker.h"
#include "db/version_set.h"
#include "memory/arena.h"
#include "options/cf_options.h"
@@ -92,8 +90,6 @@ class Compaction {
CompressionOptions compression_opts,
Temperature output_temperature, uint32_t max_subcompactions,
std::vector<FileMetaData*> grandparents,
std::optional<SequenceNumber> earliest_snapshot,
const SnapshotChecker* snapshot_checker,
bool manual_compaction = false, const std::string& trim_ts = "",
double score = -1, bool deletion_compaction = false,
bool l0_files_might_overlap = true,
@@ -464,13 +460,6 @@ class Compaction {
// `Compaction::WithinPenultimateLevelOutputRange()`.
void PopulatePenultimateLevelOutputRange();
// If oldest snapshot is specified at Compaction construction time, we have
// an opportunity to optimize inputs for compaction iterator for this case:
// When a standalone range deletion file on the start level is recognized and
// can be determined to completely shadow some input files on non-start level.
// These files will be filtered out and later not feed to compaction iterator.
void FilterInputsForCompactionIterator();
// Get the atomic file boundaries for all files in the compaction. Necessary
// in order to avoid the scenario described in
// https://github.com/facebook/rocksdb/pull/4432#discussion_r221072219 and
@@ -521,27 +510,12 @@ class Compaction {
// Compaction input files organized by level. Constant after construction
const std::vector<CompactionInputFiles> inputs_;
// All files from inputs_ that are not filtered and will be fed to compaction
// iterator, organized more closely in memory.
// A copy of inputs_, organized more closely in memory
autovector<LevelFilesBrief, 2> input_levels_;
// State used to check for number of overlapping grandparent files
// (grandparent == "output_level_ + 1")
std::vector<FileMetaData*> grandparents_;
// The earliest snapshot and snapshot checker at compaction picking time.
// These fields are only set for deletion triggered compactions picked in
// universal compaction. And when user-defined timestamp is not enabled.
// It will be used to possibly filter out some non start level input files.
std::optional<SequenceNumber> earliest_snapshot_;
const SnapshotChecker* snapshot_checker_;
// Markers for which non start level input files are filtered out if
// applicable. Only applicable if earliest_snapshot_ is provided and input
// start level has a standalone range deletion file.
std::vector<std::vector<bool>> non_start_level_input_files_filtered_;
// bool standalone_range_tombstones_used_for_filtering_inputs_;
const double score_; // score that was used to pick this compaction.
// Is this compaction creating a file in the bottom most level?
+8 -2
View File
@@ -540,12 +540,18 @@ class CompactionIterator {
inline bool CompactionIterator::DefinitelyInSnapshot(SequenceNumber seq,
SequenceNumber snapshot) {
return DataIsDefinitelyInSnapshot(seq, snapshot, snapshot_checker_);
return ((seq) <= (snapshot) &&
(snapshot_checker_ == nullptr ||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
SnapshotCheckerResult::kInSnapshot)));
}
inline bool CompactionIterator::DefinitelyNotInSnapshot(
SequenceNumber seq, SequenceNumber snapshot) {
return DataIsDefinitelyNotInSnapshot(seq, snapshot, snapshot_checker_);
return ((seq) > (snapshot) ||
(snapshot_checker_ != nullptr &&
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
SnapshotCheckerResult::kNotInSnapshot)));
}
} // namespace ROCKSDB_NAMESPACE
+12 -16
View File
@@ -469,7 +469,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
ReadOptions read_options(Env::IOActivity::kCompaction);
read_options.rate_limiter_priority = GetRateLimiterPriority();
auto* c = compact_->compaction;
if (c->mutable_cf_options()->table_factory->Name() ==
if (c->immutable_options()->table_factory->Name() ==
TableFactory::kPlainTableName()) {
return;
}
@@ -506,7 +506,9 @@ void CompactionJob::GenSubcompactionBoundaries() {
FileMetaData* f = flevel->files[i].file_metadata;
std::vector<TableReader::Anchor> my_anchors;
Status s = cfd->table_cache()->ApproximateKeyAnchors(
read_options, icomp, *f, *c->mutable_cf_options(), my_anchors);
read_options, icomp, *f,
c->mutable_cf_options()->block_protection_bytes_per_key,
my_anchors);
if (!s.ok() || my_anchors.empty()) {
my_anchors.emplace_back(f->largest.user_key(), f->fd.GetFileSize());
}
@@ -709,6 +711,8 @@ Status CompactionJob::Run() {
}
}
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
auto& prefix_extractor =
compact_->compaction->mutable_cf_options()->prefix_extractor;
std::atomic<size_t> next_file_idx(0);
auto verify_table = [&](Status& output_status) {
while (true) {
@@ -729,8 +733,7 @@ Status CompactionJob::Run() {
InternalIterator* iter = cfd->table_cache()->NewIterator(
verify_table_read_options, file_options_,
cfd->internal_comparator(), files_output[file_idx]->meta,
/*range_del_agg=*/nullptr,
*compact_->compaction->mutable_cf_options(),
/*range_del_agg=*/nullptr, prefix_extractor,
/*table_reader_ptr=*/nullptr,
cfd->internal_stats()->GetFileReadHist(
compact_->compaction->output_level()),
@@ -740,7 +743,9 @@ Status CompactionJob::Run() {
*compact_->compaction->mutable_cf_options()),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr,
/*allow_unprepared_value=*/false);
/*allow_unprepared_value=*/false,
compact_->compaction->mutable_cf_options()
->block_protection_bytes_per_key);
auto s = iter->status();
if (s.ok() && paranoid_file_checks_) {
@@ -801,12 +806,6 @@ Status CompactionJob::Run() {
}
}
// Before the compaction starts, is_remote_compaction was set to true if
// compaction_service is set. We now know whether each sub_compaction was
// done remotely or not. Reset is_remote_compaction back to false and allow
// AggregateCompactionStats() to set the right value.
compaction_job_stats_->is_remote_compaction = false;
// Finish up all bookkeeping to unify the subcompaction results.
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
uint64_t num_input_range_del = 0;
@@ -1085,7 +1084,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
}
// fallback to local compaction
assert(comp_status == CompactionServiceJobStatus::kUseLocal);
sub_compact->compaction_job_stats.is_remote_compaction = false;
}
uint64_t prev_cpu_micros = db_options_.clock->CPUMicros();
@@ -2003,12 +2001,10 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
bool has_error = false;
const ReadOptions read_options(Env::IOActivity::kCompaction);
const auto& input_table_properties = compaction->GetInputTableProperties();
// TODO(yuzhangyu): add dedicated stats for filtered files.
for (int input_level = 0;
input_level < static_cast<int>(compaction->num_input_levels());
++input_level) {
const LevelFilesBrief* flevel = compaction->input_levels(input_level);
size_t num_input_files = flevel->num_files;
size_t num_input_files = compaction->num_input_files(input_level);
uint64_t* bytes_read;
if (compaction->level(input_level) != compaction->output_level()) {
compaction_stats_.stats.num_input_files_in_non_output_levels +=
@@ -2020,7 +2016,7 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
bytes_read = &compaction_stats_.stats.bytes_read_output_level;
}
for (size_t i = 0; i < num_input_files; ++i) {
const FileMetaData* file_meta = flevel->files[i].file_metadata;
const FileMetaData* file_meta = compaction->input(input_level, i);
*bytes_read += file_meta->fd.GetFileSize();
uint64_t file_input_entries = file_meta->num_entries;
uint64_t file_num_range_del = file_meta->num_range_deletions;
+14 -25
View File
@@ -209,8 +209,6 @@ class CompactionJob {
// Returns true iff compaction_stats_.stats.num_input_records and
// num_input_range_del are calculated successfully.
bool UpdateCompactionStats(uint64_t* num_input_range_del = nullptr);
virtual void UpdateCompactionJobStats(
const InternalStats::CompactionStats& stats) const;
void LogCompaction();
virtual void RecordCompactionIOStats();
void CleanupCompaction();
@@ -281,7 +279,8 @@ class CompactionJob {
bool* compaction_released);
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
CompactionOutputs& outputs);
void UpdateCompactionJobStats(
const InternalStats::CompactionStats& stats) const;
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
CompactionJobStats* compaction_job_stats = nullptr);
@@ -386,7 +385,7 @@ struct CompactionServiceInput {
// files needed for this compaction, for both input level files and output
// level files.
std::vector<std::string> input_files;
int output_level = 0;
int output_level;
// db_id is used to generate unique id of sst on the remote compactor
std::string db_id;
@@ -397,8 +396,6 @@ struct CompactionServiceInput {
bool has_end = false;
std::string end;
uint64_t options_file_number = 0;
// serialization interface to read and write the object
static Status Read(const std::string& data_str, CompactionServiceInput* obj);
Status Write(std::string* output);
@@ -416,25 +413,20 @@ struct CompactionServiceOutputFile {
SequenceNumber largest_seqno;
std::string smallest_internal_key;
std::string largest_internal_key;
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
uint64_t file_creation_time = kUnknownFileCreationTime;
uint64_t epoch_number = kUnknownEpochNumber;
std::string file_checksum = kUnknownFileChecksum;
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
uint64_t oldest_ancester_time;
uint64_t file_creation_time;
uint64_t epoch_number;
uint64_t paranoid_hash;
bool marked_for_compaction;
UniqueId64x2 unique_id{};
TableProperties table_properties;
UniqueId64x2 unique_id;
CompactionServiceOutputFile() = default;
CompactionServiceOutputFile(
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
std::string _smallest_internal_key, std::string _largest_internal_key,
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
uint64_t _epoch_number, const std::string& _file_checksum,
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
bool _marked_for_compaction, UniqueId64x2 _unique_id,
const std::shared_ptr<const TableProperties>& _table_properties)
uint64_t _epoch_number, uint64_t _paranoid_hash,
bool _marked_for_compaction, UniqueId64x2 _unique_id)
: file_name(name),
smallest_seqno(smallest),
largest_seqno(largest),
@@ -443,12 +435,9 @@ struct CompactionServiceOutputFile {
oldest_ancester_time(_oldest_ancester_time),
file_creation_time(_file_creation_time),
epoch_number(_epoch_number),
file_checksum(_file_checksum),
file_checksum_func_name(_file_checksum_func_name),
paranoid_hash(_paranoid_hash),
marked_for_compaction(_marked_for_compaction),
unique_id(std::move(_unique_id)),
table_properties(*_table_properties.get()) {}
unique_id(std::move(_unique_id)) {}
};
// CompactionServiceResult contains the compaction result from a different db
@@ -457,11 +446,14 @@ struct CompactionServiceOutputFile {
struct CompactionServiceResult {
Status status;
std::vector<CompactionServiceOutputFile> output_files;
int output_level = 0;
int output_level;
// location of the output files
std::string output_path;
// some statistics about the compaction
uint64_t num_output_records = 0;
uint64_t total_bytes = 0;
uint64_t bytes_read = 0;
uint64_t bytes_written = 0;
CompactionJobStats stats;
@@ -507,9 +499,6 @@ class CompactionServiceCompactionJob : private CompactionJob {
protected:
void RecordCompactionIOStats() override;
void UpdateCompactionJobStats(
const InternalStats::CompactionStats& stats) const override;
private:
// Get table file name in output_path
std::string GetTableFileName(uint64_t file_number) override;
+9 -54
View File
@@ -250,7 +250,6 @@ class CompactionJobTestBase : public testing::Test {
} else {
assert(false);
}
mutable_cf_options_.table_factory = cf_options_.table_factory;
}
std::string GenerateFileName(uint64_t file_number) {
@@ -652,8 +651,7 @@ class CompactionJobTestBase : public testing::Test {
mutable_cf_options_.target_file_size_base,
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
cfd->GetLatestMutableCFOptions()->compression_opts,
Temperature::kUnknown, max_subcompactions, grandparents,
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
Temperature::kUnknown, max_subcompactions, grandparents, true);
compaction.FinalizeInputInfo(cfd->current());
assert(db_options_.info_log);
@@ -1658,44 +1656,20 @@ TEST_F(CompactionJobTest, ResultSerialization) {
};
result.status =
status_list.at(rnd.Uniform(static_cast<int>(status_list.size())));
std::string file_checksum = rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen));
std::string file_checksum_func_name = "MyAwesomeChecksumGenerator";
while (!rnd.OneIn(10)) {
TableProperties tp;
tp.user_collected_properties.emplace(
"UCP_Key1", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
tp.user_collected_properties.emplace(
"UCP_Key2", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
tp.readable_properties.emplace("RP_Key1",
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
tp.readable_properties.emplace("RP_K2y2",
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
std::shared_ptr<const TableProperties> table_properties =
std::make_shared<const TableProperties>(tp);
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
result.output_files.emplace_back(
rnd.RandomString(rnd.Uniform(kStrMaxLen)) /* file_name */,
rnd64.Uniform(UINT64_MAX) /* smallest_seqno */,
rnd64.Uniform(UINT64_MAX) /* largest_seqno */,
rnd.RandomBinaryString(
rnd.Uniform(kStrMaxLen)) /* smallest_internal_key */,
rnd.RandomBinaryString(
rnd.Uniform(kStrMaxLen)) /* largest_internal_key */,
rnd64.Uniform(UINT64_MAX) /* oldest_ancester_time */,
rnd64.Uniform(UINT64_MAX) /* file_creation_time */,
rnd64.Uniform(UINT64_MAX) /* epoch_number */,
file_checksum /* file_checksum */,
file_checksum_func_name /* file_checksum_func_name */,
rnd64.Uniform(UINT64_MAX) /* paranoid_hash */,
rnd.OneIn(2) /* marked_for_compaction */, id /* unique_id */,
table_properties);
rnd.RandomString(rnd.Uniform(kStrMaxLen)), rnd64.Uniform(UINT64_MAX),
rnd64.Uniform(UINT64_MAX),
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX),
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX), rnd.OneIn(2), id);
}
result.output_level = rnd.Uniform(10);
result.output_path = rnd.RandomString(rnd.Uniform(kStrMaxLen));
result.stats.num_output_records = rnd64.Uniform(UINT64_MAX);
result.num_output_records = rnd64.Uniform(UINT64_MAX);
result.total_bytes = rnd64.Uniform(UINT64_MAX);
result.bytes_read = 123;
result.bytes_written = rnd64.Uniform(UINT64_MAX);
result.stats.elapsed_micros = rnd64.Uniform(UINT64_MAX);
@@ -1712,21 +1686,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
ASSERT_OK(CompactionServiceResult::Read(output, &deserialized1));
ASSERT_TRUE(deserialized1.TEST_Equals(&result));
for (size_t i = 0; i < result.output_files.size(); i++) {
for (const auto& prop :
result.output_files[i].table_properties.user_collected_properties) {
ASSERT_EQ(deserialized1.output_files[i]
.table_properties.user_collected_properties[prop.first],
prop.second);
}
for (const auto& prop :
result.output_files[i].table_properties.readable_properties) {
ASSERT_EQ(deserialized1.output_files[i]
.table_properties.readable_properties[prop.first],
prop.second);
}
}
// Test mismatch
deserialized1.stats.num_input_files += 10;
std::string mismatch;
@@ -1741,10 +1700,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
ASSERT_FALSE(deserialized_tmp.TEST_Equals(&result, &mismatch));
ASSERT_EQ(mismatch, "output_files.unique_id");
deserialized_tmp.status.PermitUncheckedError();
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum, file_checksum);
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum_func_name,
file_checksum_func_name);
}
// Test unknown field
+1 -8
View File
@@ -62,9 +62,8 @@ class CompactionOutputs {
}
// TODO: Remove it when remote compaction support tiered compaction
void AddBytesWritten(uint64_t bytes) { stats_.bytes_written += bytes; }
void SetTotalBytes(uint64_t bytes) { stats_.bytes_written += bytes; }
void SetNumOutputRecords(uint64_t num) { stats_.num_output_records = num; }
void SetNumOutputFiles(uint64_t num) { stats_.num_output_files = num; }
// TODO: Move the BlobDB builder into CompactionOutputs
const std::vector<BlobFileAddition>& GetBlobFileAdditions() const {
@@ -108,12 +107,6 @@ class CompactionOutputs {
Status Finish(const Status& intput_status,
const SeqnoToTimeMapping& seqno_to_time_mapping);
// Update output table properties from already populated TableProperties.
// Used for remote compaction
void UpdateTableProperties(const TableProperties& table_properties) {
current_output().table_properties =
std::make_shared<TableProperties>(table_properties);
}
// Update output table properties from table builder
void UpdateTableProperties() {
current_output().table_properties =
+3 -11
View File
@@ -380,8 +380,7 @@ Compaction* CompactionPicker::CompactFiles(
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
mutable_cf_options.default_write_temperature,
compact_options.max_subcompactions,
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, true);
/* grandparents */ {}, true);
RegisterCompaction(c);
return c;
}
@@ -678,9 +677,7 @@ Compaction* CompactionPicker::CompactRange(
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions,
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ true, trim_ts, /* score */ -1,
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
compact_range_options.blob_garbage_collection_policy,
@@ -869,7 +866,6 @@ Compaction* CompactionPicker::CompactRange(
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
mutable_cf_options.default_write_temperature,
compact_range_options.max_subcompactions, std::move(grandparents),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
/* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
@@ -1175,8 +1171,7 @@ void CompactionPicker::UnregisterCompaction(Compaction* c) {
void CompactionPicker::PickFilesMarkedForCompaction(
const std::string& cf_name, VersionStorageInfo* vstorage, int* start_level,
int* output_level, CompactionInputFiles* start_level_inputs,
std::function<bool(const FileMetaData*)> skip_marked_file) {
int* output_level, CompactionInputFiles* start_level_inputs) {
if (vstorage->FilesMarkedForCompaction().empty()) {
return;
}
@@ -1186,9 +1181,6 @@ void CompactionPicker::PickFilesMarkedForCompaction(
// If this assert() fails that means that some function marked some
// files as being_compacted, but didn't call ComputeCompactionScore()
assert(!level_file.second->being_compacted);
if (skip_marked_file(level_file.second)) {
return false;
}
*start_level = level_file.first;
*output_level =
(*start_level == 0) ? vstorage->base_level() : *start_level + 1;
+19 -23
View File
@@ -16,7 +16,6 @@
#include <vector>
#include "db/compaction/compaction.h"
#include "db/snapshot_checker.h"
#include "db/version_set.h"
#include "options/cf_options.h"
#include "rocksdb/env.h"
@@ -56,17 +55,17 @@ class CompactionPicker {
// Returns nullptr if there is no compaction to be done.
// Otherwise returns a pointer to a heap-allocated object that
// describes the compaction. Caller should delete the result.
// Currently, only universal compaction will query existing snapshots and
// pass it to aid compaction picking. And it's only passed when user-defined
// timestamps is not enabled. The other compaction styles do not pass or use
// `existing_snapshots` or `snapshot_checker`.
virtual Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) = 0;
virtual Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage,
LogBuffer* log_buffer) = 0;
// Return a compaction object for compacting the range [begin,end] in
// the specified level. Returns nullptr if there is nothing in that
// level that overlaps the specified range. Caller should delete
// the result.
//
// The returned Compaction might not include the whole requested range.
// In that case, compaction_end will be set to the next key that needs
// compacting. In case the compaction will compact the whole range,
@@ -204,11 +203,10 @@ class CompactionPicker {
const CompactionInputFiles& output_level_inputs,
std::vector<FileMetaData*>* grandparents);
void PickFilesMarkedForCompaction(
const std::string& cf_name, VersionStorageInfo* vstorage,
int* start_level, int* output_level,
CompactionInputFiles* start_level_inputs,
std::function<bool(const FileMetaData*)> skip_marked_file);
void PickFilesMarkedForCompaction(const std::string& cf_name,
VersionStorageInfo* vstorage,
int* start_level, int* output_level,
CompactionInputFiles* start_level_inputs);
bool GetOverlappingL0Files(VersionStorageInfo* vstorage,
CompactionInputFiles* start_level_inputs,
@@ -259,13 +257,11 @@ class NullCompactionPicker : public CompactionPicker {
virtual ~NullCompactionPicker() {}
// Always return "nullptr"
Compaction* PickCompaction(
const std::string& /*cf_name*/,
const MutableCFOptions& /*mutable_cf_options*/,
const MutableDBOptions& /*mutable_db_options*/,
const std::vector<SequenceNumber>& /*existing_snapshots*/,
const SnapshotChecker* /*snapshot_checker*/,
VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */) override {
Compaction* PickCompaction(const std::string& /*cf_name*/,
const MutableCFOptions& /*mutable_cf_options*/,
const MutableDBOptions& /*mutable_db_options*/,
VersionStorageInfo* /*vstorage*/,
LogBuffer* /* log_buffer */) override {
return nullptr;
}
+8 -19
View File
@@ -118,9 +118,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
std::move(inputs), 0, 0, 0, 0, kNoCompression,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* max_subcompactions */ 0, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
CompactionReason::kFIFOTtl);
@@ -190,9 +188,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
0 /* output path ID */, mutable_cf_options.compression,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompactions */, {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr, /* is manual */ false,
0 /* max_subcompactions */, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ false,
/* l0_files_might_overlap */ true,
@@ -288,9 +284,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
/* output_path_id */ 0, kNoCompression,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* max_subcompactions */ 0, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ true,
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
@@ -416,9 +410,8 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
mutable_cf_options.compression, mutable_cf_options.compression_opts,
compaction_target_temp,
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false, /* trim_ts */ "", vstorage->CompactionScore(0),
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
vstorage->CompactionScore(0),
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kChangeTemperature);
return c;
@@ -426,9 +419,7 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
Compaction* FIFOCompactionPicker::PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /* existing_snapshots */,
const SnapshotChecker* /* snapshot_checker */, VersionStorageInfo* vstorage,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) {
Compaction* c = nullptr;
if (mutable_cf_options.ttl > 0) {
@@ -463,10 +454,8 @@ Compaction* FIFOCompactionPicker::CompactRange(
assert(output_level == 0);
*compaction_end = nullptr;
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.logger);
Compaction* c =
PickCompaction(cf_name, mutable_cf_options, mutable_db_options,
/*existing_snapshots*/ {}, /*snapshot_checker*/ nullptr,
vstorage, &log_buffer);
Compaction* c = PickCompaction(cf_name, mutable_cf_options,
mutable_db_options, vstorage, &log_buffer);
log_buffer.FlushBufferToLog();
return c;
}
+5 -6
View File
@@ -18,12 +18,11 @@ class FIFOCompactionPicker : public CompactionPicker {
const InternalKeyComparator* icmp)
: CompactionPicker(ioptions, icmp) {}
Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /* existing_snapshots */,
const SnapshotChecker* /* snapshot_checker */,
VersionStorageInfo* version, LogBuffer* log_buffer) override;
Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* version,
LogBuffer* log_buffer) override;
Compaction* CompactRange(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
+3 -10
View File
@@ -262,10 +262,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
parent_index_ = base_index_ = -1;
compaction_picker_->PickFilesMarkedForCompaction(
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_,
/*skip_marked_file*/ [](const FileMetaData* /* file */) {
return false;
});
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
if (!start_level_inputs_.empty()) {
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
return;
@@ -557,9 +554,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
vstorage_->base_level()),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, std::move(grandparents_),
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
is_manual_,
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
l0_files_might_overlap, compaction_reason_);
@@ -972,9 +967,7 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
Compaction* LevelCompactionPicker::PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /*existing_snapshots */,
const SnapshotChecker* /*snapshot_checker*/, VersionStorageInfo* vstorage,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) {
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
mutable_cf_options, ioptions_,
+5 -6
View File
@@ -20,12 +20,11 @@ class LevelCompactionPicker : public CompactionPicker {
LevelCompactionPicker(const ImmutableOptions& ioptions,
const InternalKeyComparator* icmp)
: CompactionPicker(ioptions, icmp) {}
Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& /* existing_snapshots */,
const SnapshotChecker* /* snapshot_checker */,
VersionStorageInfo* vstorage, LogBuffer* log_buffer) override;
Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage,
LogBuffer* log_buffer) override;
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
};
File diff suppressed because it is too large. Load diff
+33 -152
View File
@@ -35,9 +35,7 @@ class UniversalCompactionBuilder {
UniversalCompactionBuilder(
const ImmutableOptions& ioptions, const InternalKeyComparator* icmp,
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
UniversalCompactionPicker* picker, LogBuffer* log_buffer)
: ioptions_(ioptions),
icmp_(icmp),
@@ -46,19 +44,7 @@ class UniversalCompactionBuilder {
mutable_db_options_(mutable_db_options),
vstorage_(vstorage),
picker_(picker),
log_buffer_(log_buffer) {
assert(icmp_);
const auto* ucmp = icmp_->user_comparator();
assert(ucmp);
// These parameters are only passed when user-defined timestamp is not
// enabled.
if (ucmp->timestamp_size() == 0) {
earliest_snapshot_ = existing_snapshots.empty()
? kMaxSequenceNumber
: existing_snapshots.at(0);
snapshot_checker_ = snapshot_checker;
}
}
log_buffer_(log_buffer) {}
// Form and return the compaction object. The caller owns return object.
Compaction* PickCompaction();
@@ -66,15 +52,12 @@ class UniversalCompactionBuilder {
private:
struct SortedRun {
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
uint64_t _compensated_file_size, bool _being_compacted,
bool _level_has_marked_standalone_rangedel)
uint64_t _compensated_file_size, bool _being_compacted)
: level(_level),
file(_file),
size(_size),
compensated_file_size(_compensated_file_size),
being_compacted(_being_compacted),
level_has_marked_standalone_rangedel(
_level_has_marked_standalone_rangedel) {
being_compacted(_being_compacted) {
assert(compensated_file_size > 0);
assert(level != 0 || file != nullptr);
}
@@ -96,10 +79,6 @@ class UniversalCompactionBuilder {
uint64_t size;
uint64_t compensated_file_size;
bool being_compacted;
// True if this level has any file that is a standalone range deletion file
// marked for compaction. Best effort is made to make only deletion
// triggered compaction pick this type of file.
bool level_has_marked_standalone_rangedel;
};
// Pick Universal compaction to limit read amplification
@@ -119,11 +98,6 @@ class UniversalCompactionBuilder {
Compaction* PickDeleteTriggeredCompaction();
// Returns true if this given file (that is marked be compaction) should be
// skipped from being picked for now. We do this to best use standalone range
// tombstone files.
bool ShouldSkipMarkedFile(const FileMetaData* file) const;
// Form a compaction from the sorted run indicated by start_index to the
// oldest sorted run.
// The caller is responsible for making sure that those files are not in
@@ -260,18 +234,8 @@ class UniversalCompactionBuilder {
VersionStorageInfo* vstorage_;
UniversalCompactionPicker* picker_;
LogBuffer* log_buffer_;
// Optional earliest snapshot at time of compaction picking. This is only
// provided if the column family doesn't enable user-defined timestamps.
// And this information is only passed to `Compaction` picked by deletion
// triggered compaction for possible optimizations.
std::optional<SequenceNumber> earliest_snapshot_;
const SnapshotChecker* snapshot_checker_;
// Mapping from file id to its index in the sorted run for the files that are
// marked for compaction. This is only populated when snapshot info is
// populated.
std::map<uint64_t, size_t> file_marked_for_compaction_to_sorted_run_index_;
std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
const VersionStorageInfo& vstorage, int last_level,
uint64_t* max_run_size);
@@ -430,13 +394,11 @@ bool UniversalCompactionPicker::NeedsCompaction(
Compaction* UniversalCompactionPicker::PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) {
UniversalCompactionBuilder builder(
ioptions_, icmp_, cf_name, mutable_cf_options, mutable_db_options,
existing_snapshots, snapshot_checker, vstorage, this, log_buffer);
UniversalCompactionBuilder builder(ioptions_, icmp_, cf_name,
mutable_cf_options, mutable_db_options,
vstorage, this, log_buffer);
return builder.PickCompaction();
}
@@ -486,20 +448,14 @@ UniversalCompactionBuilder::CalculateSortedRuns(
*max_run_size = 0;
std::vector<UniversalCompactionBuilder::SortedRun> ret;
for (FileMetaData* f : vstorage.LevelFiles(0)) {
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
file_marked_for_compaction_to_sorted_run_index_.emplace(f->fd.GetNumber(),
ret.size());
}
ret.emplace_back(
0, f, f->fd.GetFileSize(), f->compensated_file_size, f->being_compacted,
f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
f->being_compacted);
*max_run_size = std::max(*max_run_size, f->fd.GetFileSize());
}
for (int level = 1; level <= last_level; level++) {
uint64_t total_compensated_size = 0U;
uint64_t total_size = 0U;
bool being_compacted = false;
bool level_has_marked_standalone_rangedel = false;
for (FileMetaData* f : vstorage.LevelFiles(level)) {
total_compensated_size += f->compensated_file_size;
total_size += f->fd.GetFileSize();
@@ -511,57 +467,16 @@ UniversalCompactionBuilder::CalculateSortedRuns(
if (f->being_compacted) {
being_compacted = f->being_compacted;
}
level_has_marked_standalone_rangedel =
level_has_marked_standalone_rangedel ||
(f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
file_marked_for_compaction_to_sorted_run_index_.emplace(
f->fd.GetNumber(), ret.size());
}
}
if (total_compensated_size > 0) {
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
being_compacted, level_has_marked_standalone_rangedel);
being_compacted);
}
*max_run_size = std::max(*max_run_size, total_size);
}
return ret;
}
bool UniversalCompactionBuilder::ShouldSkipMarkedFile(
const FileMetaData* file) const {
assert(file->marked_for_compaction);
if (!earliest_snapshot_.has_value()) {
return false;
}
if (!file->FileIsStandAloneRangeTombstone()) {
return false;
}
// Skip until earliest snapshot advances at or above this standalone range
// tombstone file. `DB::ReleaseSnapshot` will re-examine and schedule
// compaction for it.
if (!DataIsDefinitelyInSnapshot(file->fd.largest_seqno,
earliest_snapshot_.value(),
snapshot_checker_)) {
return true;
}
auto iter = file_marked_for_compaction_to_sorted_run_index_.find(
file->fd.GetNumber());
assert(iter != file_marked_for_compaction_to_sorted_run_index_.end());
size_t idx = iter->second;
const SortedRun* succeeding_sorted_run =
idx < sorted_runs_.size() - 1 ? &sorted_runs_[idx + 1] : nullptr;
// Marked standalone range tombstone file is best used if it's in the start
// input level. Skip to let that compaction happen first.
if (succeeding_sorted_run &&
succeeding_sorted_run->level_has_marked_standalone_rangedel) {
return true;
}
return false;
}
// Universal style of compaction. Pick files that are contiguous in
// time-range to compact.
Compaction* UniversalCompactionBuilder::PickCompaction() {
@@ -665,8 +580,7 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
// Get the total number of sorted runs that are not being compacted
int num_sr_not_compacted = 0;
for (size_t i = 0; i < sorted_runs_.size(); i++) {
if (sorted_runs_[i].being_compacted == false &&
!sorted_runs_[i].level_has_marked_standalone_rangedel) {
if (sorted_runs_[i].being_compacted == false) {
num_sr_not_compacted++;
}
}
@@ -829,24 +743,16 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
for (sr = nullptr; loop < sorted_runs_.size(); loop++) {
sr = &sorted_runs_[loop];
if (!sr->being_compacted && !sr->level_has_marked_standalone_rangedel) {
if (!sr->being_compacted) {
candidate_count = 1;
break;
}
char file_num_buf[kFormatFileNumberBufSize];
sr->Dump(file_num_buf, sizeof(file_num_buf));
if (sr->being_compacted) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: %s"
"[%d] being compacted, skipping",
cf_name_.c_str(), file_num_buf, loop);
} else if (sr->level_has_marked_standalone_rangedel) {
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: %s"
"[%d] has standalone range tombstone files marked for "
"compaction, skipping",
cf_name_.c_str(), file_num_buf, loop);
}
ROCKS_LOG_BUFFER(log_buffer_,
"[%s] Universal: %s"
"[%d] being compacted, skipping",
cf_name_.c_str(), file_num_buf, loop);
sr = nullptr;
}
@@ -867,8 +773,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
candidate_count < max_files_to_compact && i < sorted_runs_.size();
i++) {
const SortedRun* succeeding_sr = &sorted_runs_[i];
if (succeeding_sr->being_compacted ||
succeeding_sr->level_has_marked_standalone_rangedel) {
if (succeeding_sr->being_compacted) {
break;
}
// Pick files if the total/last candidate file size (increased by the
@@ -1018,8 +923,6 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
output_level, enable_compression),
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents,
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false, /* trim_ts */ "", score_,
false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
@@ -1036,8 +939,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
const size_t end_index = ShouldSkipLastSortedRunForSizeAmpCompaction()
? sorted_runs_.size() - 2
: sorted_runs_.size() - 1;
if (sorted_runs_[end_index].being_compacted ||
sorted_runs_[end_index].level_has_marked_standalone_rangedel) {
if (sorted_runs_[end_index].being_compacted) {
return nullptr;
}
const uint64_t base_sr_size = sorted_runs_[end_index].size;
@@ -1048,23 +950,14 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
// Get longest span (i.e, [start_index, end_index]) of available sorted runs
while (start_index > 0) {
const SortedRun* sr = &sorted_runs_[start_index - 1];
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
if (sr->being_compacted) {
char file_num_buf[kFormatFileNumberBufSize];
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
if (sr->being_compacted) {
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: stopping at sorted run undergoing compaction: "
"%s[%" ROCKSDB_PRIszt "]",
cf_name_.c_str(), file_num_buf, start_index - 1);
} else if (sr->level_has_marked_standalone_rangedel) {
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: stopping at sorted run that has standalone range "
"tombstone files marked for compaction: "
"%s[%" ROCKSDB_PRIszt "]",
cf_name_.c_str(), file_num_buf, start_index - 1);
}
ROCKS_LOG_BUFFER(
log_buffer_,
"[%s] Universal: stopping at sorted run undergoing compaction: "
"%s[%" ROCKSDB_PRIszt "]",
cf_name_.c_str(), file_num_buf, start_index - 1);
break;
}
candidate_size += sr->compensated_file_size;
@@ -1364,10 +1257,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
true /* enable_compression */),
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kUniversalSizeAmplification);
@@ -1398,7 +1288,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
continue;
}
FileMetaData* f = vstorage_->LevelFiles(0)[loop];
if (f->marked_for_compaction && !ShouldSkipMarkedFile(f)) {
if (f->marked_for_compaction) {
start_level_inputs.files.push_back(f);
start_index =
static_cast<int>(loop); // Consider this as the first candidate.
@@ -1412,7 +1302,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
for (size_t loop = start_index + 1; loop < sorted_runs_.size(); loop++) {
SortedRun* sr = &sorted_runs_[loop];
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
if (sr->being_compacted) {
break;
}
@@ -1431,10 +1321,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
// leveled. We pick one of the files marked for compaction and compact with
// overlapping files in the adjacent level.
picker_->PickFilesMarkedForCompaction(cf_name_, vstorage_, &start_level,
&output_level, &start_level_inputs,
[this](const FileMetaData* file) {
return ShouldSkipMarkedFile(file);
});
&output_level, &start_level_inputs);
if (start_level_inputs.empty()) {
return nullptr;
}
@@ -1514,9 +1401,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
snapshot_checker_,
/* is manual */ false,
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kFilesMarkedForCompaction);
@@ -1609,10 +1494,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
true /* enable_compression */),
mutable_cf_options_.default_write_temperature,
/* max_subcompactions */ 0, /* grandparents */ {},
/* earliest_snapshot */ std::nullopt,
/* snapshot_checker */ nullptr,
/* is manual */ false,
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
}
@@ -1633,8 +1515,7 @@ Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
// included in the compaction.
size_t start_index = sorted_runs_.size();
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted &&
!sorted_runs_[start_index - 1].level_has_marked_standalone_rangedel) {
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted) {
start_index--;
}
if (start_index == sorted_runs_.size()) {
+5 -7
View File
@@ -10,7 +10,6 @@
#pragma once
#include "db/compaction/compaction_picker.h"
#include "db/snapshot_checker.h"
namespace ROCKSDB_NAMESPACE {
class UniversalCompactionPicker : public CompactionPicker {
@@ -18,12 +17,11 @@ class UniversalCompactionPicker : public CompactionPicker {
UniversalCompactionPicker(const ImmutableOptions& ioptions,
const InternalKeyComparator* icmp)
: CompactionPicker(ioptions, icmp) {}
Compaction* PickCompaction(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
const std::vector<SequenceNumber>& existing_snapshots,
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
LogBuffer* log_buffer) override;
Compaction* PickCompaction(const std::string& cf_name,
const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options,
VersionStorageInfo* vstorage,
LogBuffer* log_buffer) override;
int MaxOutputLevel() const override { return NumberLevels() - 1; }
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
+28 -227
View File
@@ -48,14 +48,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
compaction_input.has_end = sub_compact->end.has_value();
compaction_input.end =
compaction_input.has_end ? sub_compact->end->ToString() : "";
compaction_input.options_file_number =
sub_compact->compaction->input_version()
->version_set()
->options_file_number();
TEST_SYNC_POINT_CALLBACK(
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
&compaction_input);
std::string compaction_input_binary;
Status s = compaction_input.Write(&compaction_input_binary);
@@ -203,8 +195,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
meta.oldest_ancester_time = file.oldest_ancester_time;
meta.file_creation_time = file.file_creation_time;
meta.epoch_number = file.epoch_number;
meta.file_checksum = file.file_checksum;
meta.file_checksum_func_name = file.file_checksum_func_name;
meta.marked_for_compaction = file.marked_for_compaction;
meta.unique_id = file.unique_id;
@@ -212,14 +202,11 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
sub_compact->Current().AddOutput(std::move(meta),
cfd->internal_comparator(), false, true,
file.paranoid_hash);
sub_compact->Current().UpdateTableProperties(file.table_properties);
}
sub_compact->compaction_job_stats = compaction_result.stats;
sub_compact->Current().SetNumOutputRecords(
compaction_result.stats.num_output_records);
sub_compact->Current().SetNumOutputFiles(
compaction_result.stats.num_output_files);
sub_compact->Current().AddBytesWritten(compaction_result.bytes_written);
compaction_result.num_output_records);
sub_compact->Current().SetTotalBytes(compaction_result.total_bytes);
RecordTick(stats_, REMOTE_COMPACT_READ_BYTES, compaction_result.bytes_read);
RecordTick(stats_, REMOTE_COMPACT_WRITE_BYTES,
compaction_result.bytes_written);
@@ -239,18 +226,6 @@ void CompactionServiceCompactionJob::RecordCompactionIOStats() {
CompactionJob::RecordCompactionIOStats();
}
void CompactionServiceCompactionJob::UpdateCompactionJobStats(
const InternalStats::CompactionStats& stats) const {
compaction_job_stats_->elapsed_micros = stats.micros;
// output information only in remote compaction
compaction_job_stats_->total_output_bytes = stats.bytes_written;
compaction_job_stats_->total_output_bytes_blob = stats.bytes_written_blob;
compaction_job_stats_->num_output_records = stats.num_output_records;
compaction_job_stats_->num_output_files = stats.num_output_files;
compaction_job_stats_->num_output_files_blob = stats.num_output_files_blob;
}
CompactionServiceCompactionJob::CompactionServiceCompactionJob(
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
const MutableDBOptions& mutable_db_options, const FileOptions& file_options,
@@ -305,9 +280,6 @@ Status CompactionServiceCompactionJob::Run() {
log_buffer_->FlushBufferToLog();
LogCompaction();
compaction_result_->stats.Reset();
const uint64_t start_micros = db_options_.clock->NowMicros();
c->GetOrInitInputTableProperties();
@@ -348,48 +320,39 @@ Status CompactionServiceCompactionJob::Run() {
if (status.ok()) {
status = io_s;
}
if (status.ok()) {
// TODO: Add verify_table()
}
// Finish up all book-keeping to unify the subcompaction results
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
UpdateCompactionStats();
RecordCompactionIOStats();
LogFlush(db_options_.info_log);
compact_->status = status;
compact_->status.PermitUncheckedError();
// Build Compaction Job Stats
// 1. Aggregate CompactionOutputStats into Internal Compaction Stats
// (compaction_stats_) and aggregate Compaction Job Stats
// (compaction_job_stats_) from the sub compactions
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
// 2. Update the Output information in the Compaction Job Stats with
// aggregated Internal Compaction Stats.
UpdateCompactionJobStats(compaction_stats_.stats);
// 3. Set fields that are not propagated as part of aggregations above
compaction_result_->stats.is_manual_compaction = c->is_manual_compaction();
compaction_result_->stats.is_full_compaction = c->is_full_compaction();
compaction_result_->stats.is_remote_compaction = true;
// 4. Update IO Stats that are not part of the aggregations above (bytes_read,
// bytes_written)
RecordCompactionIOStats();
// Build Output
// Build compaction result
compaction_result_->output_level = compact_->compaction->output_level();
compaction_result_->output_path = output_path_;
compaction_result_->stats.is_remote_compaction = true;
for (const auto& output_file : sub_compact->GetOutputs()) {
auto& meta = output_file.meta;
compaction_result_->output_files.emplace_back(
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
meta.file_checksum_func_name, output_file.validator.GetHash(),
meta.marked_for_compaction, meta.unique_id,
output_file.table_properties);
meta.file_creation_time, meta.epoch_number,
output_file.validator.GetHash(), meta.marked_for_compaction,
meta.unique_id);
}
InternalStats::CompactionStatsFull compaction_stats;
sub_compact->AggregateCompactionStats(compaction_stats);
compaction_result_->num_output_records =
compaction_stats.stats.num_output_records;
compaction_result_->total_bytes = compaction_stats.TotalBytesWritten();
TEST_SYNC_POINT_CALLBACK("CompactionServiceCompactionJob::Run:0",
&compaction_result_);
return status;
}
@@ -472,163 +435,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
{"end",
{offsetof(struct CompactionServiceInput, end), OptionType::kEncodedString,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"options_file_number",
{offsetof(struct CompactionServiceInput, options_file_number),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
};
static std::unordered_map<std::string, OptionTypeInfo>
table_properties_type_info = {
{"orig_file_number",
{offsetof(struct TableProperties, orig_file_number),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"data_size",
{offsetof(struct TableProperties, data_size), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"index_size",
{offsetof(struct TableProperties, index_size), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"index_partitions",
{offsetof(struct TableProperties, index_partitions),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"top_level_index_size",
{offsetof(struct TableProperties, top_level_index_size),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"index_key_is_user_key",
{offsetof(struct TableProperties, index_key_is_user_key),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"index_value_is_delta_encoded",
{offsetof(struct TableProperties, index_value_is_delta_encoded),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"filter_size",
{offsetof(struct TableProperties, filter_size), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"raw_key_size",
{offsetof(struct TableProperties, raw_key_size), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"raw_value_size",
{offsetof(struct TableProperties, raw_value_size),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_data_blocks",
{offsetof(struct TableProperties, num_data_blocks),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_entries",
{offsetof(struct TableProperties, num_entries), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"num_filter_entries",
{offsetof(struct TableProperties, num_filter_entries),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_deletions",
{offsetof(struct TableProperties, num_deletions), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"num_merge_operands",
{offsetof(struct TableProperties, num_merge_operands),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_range_deletions",
{offsetof(struct TableProperties, num_range_deletions),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"format_version",
{offsetof(struct TableProperties, format_version),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"fixed_key_len",
{offsetof(struct TableProperties, fixed_key_len), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"column_family_id",
{offsetof(struct TableProperties, column_family_id),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"creation_time",
{offsetof(struct TableProperties, creation_time), OptionType::kUInt64T,
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
{"oldest_key_time",
{offsetof(struct TableProperties, oldest_key_time),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_creation_time",
{offsetof(struct TableProperties, file_creation_time),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"slow_compression_estimated_data_size",
{offsetof(struct TableProperties,
slow_compression_estimated_data_size),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"fast_compression_estimated_data_size",
{offsetof(struct TableProperties,
fast_compression_estimated_data_size),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"external_sst_file_global_seqno_offset",
{offsetof(struct TableProperties,
external_sst_file_global_seqno_offset),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"tail_start_offset",
{offsetof(struct TableProperties, tail_start_offset),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"user_defined_timestamps_persisted",
{offsetof(struct TableProperties, user_defined_timestamps_persisted),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"key_largest_seqno",
{offsetof(struct TableProperties, key_largest_seqno),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"db_id",
{offsetof(struct TableProperties, db_id), OptionType::kEncodedString}},
{"db_session_id",
{offsetof(struct TableProperties, db_session_id),
OptionType::kEncodedString}},
{"db_host_id",
{offsetof(struct TableProperties, db_host_id),
OptionType::kEncodedString}},
{"column_family_name",
{offsetof(struct TableProperties, column_family_name),
OptionType::kEncodedString}},
{"filter_policy_name",
{offsetof(struct TableProperties, filter_policy_name),
OptionType::kEncodedString}},
{"comparator_name",
{offsetof(struct TableProperties, comparator_name),
OptionType::kEncodedString}},
{"merge_operator_name",
{offsetof(struct TableProperties, merge_operator_name),
OptionType::kEncodedString}},
{"prefix_extractor_name",
{offsetof(struct TableProperties, prefix_extractor_name),
OptionType::kEncodedString}},
{"property_collectors_names",
{offsetof(struct TableProperties, property_collectors_names),
OptionType::kEncodedString}},
{"compression_name",
{offsetof(struct TableProperties, compression_name),
OptionType::kEncodedString}},
{"compression_options",
{offsetof(struct TableProperties, compression_options),
OptionType::kEncodedString}},
{"seqno_to_time_mapping",
{offsetof(struct TableProperties, seqno_to_time_mapping),
OptionType::kEncodedString}},
{"user_collected_properties",
OptionTypeInfo::StringMap(
offsetof(struct TableProperties, user_collected_properties),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
{"readable_properties",
OptionTypeInfo::StringMap(
offsetof(struct TableProperties, readable_properties),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
};
static std::unordered_map<std::string, OptionTypeInfo>
@@ -665,14 +471,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct CompactionServiceOutputFile, epoch_number),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_checksum",
{offsetof(struct CompactionServiceOutputFile, file_checksum),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"file_checksum_func_name",
{offsetof(struct CompactionServiceOutputFile, file_checksum_func_name),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"paranoid_hash",
{offsetof(struct CompactionServiceOutputFile, paranoid_hash),
OptionType::kUInt64T, OptionVerificationType::kNormal,
@@ -686,11 +484,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
offsetof(struct CompactionServiceOutputFile, unique_id),
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
{0, OptionType::kUInt64T})},
{"table_properties",
OptionTypeInfo::Struct(
"table_properties", &table_properties_type_info,
offsetof(struct CompactionServiceOutputFile, table_properties),
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
};
static std::unordered_map<std::string, OptionTypeInfo>
@@ -910,6 +703,14 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
{offsetof(struct CompactionServiceResult, output_path),
OptionType::kEncodedString, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"num_output_records",
{offsetof(struct CompactionServiceResult, num_output_records),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"total_bytes",
{offsetof(struct CompactionServiceResult, total_bytes),
OptionType::kUInt64T, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"bytes_read",
{offsetof(struct CompactionServiceResult, bytes_read),
OptionType::kUInt64T, OptionVerificationType::kNormal,
+1 -524
View File
@@ -3,9 +3,9 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include "db/db_test_util.h"
#include "port/stack_trace.h"
#include "rocksdb/utilities/options_util.h"
#include "table/unique_id_impl.h"
namespace ROCKSDB_NAMESPACE {
@@ -349,27 +349,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
} else {
ASSERT_OK(result.status);
}
ASSERT_GE(result.stats.elapsed_micros, 1);
ASSERT_GE(result.stats.cpu_micros, 1);
ASSERT_EQ(20, result.stats.num_output_records);
ASSERT_EQ(result.output_files.size(), result.stats.num_output_files);
uint64_t total_size = 0;
for (auto output_file : result.output_files) {
std::string file_name = result.output_path + "/" + output_file.file_name;
uint64_t file_size = 0;
ASSERT_OK(options.env->GetFileSize(file_name, &file_size));
ASSERT_GT(file_size, 0);
total_size += file_size;
}
ASSERT_EQ(total_size, result.stats.total_output_bytes);
ASSERT_TRUE(result.stats.is_remote_compaction);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_FALSE(result.stats.is_full_compaction);
Close();
}
@@ -416,509 +396,6 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, PreservedOptionsLocalCompaction) {
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.disable_auto_compactions = true;
DestroyAndReopen(options);
Random rnd(301);
for (auto i = 0; i < 2; ++i) {
for (auto j = 0; j < 10; ++j) {
ASSERT_OK(
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
auto compaction = static_cast<Compaction*>(arg);
std::string options_file_name = OptionsFileName(
dbname_,
compaction->input_version()->version_set()->options_file_number());
// Change option twice to make sure the very first OPTIONS file gets
// purged
ASSERT_OK(dbfull()->SetOptions(
{{"level0_file_num_compaction_trigger", "4"}}));
ASSERT_EQ(4, dbfull()->GetOptions().level0_file_num_compaction_trigger);
ASSERT_OK(dbfull()->SetOptions(
{{"level0_file_num_compaction_trigger", "6"}}));
ASSERT_EQ(6, dbfull()->GetOptions().level0_file_num_compaction_trigger);
dbfull()->TEST_DeleteObsoleteFiles();
// For non-remote compactions, OPTIONS file can be deleted while
// using option at the start of the compaction
Status s = env_->FileExists(options_file_name);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsNotFound());
// Should be old value
ASSERT_EQ(2, compaction->mutable_cf_options()
->level0_file_num_compaction_trigger);
ASSERT_TRUE(dbfull()->min_options_file_numbers_.empty());
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_TRUE(s.ok());
}
TEST_F(CompactionServiceTest, PreservedOptionsRemoteCompaction) {
// For non-remote compaction do not preserve options file
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.disable_auto_compactions = true;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
Random rnd(301);
for (auto i = 0; i < 2; ++i) {
for (auto j = 0; j < 10; ++j) {
ASSERT_OK(
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"CompactionServiceTest::OptionsFileChanged",
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:1"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
[&](void* arg) {
auto options_file_number = static_cast<uint64_t*>(arg);
// Change the option twice before the compaction run
ASSERT_OK(dbfull()->SetOptions(
{{"level0_file_num_compaction_trigger", "4"}}));
ASSERT_EQ(4, dbfull()->GetOptions().level0_file_num_compaction_trigger);
ASSERT_TRUE(dbfull()->versions_->options_file_number() >
*options_file_number);
// Change the option twice before the compaction run
ASSERT_OK(dbfull()->SetOptions(
{{"level0_file_num_compaction_trigger", "5"}}));
ASSERT_EQ(5, dbfull()->GetOptions().level0_file_num_compaction_trigger);
ASSERT_TRUE(dbfull()->versions_->options_file_number() >
*options_file_number);
TEST_SYNC_POINT("CompactionServiceTest::OptionsFileChanged");
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
[&](void* arg) {
auto input = static_cast<CompactionServiceInput*>(arg);
std::string options_file_name =
OptionsFileName(dbname_, input->options_file_number);
ASSERT_OK(env_->FileExists(options_file_name));
ASSERT_FALSE(dbfull()->min_options_file_numbers_.empty());
ASSERT_EQ(dbfull()->min_options_file_numbers_.front(),
input->options_file_number);
DBOptions db_options;
ConfigOptions config_options;
std::vector<ColumnFamilyDescriptor> all_column_families;
config_options.env = env_;
ASSERT_OK(LoadOptionsFromFile(config_options, options_file_name,
&db_options, &all_column_families));
bool has_cf = false;
for (auto& cf : all_column_families) {
if (cf.name == input->cf_name) {
// Should be old value
ASSERT_EQ(2, cf.options.level0_file_num_compaction_trigger);
has_cf = true;
}
}
ASSERT_TRUE(has_cf);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
auto compaction = static_cast<Compaction*>(arg);
ASSERT_EQ(2, compaction->mutable_cf_options()
->level0_file_num_compaction_trigger);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_TRUE(s.ok());
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
class EventVerifier : public EventListener {
public:
explicit EventVerifier(uint64_t expected_num_input_records,
size_t expected_num_input_files,
uint64_t expected_num_output_records,
size_t expected_num_output_files,
const std::string& expected_smallest_output_key_prefix,
const std::string& expected_largest_output_key_prefix,
bool expected_is_remote_compaction_on_begin,
bool expected_is_remote_compaction_on_complete)
: expected_num_input_records_(expected_num_input_records),
expected_num_input_files_(expected_num_input_files),
expected_num_output_records_(expected_num_output_records),
expected_num_output_files_(expected_num_output_files),
expected_smallest_output_key_prefix_(
expected_smallest_output_key_prefix),
expected_largest_output_key_prefix_(expected_largest_output_key_prefix),
expected_is_remote_compaction_on_begin_(
expected_is_remote_compaction_on_begin),
expected_is_remote_compaction_on_complete_(
expected_is_remote_compaction_on_complete) {}
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
ASSERT_EQ(expected_num_input_files_, ci.input_files.size());
ASSERT_EQ(expected_num_input_files_, ci.input_file_infos.size());
ASSERT_EQ(expected_is_remote_compaction_on_begin_,
ci.stats.is_remote_compaction);
ASSERT_TRUE(ci.stats.is_manual_compaction);
ASSERT_FALSE(ci.stats.is_full_compaction);
}
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
ASSERT_GT(ci.stats.elapsed_micros, 0);
ASSERT_GT(ci.stats.cpu_micros, 0);
ASSERT_EQ(expected_num_input_records_, ci.stats.num_input_records);
ASSERT_EQ(expected_num_input_files_, ci.stats.num_input_files);
ASSERT_EQ(expected_num_output_records_, ci.stats.num_output_records);
ASSERT_EQ(expected_num_output_files_, ci.stats.num_output_files);
ASSERT_EQ(expected_smallest_output_key_prefix_,
ci.stats.smallest_output_key_prefix);
ASSERT_EQ(expected_largest_output_key_prefix_,
ci.stats.largest_output_key_prefix);
ASSERT_GT(ci.stats.total_input_bytes, 0);
ASSERT_GT(ci.stats.total_output_bytes, 0);
ASSERT_EQ(ci.stats.num_input_records,
ci.stats.num_output_records + ci.stats.num_records_replaced);
ASSERT_EQ(expected_is_remote_compaction_on_complete_,
ci.stats.is_remote_compaction);
ASSERT_TRUE(ci.stats.is_manual_compaction);
ASSERT_FALSE(ci.stats.is_full_compaction);
}
private:
uint64_t expected_num_input_records_;
size_t expected_num_input_files_;
uint64_t expected_num_output_records_;
size_t expected_num_output_files_;
std::string expected_smallest_output_key_prefix_;
std::string expected_largest_output_key_prefix_;
bool expected_is_remote_compaction_on_begin_;
bool expected_is_remote_compaction_on_complete_;
};
TEST_F(CompactionServiceTest, VerifyStats) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
auto event_verifier = std::make_shared<EventVerifier>(
30 /* expected_num_input_records */, 3 /* expected_num_input_files */,
20 /* expected_num_output_records */, 1 /* expected_num_output_files */,
"key00000" /* expected_smallest_output_key_prefix */,
"key00001" /* expected_largest_output_key_prefix */,
true /* expected_is_remote_compaction_on_begin */,
true /* expected_is_remote_compaction_on_complete */);
options.listeners.push_back(event_verifier);
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(0);
std::string end_str = Key(1);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
VerifyTestData();
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, VerifyStatsLocalFallback) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
auto event_verifier = std::make_shared<EventVerifier>(
30 /* expected_num_input_records */, 3 /* expected_num_input_files */,
20 /* expected_num_output_records */, 1 /* expected_num_output_files */,
"key00000" /* expected_smallest_output_key_prefix */,
"key00001" /* expected_largest_output_key_prefix */,
true /* expected_is_remote_compaction_on_begin */,
false /* expected_is_remote_compaction_on_complete */);
options.listeners.push_back(event_verifier);
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
my_cs->OverrideStartStatus(CompactionServiceJobStatus::kUseLocal);
std::string start_str = Key(0);
std::string end_str = Key(1);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
// Remote Compaction did not happen
ASSERT_EQ(my_cs->GetCompactionNum(), comp_num);
VerifyTestData();
}
TEST_F(CompactionServiceTest, CorruptedOutput) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
CompactionServiceResult* compaction_result =
*(static_cast<CompactionServiceResult**>(arg));
ASSERT_TRUE(compaction_result != nullptr &&
!compaction_result->output_files.empty());
// Corrupt files here
for (const auto& output_file : compaction_result->output_files) {
std::string file_name =
compaction_result->output_path + "/" + output_file.file_name;
uint64_t file_size = 0;
Status s = options.env->GetFileSize(file_name, &file_size);
ASSERT_OK(s);
ASSERT_GT(file_size, 0);
ASSERT_OK(test::CorruptFile(env_, file_name, 0,
static_cast<int>(file_size),
true /* verifyChecksum */));
}
});
SyncPoint::GetInstance()->EnableProcessing();
// CompactRange() should fail
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsCorruption());
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// On the worker side, the compaction is considered success
// Verification is done on the primary side
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, CorruptedOutputParanoidFileCheck) {
for (bool paranoid_file_check_enabled : {false, true}) {
SCOPED_TRACE("paranoid_file_check_enabled=" +
std::to_string(paranoid_file_check_enabled));
Options options = CurrentOptions();
Destroy(options);
options.disable_auto_compactions = true;
options.paranoid_file_checks = paranoid_file_check_enabled;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
CompactionServiceResult* compaction_result =
*(static_cast<CompactionServiceResult**>(arg));
ASSERT_TRUE(compaction_result != nullptr &&
!compaction_result->output_files.empty());
// Corrupt files here
for (const auto& output_file : compaction_result->output_files) {
std::string file_name =
compaction_result->output_path + "/" + output_file.file_name;
// Corrupt very small range of bytes. This corruption is so small
// that this isn't caught by default light-weight check
ASSERT_OK(test::CorruptFile(env_, file_name, 0, 1,
false /* verifyChecksum */));
}
});
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
if (paranoid_file_check_enabled) {
ASSERT_NOK(s);
ASSERT_EQ(Status::Corruption("Paranoid checksums do not match"), s);
} else {
// CompactRange() goes through if paranoid file check is not enabled
ASSERT_OK(s);
}
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// On the worker side, the compaction is considered success
// Verification is done on the primary side
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
}
TEST_F(CompactionServiceTest, TruncatedOutput) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
CompactionServiceResult* compaction_result =
*(static_cast<CompactionServiceResult**>(arg));
ASSERT_TRUE(compaction_result != nullptr &&
!compaction_result->output_files.empty());
// Truncate files here
for (const auto& output_file : compaction_result->output_files) {
std::string file_name =
compaction_result->output_path + "/" + output_file.file_name;
uint64_t file_size = 0;
Status s = options.env->GetFileSize(file_name, &file_size);
ASSERT_OK(s);
ASSERT_GT(file_size, 0);
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 2));
}
});
SyncPoint::GetInstance()->EnableProcessing();
// CompactRange() should fail
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsCorruption());
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// On the worker side, the compaction is considered success
// Verification is done on the primary side
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, CustomFileChecksum) {
Options options = CurrentOptions();
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
CompactionServiceResult* compaction_result =
*(static_cast<CompactionServiceResult**>(arg));
ASSERT_TRUE(compaction_result != nullptr &&
!compaction_result->output_files.empty());
// Validate Checksum files here
for (const auto& output_file : compaction_result->output_files) {
std::string file_name =
compaction_result->output_path + "/" + output_file.file_name;
FileChecksumGenContext gen_context;
gen_context.file_name = file_name;
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
options.file_checksum_gen_factory->CreateFileChecksumGenerator(
gen_context);
std::unique_ptr<SequentialFile> file_reader;
uint64_t file_size = 0;
Status s = options.env->GetFileSize(file_name, &file_size);
ASSERT_OK(s);
ASSERT_GT(file_size, 0);
s = options.env->NewSequentialFile(file_name, &file_reader,
EnvOptions());
ASSERT_OK(s);
Slice result;
std::unique_ptr<char[]> scratch(new char[file_size]);
s = file_reader->Read(file_size, &result, scratch.get());
ASSERT_OK(s);
file_checksum_gen->Update(scratch.get(), result.size());
file_checksum_gen->Finalize();
// Verify actual checksum and the func name
ASSERT_EQ(file_checksum_gen->Name(),
output_file.file_checksum_func_name);
ASSERT_EQ(file_checksum_gen->GetChecksum(),
output_file.file_checksum);
}
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, CancelCompactionOnRemoteSide) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
+1 -1
View File
@@ -39,7 +39,7 @@ void CompactionState::AggregateCompactionStats(
InternalStats::CompactionStatsFull& compaction_stats,
CompactionJobStats& compaction_job_stats) {
for (const auto& sc : sub_compact_states) {
sc.AggregateCompactionOutputStats(compaction_stats);
sc.AggregateCompactionStats(compaction_stats);
compaction_job_stats.Add(sc.compaction_job_stats);
}
}
+1 -1
View File
@@ -13,7 +13,7 @@
#include "rocksdb/sst_partitioner.h"
namespace ROCKSDB_NAMESPACE {
void SubcompactionState::AggregateCompactionOutputStats(
void SubcompactionState::AggregateCompactionStats(
InternalStats::CompactionStatsFull& compaction_stats) const {
compaction_stats.stats.Add(compaction_outputs_.stats_);
if (HasPenultimateLevelOutputs()) {
+1 -1
View File
@@ -179,7 +179,7 @@ class SubcompactionState {
void Cleanup(Cache* cache);
void AggregateCompactionOutputStats(
void AggregateCompactionStats(
InternalStats::CompactionStatsFull& compaction_stats) const;
CompactionOutputs& Current() const {
+1 -1
View File
@@ -87,7 +87,7 @@ Status VerifySstFileChecksumInternal(const Options& options,
options.block_protection_bytes_per_key, false /* skip_filters */,
!kImmortal, false /* force_direct_prefetch */, -1 /* level */);
reader_options.largest_seqno = largest_seqno;
s = options.table_factory->NewTableReader(
s = ioptions.table_factory->NewTableReader(
read_options, reader_options, std::move(file_reader), file_size,
&table_reader, false /* prefetch_index_and_filter_in_cache */);
if (!s.ok()) {
+27 -62
View File
@@ -563,7 +563,7 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
}
}
TEST_F(DBBlockCacheTest, DynamicOptions) {
TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
@@ -578,74 +578,39 @@ TEST_F(DBBlockCacheTest, DynamicOptions) {
DestroyAndReopen(options);
std::string value(kValueSize, 'a');
auto st = options.statistics;
size_t i = 1;
ASSERT_OK(Put(std::to_string(i), value));
ASSERT_OK(Flush());
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
for (size_t i = 1; i <= 5; i++) {
ASSERT_OK(Put(std::to_string(i), value));
ASSERT_OK(Flush());
ASSERT_EQ(1,
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(value, Get(std::to_string(i)));
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
ASSERT_EQ(value, Get(std::to_string(i)));
ASSERT_EQ(0,
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(
0, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
ASSERT_EQ(1,
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
}
++i;
ASSERT_OK(dbfull()->SetOptions(
{{"block_based_table_factory", "{prepopulate_block_cache=kDisable;}"}}));
ASSERT_OK(Put(std::to_string(i), value));
ASSERT_OK(Flush());
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
for (size_t i = 6; i <= kNumBlocks; i++) {
ASSERT_OK(Put(std::to_string(i), value));
ASSERT_OK(Flush());
ASSERT_EQ(0,
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(value, Get(std::to_string(i)));
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
++i;
ASSERT_OK(dbfull()->SetOptions({{"block_based_table_factory",
"{prepopulate_block_cache=kFlushOnly;}"}}));
ASSERT_OK(Put(std::to_string(i), value));
ASSERT_OK(Flush());
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(value, Get(std::to_string(i)));
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
++i;
// NOT YET SUPPORTED
// FIXME: find a way to make this fail again (until well supported)
// ASSERT_NOK(dbfull()->SetOptions(
// {{"block_based_table_factory", "{block_cache=null;}"}}));
// ASSERT_OK(Put(std::to_string(i), value));
// ASSERT_OK(Flush());
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
// ASSERT_EQ(value, Get(std::to_string(i)));
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
// ++i;
// NOT YET SUPPORTED
// FIXME: find a way to make this fail again (until well supported)
// ASSERT_NOK(dbfull()->SetOptions(
// {{"block_based_table_factory", "{block_cache=1M;}"}}));
// ASSERT_OK(Put(std::to_string(i), value));
// ASSERT_OK(Flush());
// ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
// ASSERT_EQ(value, Get(std::to_string(i)));
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
// ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
ASSERT_EQ(value, Get(std::to_string(i)));
ASSERT_EQ(1,
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
ASSERT_EQ(
1, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
ASSERT_EQ(0,
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
}
}
#endif
+1 -68
View File
@@ -1975,24 +1975,10 @@ TEST_F(DBBloomFilterTest, MutatingRibbonFilterPolicy) {
if (configs.empty()) {
break;
}
std::string factory_field =
(v[0] & 1) ? "table_factory" : "block_based_table_factory";
// Some irrelevant SetOptions to be sure they don't interfere
ASSERT_OK(db_->SetOptions({{"level0_file_num_compaction_trigger", "10"}}));
ASSERT_OK(
db_->SetOptions({{"block_based_table_factory", "{block_size=1234}"}}));
ASSERT_OK(db_->SetOptions({{factory_field + ".block_size", "12345"}}));
// Test the mutable field we're interested in
ASSERT_OK(
db_->SetOptions({{factory_field + ".filter_policy.bloom_before_level",
db_->SetOptions({{"table_factory.filter_policy.bloom_before_level",
configs.back().first}}));
// FilterPolicy pointer should not have changed
ASSERT_EQ(db_->GetOptions()
.table_factory->GetOptions<BlockBasedTableOptions>()
->filter_policy.get(),
table_options.filter_policy.get());
// Ensure original object is mutated
std::string val;
@@ -2005,59 +1991,6 @@ TEST_F(DBBloomFilterTest, MutatingRibbonFilterPolicy) {
}
}
TEST_F(DBBloomFilterTest, MutableFilterPolicy) {
// Test that BlockBasedTableOptions::filter_policy is mutable (replaceable)
// with SetOptions.
Options options = CurrentOptions();
options.statistics = CreateDBStatistics();
auto& stats = *options.statistics;
BlockBasedTableOptions table_options;
// First config, to make sure there's no issues with this shared ptr
// etc. when the DB switches filter policies.
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
double expected_bpk = 10.0;
// Other configs to try
std::vector<std::pair<std::string, double>> configs = {
{"ribbonfilter:10:-1", 7.0}, {"bloomfilter:5", 5.0}, {"nullptr", 0.0}};
table_options.cache_index_and_filter_blocks = true;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
options.level0_file_num_compaction_trigger =
static_cast<int>(configs.size()) + 2;
ASSERT_OK(TryReopen(options));
char v[] = "a";
for (;; ++(v[0])) {
const int maxKey = 8000;
for (int i = 0; i < maxKey; i++) {
ASSERT_OK(Put(Key(i), v));
}
ASSERT_OK(Flush());
for (int i = 0; i < maxKey; i++) {
ASSERT_EQ(Get(Key(i)), v);
}
uint64_t filter_bytes =
stats.getAndResetTickerCount(BLOCK_CACHE_FILTER_BYTES_INSERT);
EXPECT_NEAR(filter_bytes * 8.0 / maxKey, expected_bpk, 0.3);
if (configs.empty()) {
break;
}
ASSERT_OK(
db_->SetOptions({{"block_based_table_factory",
"{filter_policy=" + configs.back().first + "}"}}));
expected_bpk = configs.back().second;
configs.pop_back();
}
}
class SliceTransformLimitedDomain : public SliceTransform {
const char* Name() const override { return "SliceTransformLimitedDomain"; }
+16 -80
View File
@@ -659,9 +659,8 @@ Status DBImpl::CloseHelper() {
// We need to release them before the block cache is destroyed. The block
// cache may be destroyed inside versions_.reset(), when column family data
// list is destroyed, so leaving handles in table cache after
// versions_.reset() may cause issues. Here we clean all unreferenced handles
// in table cache, and (for certain builds/conditions) assert that no obsolete
// files are hanging around unreferenced (leak) in the table/blob file cache.
// versions_.reset() may cause issues.
// Here we clean all unreferenced handles in table cache.
// Now we assume all user queries have finished, so only version set itself
// can possibly hold the blocks from block cache. After releasing unreferenced
// handles here, only handles held by version set left and inside
@@ -669,9 +668,6 @@ Status DBImpl::CloseHelper() {
// time a handle is released, we erase it from the cache too. By doing that,
// we can guarantee that after versions_.reset(), table cache is empty
// so the cache can be safely destroyed.
#ifndef NDEBUG
TEST_VerifyNoObsoleteFilesCached(/*db_mutex_already_held=*/true);
#endif // !NDEBUG
table_cache_->EraseUnRefEntries();
for (auto& txn_entry : recovered_transactions_) {
@@ -1157,13 +1153,6 @@ void DBImpl::DumpStats() {
continue;
}
auto* table_factory =
cfd->GetCurrentMutableCFOptions()->table_factory.get();
assert(table_factory != nullptr);
// FIXME: need to a shared_ptr if/when block_cache is going to be mutable
Cache* cache =
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
// Release DB mutex for gathering cache entry stats. Pass over all
// column families for this first so that other stats are dumped
// near-atomically.
@@ -1172,6 +1161,10 @@ void DBImpl::DumpStats() {
// Probe block cache for problems (if not already via another CF)
if (immutable_db_options_.info_log) {
auto* table_factory = cfd->ioptions()->table_factory.get();
assert(table_factory != nullptr);
Cache* cache =
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
if (cache && probed_caches.insert(cache).second) {
cache->ReportProblems(immutable_db_options_.info_log);
}
@@ -3234,8 +3227,6 @@ Status DBImpl::MultiGetImpl(
s = Status::Aborted();
break;
}
// This could be a long-running operation
ROCKSDB_THREAD_YIELD_HOOK();
}
// Post processing (decrement reference counts and record statistics)
@@ -3993,25 +3984,14 @@ std::unique_ptr<IterType> DBImpl::NewMultiCfIterator(
"Different comparators are being used across CFs"));
}
}
std::vector<Iterator*> child_iterators;
Status s = NewIterators(_read_options, column_families, &child_iterators);
if (!s.ok()) {
return error_iterator_func(s);
}
assert(column_families.size() == child_iterators.size());
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>
cfh_iter_pairs;
cfh_iter_pairs.reserve(column_families.size());
for (size_t i = 0; i < column_families.size(); ++i) {
cfh_iter_pairs.emplace_back(column_families[i], child_iterators[i]);
}
return std::make_unique<ImplType>(_read_options,
column_families[0]->GetComparator(),
std::move(cfh_iter_pairs));
return std::make_unique<ImplType>(column_families[0]->GetComparator(),
column_families,
std::move(child_iterators));
}
Status DBImpl::NewIterators(
@@ -4315,8 +4295,8 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
}
// Avoid to go through every column family by checking a global threshold
// first.
CfdList cf_scheduled;
if (oldest_snapshot > bottommost_files_mark_threshold_) {
CfdList cf_scheduled;
for (auto* cfd : *versions_->GetColumnFamilySet()) {
if (!cfd->ioptions()->allow_ingest_behind) {
cfd->current()->storage_info()->UpdateOldestSnapshot(
@@ -4348,24 +4328,6 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
}
bottommost_files_mark_threshold_ = new_bottommost_files_mark_threshold;
}
// Avoid to go through every column family by checking a global threshold
// first.
if (oldest_snapshot >= standalone_range_deletion_files_mark_threshold_) {
for (auto* cfd : *versions_->GetColumnFamilySet()) {
if (cfd->IsDropped() || CfdListContains(cf_scheduled, cfd)) {
continue;
}
if (oldest_snapshot >=
cfd->current()
->storage_info()
->standalone_range_tombstone_files_mark_threshold()) {
EnqueuePendingCompaction(cfd);
MaybeScheduleFlushOrCompaction();
cf_scheduled.push_back(cfd);
}
}
}
}
delete casted_s;
}
@@ -4817,24 +4779,6 @@ void DBImpl::ReleaseFileNumberFromPendingOutputs(
}
}
std::list<uint64_t>::iterator DBImpl::CaptureOptionsFileNumber() {
// We need to remember the iterator of our insert, because after the
// compaction is done, we need to remove that element from
// min_options_file_numbers_.
min_options_file_numbers_.push_back(versions_->options_file_number());
auto min_options_file_numbers_inserted_elem = min_options_file_numbers_.end();
--min_options_file_numbers_inserted_elem;
return min_options_file_numbers_inserted_elem;
}
void DBImpl::ReleaseOptionsFileNumber(
std::unique_ptr<std::list<uint64_t>::iterator>& v) {
if (v.get() != nullptr) {
min_options_file_numbers_.erase(*v.get());
v.reset();
}
}
Status DBImpl::GetUpdatesSince(
SequenceNumber seq, std::unique_ptr<TransactionLogIterator>* iter,
const TransactionLogIterator::ReadOptions& read_options) {
@@ -5198,12 +5142,11 @@ Status DBImpl::GetDbIdentity(std::string& identity) const {
return Status::OK();
}
Status DBImpl::GetDbIdentityFromIdentityFile(const IOOptions& opts,
std::string* identity) const {
Status DBImpl::GetDbIdentityFromIdentityFile(std::string* identity) const {
std::string idfilename = IdentityFileName(dbname_);
const FileOptions soptions;
Status s = ReadFileToString(fs_.get(), idfilename, opts, identity);
Status s = ReadFileToString(fs_.get(), idfilename, identity);
if (!s.ok()) {
return s;
}
@@ -5514,8 +5457,7 @@ Status DBImpl::WriteOptionsFile(const WriteOptions& write_options,
file_name, fs_.get());
if (s.ok()) {
s = RenameTempFileToOptionsFile(file_name,
db_options.compaction_service != nullptr);
s = RenameTempFileToOptionsFile(file_name);
}
if (!s.ok() && GetEnv()->FileExists(file_name).ok()) {
@@ -5592,8 +5534,7 @@ Status DBImpl::DeleteObsoleteOptionsFiles() {
return Status::OK();
}
Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name,
bool is_remote_compaction_enabled) {
Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
Status s;
uint64_t options_file_number = versions_->NewFileNumber();
@@ -5637,7 +5578,7 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name,
my_disable_delete_obsolete_files = disable_delete_obsolete_files_;
}
if (!my_disable_delete_obsolete_files && !is_remote_compaction_enabled) {
if (!my_disable_delete_obsolete_files) {
// TODO: Should we check for errors here?
DeleteObsoleteOptionsFiles().PermitUncheckedError();
}
@@ -5870,6 +5811,7 @@ Status DBImpl::IngestExternalFile(
Status DBImpl::IngestExternalFiles(
const std::vector<IngestExternalFileArg>& args) {
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
const WriteOptions write_options;
if (args.empty()) {
@@ -5895,10 +5837,6 @@ Status DBImpl::IngestExternalFiles(
snprintf(err_msg, 128, "external_files[%zu] is empty", i);
return Status::InvalidArgument(err_msg);
}
if (i && args[i].options.fill_cache != args[i - 1].options.fill_cache) {
return Status::InvalidArgument(
"fill_cache should be the same across ingestion options.");
}
}
for (const auto& arg : args) {
const IngestExternalFileOptions& ingest_opts = arg.options;
@@ -6086,8 +6024,6 @@ Status DBImpl::IngestExternalFiles(
}
}
if (status.ok()) {
ReadOptions read_options;
read_options.fill_cache = args[0].options.fill_cache;
autovector<ColumnFamilyData*> cfds_to_commit;
autovector<const MutableCFOptions*> mutable_cf_options_list;
autovector<autovector<VersionEdit*>> edit_lists;
+12 -38
View File
@@ -482,8 +482,7 @@ class DBImpl : public DB {
Status GetDbIdentity(std::string& identity) const override;
virtual Status GetDbIdentityFromIdentityFile(const IOOptions& opts,
std::string* identity) const;
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
Status GetDbSessionId(std::string& session_id) const override;
@@ -854,8 +853,6 @@ class DBImpl : public DB {
uint64_t GetObsoleteSstFilesSize();
uint64_t MinOptionsFileNumberToKeep();
// Returns the list of live files in 'live' and the list
// of all files in the filesystem in 'candidate_files'.
// If force == false and the last call was less than
@@ -1200,7 +1197,9 @@ class DBImpl : public DB {
uint64_t TEST_total_log_size() const { return total_log_size_; }
void TEST_GetAllBlockCaches(std::unordered_set<const Cache*>* cache_set);
// Returns column family name to ImmutableCFOptions map.
Status TEST_GetAllImmutableCFOptions(
std::unordered_map<std::string, const ImmutableCFOptions*>* iopts_map);
// Return the lastest MutableCFOptions of a column family
Status TEST_GetLatestMutableCFOptions(ColumnFamilyHandle* column_family,
@@ -1240,13 +1239,8 @@ class DBImpl : public DB {
static Status TEST_ValidateOptions(const DBOptions& db_options) {
return ValidateOptions(db_options);
}
#endif // NDEBUG
// In certain configurations, verify that the table/blob file cache only
// contains entries for live files, to check for effective leaks of open
// files. This can only be called when purging of obsolete files has
// "settled," such as during parts of DB Close().
void TEST_VerifyNoObsoleteFilesCached(bool db_mutex_already_held) const;
#endif // NDEBUG
// persist stats to column family "_persistent_stats"
void PersistStats();
@@ -1469,8 +1463,7 @@ class DBImpl : public DB {
// The following two functions can only be called when:
// 1. WriteThread::Writer::EnterUnbatched() is used.
// 2. db_mutex is NOT held
Status RenameTempFileToOptionsFile(const std::string& file_name,
bool is_remote_compaction_enabled);
Status RenameTempFileToOptionsFile(const std::string& file_name);
Status DeleteObsoleteOptionsFiles();
void NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
@@ -1594,7 +1587,7 @@ class DBImpl : public DB {
// Read/create DB identity file (as appropriate), and write DB ID to
// version_edit if provided.
Status SetupDBId(const WriteOptions& write_options, bool read_only,
bool is_new_db, bool is_retry, VersionEdit* version_edit);
bool is_new_db, VersionEdit* version_edit);
// Assign db_id_ and write DB ID to version_edit if provided.
void SetDBId(std::string&& id, bool read_only, VersionEdit* version_edit);
@@ -1701,8 +1694,6 @@ class DBImpl : public DB {
friend class XFTransactionWriteHandler;
friend class DBBlobIndexTest;
friend class WriteUnpreparedTransactionTest_RecoveryTest_Test;
friend class CompactionServiceTest_PreservedOptionsLocalCompaction_Test;
friend class CompactionServiceTest_PreservedOptionsRemoteCompaction_Test;
#endif
struct CompactionState;
@@ -1974,12 +1965,6 @@ class DBImpl : public DB {
void ReleaseFileNumberFromPendingOutputs(
std::unique_ptr<std::list<uint64_t>::iterator>& v);
// Similar to pending_outputs, preserve OPTIONS file. Used for remote
// compaction.
std::list<uint64_t>::iterator CaptureOptionsFileNumber();
void ReleaseOptionsFileNumber(
std::unique_ptr<std::list<uint64_t>::iterator>& v);
// Sets bg error if there is an error writing to WAL.
IOStatus SyncClosedWals(const WriteOptions& write_options,
JobContext* job_context, VersionEdit* synced_wals,
@@ -2091,18 +2076,17 @@ class DBImpl : public DB {
// memtable pending flush.
// resuming_from_bg_err indicates whether the caller is attempting to resume
// from background error.
Status WaitForFlushMemTable(
ColumnFamilyData* cfd, const uint64_t* flush_memtable_id = nullptr,
bool resuming_from_bg_err = false,
std::optional<FlushReason> flush_reason = std::nullopt) {
Status WaitForFlushMemTable(ColumnFamilyData* cfd,
const uint64_t* flush_memtable_id = nullptr,
bool resuming_from_bg_err = false) {
return WaitForFlushMemTables({cfd}, {flush_memtable_id},
resuming_from_bg_err, flush_reason);
resuming_from_bg_err);
}
// Wait for memtables to be flushed for multiple column families.
Status WaitForFlushMemTables(
const autovector<ColumnFamilyData*>& cfds,
const autovector<const uint64_t*>& flush_memtable_ids,
bool resuming_from_bg_err, std::optional<FlushReason> flush_reason);
bool resuming_from_bg_err);
inline void WaitForPendingWrites() {
mutex_.AssertHeld();
@@ -2771,11 +2755,6 @@ class DBImpl : public DB {
// State is protected with db mutex.
std::list<uint64_t> pending_outputs_;
// Similar to pending_outputs_, FindObsoleteFiles()/PurgeObsoleteFiles() never
// deletes any OPTIONS file that has number bigger than any of the file number
// in min_options_file_numbers_.
std::list<uint64_t> min_options_file_numbers_;
// flush_queue_ and compaction_queue_ hold column families that we need to
// flush and compact, respectively.
// A column family is inserted into flush_queue_ when it satisfies condition
@@ -2898,11 +2877,6 @@ class DBImpl : public DB {
// garbages, among all column families.
SequenceNumber bottommost_files_mark_threshold_ = kMaxSequenceNumber;
// The min threshold to trigger compactions for standalone range deletion
// files that are marked for compaction.
SequenceNumber standalone_range_deletion_files_mark_threshold_ =
kMaxSequenceNumber;
LogsWithPrepTracker logs_with_prep_tracker_;
// Callback for compaction to check if a key is visible to a snapshot.
+8 -68
View File
@@ -1561,12 +1561,6 @@ Status DBImpl::CompactFilesImpl(
compaction_job.Prepare();
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
if (immutable_db_options().compaction_service != nullptr) {
min_options_file_number_elem.reset(
new std::list<uint64_t>::iterator(CaptureOptionsFileNumber()));
}
mutex_.Unlock();
TEST_SYNC_POINT("CompactFilesImpl:0");
TEST_SYNC_POINT("CompactFilesImpl:1");
@@ -1576,10 +1570,6 @@ Status DBImpl::CompactFilesImpl(
TEST_SYNC_POINT("CompactFilesImpl:3");
mutex_.Lock();
if (immutable_db_options().compaction_service != nullptr) {
ReleaseOptionsFileNumber(min_options_file_number_elem);
}
bool compaction_released = false;
Status status =
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
@@ -1862,9 +1852,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompactions, not applicable */,
{} /* grandparents, not applicable */,
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
{} /* grandparents, not applicable */, false /* is manual */,
"" /* trim_ts */, -1 /* score, not applicable */,
false /* is deletion compaction, not applicable */,
false /* l0_files_might_overlap, not applicable */,
CompactionReason::kRefitLevel));
@@ -2418,8 +2407,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
}
s = WaitForFlushMemTables(
cfds, flush_memtable_ids,
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */,
flush_reason);
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */);
InstrumentedMutexLock lock_guard(&mutex_);
for (auto* tmp_cfd : cfds) {
tmp_cfd->UnrefAndTryDelete();
@@ -2561,8 +2549,7 @@ Status DBImpl::AtomicFlushMemTables(
}
s = WaitForFlushMemTables(
cfds, flush_memtable_ids,
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */,
flush_reason);
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */);
InstrumentedMutexLock lock_guard(&mutex_);
for (auto* cfd : cfds) {
cfd->UnrefAndTryDelete();
@@ -2625,7 +2612,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
flush_memtable_id_ptrs.push_back(&flush_memtable_id);
}
s = WaitForFlushMemTables(cfds, flush_memtable_id_ptrs,
true /* resuming_from_bg_err */, flush_reason);
true /* resuming_from_bg_err */);
mutex_.Lock();
}
@@ -2725,7 +2712,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
Status DBImpl::WaitForFlushMemTables(
const autovector<ColumnFamilyData*>& cfds,
const autovector<const uint64_t*>& flush_memtable_ids,
bool resuming_from_bg_err, std::optional<FlushReason> flush_reason) {
bool resuming_from_bg_err) {
int num = static_cast<int>(cfds.size());
// Wait until the compaction completes
InstrumentedMutexLock l(&mutex_);
@@ -2763,15 +2750,7 @@ Status DBImpl::WaitForFlushMemTables(
(flush_memtable_ids[i] != nullptr &&
cfds[i]->imm()->GetEarliestMemTableID() >
*flush_memtable_ids[i])) {
// Make file ingestion's flush wait until SuperVersion is also updated
// since after flush, it does range overlapping check and file level
// assignment with the current SuperVersion.
if (!flush_reason.has_value() ||
flush_reason.value() != FlushReason::kExternalFileIngestion ||
cfds[i]->GetSuperVersion()->imm->GetID() ==
cfds[i]->imm()->current()->GetID()) {
++num_finished;
}
++num_finished;
}
}
if (1 == num_dropped && 1 == num) {
@@ -3562,14 +3541,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
is_manual && manual_compaction->disallow_trivial_move;
CompactionJobStats compaction_job_stats;
// Set is_remote_compaction to true on CompactionBegin Event if
// compaction_service is set except for trivial moves. We do not know whether
// remote compaction will actually be successfully scheduled, or fall back to
// local at this time. CompactionCompleted event will tell the truth where
// the compaction actually happened.
compaction_job_stats.is_remote_compaction =
immutable_db_options().compaction_service != nullptr;
Status status;
if (!error_handler_.IsBGWorkStopped()) {
if (shutting_down_.load(std::memory_order_acquire)) {
@@ -3690,20 +3661,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
// compaction is not necessary. Need to make sure mutex is held
// until we make a copy in the following code
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():BeforePickCompaction");
SnapshotChecker* snapshot_checker = nullptr;
std::vector<SequenceNumber> snapshot_seqs;
// This info is not useful for other scenarios, so save querying existing
// snapshots for those cases.
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
cfd->user_comparator()->timestamp_size() == 0) {
SequenceNumber earliest_write_conflict_snapshot;
GetSnapshotContext(job_context, &snapshot_seqs,
&earliest_write_conflict_snapshot,
&snapshot_checker);
assert(is_snapshot_supported_ || snapshots_.empty());
}
c.reset(cfd->PickCompaction(*mutable_cf_options, mutable_db_options_,
snapshot_seqs, snapshot_checker, log_buffer));
log_buffer));
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
if (c != nullptr) {
@@ -3807,8 +3766,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
compaction_job_stats.num_input_files = c->num_input_files(0);
// Trivial moves do not get compacted remotely
compaction_job_stats.is_remote_compaction = false;
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
compaction_job_stats, job_context->job_id);
@@ -3944,12 +3901,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
&bg_bottom_compaction_scheduled_);
compaction_job.Prepare();
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
if (immutable_db_options().compaction_service != nullptr) {
min_options_file_number_elem.reset(
new std::list<uint64_t>::iterator(CaptureOptionsFileNumber()));
}
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
compaction_job_stats, job_context->job_id);
mutex_.Unlock();
@@ -3959,11 +3910,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
compaction_job.Run().PermitUncheckedError();
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial:AfterRun");
mutex_.Lock();
if (immutable_db_options().compaction_service != nullptr) {
ReleaseOptionsFileNumber(min_options_file_number_elem);
}
status =
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
io_s = compaction_job.io_status();
@@ -4310,18 +4256,12 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
// newer snapshot created and released frequently, the compaction will be
// triggered soon anyway.
bottommost_files_mark_threshold_ = kMaxSequenceNumber;
standalone_range_deletion_files_mark_threshold_ = kMaxSequenceNumber;
for (auto* my_cfd : *versions_->GetColumnFamilySet()) {
if (!my_cfd->ioptions()->allow_ingest_behind) {
bottommost_files_mark_threshold_ = std::min(
bottommost_files_mark_threshold_,
my_cfd->current()->storage_info()->bottommost_files_mark_threshold());
}
standalone_range_deletion_files_mark_threshold_ =
std::min(standalone_range_deletion_files_mark_threshold_,
cfd->current()
->storage_info()
->standalone_range_tombstone_files_mark_threshold());
}
// Whenever we install new SuperVersion, we might need to issue new flushes or
+15 -53
View File
@@ -9,7 +9,6 @@
#ifndef NDEBUG
#include "db/blob/blob_file_cache.h"
#include "db/column_family.h"
#include "db/db_impl/db_impl.h"
#include "db/error_handler.h"
@@ -233,16 +232,23 @@ uint64_t DBImpl::TEST_LogfileNumber() {
return logfile_number_;
}
void DBImpl::TEST_GetAllBlockCaches(
std::unordered_set<const Cache*>* cache_set) {
InstrumentedMutexLock l(&mutex_);
for (auto cfd : *versions_->GetColumnFamilySet()) {
if (const auto bbto =
cfd->GetCurrentMutableCFOptions()
->table_factory->GetOptions<BlockBasedTableOptions>()) {
cache_set->insert(bbto->block_cache.get());
Status DBImpl::TEST_GetAllImmutableCFOptions(
std::unordered_map<std::string, const ImmutableCFOptions*>* iopts_map) {
std::vector<std::string> cf_names;
std::vector<const ImmutableCFOptions*> iopts;
{
InstrumentedMutexLock l(&mutex_);
for (auto cfd : *versions_->GetColumnFamilySet()) {
cf_names.push_back(cfd->GetName());
iopts.push_back(cfd->ioptions());
}
}
iopts_map->clear();
for (size_t i = 0; i < cf_names.size(); ++i) {
iopts_map->insert({cf_names[i], iopts[i]});
}
return Status::OK();
}
uint64_t DBImpl::TEST_FindMinLogContainingOutstandingPrep() {
@@ -322,49 +328,5 @@ size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
InstrumentedMutexLock l(&const_cast<DBImpl*>(this)->stats_history_mutex_);
return EstimateInMemoryStatsHistorySize();
}
void DBImpl::TEST_VerifyNoObsoleteFilesCached(
bool db_mutex_already_held) const {
// This check is somewhat expensive and obscure to make a part of every
// unit test in every build variety. Thus, we only enable it for ASAN builds.
if (!kMustFreeHeapAllocations) {
return;
}
std::optional<InstrumentedMutexLock> l;
if (db_mutex_already_held) {
mutex_.AssertHeld();
} else {
l.emplace(&mutex_);
}
std::vector<uint64_t> live_files;
for (auto cfd : *versions_->GetColumnFamilySet()) {
if (cfd->IsDropped()) {
continue;
}
// Sneakily add both SST and blob files to the same list
cfd->current()->AddLiveFiles(&live_files, &live_files);
}
std::sort(live_files.begin(), live_files.end());
auto fn = [&live_files](const Slice& key, Cache::ObjectPtr, size_t,
const Cache::CacheItemHelper* helper) {
if (helper != BlobFileCache::GetHelper()) {
// Skip non-blob files for now
// FIXME: diagnose and fix the leaks of obsolete SST files revealed in
// unit tests.
return;
}
// See TableCache and BlobFileCache
assert(key.size() == sizeof(uint64_t));
uint64_t file_number;
GetUnaligned(reinterpret_cast<const uint64_t*>(key.data()), &file_number);
// Assert file is in sorted live_files
assert(
std::binary_search(live_files.begin(), live_files.end(), file_number));
};
table_cache_->ApplyToAllEntries(fn, {});
}
} // namespace ROCKSDB_NAMESPACE
#endif // NDEBUG
+3 -22
View File
@@ -43,14 +43,6 @@ uint64_t DBImpl::GetObsoleteSstFilesSize() {
return versions_->GetObsoleteSstFilesSize();
}
uint64_t DBImpl::MinOptionsFileNumberToKeep() {
mutex_.AssertHeld();
if (!min_options_file_numbers_.empty()) {
return *min_options_file_numbers_.begin();
}
return std::numeric_limits<uint64_t>::max();
}
Status DBImpl::DisableFileDeletions() {
Status s;
int my_disable_delete_obsolete_files;
@@ -155,7 +147,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
// here but later find newer generated unfinalized files while scanning.
job_context->min_pending_output = MinObsoleteSstNumberToKeep();
job_context->files_to_quarantine = error_handler_.GetFilesToQuarantine();
job_context->min_options_file_number = MinOptionsFileNumberToKeep();
// Get obsolete files. This function will also update the list of
// pending files in VersionSet().
@@ -507,7 +498,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
dbname_);
// File numbers of most recent two OPTIONS file in candidate_files (found in
// previous FindObsoleteFiles(full_scan=true))
// previos FindObsoleteFiles(full_scan=true))
// At this point, there must not be any duplicate file numbers in
// candidate_files.
uint64_t optsfile_num1 = std::numeric_limits<uint64_t>::min();
@@ -528,11 +519,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
}
}
// For remote compactions, we need to keep OPTIONS file that may get
// referenced by the remote worker
optsfile_num2 = std::min(optsfile_num2, state.min_options_file_number);
// Close WALs before trying to delete them.
for (const auto w : state.logs_to_free) {
// TODO: maybe check the return value of Close.
@@ -980,8 +966,7 @@ void DBImpl::SetDBId(std::string&& id, bool read_only,
}
Status DBImpl::SetupDBId(const WriteOptions& write_options, bool read_only,
bool is_new_db, bool is_retry,
VersionEdit* version_edit) {
bool is_new_db, VersionEdit* version_edit) {
Status s;
if (!is_new_db) {
// Check for the IDENTITY file and create it if not there or
@@ -989,11 +974,7 @@ Status DBImpl::SetupDBId(const WriteOptions& write_options, bool read_only,
std::string db_id_in_file;
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
if (s.ok()) {
IOOptions opts;
if (is_retry) {
opts.verify_and_reconstruct_read = true;
}
s = GetDbIdentityFromIdentityFile(opts, &db_id_in_file);
s = GetDbIdentityFromIdentityFile(&db_id_in_file);
if (s.ok() && !db_id_in_file.empty()) {
if (db_id_.empty()) {
// Loaded from file and wasn't already known from manifest
+14 -25
View File
@@ -301,7 +301,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
VersionEdit new_db_edit;
const WriteOptions write_options(Env::IOActivity::kDBOpen);
Status s = SetupDBId(write_options, /*read_only=*/false, /*is_new_db=*/true,
/*is_retry=*/false, &new_db_edit);
&new_db_edit);
if (!s.ok()) {
return s;
}
@@ -675,11 +675,11 @@ Status DBImpl::Recover(
// Already set up DB ID in NewDB
} else if (immutable_db_options_.write_dbid_to_manifest && recovery_ctx) {
VersionEdit edit;
s = SetupDBId(write_options, read_only, is_new_db, is_retry, &edit);
s = SetupDBId(write_options, read_only, is_new_db, &edit);
recovery_ctx->UpdateVersionEdits(
versions_->GetColumnFamilySet()->GetDefault(), edit);
} else {
s = SetupDBId(write_options, read_only, is_new_db, is_retry, nullptr);
s = SetupDBId(write_options, read_only, is_new_db, nullptr);
}
assert(!s.ok() || !db_id_.empty());
ROCKS_LOG_INFO(immutable_db_options_.info_log, "DB ID: %s\n", db_id_.c_str());
@@ -1274,8 +1274,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
reader.GetRecordedTimestampSize();
status = HandleWriteBatchTimestampSizeDifference(
&batch, running_ts_sz, record_ts_sz,
TimestampSizeConsistencyMode::kReconcileInconsistency, seq_per_batch_,
batch_per_txn_, &new_batch);
TimestampSizeConsistencyMode::kReconcileInconsistency, &new_batch);
if (!status.ok()) {
return status;
}
@@ -1668,19 +1667,10 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
Arena arena;
Status s;
TableProperties table_properties;
const auto* ucmp = cfd->internal_comparator().user_comparator();
assert(ucmp);
const size_t ts_sz = ucmp->timestamp_size();
const bool logical_strip_timestamp =
ts_sz > 0 && !cfd->ioptions()->persist_user_defined_timestamps;
{
ScopedArenaPtr<InternalIterator> iter(
logical_strip_timestamp
? mem->NewTimestampStrippingIterator(
ro, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr, ts_sz)
: mem->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr));
mem->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr));
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
"[%s] [WriteLevel0TableForRecovery]"
" Level-0 table #%" PRIu64 ": started",
@@ -1715,14 +1705,11 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
range_del_iters;
auto range_del_iter =
logical_strip_timestamp
? mem->NewTimestampStrippingRangeTombstoneIterator(
ro, kMaxSequenceNumber, ts_sz)
// This is called during recovery, where a live memtable is
// flushed directly. In this case, no fragmented tombstone list is
// cached in this memtable yet.
: mem->NewRangeTombstoneIterator(ro, kMaxSequenceNumber,
false /* immutable_memtable */);
// This is called during recovery, where a live memtable is flushed
// directly. In this case, no fragmented tombstone list is cached in
// this memtable yet.
mem->NewRangeTombstoneIterator(ro, kMaxSequenceNumber,
false /* immutable_memtable */);
if (range_del_iter != nullptr) {
range_del_iters.emplace_back(range_del_iter);
}
@@ -1808,7 +1795,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
// For UDT in memtable only feature, move up the cutoff timestamp whenever
// a flush happens.
if (logical_strip_timestamp) {
const Comparator* ucmp = cfd->user_comparator();
size_t ts_sz = ucmp->timestamp_size();
if (ts_sz > 0 && !cfd->ioptions()->persist_user_defined_timestamps) {
Slice mem_newest_udt = mem->GetNewestUDT();
std::string full_history_ts_low = cfd->GetFullHistoryTsLow();
if (full_history_ts_low.empty() ||
+1 -2
View File
@@ -265,8 +265,7 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
std::string manifest_path;
uint64_t manifest_file_number;
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), /*is_retry=*/false,
&manifest_path,
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), &manifest_path,
&manifest_file_number);
} else {
// Historic behavior that doesn't necessarily make sense
+11 -14
View File
@@ -233,8 +233,7 @@ Status DBImplSecondary::RecoverLogFiles(
reader->GetRecordedTimestampSize();
status = HandleWriteBatchTimestampSizeDifference(
&batch, running_ts_sz, record_ts_sz,
TimestampSizeConsistencyMode::kVerifyConsistency, seq_per_batch_,
batch_per_txn_);
TimestampSizeConsistencyMode::kVerifyConsistency);
if (!status.ok()) {
break;
}
@@ -952,23 +951,21 @@ Status DB::OpenAndCompact(
return s;
}
// 2. Load the options
// 2. Load the options from latest OPTIONS file
DBOptions db_options;
ConfigOptions config_options;
config_options.env = override_options.env;
std::vector<ColumnFamilyDescriptor> all_column_families;
TEST_SYNC_POINT_CALLBACK(
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
&compaction_input.options_file_number);
TEST_SYNC_POINT("DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:1");
std::string options_file_name =
OptionsFileName(name, compaction_input.options_file_number);
s = LoadOptionsFromFile(config_options, options_file_name, &db_options,
&all_column_families);
s = LoadLatestOptions(config_options, name, &db_options,
&all_column_families);
// In a very rare scenario, loading options may fail if the options changed by
// the primary host at the same time. Just retry once for now.
if (!s.ok()) {
return s;
s = LoadLatestOptions(config_options, name, &db_options,
&all_column_families);
if (!s.ok()) {
return s;
}
}
// 3. Override pointer configurations in DBOptions with
+24 -33
View File
@@ -735,6 +735,17 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
size_t total_byte_size = 0;
if (w.status.ok()) {
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
// grabs but does not seem thread-safe.
if (tracer_) {
InstrumentedMutexLock lock(&trace_mutex_);
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
for (auto* writer : wal_write_group) {
// TODO: maybe handle the tracing status?
tracer_->Write(writer->batch).PermitUncheckedError();
}
}
}
SequenceNumber next_sequence = current_sequence;
for (auto* writer : wal_write_group) {
assert(writer);
@@ -749,22 +760,6 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
}
}
}
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
// grabs but does not seem thread-safe.
if (tracer_) {
InstrumentedMutexLock lock(&trace_mutex_);
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
for (auto* writer : wal_write_group) {
if (writer->CallbackFailed()) {
// When optimisitc txn conflict checking fails, we should
// not record to trace.
continue;
}
// TODO: maybe handle the tracing status?
tracer_->Write(writer->batch).PermitUncheckedError();
}
}
}
if (w.disable_wal) {
has_unpersisted_data_.store(true, std::memory_order_relaxed);
}
@@ -1010,6 +1005,19 @@ Status DBImpl::WriteImplWALOnly(
WriteThread::WriteGroup write_group;
uint64_t last_sequence;
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
// Note: no need to update last_batch_group_size_ here since the batch writes
// to WAL only
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
// grabs but does not seem thread-safe.
if (tracer_) {
InstrumentedMutexLock lock(&trace_mutex_);
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
for (auto* writer : write_group) {
// TODO: maybe handle the tracing status?
tracer_->Write(writer->batch).PermitUncheckedError();
}
}
}
size_t pre_release_callback_cnt = 0;
size_t total_byte_size = 0;
@@ -1024,23 +1032,6 @@ Status DBImpl::WriteImplWALOnly(
}
}
// Note: no need to update last_batch_group_size_ here since the batch writes
// to WAL only
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
// grabs but does not seem thread-safe.
if (tracer_) {
InstrumentedMutexLock lock(&trace_mutex_);
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
for (auto* writer : write_group) {
if (writer->CallbackFailed()) {
continue;
}
// TODO: maybe handle the tracing status?
tracer_->Write(writer->batch).PermitUncheckedError();
}
}
}
const bool concurrent_update = true;
// Update stats while we are an exclusive group leader, so we know
// that nobody else can be writing to these particular stats.
+9 -161
View File
@@ -27,14 +27,12 @@ class CorruptionFS : public FileSystemWrapper {
num_writable_file_errors_(0),
corruption_trigger_(INT_MAX),
read_count_(0),
corrupt_offset_(0),
corrupt_len_(0),
rnd_(300),
fs_buffer_(fs_buffer),
verify_read_(verify_read) {}
~CorruptionFS() override {
// Assert that the corruption was reset, which means it got triggered
assert(corruption_trigger_ == INT_MAX || corrupt_len_ > 0);
assert(corruption_trigger_ == INT_MAX);
}
const char* Name() const override { return "ErrorEnv"; }
@@ -50,10 +48,8 @@ class CorruptionFS : public FileSystemWrapper {
}
void SetCorruptionTrigger(const int trigger) {
MutexLock l(&mutex_);
corruption_trigger_ = trigger;
read_count_ = 0;
corrupt_fname_.clear();
}
IOStatus NewRandomAccessFile(const std::string& fname,
@@ -62,31 +58,25 @@ class CorruptionFS : public FileSystemWrapper {
IODebugContext* dbg) override {
class CorruptionRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
public:
CorruptionRandomAccessFile(CorruptionFS& fs, const std::string& fname,
CorruptionRandomAccessFile(CorruptionFS& fs,
std::unique_ptr<FSRandomAccessFile>& file)
: FSRandomAccessFileOwnerWrapper(std::move(file)),
fs_(fs),
fname_(fname) {}
: FSRandomAccessFileOwnerWrapper(std::move(file)), fs_(fs) {}
IOStatus Read(uint64_t offset, size_t len, const IOOptions& opts,
Slice* result, char* scratch,
IODebugContext* dbg) const override {
IOStatus s = target()->Read(offset, len, opts, result, scratch, dbg);
if (opts.verify_and_reconstruct_read) {
fs_.MaybeResetOverlapWithCorruptedChunk(fname_, offset,
result->size());
return s;
}
MutexLock l(&fs_.mutex_);
if (s.ok() && ++fs_.read_count_ >= fs_.corruption_trigger_) {
fs_.read_count_ = 0;
fs_.corruption_trigger_ = INT_MAX;
char* data = const_cast<char*>(result->data());
std::memcpy(
data,
fs_.rnd_.RandomString(static_cast<int>(result->size())).c_str(),
result->size());
fs_.SetCorruptedChunk(fname_, offset, result->size());
}
return s;
}
@@ -111,76 +101,14 @@ class CorruptionFS : public FileSystemWrapper {
return IOStatus::OK();
}
IOStatus Prefetch(uint64_t /*offset*/, size_t /*n*/,
const IOOptions& /*options*/,
IODebugContext* /*dbg*/) override {
return IOStatus::NotSupported("Prefetch");
}
private:
CorruptionFS& fs_;
std::string fname_;
};
std::unique_ptr<FSRandomAccessFile> file;
IOStatus s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
EXPECT_OK(s);
result->reset(new CorruptionRandomAccessFile(*this, fname, file));
return s;
}
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg) override {
class CorruptionSequentialFile : public FSSequentialFileOwnerWrapper {
public:
CorruptionSequentialFile(CorruptionFS& fs, const std::string& fname,
std::unique_ptr<FSSequentialFile>& file)
: FSSequentialFileOwnerWrapper(std::move(file)),
fs_(fs),
fname_(fname),
offset_(0) {}
IOStatus Read(size_t len, const IOOptions& opts, Slice* result,
char* scratch, IODebugContext* dbg) override {
IOStatus s = target()->Read(len, opts, result, scratch, dbg);
if (result->size() == 0 ||
fname_.find("IDENTITY") != std::string::npos) {
return s;
}
if (opts.verify_and_reconstruct_read) {
fs_.MaybeResetOverlapWithCorruptedChunk(fname_, offset_,
result->size());
return s;
}
MutexLock l(&fs_.mutex_);
if (s.ok() && ++fs_.read_count_ >= fs_.corruption_trigger_) {
fs_.corruption_trigger_ = INT_MAX;
char* data = const_cast<char*>(result->data());
std::memcpy(
data,
fs_.rnd_.RandomString(static_cast<int>(result->size())).c_str(),
result->size());
fs_.SetCorruptedChunk(fname_, offset_, result->size());
}
offset_ += result->size();
return s;
}
private:
CorruptionFS& fs_;
std::string fname_;
size_t offset_;
};
std::unique_ptr<FSSequentialFile> file;
IOStatus s = target()->NewSequentialFile(fname, file_opts, &file, dbg);
EXPECT_OK(s);
result->reset(new CorruptionSequentialFile(*this, fname, file));
result->reset(new CorruptionRandomAccessFile(*this, file));
return s;
}
@@ -195,40 +123,12 @@ class CorruptionFS : public FileSystemWrapper {
}
}
void SetCorruptedChunk(const std::string& fname, size_t offset, size_t len) {
assert(corrupt_fname_.empty());
corrupt_fname_ = fname;
corrupt_offset_ = offset;
corrupt_len_ = len;
}
void MaybeResetOverlapWithCorruptedChunk(const std::string& fname,
size_t offset, size_t len) {
if (fname == corrupt_fname_ &&
((offset <= corrupt_offset_ && (offset + len) > corrupt_offset_) ||
(offset >= corrupt_offset_ &&
offset < (corrupt_offset_ + corrupt_len_)))) {
corrupt_fname_.clear();
}
}
bool VerifyRetry() { return corrupt_len_ > 0 && corrupt_fname_.empty(); }
int read_count() { return read_count_; }
int corruption_trigger() { return corruption_trigger_; }
private:
int corruption_trigger_;
int read_count_;
std::string corrupt_fname_;
size_t corrupt_offset_;
size_t corrupt_len_;
Random rnd_;
bool fs_buffer_;
bool verify_read_;
port::Mutex mutex_;
};
} // anonymous namespace
@@ -817,7 +717,6 @@ class DBIOCorruptionTest
bbto.num_file_reads_for_auto_readahead = 0;
options_.table_factory.reset(NewBlockBasedTableFactory(bbto));
options_.disable_auto_compactions = true;
options_.max_file_opening_threads = 0;
Reopen(options_);
}
@@ -958,8 +857,8 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
Status s = Flush();
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
std::string val;
@@ -986,8 +885,8 @@ TEST_P(DBIOCorruptionTest, ManifestCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_OK(ReopenDB());
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_EQ(ReopenDB(), Status::Corruption());
@@ -1071,57 +970,6 @@ TEST_P(DBIOCorruptionTest, TablePropertiesCorruptionRetry) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBIOCorruptionTest, DBOpenReadCorruptionRetry) {
if (!std::get<2>(GetParam())) {
return;
}
CorruptionFS* fs =
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
for (int sst = 0; sst < 3; ++sst) {
for (int key = 0; key < 100; ++key) {
std::stringstream ss;
ss << std::setw(3) << 100 * sst + key;
ASSERT_OK(Put("key" + ss.str(), "val" + ss.str()));
}
ASSERT_OK(Flush());
}
Close();
// DB open will create table readers unless we reduce the table cache
// capacity.
// SanitizeOptions will set max_open_files to minimum of 20. Table cache
// is allocated with max_open_files - 10 as capacity. So override
// max_open_files to 11 so table cache capacity will become 1. This will
// prevent file open during DB open and force the file to be opened
// during MultiGet
SyncPoint::GetInstance()->SetCallBack(
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
int* max_open_files = (int*)arg;
*max_open_files = 11;
});
SyncPoint::GetInstance()->EnableProcessing();
// Progressively increase the IO count trigger for corruption, and verify
// that it was retried
int corruption_trigger = 1;
fs->SetCorruptionTrigger(corruption_trigger);
do {
fs->SetCorruptionTrigger(corruption_trigger);
ASSERT_OK(ReopenDB());
for (int sst = 0; sst < 3; ++sst) {
for (int key = 0; key < 100; ++key) {
std::stringstream ss;
ss << std::setw(3) << 100 * sst + key;
ASSERT_EQ(Get("key" + ss.str()), "val" + ss.str());
}
}
// Verify that the injected corruption was repaired
ASSERT_TRUE(fs->VerifyRetry());
corruption_trigger++;
} while (fs->corruption_trigger() == INT_MAX);
}
// The parameters are - 1. Use FS provided buffer, 2. Use async IO ReadOption,
// 3. Retry with verify_and_reconstruct_read IOOption
INSTANTIATE_TEST_CASE_P(DBIOCorruptionTest, DBIOCorruptionTest,
+85 -113
View File
@@ -52,9 +52,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
user_comparator_(cmp),
merge_operator_(ioptions.merge_operator.get()),
iter_(iter),
blob_reader_(version, read_options.read_tier,
read_options.verify_checksums, read_options.fill_cache,
read_options.io_activity),
version_(version),
read_callback_(read_callback),
sequence_(s),
statistics_(ioptions.stats),
@@ -73,10 +71,13 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
expect_total_order_inner_iter_(prefix_extractor_ == nullptr ||
read_options.total_order_seek ||
read_options.auto_prefix_mode),
read_tier_(read_options.read_tier),
fill_cache_(read_options.fill_cache),
verify_checksums_(read_options.verify_checksums),
expose_blob_index_(expose_blob_index),
allow_unprepared_value_(read_options.allow_unprepared_value),
is_blob_(false),
arena_mode_(arena_mode),
io_activity_(read_options.io_activity),
cfh_(cfh),
timestamp_ub_(read_options.timestamp),
timestamp_lb_(read_options.iter_start_ts),
@@ -150,7 +151,7 @@ void DBIter::Next() {
PERF_CPU_TIMER_GUARD(iter_next_cpu_nanos, clock_);
// Release temporarily pinned blocks from last operation
ReleaseTempPinnedData();
ResetBlobData();
ResetBlobValue();
ResetValueAndColumns();
local_stats_.skip_count_ += num_internal_keys_skipped_;
local_stats_.skip_count_--;
@@ -193,21 +194,29 @@ void DBIter::Next() {
}
}
Status DBIter::BlobReader::RetrieveAndSetBlobValue(const Slice& user_key,
const Slice& blob_index) {
bool DBIter::SetBlobValueIfNeeded(const Slice& user_key,
const Slice& blob_index) {
assert(!is_blob_);
assert(blob_value_.empty());
if (expose_blob_index_) { // Stacked BlobDB implementation
is_blob_ = true;
return true;
}
if (!version_) {
return Status::Corruption("Encountered unexpected blob index.");
status_ = Status::Corruption("Encountered unexpected blob index.");
valid_ = false;
return false;
}
// TODO: consider moving ReadOptions from ArenaWrappedDBIter to DBIter to
// avoid having to copy options back and forth.
// TODO: plumb Env::IOPriority
// TODO: plumb Env::IOActivity, Env::IOPriority
ReadOptions read_options;
read_options.read_tier = read_tier_;
read_options.verify_checksums = verify_checksums_;
read_options.fill_cache = fill_cache_;
read_options.verify_checksums = verify_checksums_;
read_options.io_activity = io_activity_;
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
constexpr uint64_t* bytes_read = nullptr;
@@ -215,49 +224,14 @@ Status DBIter::BlobReader::RetrieveAndSetBlobValue(const Slice& user_key,
const Status s = version_->GetBlob(read_options, user_key, blob_index,
prefetch_buffer, &blob_value_, bytes_read);
if (!s.ok()) {
return s;
}
return Status::OK();
}
bool DBIter::SetValueAndColumnsFromBlobImpl(const Slice& user_key,
const Slice& blob_index) {
const Status s = blob_reader_.RetrieveAndSetBlobValue(user_key, blob_index);
if (!s.ok()) {
status_ = s;
valid_ = false;
is_blob_ = false;
return false;
}
SetValueAndColumnsFromPlain(blob_reader_.GetBlobValue());
return true;
}
bool DBIter::SetValueAndColumnsFromBlob(const Slice& user_key,
const Slice& blob_index) {
assert(!is_blob_);
is_blob_ = true;
if (expose_blob_index_) {
SetValueAndColumnsFromPlain(blob_index);
return true;
}
if (allow_unprepared_value_) {
assert(value_.empty());
assert(wide_columns_.empty());
assert(lazy_blob_index_.empty());
lazy_blob_index_ = blob_index;
return true;
}
return SetValueAndColumnsFromBlobImpl(user_key, blob_index);
return true;
}
bool DBIter::SetValueAndColumnsFromEntity(Slice slice) {
@@ -305,24 +279,6 @@ bool DBIter::SetValueAndColumnsFromMergeResult(const Status& merge_status,
return true;
}
bool DBIter::PrepareValue() {
assert(valid_);
if (lazy_blob_index_.empty()) {
return true;
}
assert(allow_unprepared_value_);
assert(is_blob_);
const bool result =
SetValueAndColumnsFromBlobImpl(saved_key_.GetUserKey(), lazy_blob_index_);
lazy_blob_index_.clear();
return result;
}
// PRE: saved_key_ has the current user key if skipping_saved_key
// POST: saved_key_ should have the next user key if valid_,
// if the current entry is a result of merge
@@ -452,7 +408,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
case kTypeValuePreferredSeqno:
case kTypeBlobIndex:
case kTypeWideColumnEntity:
if (!PrepareValueInternal()) {
if (!PrepareValue()) {
return false;
}
if (timestamp_lb_) {
@@ -464,9 +420,12 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
}
if (ikey_.type == kTypeBlobIndex) {
if (!SetValueAndColumnsFromBlob(ikey_.user_key, iter_.value())) {
if (!SetBlobValueIfNeeded(ikey_.user_key, iter_.value())) {
return false;
}
SetValueAndColumnsFromPlain(expose_blob_index_ ? iter_.value()
: blob_value_);
} else if (ikey_.type == kTypeWideColumnEntity) {
if (!SetValueAndColumnsFromEntity(iter_.value())) {
return false;
@@ -486,7 +445,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
return true;
break;
case kTypeMerge:
if (!PrepareValueInternal()) {
if (!PrepareValue()) {
return false;
}
saved_key_.SetUserKey(
@@ -581,8 +540,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
} else {
iter_.Next();
}
// This could be a long-running operation due to tombstones, etc.
ROCKSDB_THREAD_YIELD_HOOK();
} while (iter_.Valid());
valid_ = false;
@@ -633,7 +590,7 @@ bool DBIter::MergeValuesNewToOld() {
iter_.Next();
break;
}
if (!PrepareValueInternal()) {
if (!PrepareValue()) {
return false;
}
@@ -662,9 +619,23 @@ bool DBIter::MergeValuesNewToOld() {
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
PERF_COUNTER_ADD(internal_merge_count, 1);
} else if (kTypeBlobIndex == ikey.type) {
if (!MergeWithBlobBaseValue(iter_.value(), ikey.user_key)) {
if (expose_blob_index_) {
status_ =
Status::NotSupported("BlobDB does not support merge operator.");
valid_ = false;
return false;
}
// hit a put, merge the put value with operands and store the
// final result in saved_value_. We are done!
if (!SetBlobValueIfNeeded(ikey.user_key, iter_.value())) {
return false;
}
valid_ = true;
if (!MergeWithPlainBaseValue(blob_value_, ikey.user_key)) {
return false;
}
ResetBlobValue();
// iter_ is positioned after put
iter_.Next();
@@ -672,7 +643,6 @@ bool DBIter::MergeValuesNewToOld() {
valid_ = false;
return false;
}
return true;
} else if (kTypeWideColumnEntity == ikey.type) {
if (!MergeWithWideColumnBaseValue(iter_.value(), ikey.user_key)) {
@@ -719,7 +689,7 @@ void DBIter::Prev() {
PERF_COUNTER_ADD(iter_prev_count, 1);
PERF_CPU_TIMER_GUARD(iter_prev_cpu_nanos, clock_);
ReleaseTempPinnedData();
ResetBlobData();
ResetBlobValue();
ResetValueAndColumns();
ResetInternalKeysSkippedCounter();
bool ok = true;
@@ -956,7 +926,7 @@ bool DBIter::FindValueForCurrentKey() {
return FindValueForCurrentKeyUsingSeek();
}
if (!PrepareValueInternal()) {
if (!PrepareValue()) {
return false;
}
@@ -1071,9 +1041,21 @@ bool DBIter::FindValueForCurrentKey() {
}
return true;
} else if (last_not_merge_type == kTypeBlobIndex) {
if (!MergeWithBlobBaseValue(pinned_value_, saved_key_.GetUserKey())) {
if (expose_blob_index_) {
status_ =
Status::NotSupported("BlobDB does not support merge operator.");
valid_ = false;
return false;
}
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
return false;
}
valid_ = true;
if (!MergeWithPlainBaseValue(blob_value_, saved_key_.GetUserKey())) {
return false;
}
ResetBlobValue();
return true;
} else if (last_not_merge_type == kTypeWideColumnEntity) {
@@ -1098,9 +1080,13 @@ bool DBIter::FindValueForCurrentKey() {
break;
case kTypeBlobIndex:
if (!SetValueAndColumnsFromBlob(saved_key_.GetUserKey(), pinned_value_)) {
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
return false;
}
SetValueAndColumnsFromPlain(expose_blob_index_ ? pinned_value_
: blob_value_);
break;
case kTypeWideColumnEntity:
if (!SetValueAndColumnsFromEntity(pinned_value_)) {
@@ -1187,7 +1173,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
}
return true;
}
if (!PrepareValueInternal()) {
if (!PrepareValue()) {
return false;
}
if (timestamp_size_ > 0) {
@@ -1204,9 +1190,12 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
pinned_value_ = iter_.value();
}
if (ikey.type == kTypeBlobIndex) {
if (!SetValueAndColumnsFromBlob(ikey.user_key, pinned_value_)) {
if (!SetBlobValueIfNeeded(ikey.user_key, pinned_value_)) {
return false;
}
SetValueAndColumnsFromPlain(expose_blob_index_ ? pinned_value_
: blob_value_);
} else if (ikey.type == kTypeWideColumnEntity) {
if (!SetValueAndColumnsFromEntity(pinned_value_)) {
return false;
@@ -1254,7 +1243,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
ikey.type == kTypeDeletionWithTimestamp) {
break;
}
if (!PrepareValueInternal()) {
if (!PrepareValue()) {
return false;
}
@@ -1272,9 +1261,21 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
PERF_COUNTER_ADD(internal_merge_count, 1);
} else if (ikey.type == kTypeBlobIndex) {
if (!MergeWithBlobBaseValue(iter_.value(), saved_key_.GetUserKey())) {
if (expose_blob_index_) {
status_ =
Status::NotSupported("BlobDB does not support merge operator.");
valid_ = false;
return false;
}
if (!SetBlobValueIfNeeded(ikey.user_key, iter_.value())) {
return false;
}
valid_ = true;
if (!MergeWithPlainBaseValue(blob_value_, saved_key_.GetUserKey())) {
return false;
}
ResetBlobValue();
return true;
} else if (ikey.type == kTypeWideColumnEntity) {
@@ -1341,35 +1342,6 @@ bool DBIter::MergeWithPlainBaseValue(const Slice& value,
return SetValueAndColumnsFromMergeResult(s, result_type);
}
bool DBIter::MergeWithBlobBaseValue(const Slice& blob_index,
const Slice& user_key) {
assert(!is_blob_);
if (expose_blob_index_) {
status_ =
Status::NotSupported("Legacy BlobDB does not support merge operator.");
valid_ = false;
return false;
}
const Status s = blob_reader_.RetrieveAndSetBlobValue(user_key, blob_index);
if (!s.ok()) {
status_ = s;
valid_ = false;
return false;
}
valid_ = true;
if (!MergeWithPlainBaseValue(blob_reader_.GetBlobValue(), user_key)) {
return false;
}
blob_reader_.ResetBlobValue();
return true;
}
bool DBIter::MergeWithWideColumnBaseValue(const Slice& entity,
const Slice& user_key) {
// `op_failure_scope` (an output parameter) is not provided (set to nullptr)
@@ -1559,7 +1531,7 @@ void DBIter::Seek(const Slice& target) {
status_ = Status::OK();
ReleaseTempPinnedData();
ResetBlobData();
ResetBlobValue();
ResetValueAndColumns();
ResetInternalKeysSkippedCounter();
@@ -1635,7 +1607,7 @@ void DBIter::SeekForPrev(const Slice& target) {
status_ = Status::OK();
ReleaseTempPinnedData();
ResetBlobData();
ResetBlobValue();
ResetValueAndColumns();
ResetInternalKeysSkippedCounter();
@@ -1696,7 +1668,7 @@ void DBIter::SeekToFirst() {
status_.PermitUncheckedError();
direction_ = kForward;
ReleaseTempPinnedData();
ResetBlobData();
ResetBlobValue();
ResetValueAndColumns();
ResetInternalKeysSkippedCounter();
ClearSavedValue();
@@ -1759,7 +1731,7 @@ void DBIter::SeekToLast() {
status_.PermitUncheckedError();
direction_ = kReverse;
ReleaseTempPinnedData();
ResetBlobData();
ResetBlobValue();
ResetValueAndColumns();
ResetInternalKeysSkippedCounter();
ClearSavedValue();
+17 -44
View File
@@ -218,34 +218,7 @@ class DBIter final : public Iterator {
}
void set_valid(bool v) { valid_ = v; }
bool PrepareValue() override;
private:
class BlobReader {
public:
BlobReader(const Version* version, ReadTier read_tier,
bool verify_checksums, bool fill_cache,
Env::IOActivity io_activity)
: version_(version),
read_tier_(read_tier),
verify_checksums_(verify_checksums),
fill_cache_(fill_cache),
io_activity_(io_activity) {}
const Slice& GetBlobValue() const { return blob_value_; }
Status RetrieveAndSetBlobValue(const Slice& user_key,
const Slice& blob_index);
void ResetBlobValue() { blob_value_.Reset(); }
private:
PinnableSlice blob_value_;
const Version* version_;
ReadTier read_tier_;
bool verify_checksums_;
bool fill_cache_;
Env::IOActivity io_activity_;
};
// For all methods in this block:
// PRE: iter_->Valid() && status_.ok()
// Return false if there was an error, and status() is non-ok, valid_ = false;
@@ -326,6 +299,15 @@ class DBIter final : public Iterator {
: user_comparator_.CompareWithoutTimestamp(a, b);
}
// Retrieves the blob value for the specified user key using the given blob
// index when using the integrated BlobDB implementation.
bool SetBlobValueIfNeeded(const Slice& user_key, const Slice& blob_index);
void ResetBlobValue() {
is_blob_ = false;
blob_value_.Reset();
}
void SetValueAndColumnsFromPlain(const Slice& slice) {
assert(value_.empty());
assert(wide_columns_.empty());
@@ -334,11 +316,6 @@ class DBIter final : public Iterator {
wide_columns_.emplace_back(kDefaultWideColumnName, slice);
}
bool SetValueAndColumnsFromBlobImpl(const Slice& user_key,
const Slice& blob_index);
bool SetValueAndColumnsFromBlob(const Slice& user_key,
const Slice& blob_index);
bool SetValueAndColumnsFromEntity(Slice slice);
bool SetValueAndColumnsFromMergeResult(const Status& merge_status,
@@ -349,21 +326,14 @@ class DBIter final : public Iterator {
wide_columns_.clear();
}
void ResetBlobData() {
blob_reader_.ResetBlobValue();
lazy_blob_index_.clear();
is_blob_ = false;
}
// The following methods perform the actual merge operation for the
// no/plain/blob/wide-column base value cases.
// no base value/plain base value/wide-column base value cases.
// If user-defined timestamp is enabled, `user_key` includes timestamp.
bool MergeWithNoBaseValue(const Slice& user_key);
bool MergeWithPlainBaseValue(const Slice& value, const Slice& user_key);
bool MergeWithBlobBaseValue(const Slice& blob_index, const Slice& user_key);
bool MergeWithWideColumnBaseValue(const Slice& entity, const Slice& user_key);
bool PrepareValueInternal() {
bool PrepareValue() {
if (!iter_.PrepareValue()) {
assert(!iter_.status().ok());
valid_ = false;
@@ -386,7 +356,7 @@ class DBIter final : public Iterator {
UserComparatorWrapper user_comparator_;
const MergeOperator* const merge_operator_;
IteratorWrapper iter_;
BlobReader blob_reader_;
const Version* version_;
ReadCallback* read_callback_;
// Max visible sequence number. It is normally the snapshot seq unless we have
// uncommitted data in db as in WriteUnCommitted.
@@ -406,6 +376,7 @@ class DBIter final : public Iterator {
std::string saved_value_;
Slice pinned_value_;
// for prefix seek mode to support prev()
PinnableSlice blob_value_;
// Value of the default column
Slice value_;
// All columns (i.e. name-value pairs)
@@ -439,13 +410,15 @@ class DBIter final : public Iterator {
// Expect the inner iterator to maintain a total order.
// prefix_extractor_ must be non-NULL if the value is false.
const bool expect_total_order_inner_iter_;
ReadTier read_tier_;
bool fill_cache_;
bool verify_checksums_;
// Whether the iterator is allowed to expose blob references. Set to true when
// the stacked BlobDB implementation is used, false otherwise.
bool expose_blob_index_;
bool allow_unprepared_value_;
Slice lazy_blob_index_;
bool is_blob_;
bool arena_mode_;
const Env::IOActivity io_activity_;
// List of operands for merge operator.
MergeContext merge_context_;
LocalStatistics local_stats_;
+6 -22
View File
@@ -231,33 +231,21 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
ASSERT_OK(dbfull()->SetOptions(
cfh, {{"table_factory.block_size", "16384"},
{"table_factory.block_restart_interval", "11"}}));
// Old c_bbto
ASSERT_EQ(c_bbto->block_size, 8192);
ASSERT_EQ(c_bbto->block_restart_interval, 7);
// New c_bbto
c_opts = dbfull()->GetOptions(cfh);
c_bbto = c_opts.table_factory->GetOptions<BlockBasedTableOptions>();
ASSERT_EQ(c_bbto->block_size, 16384);
ASSERT_EQ(c_bbto->block_restart_interval, 11);
// Now set an option that is not mutable - options should not change
// FIXME: find a way to make this fail again
// ASSERT_NOK(
// dbfull()->SetOptions(cfh, {{"table_factory.no_block_cache", "false"}}));
c_opts = dbfull()->GetOptions(cfh);
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
ASSERT_NOK(
dbfull()->SetOptions(cfh, {{"table_factory.no_block_cache", "false"}}));
ASSERT_EQ(c_bbto->no_block_cache, true);
ASSERT_EQ(c_bbto->block_size, 16384);
ASSERT_EQ(c_bbto->block_restart_interval, 11);
// Set some that are mutable and some that are not - options should not change
// FIXME: find a way to make this fail again
// ASSERT_NOK(dbfull()->SetOptions(
// cfh, {{"table_factory.no_block_cache", "false"},
// {"table_factory.block_size", "8192"},
// {"table_factory.block_restart_interval", "7"}}));
c_opts = dbfull()->GetOptions(cfh);
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
ASSERT_NOK(dbfull()->SetOptions(
cfh, {{"table_factory.no_block_cache", "false"},
{"table_factory.block_size", "8192"},
{"table_factory.block_restart_interval", "7"}}));
ASSERT_EQ(c_bbto->no_block_cache, true);
ASSERT_EQ(c_bbto->block_size, 16384);
ASSERT_EQ(c_bbto->block_restart_interval, 11);
@@ -268,8 +256,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
cfh, {{"table_factory.block_size", "8192"},
{"table_factory.does_not_exist", "true"},
{"table_factory.block_restart_interval", "7"}}));
c_opts = dbfull()->GetOptions(cfh);
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
ASSERT_EQ(c_bbto->no_block_cache, true);
ASSERT_EQ(c_bbto->block_size, 16384);
ASSERT_EQ(c_bbto->block_restart_interval, 11);
@@ -285,7 +271,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
{"table_factory.block_restart_interval", "13"}}));
c_opts = dbfull()->GetOptions(cfh);
ASSERT_EQ(c_opts.blob_file_size, 32768);
c_bbto = c_opts.table_factory->GetOptions<BlockBasedTableOptions>();
ASSERT_EQ(c_bbto->block_size, 16384);
ASSERT_EQ(c_bbto->block_restart_interval, 13);
// Set some on the table and a bad one on the ColumnFamily - options should
@@ -294,7 +279,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
cfh, {{"table_factory.block_size", "1024"},
{"no_such_option", "32768"},
{"table_factory.block_restart_interval", "7"}}));
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
ASSERT_EQ(c_bbto->block_size, 16384);
ASSERT_EQ(c_bbto->block_restart_interval, 13);
}
+1 -1
View File
@@ -244,7 +244,7 @@ TEST_F(DBSecondaryTest, SimpleInternalCompaction) {
ASSERT_EQ(largest.user_key().ToString(), "foo");
ASSERT_EQ(result.output_level, 1);
ASSERT_EQ(result.output_path, this->secondary_path_);
ASSERT_EQ(result.stats.num_output_records, 2);
ASSERT_EQ(result.num_output_records, 2);
ASSERT_GT(result.bytes_written, 0);
ASSERT_OK(result.status);
}
+16 -48
View File
@@ -1826,30 +1826,21 @@ TEST_F(DBTest, GetApproximateMemTableStats) {
uint64_t count;
uint64_t size;
// Because Random::GetTLSInstance() seed is reset in DBTestBase,
// this test is deterministic.
std::string start = Key(50);
std::string end = Key(60);
Range r(start, end);
db_->GetApproximateMemTableStats(r, &count, &size);
// When actual count is <= 10, it returns that as the minimum
EXPECT_EQ(count, 10);
EXPECT_EQ(size, 10440);
start = Key(20);
end = Key(100);
r = Range(start, end);
db_->GetApproximateMemTableStats(r, &count, &size);
EXPECT_EQ(count, 72);
EXPECT_EQ(size, 75168);
ASSERT_GT(count, 0);
ASSERT_LE(count, N);
ASSERT_GT(size, 6000);
ASSERT_LT(size, 204800);
start = Key(500);
end = Key(600);
r = Range(start, end);
db_->GetApproximateMemTableStats(r, &count, &size);
EXPECT_EQ(count, 0);
EXPECT_EQ(size, 0);
ASSERT_EQ(count, 0);
ASSERT_EQ(size, 0);
ASSERT_OK(Flush());
@@ -1857,8 +1848,8 @@ TEST_F(DBTest, GetApproximateMemTableStats) {
end = Key(60);
r = Range(start, end);
db_->GetApproximateMemTableStats(r, &count, &size);
EXPECT_EQ(count, 0);
EXPECT_EQ(size, 0);
ASSERT_EQ(count, 0);
ASSERT_EQ(size, 0);
for (int i = 0; i < N; i++) {
ASSERT_OK(Put(Key(1000 + i), rnd.RandomString(1024)));
@@ -1866,11 +1857,10 @@ TEST_F(DBTest, GetApproximateMemTableStats) {
start = Key(100);
end = Key(1020);
// Actually 20 keys in the range ^^
r = Range(start, end);
db_->GetApproximateMemTableStats(r, &count, &size);
EXPECT_EQ(count, 20);
EXPECT_EQ(size, 20880);
ASSERT_GT(count, 20);
ASSERT_GT(size, 6000);
}
TEST_F(DBTest, ApproximateSizes) {
@@ -5179,14 +5169,10 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
options.max_bytes_for_level_multiplier = 4;
options.max_background_compactions = 1;
options.num_levels = 5;
options.statistics = CreateDBStatistics();
options.compression_per_level.resize(3);
// No compression for L0
options.compression_per_level[0] = kNoCompression;
// No compression for the Ln whre L0 is compacted to
options.compression_per_level[1] = kNoCompression;
// Snpapy compression for Ln+1
options.compression_per_level[2] = kSnappyCompression;
OnFileDeletionListener* listener = new OnFileDeletionListener();
@@ -5195,7 +5181,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
DestroyAndReopen(options);
// Insert more than 80K. L4 should be base level. Neither L0 nor L4 should
// be compressed, so there shouldn't be any compression.
// be compressed, so total data size should be more than 80K.
for (int i = 0; i < 20; i++) {
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
}
@@ -5205,17 +5191,10 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_EQ(NumTableFilesAtLevel(3), 0);
ASSERT_TRUE(NumTableFilesAtLevel(0) > 0 || NumTableFilesAtLevel(4) > 0);
// Assuming each files' metadata is at least 50 bytes/
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(4), 20U * 4000U + 50U * 4);
// Verify there was no compression
auto num_block_compressed =
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
ASSERT_EQ(num_block_compressed, 0);
// Insert 400KB and there will be some files end up in L3. According to the
// above compression settings for each level, there will be some compression.
ASSERT_OK(options.statistics->Reset());
ASSERT_EQ(num_block_compressed, 0);
// Insert 400KB. Some data will be compressed
for (int i = 21; i < 120; i++) {
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
}
@@ -5223,14 +5202,9 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_GE(NumTableFilesAtLevel(3), 1);
ASSERT_GE(NumTableFilesAtLevel(4), 1);
// Verify there was compression
num_block_compressed =
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
ASSERT_GT(num_block_compressed, 0);
ASSERT_LT(SizeAtLevel(0) + SizeAtLevel(3) + SizeAtLevel(4),
120U * 4000U + 50U * 24);
// Make sure data in files in L3 is not compacted by removing all files
// in L4 and calculate number of rows
ASSERT_OK(dbfull()->SetOptions({
@@ -5250,12 +5224,6 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
num_keys++;
}
ASSERT_OK(iter->status());
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
ASSERT_GE(NumTableFilesAtLevel(3), 1);
ASSERT_EQ(NumTableFilesAtLevel(4), 0);
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(3), num_keys * 4000U + num_keys * 10U);
}
+1 -13
View File
@@ -272,23 +272,11 @@ LookupKey::LookupKey(const Slice& _user_key, SequenceNumber s,
void IterKey::EnlargeBuffer(size_t key_size) {
// If size is smaller than buffer size, continue using current buffer,
// or the inline one, as default
// or the static allocated one, as default
assert(key_size > buf_size_);
// Need to enlarge the buffer.
ResetBuffer();
buf_ = new char[key_size];
buf_size_ = key_size;
}
void IterKey::EnlargeSecondaryBufferIfNeeded(size_t key_size) {
// If size is smaller than buffer size, continue using current buffer,
// or the inline one, as default
if (key_size <= secondary_buf_size_) {
return;
}
// Need to enlarge the secondary buffer.
ResetSecondaryBuffer();
secondary_buf_ = new char[key_size];
secondary_buf_size_ = key_size;
}
} // namespace ROCKSDB_NAMESPACE
+55 -137
View File
@@ -10,7 +10,6 @@
#pragma once
#include <stdio.h>
#include <array>
#include <memory>
#include <optional>
#include <string>
@@ -563,28 +562,18 @@ inline uint64_t GetInternalKeySeqno(const Slice& internal_key) {
// allocation for smaller keys.
// 3. It tracks user key or internal key, and allow conversion between them.
class IterKey {
static constexpr size_t kInlineBufferSize = 39;
// This is only used by user-defined timestamps in MemTable only feature,
// which only supports uint64_t timestamps.
static constexpr char kTsMin[] = "\x00\x00\x00\x00\x00\x00\x00\x00";
public:
IterKey()
: buf_(space_),
key_(buf_),
key_size_(0),
buf_size_(kInlineBufferSize),
is_user_key_(true),
secondary_buf_(space_for_secondary_buf_),
secondary_buf_size_(kInlineBufferSize) {}
buf_size_(sizeof(space_)),
is_user_key_(true) {}
// No copying allowed
IterKey(const IterKey&) = delete;
void operator=(const IterKey&) = delete;
~IterKey() {
ResetBuffer();
ResetSecondaryBuffer();
}
~IterKey() { ResetBuffer(); }
// The bool will be picked up by the next calls to SetKey
void SetIsUserKey(bool is_user_key) { is_user_key_ = is_user_key; }
@@ -652,15 +641,13 @@ class IterKey {
const char* non_shared_data,
const size_t non_shared_len,
const size_t ts_sz) {
// This function is only used by the UDT in memtable feature, which only
// support built in comparators with uint64 timestamps.
assert(ts_sz == sizeof(uint64_t));
size_t next_key_slice_index = 0;
std::string kTsMin(ts_sz, static_cast<unsigned char>(0));
std::string key_with_ts;
std::vector<Slice> key_parts_with_ts;
if (IsUserKey()) {
key_slices_[next_key_slice_index++] = Slice(key_, shared_len);
key_slices_[next_key_slice_index++] =
Slice(non_shared_data, non_shared_len);
key_slices_[next_key_slice_index++] = Slice(kTsMin, ts_sz);
key_parts_with_ts = {Slice(key_, shared_len),
Slice(non_shared_data, non_shared_len),
Slice(kTsMin)};
} else {
assert(shared_len + non_shared_len >= kNumInternalBytes);
// Invaraint: shared_user_key_len + shared_internal_bytes_len = shared_len
@@ -677,46 +664,30 @@ class IterKey {
// One Slice among the three Slices will get split into two Slices, plus
// a timestamp slice.
key_parts_with_ts.reserve(5);
bool ts_added = false;
// Add slice parts and find the right location to add the min timestamp.
MaybeAddKeyPartsWithTimestamp(
key_, shared_user_key_len,
shared_internal_bytes_len + non_shared_len < kNumInternalBytes,
shared_len + non_shared_len - kNumInternalBytes, ts_sz,
&next_key_slice_index, &ts_added);
shared_len + non_shared_len - kNumInternalBytes, kTsMin,
key_parts_with_ts, &ts_added);
MaybeAddKeyPartsWithTimestamp(
key_ + user_key_len, shared_internal_bytes_len,
non_shared_len < kNumInternalBytes,
shared_internal_bytes_len + non_shared_len - kNumInternalBytes, ts_sz,
&next_key_slice_index, &ts_added);
shared_internal_bytes_len + non_shared_len - kNumInternalBytes,
kTsMin, key_parts_with_ts, &ts_added);
MaybeAddKeyPartsWithTimestamp(non_shared_data, non_shared_len,
non_shared_len >= kNumInternalBytes,
non_shared_len - kNumInternalBytes, ts_sz,
&next_key_slice_index, &ts_added);
non_shared_len - kNumInternalBytes, kTsMin,
key_parts_with_ts, &ts_added);
assert(ts_added);
}
SetKeyImpl(next_key_slice_index,
/* total_bytes= */ shared_len + non_shared_len + ts_sz);
}
Slice SetKeyWithPaddedMinTimestamp(const Slice& key, size_t ts_sz) {
// This function is only used by the UDT in memtable feature, which only
// support built in comparators with uint64 timestamps.
assert(ts_sz == sizeof(uint64_t));
size_t num_key_slices = 0;
if (is_user_key_) {
key_slices_[0] = key;
key_slices_[1] = Slice(kTsMin, ts_sz);
num_key_slices = 2;
} else {
assert(key.size() >= kNumInternalBytes);
size_t user_key_size = key.size() - kNumInternalBytes;
key_slices_[0] = Slice(key.data(), user_key_size);
key_slices_[1] = Slice(kTsMin, ts_sz);
key_slices_[2] = Slice(key.data() + user_key_size, kNumInternalBytes);
num_key_slices = 3;
}
return SetKeyImpl(num_key_slices, key.size() + ts_sz);
Slice new_key(SliceParts(&key_parts_with_ts.front(),
static_cast<int>(key_parts_with_ts.size())),
&key_with_ts);
SetKey(new_key);
}
Slice SetKey(const Slice& key, bool copy = true) {
@@ -747,6 +718,15 @@ class IterKey {
return Slice(key_, key_n);
}
// Copy the key into IterKey own buf_
void OwnKey() {
assert(IsKeyPinned() == true);
Reserve(key_size_);
memcpy(buf_, key_, key_size_);
key_ = buf_;
}
// Update the sequence number in the internal key. Guarantees not to
// invalidate slices to the key (and the user key).
void UpdateInternalKey(uint64_t seq, ValueType t, const Slice* ts = nullptr) {
@@ -758,15 +738,10 @@ class IterKey {
ts->size());
}
uint64_t newval = (seq << 8) | t;
if (key_ == buf_) {
EncodeFixed64(&buf_[key_size_ - kNumInternalBytes], newval);
} else {
assert(key_ == secondary_buf_);
EncodeFixed64(&secondary_buf_[key_size_ - kNumInternalBytes], newval);
}
EncodeFixed64(&buf_[key_size_ - kNumInternalBytes], newval);
}
bool IsKeyPinned() const { return key_ != buf_ && key_ != secondary_buf_; }
bool IsKeyPinned() const { return (key_ != buf_); }
// If `ts` is provided, user_key should not contain timestamp,
// and `ts` is appended after user_key.
@@ -831,24 +806,8 @@ class IterKey {
const char* key_;
size_t key_size_;
size_t buf_size_;
char space_[kInlineBufferSize]; // Avoid allocation for short keys
char space_[39]; // Avoid allocation for short keys
bool is_user_key_;
// Below variables are only used by user-defined timestamps in MemTable only
// feature for iterating keys in an index block or a data block.
//
// We will alternate between buf_ and secondary_buf_ to hold the key. key_
// will be modified in accordance to point to the right one. This is to avoid
// an extra copy when we need to copy some shared bytes from previous key
// (delta encoding), and we need to pad a min timestamp at the right location.
char space_for_secondary_buf_[kInlineBufferSize]; // Avoid allocation for
// short keys
char* secondary_buf_;
size_t secondary_buf_size_;
// Use to track the pieces that together make the whole key. We then copy
// these pieces in order either into buf_ or secondary_buf_ depending on where
// the previous key is held.
std::array<Slice, 5> key_slices_;
// End of variables used by user-defined timestamps in MemTable only feature.
Slice SetKeyImpl(const Slice& key, bool copy) {
size_t size = key.size();
@@ -865,64 +824,18 @@ class IterKey {
return Slice(key_, key_size_);
}
Slice SetKeyImpl(size_t num_key_slices, size_t total_bytes) {
assert(num_key_slices <= 5);
char* buf_start = nullptr;
if (key_ == buf_) {
// If the previous key is in buf_, we copy key_slices_ in order into
// secondary_buf_.
EnlargeSecondaryBufferIfNeeded(total_bytes);
buf_start = secondary_buf_;
key_ = secondary_buf_;
} else {
// Copy key_slices_ in order into buf_.
EnlargeBufferIfNeeded(total_bytes);
buf_start = buf_;
key_ = buf_;
}
#ifndef NDEBUG
size_t actual_total_bytes = 0;
#endif // NDEBUG
for (size_t i = 0; i < num_key_slices; i++) {
size_t key_slice_size = key_slices_[i].size();
memcpy(buf_start, key_slices_[i].data(), key_slice_size);
buf_start += key_slice_size;
#ifndef NDEBUG
actual_total_bytes += key_slice_size;
#endif // NDEBUG
}
#ifndef NDEBUG
assert(actual_total_bytes == total_bytes);
#endif // NDEBUG
key_size_ = total_bytes;
return Slice(key_, key_size_);
}
void ResetBuffer() {
if (key_ == buf_) {
key_size_ = 0;
}
if (buf_ != space_) {
delete[] buf_;
buf_ = space_;
}
buf_size_ = kInlineBufferSize;
}
void ResetSecondaryBuffer() {
if (key_ == secondary_buf_) {
key_size_ = 0;
}
if (secondary_buf_ != space_for_secondary_buf_) {
delete[] secondary_buf_;
secondary_buf_ = space_for_secondary_buf_;
}
secondary_buf_size_ = kInlineBufferSize;
buf_size_ = sizeof(space_);
key_size_ = 0;
}
// Enlarge the buffer size if needed based on key_size.
// By default, inline buffer is used. Once there is a key
// larger than the inline buffer, another buffer is dynamically
// By default, static allocated buffer is used. Once there is a key
// larger than the static allocated buffer, another buffer is dynamically
// allocated, until a larger key buffer is requested. In that case, we
// reallocate buffer and delete the old one.
void EnlargeBufferIfNeeded(size_t key_size) {
@@ -933,27 +846,23 @@ class IterKey {
}
}
void EnlargeSecondaryBufferIfNeeded(size_t key_size);
void EnlargeBuffer(size_t key_size);
void MaybeAddKeyPartsWithTimestamp(const char* slice_data,
const size_t slice_sz, bool add_timestamp,
const size_t left_sz, const size_t ts_sz,
size_t* next_key_slice_idx,
const size_t left_sz,
const std::string& min_timestamp,
std::vector<Slice>& key_parts,
bool* ts_added) {
assert(next_key_slice_idx);
if (add_timestamp && !*ts_added) {
assert(slice_sz >= left_sz);
key_slices_[(*next_key_slice_idx)++] = Slice(slice_data, left_sz);
key_slices_[(*next_key_slice_idx)++] = Slice(kTsMin, ts_sz);
key_slices_[(*next_key_slice_idx)++] =
Slice(slice_data + left_sz, slice_sz - left_sz);
key_parts.emplace_back(slice_data, left_sz);
key_parts.emplace_back(min_timestamp);
key_parts.emplace_back(slice_data + left_sz, slice_sz - left_sz);
*ts_added = true;
} else {
key_slices_[(*next_key_slice_idx)++] = Slice(slice_data, slice_sz);
key_parts.emplace_back(slice_data, slice_sz);
}
assert(*next_key_slice_idx <= 5);
}
};
@@ -1027,13 +936,22 @@ struct RangeTombstone {
// User-defined timestamp is enabled, `sk` and `ek` should be user key
// with timestamp, `ts` will replace the timestamps in `sk` and
// `ek`.
RangeTombstone(Slice sk, Slice ek, SequenceNumber sn, Slice ts) : seq_(sn) {
// When `logical_strip_timestamp` is true, the timestamps in `sk` and `ek`
// will be replaced with min timestamp.
RangeTombstone(Slice sk, Slice ek, SequenceNumber sn, Slice ts,
bool logical_strip_timestamp)
: seq_(sn) {
const size_t ts_sz = ts.size();
assert(ts_sz > 0);
pinned_start_key_.reserve(sk.size());
pinned_end_key_.reserve(ek.size());
AppendUserKeyWithDifferentTimestamp(&pinned_start_key_, sk, ts);
AppendUserKeyWithDifferentTimestamp(&pinned_end_key_, ek, ts);
if (logical_strip_timestamp) {
AppendUserKeyWithMinTimestamp(&pinned_start_key_, sk, ts_sz);
AppendUserKeyWithMinTimestamp(&pinned_end_key_, ek, ts_sz);
} else {
AppendUserKeyWithDifferentTimestamp(&pinned_start_key_, sk, ts);
AppendUserKeyWithDifferentTimestamp(&pinned_end_key_, ek, ts);
}
start_key_ = pinned_start_key_;
end_key_ = pinned_end_key_;
ts_ = Slice(pinned_start_key_.data() + sk.size() - ts_sz, ts_sz);
+8 -366
View File
@@ -1790,8 +1790,8 @@ TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = sst_files_dir_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a1"));
ASSERT_OK(sst_file_writer.Put("i", "i1"));
ASSERT_OK(sst_file_writer.Put("a", "z"));
ASSERT_OK(sst_file_writer.Put("i", "m"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
@@ -1800,29 +1800,16 @@ TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("i", "i2"));
ASSERT_OK(sst_file_writer.Put("i", "k"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
}
{
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file3 = sst_files_dir_ + "file3.sst";
ASSERT_OK(sst_file_writer.Open(file3));
ASSERT_OK(sst_file_writer.Put("j", "j1"));
ASSERT_OK(sst_file_writer.Put("m", "m1"));
ExternalSstFileInfo file3_info;
ASSERT_OK(sst_file_writer.Finish(&file3_info));
files.push_back(std::move(file3));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a1");
ASSERT_EQ(Get("i"), "i2");
ASSERT_EQ(Get("j"), "j1");
ASSERT_EQ(Get("m"), "m1");
ASSERT_EQ(Get("a"), "z");
ASSERT_EQ(Get("i"), "k");
int total_keys = 0;
Iterator* iter = db_->NewIterator(ReadOptions());
@@ -1830,355 +1817,10 @@ TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
ASSERT_OK(iter->status());
total_keys++;
}
ASSERT_OK(iter->status());
delete iter;
ASSERT_EQ(total_keys, 4);
ASSERT_EQ(total_keys, 2);
ASSERT_EQ(1, NumTableFilesAtLevel(6));
ASSERT_EQ(2, NumTableFilesAtLevel(5));
}
TEST_F(ExternalSSTFileBasicTest, AtomicReplaceDataWithStandaloneRangeDeletion) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
DestroyAndReopen(options);
std::vector<std::string> files;
{
// Writes first version of data in range partitioned files.
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = sst_files_dir_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a1"));
ASSERT_OK(sst_file_writer.Put("b", "b1"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("x", "x1"));
ASSERT_OK(sst_file_writer.Put("y", "y1"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a1");
ASSERT_EQ(Get("b"), "b1");
ASSERT_EQ(Get("x"), "x1");
ASSERT_EQ(Get("y"), "y1");
ASSERT_EQ(2, NumTableFilesAtLevel(6));
{
// Atomically delete old version of data with one range delete file.
// And a new batch of range partitioned files with new version of data.
files.clear();
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.DeleteRange("a", "z"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
std::string file3 = sst_files_dir_ + "file3.sst";
ASSERT_OK(sst_file_writer.Open(file3));
ASSERT_OK(sst_file_writer.Put("a", "a2"));
ASSERT_OK(sst_file_writer.Put("b", "b2"));
ExternalSstFileInfo file3_info;
ASSERT_OK(sst_file_writer.Finish(&file3_info));
files.push_back(std::move(file3));
std::string file4 = sst_files_dir_ + "file4.sst";
ASSERT_OK(sst_file_writer.Open(file4));
ASSERT_OK(sst_file_writer.Put("x", "x2"));
ASSERT_OK(sst_file_writer.Put("y", "y2"));
ExternalSstFileInfo file4_info;
ASSERT_OK(sst_file_writer.Finish(&file4_info));
files.push_back(std::move(file4));
}
const Snapshot* snapshot = db_->GetSnapshot();
auto seqno_before_ingestion = db_->GetLatestSequenceNumber();
ASSERT_OK(db_->IngestExternalFile(files, ifo));
// Overlapping files each occupy one new sequence number.
ASSERT_EQ(db_->GetLatestSequenceNumber(), seqno_before_ingestion + 3);
// Check old version of data, big range deletion, new version of data are
// on separate levels.
ASSERT_EQ(2, NumTableFilesAtLevel(4));
ASSERT_EQ(1, NumTableFilesAtLevel(5));
ASSERT_EQ(2, NumTableFilesAtLevel(6));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(2, NumTableFilesAtLevel(4));
ASSERT_EQ(1, NumTableFilesAtLevel(5));
ASSERT_EQ(2, NumTableFilesAtLevel(6));
bool compaction_iter_input_checked = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionSet::MakeInputIterator:NewCompactionMergingIterator",
[&](void* arg) {
size_t* num_input_files = static_cast<size_t*>(arg);
EXPECT_EQ(1, *num_input_files);
compaction_iter_input_checked = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
db_->ReleaseSnapshot(snapshot);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(2, NumTableFilesAtLevel(4));
ASSERT_EQ(0, NumTableFilesAtLevel(5));
ASSERT_EQ(0, NumTableFilesAtLevel(6));
ASSERT_TRUE(compaction_iter_input_checked);
ASSERT_EQ(Get("a"), "a2");
ASSERT_EQ(Get("b"), "b2");
ASSERT_EQ(Get("x"), "x2");
ASSERT_EQ(Get("y"), "y2");
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(ExternalSSTFileBasicTest,
PartiallyReplaceDataWithOneStandaloneRangeDeletion) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
DestroyAndReopen(options);
std::vector<std::string> files;
{
// Writes first version of data in range partitioned files.
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = sst_files_dir_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a1"));
ASSERT_OK(sst_file_writer.Put("b", "b1"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("x", "x1"));
ASSERT_OK(sst_file_writer.Put("y", "y"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a1");
ASSERT_EQ(Get("b"), "b1");
ASSERT_EQ(Get("x"), "x1");
ASSERT_EQ(Get("y"), "y");
ASSERT_EQ(2, NumTableFilesAtLevel(6));
{
// Partially delete old version of data with one range delete file. And
// add new version of data for deleted range.
files.clear();
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.DeleteRange("a", "y"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
std::string file3 = sst_files_dir_ + "file3.sst";
ASSERT_OK(sst_file_writer.Open(file3));
ASSERT_OK(sst_file_writer.Put("a", "a2"));
ASSERT_OK(sst_file_writer.Put("b", "b2"));
ExternalSstFileInfo file3_info;
ASSERT_OK(sst_file_writer.Finish(&file3_info));
files.push_back(std::move(file3));
std::string file4 = sst_files_dir_ + "file4.sst";
ASSERT_OK(sst_file_writer.Open(file4));
ASSERT_OK(sst_file_writer.Put("h", "h1"));
ASSERT_OK(sst_file_writer.Put("x", "x2"));
ExternalSstFileInfo file4_info;
ASSERT_OK(sst_file_writer.Finish(&file4_info));
files.push_back(std::move(file4));
}
bool compaction_iter_input_checked = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionSet::MakeInputIterator:NewCompactionMergingIterator",
[&](void* arg) {
size_t* num_input_files = static_cast<size_t*>(arg);
EXPECT_EQ(2, *num_input_files);
compaction_iter_input_checked = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(2, NumTableFilesAtLevel(4));
ASSERT_EQ(0, NumTableFilesAtLevel(5));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
ASSERT_TRUE(compaction_iter_input_checked);
ASSERT_EQ(Get("a"), "a2");
ASSERT_EQ(Get("b"), "b2");
ASSERT_EQ(Get("h"), "h1");
ASSERT_EQ(Get("x"), "x2");
ASSERT_EQ(Get("y"), "y");
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(ExternalSSTFileBasicTest,
PartiallyReplaceDataWithMultipleStandaloneRangeDeletions) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
DestroyAndReopen(options);
std::vector<std::string> files;
{
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = sst_files_dir_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a1"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.Put("h", "h"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
std::string file3 = sst_files_dir_ + "file3.sst";
ASSERT_OK(sst_file_writer.Open(file3));
ASSERT_OK(sst_file_writer.Put("x", "x1"));
ExternalSstFileInfo file3_info;
ASSERT_OK(sst_file_writer.Finish(&file3_info));
files.push_back(std::move(file3));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a1");
ASSERT_EQ(Get("h"), "h");
ASSERT_EQ(Get("x"), "x1");
ASSERT_EQ(3, NumTableFilesAtLevel(6));
{
files.clear();
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file4 = sst_files_dir_ + "file4.sst";
ASSERT_OK(sst_file_writer.Open(file4));
ASSERT_OK(sst_file_writer.DeleteRange("a", "b"));
ExternalSstFileInfo file4_info;
ASSERT_OK(sst_file_writer.Finish(&file4_info));
files.push_back(std::move(file4));
std::string file5 = sst_files_dir_ + "file5.sst";
ASSERT_OK(sst_file_writer.Open(file5));
ASSERT_OK(sst_file_writer.DeleteRange("x", "y"));
ExternalSstFileInfo file5_info;
ASSERT_OK(sst_file_writer.Finish(&file5_info));
files.push_back(std::move(file5));
std::string file6 = sst_files_dir_ + "file6.sst";
ASSERT_OK(sst_file_writer.Open(file6));
ASSERT_OK(sst_file_writer.Put("a", "a2"));
ExternalSstFileInfo file6_info;
ASSERT_OK(sst_file_writer.Finish(&file6_info));
files.push_back(std::move(file6));
std::string file7 = sst_files_dir_ + "file7.sst";
ASSERT_OK(sst_file_writer.Open(file7));
ASSERT_OK(sst_file_writer.Put("x", "x2"));
ExternalSstFileInfo file7_info;
ASSERT_OK(sst_file_writer.Finish(&file7_info));
files.push_back(std::move(file7));
}
int num_compactions = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionSet::MakeInputIterator:NewCompactionMergingIterator",
[&](void* arg) {
size_t* num_input_files = static_cast<size_t*>(arg);
EXPECT_EQ(1, *num_input_files);
num_compactions += 1;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(2, NumTableFilesAtLevel(4));
ASSERT_EQ(0, NumTableFilesAtLevel(5));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
ASSERT_EQ(2, num_compactions);
ASSERT_EQ(Get("a"), "a2");
ASSERT_EQ(Get("h"), "h");
ASSERT_EQ(Get("x"), "x2");
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(ExternalSSTFileBasicTest, StandaloneRangeDeletionEndKeyIsExclusive) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
DestroyAndReopen(options);
std::vector<std::string> files;
{
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file1 = sst_files_dir_ + "file1.sst";
ASSERT_OK(sst_file_writer.Open(file1));
ASSERT_OK(sst_file_writer.Put("a", "a"));
ASSERT_OK(sst_file_writer.Put("b", "b"));
ExternalSstFileInfo file1_info;
ASSERT_OK(sst_file_writer.Finish(&file1_info));
files.push_back(std::move(file1));
}
IngestExternalFileOptions ifo;
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_EQ(Get("a"), "a");
ASSERT_EQ(Get("b"), "b");
ASSERT_EQ(1, NumTableFilesAtLevel(6));
{
// A standalone range deletion with its exclusive end matching the range end
// of file doesn't fully delete it.
files.clear();
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string file2 = sst_files_dir_ + "file2.sst";
ASSERT_OK(sst_file_writer.Open(file2));
ASSERT_OK(sst_file_writer.DeleteRange("a", "b"));
ExternalSstFileInfo file2_info;
ASSERT_OK(sst_file_writer.Finish(&file2_info));
files.push_back(std::move(file2));
}
bool compaction_iter_input_checked = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionSet::MakeInputIterator:NewCompactionMergingIterator",
[&](void* arg) {
size_t* num_input_files = static_cast<size_t*>(arg);
// Standalone range deletion file for ["a", "b") + file with ["a", "b"].
EXPECT_EQ(2, *num_input_files);
compaction_iter_input_checked = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(db_->IngestExternalFile(files, ifo));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(0, NumTableFilesAtLevel(4));
ASSERT_EQ(0, NumTableFilesAtLevel(5));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
ASSERT_TRUE(compaction_iter_input_checked);
ASSERT_EQ(Get("a"), "NOT_FOUND");
ASSERT_EQ(Get("b"), "b");
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ASSERT_EQ(2, NumTableFilesAtLevel(0));
}
TEST_F(ExternalSSTFileBasicTest, IngestFileAfterDBPut) {
@@ -2403,7 +2045,7 @@ TEST_F(ExternalSSTFileBasicTest, FailIfNotBottommostLevel) {
ifo.fail_if_not_bottommost_level = true;
ifo.snapshot_consistency = true;
const Status s = db_->IngestExternalFile({file_path}, ifo);
ASSERT_TRUE(s.ok());
ASSERT_TRUE(s.IsTryAgain());
}
// Test level compaction
+90 -135
View File
@@ -67,6 +67,7 @@ Status ExternalSstFileIngestionJob::Prepare(
files_to_ingest_.emplace_back(std::move(file_to_ingest));
}
const Comparator* ucmp = cfd_->internal_comparator().user_comparator();
auto num_files = files_to_ingest_.size();
if (num_files == 0) {
return Status::InvalidArgument("The list of files is empty");
@@ -77,12 +78,16 @@ Status ExternalSstFileIngestionJob::Prepare(
sorted_files.push_back(&files_to_ingest_[i]);
}
std::sort(sorted_files.begin(), sorted_files.end(), file_range_checker_);
std::sort(
sorted_files.begin(), sorted_files.end(),
[&ucmp](const IngestedFileInfo* info1, const IngestedFileInfo* info2) {
return sstableKeyCompare(ucmp, info1->smallest_internal_key,
info2->smallest_internal_key) < 0;
});
for (size_t i = 0; i + 1 < num_files; i++) {
if (file_range_checker_.OverlapsWithPrev(sorted_files[i],
sorted_files[i + 1],
/* ranges_sorted= */ true)) {
if (sstableKeyCompare(ucmp, sorted_files[i]->largest_internal_key,
sorted_files[i + 1]->smallest_internal_key) >= 0) {
files_overlap_ = true;
break;
}
@@ -95,7 +100,7 @@ Status ExternalSstFileIngestionJob::Prepare(
"behind mode.");
}
if (ucmp_->timestamp_size() > 0 && files_overlap_) {
if (ucmp->timestamp_size() > 0 && files_overlap_) {
return Status::NotSupported(
"Files with overlapping ranges cannot be ingested to column "
"family with user-defined timestamp enabled.");
@@ -331,35 +336,9 @@ Status ExternalSstFileIngestionJob::Prepare(
}
}
if (status.ok()) {
DivideInputFilesIntoBatches();
}
return status;
}
void ExternalSstFileIngestionJob::DivideInputFilesIntoBatches() {
if (!files_overlap_) {
// No overlap, treat as one batch without the need of tracking overall batch
// range.
file_batches_to_ingest_.emplace_back(/* _track_batch_range= */ false);
for (auto& file : files_to_ingest_) {
file_batches_to_ingest_.back().AddFile(&file, file_range_checker_);
}
return;
}
file_batches_to_ingest_.emplace_back(/* _track_batch_range= */ true);
for (auto& file : files_to_ingest_) {
if (file_range_checker_.OverlapsWithPrev(&file_batches_to_ingest_.back(),
&file,
/* ranges_sorted= */ false)) {
file_batches_to_ingest_.emplace_back(/* _track_batch_range= */ true);
}
file_batches_to_ingest_.back().AddFile(&file, file_range_checker_);
}
}
Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
SuperVersion* super_version) {
size_t n = files_to_ingest_.size();
@@ -374,7 +353,9 @@ Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
if (!ingestion_options_.allow_blocking_flush) {
status = Status::InvalidArgument("External file requires flush");
}
if (ucmp_->timestamp_size() > 0) {
auto ucmp = cfd_->user_comparator();
assert(ucmp);
if (ucmp->timestamp_size() > 0) {
status = Status::InvalidArgument(
"Column family enables user-defined timestamps, please make "
"sure the key range (without timestamp) of external file does not "
@@ -416,39 +397,14 @@ Status ExternalSstFileIngestionJob::Run() {
edit_.SetColumnFamily(cfd_->GetID());
// The levels that the files will be ingested into
std::optional<int> prev_batch_uppermost_level;
for (auto& batch : file_batches_to_ingest_) {
int batch_uppermost_level = 0;
status = AssignLevelsForOneBatch(batch, super_version, force_global_seqno,
&last_seqno, &batch_uppermost_level,
prev_batch_uppermost_level);
if (!status.ok()) {
return status;
}
prev_batch_uppermost_level = batch_uppermost_level;
}
CreateEquivalentFileIngestingCompactions();
return status;
}
Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
FileBatchInfo& batch, SuperVersion* super_version, bool force_global_seqno,
SequenceNumber* last_seqno, int* batch_uppermost_level,
std::optional<int> prev_batch_uppermost_level) {
Status status;
assert(batch_uppermost_level);
*batch_uppermost_level = std::numeric_limits<int>::max();
for (IngestedFileInfo* file : batch.files) {
assert(file);
for (IngestedFileInfo& f : files_to_ingest_) {
SequenceNumber assigned_seqno = 0;
if (ingestion_options_.ingest_behind) {
status = CheckLevelForIngestedBehindFile(file);
status = CheckLevelForIngestedBehindFile(&f);
} else {
status = AssignLevelAndSeqnoForIngestedFile(
super_version, force_global_seqno, cfd_->ioptions()->compaction_style,
*last_seqno, file, &assigned_seqno, prev_batch_uppermost_level);
last_seqno, &f, &assigned_seqno);
}
// Modify the smallest/largest internal key to include the sequence number
@@ -457,38 +413,38 @@ Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
// exclusive endpoint.
ParsedInternalKey smallest_parsed, largest_parsed;
if (status.ok()) {
status = ParseInternalKey(*(file->smallest_internal_key.rep()),
status = ParseInternalKey(*f.smallest_internal_key.rep(),
&smallest_parsed, false /* log_err_key */);
}
if (status.ok()) {
status = ParseInternalKey(*(file->largest_internal_key.rep()),
&largest_parsed, false /* log_err_key */);
status = ParseInternalKey(*f.largest_internal_key.rep(), &largest_parsed,
false /* log_err_key */);
}
if (!status.ok()) {
return status;
}
if (smallest_parsed.sequence == 0 && assigned_seqno != 0) {
UpdateInternalKey(file->smallest_internal_key.rep(), assigned_seqno,
UpdateInternalKey(f.smallest_internal_key.rep(), assigned_seqno,
smallest_parsed.type);
}
if (largest_parsed.sequence == 0 && assigned_seqno != 0) {
UpdateInternalKey(file->largest_internal_key.rep(), assigned_seqno,
UpdateInternalKey(f.largest_internal_key.rep(), assigned_seqno,
largest_parsed.type);
}
status = AssignGlobalSeqnoForIngestedFile(file, assigned_seqno);
status = AssignGlobalSeqnoForIngestedFile(&f, assigned_seqno);
if (!status.ok()) {
return status;
}
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
&assigned_seqno);
assert(assigned_seqno == 0 || assigned_seqno == *last_seqno + 1);
if (assigned_seqno > *last_seqno) {
*last_seqno = assigned_seqno;
assert(assigned_seqno == 0 || assigned_seqno == last_seqno + 1);
if (assigned_seqno > last_seqno) {
last_seqno = assigned_seqno;
++consumed_seqno_count_;
}
status = GenerateChecksumForIngestedFile(file);
status = GenerateChecksumForIngestedFile(&f);
if (!status.ok()) {
return status;
}
@@ -503,40 +459,31 @@ Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
static_cast<uint64_t>(temp_current_time);
}
uint64_t tail_size = 0;
bool contain_no_data_blocks = file->table_properties.num_entries > 0 &&
(file->table_properties.num_entries ==
file->table_properties.num_range_deletions);
if (file->table_properties.tail_start_offset > 0 ||
contain_no_data_blocks) {
uint64_t file_size = file->fd.GetFileSize();
assert(file->table_properties.tail_start_offset <= file_size);
tail_size = file_size - file->table_properties.tail_start_offset;
bool contain_no_data_blocks = f.table_properties.num_entries > 0 &&
(f.table_properties.num_entries ==
f.table_properties.num_range_deletions);
if (f.table_properties.tail_start_offset > 0 || contain_no_data_blocks) {
uint64_t file_size = f.fd.GetFileSize();
assert(f.table_properties.tail_start_offset <= file_size);
tail_size = file_size - f.table_properties.tail_start_offset;
}
bool marked_for_compaction =
file->table_properties.num_range_deletions == 1 &&
(file->table_properties.num_entries ==
file->table_properties.num_range_deletions);
FileMetaData f_metadata(
file->fd.GetNumber(), file->fd.GetPathId(), file->fd.GetFileSize(),
file->smallest_internal_key, file->largest_internal_key,
file->assigned_seqno, file->assigned_seqno, false,
file->file_temperature, kInvalidBlobFileNumber, oldest_ancester_time,
current_time,
f.fd.GetNumber(), f.fd.GetPathId(), f.fd.GetFileSize(),
f.smallest_internal_key, f.largest_internal_key, f.assigned_seqno,
f.assigned_seqno, false, f.file_temperature, kInvalidBlobFileNumber,
oldest_ancester_time, current_time,
ingestion_options_.ingest_behind
? kReservedEpochNumberForFileIngestedBehind
: cfd_->NewEpochNumber(),
file->file_checksum, file->file_checksum_func_name, file->unique_id, 0,
tail_size, file->user_defined_timestamps_persisted);
f_metadata.temperature = file->file_temperature;
f_metadata.marked_for_compaction = marked_for_compaction;
edit_.AddFile(file->picked_level, f_metadata);
*batch_uppermost_level =
std::min(*batch_uppermost_level, file->picked_level);
f.file_checksum, f.file_checksum_func_name, f.unique_id, 0, tail_size,
f.user_defined_timestamps_persisted);
f_metadata.temperature = f.file_temperature;
edit_.AddFile(f.picked_level, f_metadata);
}
return Status::OK();
CreateEquivalentFileIngestingCompactions();
return status;
}
void ExternalSstFileIngestionJob::CreateEquivalentFileIngestingCompactions() {
@@ -572,17 +519,20 @@ void ExternalSstFileIngestionJob::CreateEquivalentFileIngestingCompactions() {
file_ingesting_compactions_.push_back(new Compaction(
cfd_->current()->storage_info(), *cfd_->ioptions(), mutable_cf_options,
mutable_db_options_, {input}, output_level,
/* output file size limit not applicable */
MaxFileSizeForLevel(mutable_cf_options, output_level,
cfd_->ioptions()->compaction_style),
MaxFileSizeForLevel(
mutable_cf_options, output_level,
cfd_->ioptions()->compaction_style) /* output file size
limit,
* not applicable
*/
,
LLONG_MAX /* max compaction bytes, not applicable */,
0 /* output path ID, not applicable */, mutable_cf_options.compression,
mutable_cf_options.compression_opts,
mutable_cf_options.default_write_temperature,
0 /* max_subcompaction, not applicable */,
{} /* grandparents, not applicable */,
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
{} /* grandparents, not applicable */, false /* is manual */,
"" /* trim_ts */, -1 /* score, not applicable */,
false /* is deletion compaction, not applicable */,
files_overlap_ /* l0_files_might_overlap, not applicable */,
CompactionReason::kExternalSstIngestion));
@@ -729,10 +679,7 @@ Status ExternalSstFileIngestionJob::ResetTableReader(
new RandomAccessFileReader(std::move(sst_file), external_file,
nullptr /*Env*/, io_tracer_));
table_reader->reset();
ReadOptions ro;
ro.fill_cache = ingestion_options_.fill_cache;
status = sv->mutable_cf_options.table_factory->NewTableReader(
ro,
status = cfd_->ioptions()->table_factory->NewTableReader(
TableReaderOptions(
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
env_options_, cfd_->internal_comparator(),
@@ -744,9 +691,7 @@ Status ExternalSstFileIngestionJob::ResetTableReader(
/*cur_file_num*/ new_file_number,
/* unique_id */ {}, /* largest_seqno */ 0,
/* tail_size */ 0, user_defined_timestamps_persisted),
std::move(sst_file_reader), file_to_ingest->file_size, table_reader,
// No need to prefetch index/filter if caching is not needed.
/*prefetch_index_and_filter_in_cache=*/ingestion_options_.fill_cache);
std::move(sst_file_reader), file_to_ingest->file_size, table_reader);
return status;
}
@@ -762,7 +707,6 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
// Get table version
auto version_iter = uprops.find(ExternalSstFilePropertyNames::kVersion);
if (version_iter == uprops.end()) {
assert(!SstFileWriter::CreatedBySstFileWriter(*props));
if (!ingestion_options_.allow_db_generated_files) {
return Status::Corruption("External file version not found");
} else {
@@ -771,7 +715,6 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
file_to_ingest->version = 0;
}
} else {
assert(SstFileWriter::CreatedBySstFileWriter(*props));
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
}
@@ -844,7 +787,9 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
// `TableReader` is initialized with `user_defined_timestamps_persisted` flag
// to be true. If its value changed to false after this sanity check, we
// need to reset the `TableReader`.
if (ucmp_->timestamp_size() > 0 &&
auto ucmp = cfd_->user_comparator();
assert(ucmp);
if (ucmp->timestamp_size() > 0 &&
!file_to_ingest->user_defined_timestamps_persisted) {
s = ResetTableReader(external_file, new_file_number,
file_to_ingest->user_defined_timestamps_persisted, sv,
@@ -894,7 +839,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
// TODO: plumb Env::IOActivity, Env::IOPriority
ReadOptions ro;
ro.readahead_size = ingestion_options_.verify_checksums_readahead_size;
ro.fill_cache = ingestion_options_.fill_cache;
status = table_reader->VerifyChecksum(
ro, TableReaderCaller::kExternalSSTIngestion);
if (!status.ok()) {
@@ -905,12 +849,16 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
ParsedInternalKey key;
// TODO: plumb Env::IOActivity, Env::IOPriority
ReadOptions ro;
ro.fill_cache = ingestion_options_.fill_cache;
std::unique_ptr<InternalIterator> iter(table_reader->NewIterator(
ro, sv->mutable_cf_options.prefix_extractor.get(), /*arena=*/nullptr,
/*skip_filters=*/false, TableReaderCaller::kExternalSSTIngestion));
// Get first (smallest) and last (largest) key from file.
file_to_ingest->smallest_internal_key =
InternalKey("", 0, ValueType::kTypeValue);
file_to_ingest->largest_internal_key =
InternalKey("", 0, ValueType::kTypeValue);
bool bounds_set = false;
bool allow_data_in_errors = db_options_.allow_data_in_errors;
iter->SeekToFirst();
if (iter->Valid()) {
@@ -926,8 +874,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
file_to_ingest->smallest_internal_key.SetFrom(key);
Slice largest;
if (strcmp(sv->mutable_cf_options.table_factory->Name(), "PlainTable") ==
0) {
if (strcmp(cfd_->ioptions()->table_factory->Name(), "PlainTable") == 0) {
// PlainTable iterator does not support SeekToLast().
largest = iter->key();
for (; iter->Valid(); iter->Next()) {
@@ -961,6 +908,8 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
return Status::Corruption("External file has non zero sequence number");
}
file_to_ingest->largest_internal_key.SetFrom(key);
bounds_set = true;
} else if (!iter->status().ok()) {
return iter->status();
}
@@ -997,6 +946,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
table_reader->NewRangeTombstoneIterator(ro));
// We may need to adjust these key bounds, depending on whether any range
// deletion tombstones extend past them.
const Comparator* ucmp = cfd_->user_comparator();
if (range_del_iter != nullptr) {
for (range_del_iter->SeekToFirst(); range_del_iter->Valid();
range_del_iter->Next()) {
@@ -1012,13 +962,24 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
"number.");
}
RangeTombstone tombstone(key, range_del_iter->value());
file_range_checker_.MaybeUpdateRange(tombstone.SerializeKey(),
tombstone.SerializeEndKey(),
file_to_ingest);
InternalKey start_key = tombstone.SerializeKey();
if (!bounds_set ||
sstableKeyCompare(ucmp, start_key,
file_to_ingest->smallest_internal_key) < 0) {
file_to_ingest->smallest_internal_key = start_key;
}
InternalKey end_key = tombstone.SerializeEndKey();
if (!bounds_set ||
sstableKeyCompare(ucmp, end_key,
file_to_ingest->largest_internal_key) > 0) {
file_to_ingest->largest_internal_key = end_key;
}
bounds_set = true;
}
}
const size_t ts_sz = ucmp_->timestamp_size();
const size_t ts_sz = ucmp->timestamp_size();
Slice smallest = file_to_ingest->smallest_internal_key.user_key();
Slice largest = file_to_ingest->largest_internal_key.user_key();
if (ts_sz > 0) {
@@ -1047,19 +1008,16 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
SuperVersion* sv, bool force_global_seqno, CompactionStyle compaction_style,
SequenceNumber last_seqno, IngestedFileInfo* file_to_ingest,
SequenceNumber* assigned_seqno,
std::optional<int> prev_batch_uppermost_level) {
SequenceNumber* assigned_seqno) {
Status status;
*assigned_seqno = 0;
const size_t ts_sz = ucmp_->timestamp_size();
assert(!prev_batch_uppermost_level.has_value() ||
prev_batch_uppermost_level.value() < cfd_->NumberLevels());
bool must_assign_to_l0 = prev_batch_uppermost_level.has_value() &&
prev_batch_uppermost_level.value() == 0;
auto ucmp = cfd_->user_comparator();
const size_t ts_sz = ucmp->timestamp_size();
if (force_global_seqno || files_overlap_ ||
compaction_style == kCompactionStyleFIFO || must_assign_to_l0) {
compaction_style == kCompactionStyleFIFO) {
*assigned_seqno = last_seqno + 1;
if (compaction_style == kCompactionStyleFIFO || must_assign_to_l0) {
// If files overlap, we have to ingest them at level 0.
if (files_overlap_ || compaction_style == kCompactionStyleFIFO) {
assert(ts_sz == 0);
file_to_ingest->picked_level = 0;
if (ingestion_options_.fail_if_not_bottommost_level &&
@@ -1076,16 +1034,11 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
Arena arena;
// TODO: plumb Env::IOActivity, Env::IOPriority
ReadOptions ro;
ro.fill_cache = ingestion_options_.fill_cache;
ro.total_order_seek = true;
int target_level = 0;
auto* vstorage = cfd_->current()->storage_info();
assert(!must_assign_to_l0);
int exclusive_end_level = prev_batch_uppermost_level.has_value()
? prev_batch_uppermost_level.value()
: cfd_->NumberLevels();
for (int lvl = 0; lvl < exclusive_end_level; lvl++) {
for (int lvl = 0; lvl < cfd_->NumberLevels(); lvl++) {
if (lvl > 0 && lvl < vstorage->base_level()) {
continue;
}
@@ -1112,6 +1065,8 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
overlap_with_db = true;
break;
}
} else if (compaction_style == kCompactionStyleUniversal) {
continue;
}
// We don't overlap with any keys in this level, but we still need to check
+11 -126
View File
@@ -25,74 +25,13 @@ namespace ROCKSDB_NAMESPACE {
class Directories;
class SystemClock;
struct KeyRangeInfo {
// Smallest internal key in an external file or for a batch of external files.
InternalKey smallest_internal_key;
// Largest internal key in an external file or for a batch of external files.
InternalKey largest_internal_key;
bool empty() const {
return smallest_internal_key.size() == 0 &&
largest_internal_key.size() == 0;
}
};
// Helper class to apply SST file key range checks to the external files.
class ExternalFileRangeChecker {
public:
explicit ExternalFileRangeChecker(const Comparator* ucmp) : ucmp_(ucmp) {}
// Operator used for sorting ranges.
bool operator()(const KeyRangeInfo* prev_range,
const KeyRangeInfo* range) const {
assert(prev_range);
assert(range);
return sstableKeyCompare(ucmp_, prev_range->smallest_internal_key,
range->smallest_internal_key) < 0;
}
// Check whether `range` overlaps with `prev_range`. `ranges_sorted` can be
// set to true when the inputs are already sorted based on the sorting logic
// provided by this checker's operator(), which can help simplify the check.
bool OverlapsWithPrev(const KeyRangeInfo* prev_range,
const KeyRangeInfo* range,
bool ranges_sorted = false) const {
assert(prev_range);
assert(range);
if (prev_range->empty() || range->empty()) {
return false;
}
if (ranges_sorted) {
return sstableKeyCompare(ucmp_, prev_range->largest_internal_key,
range->smallest_internal_key) >= 0;
}
return sstableKeyCompare(ucmp_, prev_range->largest_internal_key,
range->smallest_internal_key) >= 0 &&
sstableKeyCompare(ucmp_, prev_range->smallest_internal_key,
range->largest_internal_key) <= 0;
}
void MaybeUpdateRange(const InternalKey& start_key,
const InternalKey& end_key, KeyRangeInfo* range) const {
assert(range);
if (range->smallest_internal_key.size() == 0 ||
sstableKeyCompare(ucmp_, start_key, range->smallest_internal_key) < 0) {
range->smallest_internal_key = start_key;
}
if (range->largest_internal_key.size() == 0 ||
sstableKeyCompare(ucmp_, end_key, range->largest_internal_key) > 0) {
range->largest_internal_key = end_key;
}
}
private:
const Comparator* ucmp_;
};
struct IngestedFileInfo : public KeyRangeInfo {
struct IngestedFileInfo {
// External file path
std::string external_file_path;
// Smallest internal key in external file
InternalKey smallest_internal_key;
// Largest internal key in external file
InternalKey largest_internal_key;
// NOTE: use below two fields for all `*Overlap*` types of checks instead of
// smallest_internal_key.user_key() and largest_internal_key.user_key().
// The smallest / largest user key contained in the file for key range checks.
@@ -155,30 +94,6 @@ struct IngestedFileInfo : public KeyRangeInfo {
bool user_defined_timestamps_persisted = true;
};
// A batch of files.
struct FileBatchInfo : public KeyRangeInfo {
autovector<IngestedFileInfo*> files;
// When true, `smallest_internal_key` and `largest_internal_key` will be
// tracked and updated as new file get added via `AddFile`. When false, we
// bypass this tracking. This is used when the all input external files
// are already checked and not overlapping, and they just need to be added
// into one default batch.
bool track_batch_range;
void AddFile(IngestedFileInfo* file,
const ExternalFileRangeChecker& key_range_checker) {
assert(file);
files.push_back(file);
if (track_batch_range) {
key_range_checker.MaybeUpdateRange(file->smallest_internal_key,
file->largest_internal_key, this);
}
}
explicit FileBatchInfo(bool _track_batch_range)
: track_batch_range(_track_batch_range) {}
};
class ExternalSstFileIngestionJob {
public:
ExternalSstFileIngestionJob(
@@ -193,8 +108,6 @@ class ExternalSstFileIngestionJob {
fs_(db_options.fs, io_tracer),
versions_(versions),
cfd_(cfd),
ucmp_(cfd ? cfd->user_comparator() : nullptr),
file_range_checker_(ucmp_),
db_options_(db_options),
mutable_db_options_(mutable_db_options),
env_options_(env_options),
@@ -206,8 +119,6 @@ class ExternalSstFileIngestionJob {
consumed_seqno_count_(0),
io_tracer_(io_tracer) {
assert(directories != nullptr);
assert(cfd_);
assert(ucmp_);
}
~ExternalSstFileIngestionJob() { UnregisterRange(); }
@@ -283,38 +194,15 @@ class ExternalSstFileIngestionJob {
IngestedFileInfo* file_to_ingest,
SuperVersion* sv);
// If the input files' key range overlaps themselves, this function divides
// them in the user specified order into multiple batches. Where the files
// within a batch do not overlap with each other, but key range could overlap
// between batches.
// If the input files' key range don't overlap themselves, they always just
// make one batch.
void DivideInputFilesIntoBatches();
// Assign level for the files in one batch. The files within one batch are not
// overlapping, and we assign level to each file one after another.
// If `prev_batch_uppermost_level` is specified, all files in this batch will
// be assigned to levels that are higher than `prev_batch_uppermost_level`.
// The uppermost level used by this batch of files is tracked too, so that it
// can be used by the next batch.
// REQUIRES: Mutex held
Status AssignLevelsForOneBatch(FileBatchInfo& batch,
SuperVersion* super_version,
bool force_global_seqno,
SequenceNumber* last_seqno,
int* batch_uppermost_level,
std::optional<int> prev_batch_uppermost_level);
// Assign `file_to_ingest` the appropriate sequence number and the lowest
// possible level that it can be ingested to according to compaction_style.
// If `prev_batch_uppermost_level` is specified, the file will only be
// assigned to levels tha are higher than `prev_batch_uppermost_level`.
// REQUIRES: Mutex held
Status AssignLevelAndSeqnoForIngestedFile(
SuperVersion* sv, bool force_global_seqno,
CompactionStyle compaction_style, SequenceNumber last_seqno,
IngestedFileInfo* file_to_ingest, SequenceNumber* assigned_seqno,
std::optional<int> prev_batch_uppermost_level);
Status AssignLevelAndSeqnoForIngestedFile(SuperVersion* sv,
bool force_global_seqno,
CompactionStyle compaction_style,
SequenceNumber last_seqno,
IngestedFileInfo* file_to_ingest,
SequenceNumber* assigned_seqno);
// File that we want to ingest behind always goes to the lowest level;
// we just check that it fits in the level, that DB allows ingest_behind,
@@ -349,14 +237,11 @@ class ExternalSstFileIngestionJob {
FileSystemPtr fs_;
VersionSet* versions_;
ColumnFamilyData* cfd_;
const Comparator* ucmp_;
ExternalFileRangeChecker file_range_checker_;
const ImmutableDBOptions& db_options_;
const MutableDBOptions& mutable_db_options_;
const EnvOptions& env_options_;
SnapshotList* db_snapshots_;
autovector<IngestedFileInfo> files_to_ingest_;
std::vector<FileBatchInfo> file_batches_to_ingest_;
const IngestExternalFileOptions& ingestion_options_;
Directories* directories_;
EventLogger* event_logger_;
+9 -55
View File
@@ -3,8 +3,6 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include <table/block_based/block_based_table_factory.h>
#include <functional>
#include <memory>
@@ -152,7 +150,7 @@ class ExternalSSTFileTest
bool verify_checksums_before_ingest = true, bool ingest_behind = false,
bool sort_data = false,
std::map<std::string, std::string>* true_data = nullptr,
ColumnFamilyHandle* cfh = nullptr, bool fill_cache = false) {
ColumnFamilyHandle* cfh = nullptr) {
// Generate a file id if not provided
if (file_id == -1) {
file_id = last_file_id_ + 1;
@@ -196,7 +194,6 @@ class ExternalSSTFileTest
ifo.write_global_seqno = allow_global_seqno ? write_global_seqno : false;
ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
ifo.ingest_behind = ingest_behind;
ifo.fill_cache = fill_cache;
if (cfh) {
s = db_->IngestExternalFile(cfh, {file_path}, ifo);
} else {
@@ -270,15 +267,15 @@ class ExternalSSTFileTest
bool verify_checksums_before_ingest = true, bool ingest_behind = false,
bool sort_data = false,
std::map<std::string, std::string>* true_data = nullptr,
ColumnFamilyHandle* cfh = nullptr, bool fill_cache = false) {
ColumnFamilyHandle* cfh = nullptr) {
std::vector<std::pair<std::string, std::string>> file_data;
for (auto& k : keys) {
file_data.emplace_back(Key(k), Key(k) + std::to_string(file_id));
}
return GenerateAndAddExternalFile(
options, file_data, file_id, allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest, ingest_behind, sort_data, true_data,
cfh, fill_cache);
return GenerateAndAddExternalFile(options, file_data, file_id,
allow_global_seqno, write_global_seqno,
verify_checksums_before_ingest,
ingest_behind, sort_data, true_data, cfh);
}
Status DeprecatedAddFile(const std::vector<std::string>& files,
@@ -317,49 +314,6 @@ TEST_F(ExternalSSTFileTest, ComparatorMismatch) {
ASSERT_NOK(DeprecatedAddFile({file}));
}
TEST_F(ExternalSSTFileTest, NoBlockCache) {
LRUCacheOptions co;
co.capacity = 32 << 20;
std::shared_ptr<Cache> cache = NewLRUCache(co);
BlockBasedTableOptions table_options;
table_options.block_cache = cache;
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
table_options.cache_index_and_filter_blocks = true;
Options options = CurrentOptions();
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
Reopen(options);
size_t usage_before_ingestion = cache->GetUsage();
std::map<std::string, std::string> true_data;
// Ingest with fill_cache = true
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, false, false, true,
false, false, &true_data, nullptr,
/*fill_cache=*/true));
ASSERT_EQ(FilesPerLevel(), "0,0,0,0,0,0,1");
EXPECT_GT(cache->GetUsage(), usage_before_ingestion);
TablePropertiesCollection tp;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tp));
for (const auto& entry : tp) {
EXPECT_GT(entry.second->index_size, 0);
EXPECT_GT(entry.second->filter_size, 0);
}
usage_before_ingestion = cache->GetUsage();
// Ingest with fill_cache = false
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 4}, -1, false, false, true,
false, false, &true_data, nullptr,
/*fill_cache=*/false));
EXPECT_EQ(usage_before_ingestion, cache->GetUsage());
tp.clear();
ASSERT_OK(db_->GetPropertiesOfAllTables(&tp));
for (const auto& entry : tp) {
EXPECT_GT(entry.second->index_size, 0);
EXPECT_GT(entry.second->filter_size, 0);
}
}
TEST_F(ExternalSSTFileTest, Basic) {
do {
Options options = CurrentOptions();
@@ -1987,9 +1941,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
options, file_data, -1, true, write_global_seqno,
verify_checksums_before_ingest, false, false, &true_data));
// This file overlap with files in L4, we will ingest it into the closest
// non-overlapping level, in this case, it's L3.
ASSERT_EQ("2,0,0,1,3", FilesPerLevel());
// This file overlap with files in L4, we will ingest it into the last
// non-overlapping and non-empty level, in this case, it's L0.
ASSERT_EQ("3,0,0,0,3", FilesPerLevel());
size_t kcnt = 0;
VerifyDBFromMap(true_data, &kcnt, false);
+4 -21
View File
@@ -858,12 +858,6 @@ Status FlushJob::WriteLevel0Table() {
meta_.temperature = mutable_cf_options_.default_write_temperature;
file_options_.temperature = meta_.temperature;
const auto* ucmp = cfd_->internal_comparator().user_comparator();
assert(ucmp);
const size_t ts_sz = ucmp->timestamp_size();
const bool logical_strip_timestamp =
ts_sz > 0 && !cfd_->ioptions()->persist_user_defined_timestamps;
std::vector<BlobFileAddition> blob_file_additions;
{
@@ -899,21 +893,10 @@ Status FlushJob::WriteLevel0Table() {
db_options_.info_log,
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
if (logical_strip_timestamp) {
memtables.push_back(m->NewTimestampStrippingIterator(
ro, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr, ts_sz));
} else {
memtables.push_back(
m->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr));
}
auto* range_del_iter =
logical_strip_timestamp
? m->NewTimestampStrippingRangeTombstoneIterator(
ro, kMaxSequenceNumber, ts_sz)
: m->NewRangeTombstoneIterator(ro, kMaxSequenceNumber,
true /* immutable_memtable */);
memtables.push_back(m->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr,
&arena, /*prefix_extractor=*/nullptr));
auto* range_del_iter = m->NewRangeTombstoneIterator(
ro, kMaxSequenceNumber, true /* immutable_memtable */);
if (range_del_iter != nullptr) {
range_del_iters.emplace_back(range_del_iter);
}
+3 -9
View File
@@ -874,15 +874,9 @@ TEST_P(FlushJobTimestampTest, NoKeyExpired) {
expected_full_history_ts_low = full_history_ts_low;
}
InternalKey smallest(smallest_key, curr_seq_ - 1, ValueType::kTypeValue);
if (!persist_udt_) {
InternalKey largest(largest_key, curr_seq_ - 1, ValueType::kTypeValue);
CheckFileMetaData(cfd, smallest, largest, &fmeta);
CheckFullHistoryTsLow(cfd, expected_full_history_ts_low);
} else {
InternalKey largest(largest_key, kStartSeq, ValueType::kTypeValue);
CheckFileMetaData(cfd, smallest, largest, &fmeta);
CheckFullHistoryTsLow(cfd, expected_full_history_ts_low);
}
InternalKey largest(largest_key, kStartSeq, ValueType::kTypeValue);
CheckFileMetaData(cfd, smallest, largest, &fmeta);
CheckFullHistoryTsLow(cfd, expected_full_history_ts_low);
}
job_context.Clean();
ASSERT_TRUE(to_delete.empty());
+27 -19
View File
@@ -32,11 +32,11 @@ namespace ROCKSDB_NAMESPACE {
// iter.Next()
class ForwardLevelIterator : public InternalIterator {
public:
ForwardLevelIterator(const ColumnFamilyData* const cfd,
const ReadOptions& read_options,
const std::vector<FileMetaData*>& files,
const MutableCFOptions& mutable_cf_options,
bool allow_unprepared_value)
ForwardLevelIterator(
const ColumnFamilyData* const cfd, const ReadOptions& read_options,
const std::vector<FileMetaData*>& files,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
bool allow_unprepared_value, uint8_t block_protection_bytes_per_key)
: cfd_(cfd),
read_options_(read_options),
files_(files),
@@ -44,8 +44,9 @@ class ForwardLevelIterator : public InternalIterator {
file_index_(std::numeric_limits<uint32_t>::max()),
file_iter_(nullptr),
pinned_iters_mgr_(nullptr),
mutable_cf_options_(mutable_cf_options),
allow_unprepared_value_(allow_unprepared_value) {
prefix_extractor_(prefix_extractor),
allow_unprepared_value_(allow_unprepared_value),
block_protection_bytes_per_key_(block_protection_bytes_per_key) {
status_.PermitUncheckedError(); // Allow uninitialized status through
}
@@ -82,12 +83,13 @@ class ForwardLevelIterator : public InternalIterator {
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
*files_[file_index_],
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
mutable_cf_options_, /*table_reader_ptr=*/nullptr,
prefix_extractor_, /*table_reader_ptr=*/nullptr,
/*file_read_hist=*/nullptr, TableReaderCaller::kUserIterator,
/*arena=*/nullptr, /*skip_filters=*/false, /*level=*/-1,
/*max_file_size_for_l0_meta_pin=*/0,
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr, allow_unprepared_value_);
/*largest_compaction_key=*/nullptr, allow_unprepared_value_,
block_protection_bytes_per_key_);
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
valid_ = false;
if (!range_del_agg.IsEmpty()) {
@@ -212,9 +214,10 @@ class ForwardLevelIterator : public InternalIterator {
Status status_;
InternalIterator* file_iter_;
PinnedIteratorsManager* pinned_iters_mgr_;
const MutableCFOptions& mutable_cf_options_;
// Kept alive by ForwardIterator::sv_->mutable_cf_options
const std::shared_ptr<const SliceTransform>& prefix_extractor_;
const bool allow_unprepared_value_;
const uint8_t block_protection_bytes_per_key_;
};
ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
@@ -747,13 +750,14 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
l0_iters_.push_back(cfd_->table_cache()->NewIterator(
read_options_, *cfd_->soptions(), cfd_->internal_comparator(), *l0,
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
sv_->mutable_cf_options,
sv_->mutable_cf_options.prefix_extractor,
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
/*skip_filters=*/false, /*level=*/-1,
MaxFileSizeForL0MetaPin(sv_->mutable_cf_options),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr, allow_unprepared_value_));
/*largest_compaction_key=*/nullptr, allow_unprepared_value_,
sv_->mutable_cf_options.block_protection_bytes_per_key));
}
BuildLevelIterators(vstorage, sv_);
current_ = nullptr;
@@ -834,13 +838,14 @@ void ForwardIterator::RenewIterators() {
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
*l0_files_new[inew],
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
svnew->mutable_cf_options,
svnew->mutable_cf_options.prefix_extractor,
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
/*skip_filters=*/false, /*level=*/-1,
MaxFileSizeForL0MetaPin(svnew->mutable_cf_options),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr, allow_unprepared_value_));
/*largest_compaction_key=*/nullptr, allow_unprepared_value_,
svnew->mutable_cf_options.block_protection_bytes_per_key));
}
for (auto* f : l0_iters_) {
@@ -883,8 +888,9 @@ void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage,
}
} else {
level_iters_.push_back(new ForwardLevelIterator(
cfd_, read_options_, level_files, sv->mutable_cf_options,
allow_unprepared_value_));
cfd_, read_options_, level_files,
sv->mutable_cf_options.prefix_extractor, allow_unprepared_value_,
sv->mutable_cf_options.block_protection_bytes_per_key));
}
}
}
@@ -899,13 +905,15 @@ void ForwardIterator::ResetIncompleteIterators() {
DeleteIterator(l0_iters_[i]);
l0_iters_[i] = cfd_->table_cache()->NewIterator(
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
*l0_files[i], /*range_del_agg=*/nullptr, sv_->mutable_cf_options,
*l0_files[i], /*range_del_agg=*/nullptr,
sv_->mutable_cf_options.prefix_extractor,
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
/*skip_filters=*/false, /*level=*/-1,
MaxFileSizeForL0MetaPin(sv_->mutable_cf_options),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr, allow_unprepared_value_);
/*largest_compaction_key=*/nullptr, allow_unprepared_value_,
sv_->mutable_cf_options.block_protection_bytes_per_key);
l0_iters_[i]->SetPinnedItersMgr(pinned_iters_mgr_);
}
+2 -3
View File
@@ -329,7 +329,7 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
// TODO(yuzhangyu): User-defined timestamps doesn't support importing column
// family. Pass in the correct `user_defined_timestamps_persisted` flag for
// creating `TableReaderOptions` when the support is there.
status = sv->mutable_cf_options.table_factory->NewTableReader(
status = cfd_->ioptions()->table_factory->NewTableReader(
TableReaderOptions(
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
env_options_, cfd_->internal_comparator(),
@@ -371,8 +371,7 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
if (iter->Valid()) {
file_to_import->smallest_internal_key.DecodeFrom(iter->key());
Slice largest;
if (strcmp(sv->mutable_cf_options.table_factory->Name(), "PlainTable") ==
0) {
if (strcmp(cfd_->ioptions()->table_factory->Name(), "PlainTable") == 0) {
// PlainTable iterator does not support SeekToLast().
largest = iter->key();
for (; iter->Valid(); iter->Next()) {
-2
View File
@@ -951,8 +951,6 @@ TEST_F(ImportColumnFamilyTest, AssignEpochNumberToMultipleCF) {
Options options = CurrentOptions();
options.level_compaction_dynamic_level_bytes = true;
options.max_background_jobs = 8;
// Always allow parallel compaction
options.soft_pending_compaction_bytes_limit = 10;
env_->SetBackgroundThreads(2, Env::LOW);
env_->SetBackgroundThreads(0, Env::BOTTOM);
CreateAndReopenWithCF({"CF1", "CF2"}, options);
+2 -5
View File
@@ -1495,10 +1495,8 @@ bool InternalStats::HandleEstimateOldestKeyTime(uint64_t* value, DBImpl* /*db*/,
}
Cache* InternalStats::GetBlockCacheForStats() {
// NOTE: called in startup before GetCurrentMutableCFOptions() is ready
auto* table_factory = cfd_->GetLatestMutableCFOptions()->table_factory.get();
auto* table_factory = cfd_->ioptions()->table_factory.get();
assert(table_factory != nullptr);
// FIXME: need to a shared_ptr if/when block_cache is going to be mutable
return table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
}
@@ -2163,8 +2161,7 @@ class BlockCachePropertyAggregator : public IntPropertyAggregator {
virtual ~BlockCachePropertyAggregator() override = default;
void Add(ColumnFamilyData* cfd, uint64_t value) override {
auto* table_factory =
cfd->GetCurrentMutableCFOptions()->table_factory.get();
auto* table_factory = cfd->ioptions()->table_factory.get();
assert(table_factory != nullptr);
Cache* cache =
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
-4
View File
@@ -202,10 +202,6 @@ struct JobContext {
// that corresponds to the set of files in 'live'.
uint64_t manifest_file_number;
uint64_t pending_manifest_file_number;
// Used for remote compaction. To prevent OPTIONS files from getting
// purged by PurgeObsoleteFiles() of the primary host
uint64_t min_options_file_number;
uint64_t log_number;
uint64_t prev_log_number;
-153
View File
@@ -613,135 +613,6 @@ InternalIterator* MemTable::NewIterator(
seqno_to_time_mapping, arena, prefix_extractor);
}
// An iterator wrapper that wraps a MemTableIterator and logically strips each
// key's user-defined timestamp.
class TimestampStrippingIterator : public InternalIterator {
public:
TimestampStrippingIterator(
MemTableIterator::Kind kind, const MemTable& memtable,
const ReadOptions& read_options,
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping, Arena* arena,
const SliceTransform* cf_prefix_extractor, size_t ts_sz)
: arena_mode_(arena != nullptr), kind_(kind), ts_sz_(ts_sz) {
assert(ts_sz_ != 0);
void* mem = arena ? arena->AllocateAligned(sizeof(MemTableIterator)) :
operator new(sizeof(MemTableIterator));
iter_ = new (mem)
MemTableIterator(kind, memtable, read_options, seqno_to_time_mapping,
arena, cf_prefix_extractor);
}
// No copying allowed
TimestampStrippingIterator(const TimestampStrippingIterator&) = delete;
void operator=(const TimestampStrippingIterator&) = delete;
~TimestampStrippingIterator() override {
if (arena_mode_) {
iter_->~MemTableIterator();
} else {
delete iter_;
}
}
void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override {
iter_->SetPinnedItersMgr(pinned_iters_mgr);
}
bool Valid() const override { return iter_->Valid(); }
void Seek(const Slice& k) override {
iter_->Seek(k);
UpdateKeyAndValueBuffer();
}
void SeekForPrev(const Slice& k) override {
iter_->SeekForPrev(k);
UpdateKeyAndValueBuffer();
}
void SeekToFirst() override {
iter_->SeekToFirst();
UpdateKeyAndValueBuffer();
}
void SeekToLast() override {
iter_->SeekToLast();
UpdateKeyAndValueBuffer();
}
void Next() override {
iter_->Next();
UpdateKeyAndValueBuffer();
}
bool NextAndGetResult(IterateResult* result) override {
iter_->Next();
UpdateKeyAndValueBuffer();
bool is_valid = Valid();
if (is_valid) {
result->key = key();
result->bound_check_result = IterBoundCheck::kUnknown;
result->value_prepared = true;
}
return is_valid;
}
void Prev() override {
iter_->Prev();
UpdateKeyAndValueBuffer();
}
Slice key() const override {
assert(Valid());
return key_buf_;
}
uint64_t write_unix_time() const override { return iter_->write_unix_time(); }
Slice value() const override {
if (kind_ == MemTableIterator::Kind::kRangeDelEntries) {
return value_buf_;
}
return iter_->value();
}
Status status() const override { return iter_->status(); }
bool IsKeyPinned() const override {
// Key is only in a buffer that is updated in each iteration.
return false;
}
bool IsValuePinned() const override {
if (kind_ == MemTableIterator::Kind::kRangeDelEntries) {
return false;
}
return iter_->IsValuePinned();
}
private:
void UpdateKeyAndValueBuffer() {
key_buf_.clear();
if (kind_ == MemTableIterator::Kind::kRangeDelEntries) {
value_buf_.clear();
}
if (!Valid()) {
return;
}
Slice original_key = iter_->key();
ReplaceInternalKeyWithMinTimestamp(&key_buf_, original_key, ts_sz_);
if (kind_ == MemTableIterator::Kind::kRangeDelEntries) {
Slice original_value = iter_->value();
AppendUserKeyWithMinTimestamp(&value_buf_, original_value, ts_sz_);
}
}
bool arena_mode_;
MemTableIterator::Kind kind_;
size_t ts_sz_;
MemTableIterator* iter_;
std::string key_buf_;
std::string value_buf_;
};
InternalIterator* MemTable::NewTimestampStrippingIterator(
const ReadOptions& read_options,
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping, Arena* arena,
const SliceTransform* prefix_extractor, size_t ts_sz) {
assert(arena != nullptr);
auto mem = arena->AllocateAligned(sizeof(TimestampStrippingIterator));
return new (mem) TimestampStrippingIterator(
MemTableIterator::kPointEntries, *this, read_options,
seqno_to_time_mapping, arena, prefix_extractor, ts_sz);
}
FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIterator(
const ReadOptions& read_options, SequenceNumber read_seq,
bool immutable_memtable) {
@@ -753,30 +624,6 @@ FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIterator(
immutable_memtable);
}
FragmentedRangeTombstoneIterator*
MemTable::NewTimestampStrippingRangeTombstoneIterator(
const ReadOptions& read_options, SequenceNumber read_seq, size_t ts_sz) {
if (read_options.ignore_range_deletions ||
is_range_del_table_empty_.load(std::memory_order_relaxed)) {
return nullptr;
}
if (!timestamp_stripping_fragmented_range_tombstone_list_) {
// TODO: plumb Env::IOActivity, Env::IOPriority
auto* unfragmented_iter = new TimestampStrippingIterator(
MemTableIterator::kRangeDelEntries, *this, ReadOptions(),
/*seqno_to_time_mapping*/ nullptr, /* arena */ nullptr,
/* prefix_extractor */ nullptr, ts_sz);
timestamp_stripping_fragmented_range_tombstone_list_ =
std::make_unique<FragmentedRangeTombstoneList>(
std::unique_ptr<InternalIterator>(unfragmented_iter),
comparator_.comparator);
}
return new FragmentedRangeTombstoneIterator(
timestamp_stripping_fragmented_range_tombstone_list_.get(),
comparator_.comparator, read_seq, read_options.timestamp);
}
FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIteratorInternal(
const ReadOptions& read_options, SequenceNumber read_seq,
bool immutable_memtable) {
-21
View File
@@ -213,14 +213,6 @@ class MemTable {
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping, Arena* arena,
const SliceTransform* prefix_extractor);
// Returns an iterator that wraps a MemTableIterator and logically strips the
// user-defined timestamp of each key. This API is only used by flush when
// user-defined timestamps in MemTable only feature is enabled.
InternalIterator* NewTimestampStrippingIterator(
const ReadOptions& read_options,
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping, Arena* arena,
const SliceTransform* prefix_extractor, size_t ts_sz);
// Returns an iterator that yields the range tombstones of the memtable.
// The caller must ensure that the underlying MemTable remains live
// while the returned iterator is live.
@@ -235,13 +227,6 @@ class MemTable {
const ReadOptions& read_options, SequenceNumber read_seq,
bool immutable_memtable);
// Returns an iterator that yields the range tombstones of the memtable and
// logically strips the user-defined timestamp of each key (including start
// key, and end key). This API is only used by flush when user-defined
// timestamps in MemTable only feature is enabled.
FragmentedRangeTombstoneIterator* NewTimestampStrippingRangeTombstoneIterator(
const ReadOptions& read_options, SequenceNumber read_seq, size_t ts_sz);
Status VerifyEncodedEntry(Slice encoded,
const ProtectionInfoKVOS64& kv_prot_info);
@@ -719,12 +704,6 @@ class MemTable {
std::unique_ptr<FragmentedRangeTombstoneList>
fragmented_range_tombstone_list_;
// The fragmented range tombstone of this memtable with all keys' user-defined
// timestamps logically stripped. This is constructed and used by flush when
// user-defined timestamps in memtable only feature is enabled.
std::unique_ptr<FragmentedRangeTombstoneList>
timestamp_stripping_fragmented_range_tombstone_list_;
// makes sure there is a single range tombstone writer to invalidate cache
std::mutex range_del_mutex_;
CoreLocalArray<std::shared_ptr<FragmentedRangeTombstoneListCache>>
+1 -14
View File
@@ -359,15 +359,11 @@ bool MemTableListVersion::MemtableLimitExceeded(size_t usage) {
}
}
bool MemTableListVersion::HistoryShouldBeTrimmed(size_t usage) {
return MemtableLimitExceeded(usage) && !memlist_history_.empty();
}
// Make sure we don't use up too much space in history
bool MemTableListVersion::TrimHistory(autovector<MemTable*>* to_delete,
size_t usage) {
bool ret = false;
while (HistoryShouldBeTrimmed(usage)) {
while (MemtableLimitExceeded(usage) && !memlist_history_.empty()) {
MemTable* x = memlist_history_.back();
memlist_history_.pop_back();
@@ -665,16 +661,8 @@ void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete) {
}
bool MemTableList::TrimHistory(autovector<MemTable*>* to_delete, size_t usage) {
// Check if history trim is needed first, so that we can avoid installing a
// new MemTableListVersion without installing a SuperVersion (installed based
// on return value of this function).
if (!current_->HistoryShouldBeTrimmed(usage)) {
ResetTrimHistoryNeeded();
return false;
}
InstallNewVersion();
bool ret = current_->TrimHistory(to_delete, usage);
assert(ret);
UpdateCachedValuesFromMemTableListVersion();
ResetTrimHistoryNeeded();
return ret;
@@ -726,7 +714,6 @@ void MemTableList::InstallNewVersion() {
// somebody else holds the current version, we need to create new one
MemTableListVersion* version = current_;
current_ = new MemTableListVersion(&current_memory_usage_, *version);
current_->SetID(++last_memtable_list_version_id_);
current_->Ref();
version->Unref();
}
+2 -19
View File
@@ -141,11 +141,6 @@ class MemTableListVersion {
// Return kMaxSequenceNumber if the list is empty.
SequenceNumber GetFirstSequenceNumber() const;
// REQUIRES: db_mutex held.
void SetID(uint64_t id) { id_ = id; }
uint64_t GetID() const { return id_; }
private:
friend class MemTableList;
@@ -166,11 +161,7 @@ class MemTableListVersion {
// REQUIRE: m is an immutable memtable
void Remove(MemTable* m, autovector<MemTable*>* to_delete);
// Return true if the memtable list should be trimmed to get memory usage
// under budget.
bool HistoryShouldBeTrimmed(size_t usage);
// Trim history, Return true if memtable is trimmed
// Return true if memtable is trimmed
bool TrimHistory(autovector<MemTable*>* to_delete, size_t usage);
bool GetFromList(std::list<MemTable*>* list, const LookupKey& key,
@@ -214,9 +205,6 @@ class MemTableListVersion {
int refs_ = 0;
size_t* parent_memtable_list_memory_usage_;
// MemtableListVersion id to track for flush results checking.
uint64_t id_ = 0;
};
// This class stores references to all the immutable memtables.
@@ -247,8 +235,7 @@ class MemTableList {
flush_requested_(false),
current_memory_usage_(0),
current_memory_allocted_bytes_excluding_last_(0),
current_has_history_(false),
last_memtable_list_version_id_(0) {
current_has_history_(false) {
current_->Ref();
}
@@ -513,10 +500,6 @@ class MemTableList {
// Cached value of current_->HasHistory().
std::atomic<bool> current_has_history_;
// Last memtabe list version id, increase by 1 each time a new
// MemtableListVersion is installed.
uint64_t last_memtable_list_version_id_;
};
// Installs memtable atomic flush results.
+61 -119
View File
@@ -21,21 +21,27 @@ struct MultiCfIteratorInfo {
int order;
};
template <typename ResetFunc, typename PopulateFunc>
class MultiCfIteratorImpl {
public:
MultiCfIteratorImpl(
const ReadOptions& read_options, const Comparator* comparator,
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
cfh_iter_pairs,
ResetFunc reset_func, PopulateFunc populate_func)
: allow_unprepared_value_(read_options.allow_unprepared_value),
comparator_(comparator),
cfh_iter_pairs_(std::move(cfh_iter_pairs)),
reset_func_(std::move(reset_func)),
populate_func_(std::move(populate_func)),
const Comparator* comparator,
const std::vector<ColumnFamilyHandle*>& column_families,
const std::vector<Iterator*>& child_iterators,
std::function<void()> reset_func,
std::function<void(const autovector<MultiCfIteratorInfo>&)> populate_func)
: comparator_(comparator),
heap_(MultiCfMinHeap(
MultiCfHeapItemComparator<std::greater<int>>(comparator_))) {}
MultiCfHeapItemComparator<std::greater<int>>(comparator_))),
reset_func_(std::move(reset_func)),
populate_func_(std::move(populate_func)) {
assert(column_families.size() > 0 &&
column_families.size() == child_iterators.size());
cfh_iter_pairs_.reserve(column_families.size());
for (size_t i = 0; i < column_families.size(); ++i) {
cfh_iter_pairs_.emplace_back(
column_families[i], std::unique_ptr<Iterator>(child_iterators[i]));
}
}
~MultiCfIteratorImpl() { status_.PermitUncheckedError(); }
// No copy allowed
@@ -95,28 +101,10 @@ class MultiCfIteratorImpl {
AdvanceIterator(max_heap, [](Iterator* iter) { iter->Prev(); });
}
bool PrepareValue() {
assert(Valid());
if (!allow_unprepared_value_) {
return true;
}
if (std::holds_alternative<MultiCfMaxHeap>(heap_)) {
return PopulateIterator(std::get<MultiCfMaxHeap>(heap_));
}
return PopulateIterator(std::get<MultiCfMinHeap>(heap_));
}
private:
Status status_;
bool allow_unprepared_value_;
const Comparator* comparator_;
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>
cfh_iter_pairs_;
ResetFunc reset_func_;
PopulateFunc populate_func_;
Status status_;
template <typename CompareOp>
class MultiCfHeapItemComparator {
@@ -137,7 +125,7 @@ class MultiCfIteratorImpl {
private:
const Comparator* comparator_;
};
const Comparator* comparator_;
using MultiCfMinHeap =
BinaryHeap<MultiCfIteratorInfo,
MultiCfHeapItemComparator<std::greater<int>>>;
@@ -148,6 +136,9 @@ class MultiCfIteratorImpl {
MultiCfIterHeap heap_;
std::function<void()> reset_func_;
std::function<void(autovector<MultiCfIteratorInfo>)> populate_func_;
Iterator* current() const {
if (std::holds_alternative<MultiCfMaxHeap>(heap_)) {
auto& max_heap = std::get<MultiCfMaxHeap>(heap_);
@@ -172,11 +163,11 @@ class MultiCfIteratorImpl {
}
void InitMinHeap() {
heap_.template emplace<MultiCfMinHeap>(
heap_.emplace<MultiCfMinHeap>(
MultiCfHeapItemComparator<std::greater<int>>(comparator_));
}
void InitMaxHeap() {
heap_.template emplace<MultiCfMaxHeap>(
heap_.emplace<MultiCfMaxHeap>(
MultiCfHeapItemComparator<std::less<int>>(comparator_));
}
@@ -195,14 +186,13 @@ class MultiCfIteratorImpl {
if (!status_.ok()) {
// Non-OK status from the iterator. Bail out early
heap.clear();
return;
break;
}
}
++i;
}
if (!allow_unprepared_value_ && !heap.empty()) {
[[maybe_unused]] const bool result = PopulateIterator(heap);
assert(result || (!Valid() && !status_.ok()));
if (!heap.empty()) {
PopulateIterator(heap);
}
}
@@ -222,41 +212,32 @@ class MultiCfIteratorImpl {
// 2. Make sure all others have iterated past the top iterator key slice
// 3. Advance the top iterator, and add it back to the heap if valid
auto top = heap.top();
assert(top.iterator);
assert(top.iterator->Valid());
assert(top.iterator->status().ok());
heap.pop();
while (!heap.empty()) {
if (!heap.empty()) {
auto current = heap.top();
assert(current.iterator);
assert(current.iterator->Valid());
assert(current.iterator->status().ok());
if (comparator_->Compare(current.iterator->key(), top.iterator->key()) !=
0) {
break;
}
advance_func(current.iterator);
if (current.iterator->Valid()) {
while (current.iterator->Valid() &&
comparator_->Compare(top.iterator->key(),
current.iterator->key()) == 0) {
assert(current.iterator->status().ok());
heap.replace_top(current);
} else {
considerStatus(current.iterator->status());
if (!status_.ok()) {
heap.clear();
return;
advance_func(current.iterator);
if (current.iterator->Valid()) {
heap.replace_top(heap.top());
} else {
heap.pop();
considerStatus(current.iterator->status());
if (!status_.ok()) {
heap.clear();
return;
} else {
heap.pop();
}
}
if (!heap.empty()) {
current = heap.top();
}
}
}
advance_func(top.iterator);
if (top.iterator->Valid()) {
assert(top.iterator->status().ok());
heap.push(top);
@@ -268,14 +249,13 @@ class MultiCfIteratorImpl {
}
}
if (!allow_unprepared_value_ && !heap.empty()) {
[[maybe_unused]] const bool result = PopulateIterator(heap);
assert(result || (!Valid() && !status_.ok()));
if (!heap.empty()) {
PopulateIterator(heap);
}
}
template <typename BinaryHeap>
bool PopulateIterator(BinaryHeap& heap) {
void PopulateIterator(BinaryHeap& heap) {
// 1. Keep the top iterator (by popping it from the heap) and add it to list
// to populate
// 2. For all non-top iterators having the same key as top iter popped
@@ -285,69 +265,31 @@ class MultiCfIteratorImpl {
// populate the value/columns and attribute_groups from the list
// collected in step 1 and 2 and add all the iters back to the heap
assert(!heap.empty());
auto prepare_value = [this, &heap](Iterator* iterator) {
assert(iterator);
assert(iterator->Valid());
assert(iterator->status().ok());
if (!iterator->PrepareValue()) {
assert(!iterator->Valid());
assert(!iterator->status().ok());
considerStatus(iterator->status());
heap.clear();
assert(!Valid());
assert(!status_.ok());
return false;
}
return true;
};
auto top = heap.top();
assert(top.iterator);
assert(top.iterator->Valid());
assert(top.iterator->status().ok());
if (!prepare_value(top.iterator)) {
return false;
}
autovector<MultiCfIteratorInfo> to_populate;
to_populate.push_back(top);
heap.pop();
while (!heap.empty()) {
autovector<MultiCfIteratorInfo> to_populate;
to_populate.push_back(top);
if (!heap.empty()) {
auto current = heap.top();
assert(current.iterator);
assert(current.iterator->Valid());
assert(current.iterator->status().ok());
if (comparator_->Compare(current.iterator->key(), top.iterator->key()) !=
0) {
break;
while (current.iterator->Valid() &&
comparator_->Compare(top.iterator->key(),
current.iterator->key()) == 0) {
assert(current.iterator->status().ok());
to_populate.push_back(current);
heap.pop();
if (!heap.empty()) {
current = heap.top();
} else {
break;
}
}
if (!prepare_value(current.iterator)) {
return false;
}
to_populate.push_back(current);
heap.pop();
}
// Add the items back to the heap
for (auto& item : to_populate) {
heap.push(item);
}
populate_func_(to_populate);
return true;
}
};
+98 -353
View File
@@ -15,74 +15,53 @@ class CoalescingIteratorTest : public DBTestBase {
// Verify Iteration of CoalescingIterator
// by SeekToFirst() + Next() and SeekToLast() + Prev()
void VerifyCoalescingIterator(const std::vector<ColumnFamilyHandle*>& cfhs,
void verifyCoalescingIterator(const std::vector<ColumnFamilyHandle*>& cfhs,
const std::vector<Slice>& expected_keys,
const std::vector<Slice>& expected_values,
const std::optional<std::vector<WideColumns>>&
expected_wide_columns = std::nullopt,
const Slice* lower_bound = nullptr,
const Slice* upper_bound = nullptr,
bool allow_unprepared_value = false) {
const size_t num_keys = expected_keys.size();
const Slice* upper_bound = nullptr) {
int i = 0;
ReadOptions read_options;
read_options.iterate_lower_bound = lower_bound;
read_options.iterate_upper_bound = upper_bound;
read_options.allow_unprepared_value = allow_unprepared_value;
std::unique_ptr<Iterator> iter =
db_->NewCoalescingIterator(read_options, cfhs);
auto check_iter_entry = [&](size_t idx) {
ASSERT_EQ(iter->key(), expected_keys[idx]);
if (allow_unprepared_value) {
ASSERT_TRUE(iter->value().empty());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_TRUE(iter->Valid());
}
ASSERT_EQ(iter->value(), expected_values[idx]);
if (expected_wide_columns.has_value()) {
ASSERT_EQ(iter->columns(), expected_wide_columns.value()[idx]);
}
};
{
size_t i = 0;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
check_iter_entry(i);
++i;
}
ASSERT_EQ(num_keys, i);
ASSERT_OK(iter->status());
}
{
size_t i = 0;
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
check_iter_entry(num_keys - 1 - i);
++i;
}
ASSERT_EQ(num_keys, i);
ASSERT_OK(iter->status());
}
}
void VerifyExpectedKeys(ColumnFamilyHandle* cfh,
const std::vector<Slice>& expected_keys) {
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions(), cfh));
size_t i = 0;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
ASSERT_EQ(iter->key(), expected_keys[i]);
ASSERT_EQ(expected_keys[i], iter->key());
ASSERT_EQ(expected_values[i], iter->value());
if (expected_wide_columns.has_value()) {
ASSERT_EQ(expected_wide_columns.value()[i], iter->columns());
}
++i;
}
ASSERT_EQ(i, expected_keys.size());
ASSERT_EQ(expected_keys.size(), i);
ASSERT_OK(iter->status());
int rev_i = i - 1;
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
ASSERT_EQ(expected_keys[rev_i], iter->key());
ASSERT_EQ(expected_values[rev_i], iter->value());
if (expected_wide_columns.has_value()) {
ASSERT_EQ(expected_wide_columns.value()[rev_i], iter->columns());
}
rev_i--;
}
ASSERT_OK(iter->status());
}
void verifyExpectedKeys(ColumnFamilyHandle* cfh,
const std::vector<Slice>& expected_keys) {
int i = 0;
Iterator* iter = db_->NewIterator(ReadOptions(), cfh);
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
ASSERT_EQ(expected_keys[i], iter->key());
++i;
}
ASSERT_EQ(expected_keys.size(), i);
ASSERT_OK(iter->status());
delete iter;
}
};
@@ -117,7 +96,7 @@ TEST_F(CoalescingIteratorTest, SimpleValues) {
// Test for iteration over CF default->1->2->3
std::vector<ColumnFamilyHandle*> cfhs_order_0_1_2_3 = {
handles_[0], handles_[1], handles_[2], handles_[3]};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values);
// Test for iteration over CF 3->1->default_cf->2
@@ -125,7 +104,7 @@ TEST_F(CoalescingIteratorTest, SimpleValues) {
handles_[3], handles_[1], handles_[0], handles_[2]};
// Iteration order and the return values should be the same since keys are
// unique per CF
VerifyCoalescingIterator(cfhs_order_3_1_0_2, expected_keys,
verifyCoalescingIterator(cfhs_order_3_1_0_2, expected_keys,
expected_values);
// Verify Seek()
@@ -184,14 +163,14 @@ TEST_F(CoalescingIteratorTest, SimpleValues) {
handles_[0], handles_[1], handles_[2], handles_[3]};
std::vector<Slice> expected_values = {"key_1_cf_3_val", "key_2_cf_2_val",
"key_3_cf_3_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values);
// Test for iteration over CFs 3->2->default_cf->1
std::vector<ColumnFamilyHandle*> cfhs_order_3_2_0_1 = {
handles_[3], handles_[2], handles_[0], handles_[1]};
expected_values = {"key_1_cf_0_val", "key_2_cf_1_val", "key_3_cf_1_val"};
VerifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
verifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
expected_values);
// Verify Seek()
@@ -248,7 +227,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
std::vector<Slice> expected_keys = {"key_2", "key_3", "key_4"};
std::vector<Slice> expected_values = {"key_2_cf_1_val", "key_3_cf_2_val",
"key_4_cf_3_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values, std::nullopt, &lb);
}
// with upper_bound
@@ -257,7 +236,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice ub = Slice("key_3");
std::vector<Slice> expected_keys = {"key_1", "key_2"};
std::vector<Slice> expected_values = {"key_1_cf_0_val", "key_2_cf_1_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values, std::nullopt, nullptr, &ub);
}
// with lower and upper bound
@@ -266,7 +245,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice ub = Slice("key_4");
std::vector<Slice> expected_keys = {"key_2", "key_3"};
std::vector<Slice> expected_values = {"key_2_cf_1_val", "key_3_cf_2_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values, std::nullopt, &lb, &ub);
}
@@ -333,7 +312,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice lb = Slice("key_2");
std::vector<Slice> expected_keys = {"key_2", "key_3"};
std::vector<Slice> expected_values = {"key_2_cf_2_val", "key_3_cf_3_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values, std::nullopt, &lb);
}
// with upper_bound
@@ -342,7 +321,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice ub = Slice("key_3");
std::vector<Slice> expected_keys = {"key_1", "key_2"};
std::vector<Slice> expected_values = {"key_1_cf_3_val", "key_2_cf_2_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values, std::nullopt, nullptr, &ub);
}
// with lower and upper bound
@@ -351,7 +330,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice ub = Slice("key_3");
std::vector<Slice> expected_keys = {"key_2"};
std::vector<Slice> expected_values = {"key_2_cf_2_val"};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys,
expected_values, std::nullopt, &lb, &ub);
}
@@ -363,7 +342,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice lb = Slice("key_2");
std::vector<Slice> expected_keys = {"key_2", "key_3"};
std::vector<Slice> expected_values = {"key_2_cf_1_val", "key_3_cf_1_val"};
VerifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
verifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
expected_values, std::nullopt, &lb);
}
// with upper_bound
@@ -372,7 +351,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice ub = Slice("key_3");
std::vector<Slice> expected_keys = {"key_1", "key_2"};
std::vector<Slice> expected_values = {"key_1_cf_0_val", "key_2_cf_1_val"};
VerifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
verifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
expected_values, std::nullopt, nullptr, &ub);
}
// with lower and upper bound
@@ -381,7 +360,7 @@ TEST_F(CoalescingIteratorTest, LowerAndUpperBounds) {
Slice ub = Slice("key_3");
std::vector<Slice> expected_keys = {"key_2"};
std::vector<Slice> expected_values = {"key_2_cf_1_val"};
VerifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
verifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys,
expected_values, std::nullopt, &lb, &ub);
}
{
@@ -683,7 +662,7 @@ TEST_F(CoalescingIteratorTest, WideColumns) {
key_2_expected_columns_cfh_order_1_2, key_3_expected_columns,
key_4_expected_columns};
VerifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys, expected_values,
verifyCoalescingIterator(cfhs_order_0_1_2_3, expected_keys, expected_values,
expected_wide_columns_0_1_2_3);
}
@@ -698,7 +677,7 @@ TEST_F(CoalescingIteratorTest, WideColumns) {
key_2_expected_columns_cfh_order_2_1, key_3_expected_columns,
key_4_expected_columns};
VerifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys, expected_values,
verifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys, expected_values,
expected_wide_columns_3_2_0_1);
}
}
@@ -721,8 +700,8 @@ TEST_F(CoalescingIteratorTest, DifferentComparatorsInMultiCFs) {
ASSERT_OK(Put(1, "key_2", "value_2"));
ASSERT_OK(Put(1, "key_3", "value_3"));
VerifyExpectedKeys(handles_[0], {"key_1", "key_2", "key_3"});
VerifyExpectedKeys(handles_[1], {"key_3", "key_2", "key_1"});
verifyExpectedKeys(handles_[0], {"key_1", "key_2", "key_3"});
verifyExpectedKeys(handles_[1], {"key_3", "key_2", "key_1"});
std::unique_ptr<Iterator> iter =
db_->NewCoalescingIterator(ReadOptions(), handles_);
@@ -763,10 +742,10 @@ TEST_F(CoalescingIteratorTest, CustomComparatorsInMultiCFs) {
ASSERT_OK(Put(1, "key_003_005", "value_1_5"));
ASSERT_OK(Put(1, "key_003_006", "value_1_4"));
VerifyExpectedKeys(
verifyExpectedKeys(
handles_[0], {"key_001_003", "key_001_002", "key_001_001", "key_002_003",
"key_002_002", "key_002_001"});
VerifyExpectedKeys(
verifyExpectedKeys(
handles_[1], {"key_001_003", "key_001_002", "key_001_001", "key_003_006",
"key_003_005", "key_003_004"});
@@ -776,205 +755,66 @@ TEST_F(CoalescingIteratorTest, CustomComparatorsInMultiCFs) {
std::vector<Slice> expected_values = {"value_1_1", "value_1_2", "value_1_3",
"value_0_4", "value_0_5", "value_0_6",
"value_1_4", "value_1_5", "value_1_6"};
int i = 0;
std::unique_ptr<Iterator> iter =
db_->NewCoalescingIterator(ReadOptions(), handles_);
size_t i = 0;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
ASSERT_EQ(expected_keys[i], iter->key());
ASSERT_EQ(expected_values[i], iter->value());
++i;
}
ASSERT_EQ(expected_keys.size(), i);
ASSERT_OK(iter->status());
}
TEST_F(CoalescingIteratorTest, AllowUnpreparedValue) {
Options options = GetDefaultOptions();
options.enable_blob_files = true;
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, options);
ASSERT_OK(Put(0, "key_1", "key_1_cf_0_val"));
ASSERT_OK(Put(3, "key_1", "key_1_cf_3_val"));
ASSERT_OK(Put(1, "key_2", "key_2_cf_1_val"));
ASSERT_OK(Put(2, "key_2", "key_2_cf_2_val"));
ASSERT_OK(Put(0, "key_3", "key_3_cf_0_val"));
ASSERT_OK(Put(1, "key_3", "key_3_cf_1_val"));
ASSERT_OK(Put(3, "key_3", "key_3_cf_3_val"));
ASSERT_OK(Flush());
std::vector<ColumnFamilyHandle*> cfhs_order_3_2_0_1{handles_[3], handles_[2],
handles_[0], handles_[1]};
std::vector<Slice> expected_keys{"key_1", "key_2", "key_3"};
std::vector<Slice> expected_values{"key_1_cf_0_val", "key_2_cf_1_val",
"key_3_cf_1_val"};
VerifyCoalescingIterator(cfhs_order_3_2_0_1, expected_keys, expected_values,
/* expected_wide_columns */ std::nullopt,
/* lower_bound */ nullptr, /* upper_bound */ nullptr,
/* allow_unprepared_value */ true);
ReadOptions read_options;
read_options.allow_unprepared_value = true;
{
std::unique_ptr<Iterator> iter =
db_->NewCoalescingIterator(read_options, cfhs_order_3_2_0_1);
iter->Seek("");
ASSERT_EQ(IterStatus(iter.get()), "key_1->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_1->key_1_cf_0_val");
iter->Seek("key_1");
ASSERT_EQ(IterStatus(iter.get()), "key_1->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_1->key_1_cf_0_val");
iter->Seek("key_2");
ASSERT_EQ(IterStatus(iter.get()), "key_2->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_2->key_2_cf_1_val");
iter->Next();
ASSERT_EQ(IterStatus(iter.get()), "key_3->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_3->key_3_cf_1_val");
iter->Seek("key_x");
ASSERT_EQ(IterStatus(iter.get()), "(invalid)");
}
{
std::unique_ptr<Iterator> iter =
db_->NewCoalescingIterator(read_options, cfhs_order_3_2_0_1);
iter->SeekForPrev("");
ASSERT_EQ(IterStatus(iter.get()), "(invalid)");
iter->SeekForPrev("key_1");
ASSERT_EQ(IterStatus(iter.get()), "key_1->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_1->key_1_cf_0_val");
iter->Next();
ASSERT_EQ(IterStatus(iter.get()), "key_2->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_2->key_2_cf_1_val");
iter->SeekForPrev("key_x");
ASSERT_EQ(IterStatus(iter.get()), "key_3->");
ASSERT_TRUE(iter->PrepareValue());
ASSERT_EQ(IterStatus(iter.get()), "key_3->key_3_cf_1_val");
iter->Next();
ASSERT_EQ(IterStatus(iter.get()), "(invalid)");
}
}
TEST_F(CoalescingIteratorTest, AllowUnpreparedValue_Corruption) {
Options options = GetDefaultOptions();
options.enable_blob_files = true;
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, options);
ASSERT_OK(Put(0, "key_1", "key_1_cf_0_val"));
ASSERT_OK(Put(3, "key_1", "key_1_cf_3_val"));
ASSERT_OK(Put(1, "key_2", "key_2_cf_1_val"));
ASSERT_OK(Put(2, "key_2", "key_2_cf_2_val"));
ASSERT_OK(Put(0, "key_3", "key_3_cf_0_val"));
ASSERT_OK(Put(1, "key_3", "key_3_cf_1_val"));
ASSERT_OK(Put(3, "key_3", "key_3_cf_3_val"));
ASSERT_OK(Flush());
ReadOptions read_options;
read_options.allow_unprepared_value = true;
std::vector<ColumnFamilyHandle*> cfhs_order_3_2_0_1{handles_[3], handles_[2],
handles_[0], handles_[1]};
std::unique_ptr<Iterator> iter =
db_->NewCoalescingIterator(read_options, cfhs_order_3_2_0_1);
iter->SeekToFirst();
ASSERT_TRUE(iter->Valid());
ASSERT_OK(iter->status());
ASSERT_EQ(iter->key(), "key_1");
ASSERT_TRUE(iter->value().empty());
SyncPoint::GetInstance()->SetCallBack(
"BlobFileReader::GetBlob:TamperWithResult", [](void* arg) {
Slice* const blob_index = static_cast<Slice*>(arg);
assert(blob_index);
assert(!blob_index->empty());
blob_index->remove_prefix(1);
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_FALSE(iter->PrepareValue());
ASSERT_FALSE(iter->Valid());
ASSERT_TRUE(iter->status().IsCorruption());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
class AttributeGroupIteratorTest : public DBTestBase {
public:
AttributeGroupIteratorTest()
: DBTestBase("attribute_group_iterator_test", /*env_do_fsync=*/true) {}
void VerifyAttributeGroupIterator(
void verifyAttributeGroupIterator(
const std::vector<ColumnFamilyHandle*>& cfhs,
const std::vector<Slice>& expected_keys,
const std::vector<IteratorAttributeGroups>& expected_attribute_groups,
const Slice* lower_bound = nullptr, const Slice* upper_bound = nullptr,
bool allow_unprepared_value = false) {
const size_t num_keys = expected_keys.size();
const std::vector<AttributeGroups>& expected_attribute_groups,
const Slice* lower_bound = nullptr, const Slice* upper_bound = nullptr) {
int i = 0;
ReadOptions read_options;
read_options.iterate_lower_bound = lower_bound;
read_options.iterate_upper_bound = upper_bound;
read_options.allow_unprepared_value = allow_unprepared_value;
std::unique_ptr<AttributeGroupIterator> iter =
db_->NewAttributeGroupIterator(read_options, cfhs);
auto check_iter_entry = [&](size_t idx) {
ASSERT_EQ(iter->key(), expected_keys[idx]);
if (allow_unprepared_value) {
ASSERT_TRUE(iter->attribute_groups().empty());
ASSERT_TRUE(iter->PrepareValue());
ASSERT_TRUE(iter->Valid());
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
ASSERT_EQ(expected_keys[i], iter->key());
auto iterator_attribute_groups = iter->attribute_groups();
ASSERT_EQ(expected_attribute_groups[i].size(),
iterator_attribute_groups.size());
for (size_t cfh_i = 0; cfh_i < iterator_attribute_groups.size();
cfh_i++) {
ASSERT_EQ(expected_attribute_groups[i][cfh_i].column_family(),
iterator_attribute_groups[cfh_i].column_family());
ASSERT_EQ(expected_attribute_groups[i][cfh_i].columns(),
iterator_attribute_groups[cfh_i].columns());
}
ASSERT_EQ(iter->attribute_groups(), expected_attribute_groups[idx]);
};
{
size_t i = 0;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
check_iter_entry(i);
++i;
}
ASSERT_EQ(i, num_keys);
ASSERT_OK(iter->status());
++i;
}
ASSERT_EQ(expected_keys.size(), i);
ASSERT_OK(iter->status());
{
size_t i = 0;
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
check_iter_entry(num_keys - 1 - i);
++i;
int rev_i = i - 1;
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
ASSERT_EQ(expected_keys[rev_i], iter->key());
auto iterator_attribute_groups = iter->attribute_groups();
ASSERT_EQ(expected_attribute_groups[rev_i].size(),
iterator_attribute_groups.size());
for (size_t cfh_i = 0; cfh_i < iterator_attribute_groups.size();
cfh_i++) {
ASSERT_EQ(expected_attribute_groups[rev_i][cfh_i].column_family(),
iterator_attribute_groups[cfh_i].column_family());
ASSERT_EQ(expected_attribute_groups[rev_i][cfh_i].columns(),
iterator_attribute_groups[cfh_i].columns());
}
ASSERT_EQ(i, num_keys);
ASSERT_OK(iter->status());
rev_i--;
}
ASSERT_OK(iter->status());
}
};
@@ -1030,140 +870,45 @@ TEST_F(AttributeGroupIteratorTest, IterateAttributeGroups) {
ASSERT_OK(db_->PutEntity(WriteOptions(), key_3, key_3_attribute_groups));
ASSERT_OK(db_->PutEntity(WriteOptions(), key_4, key_4_attribute_groups));
IteratorAttributeGroups key_1_expected_attribute_groups{
IteratorAttributeGroup(key_1_attribute_groups[0]),
IteratorAttributeGroup(key_1_attribute_groups[1])};
IteratorAttributeGroups key_2_expected_attribute_groups{
IteratorAttributeGroup(key_2_attribute_groups[0]),
IteratorAttributeGroup(key_2_attribute_groups[1])};
IteratorAttributeGroups key_3_expected_attribute_groups{
IteratorAttributeGroup(key_3_attribute_groups[0]),
IteratorAttributeGroup(key_3_attribute_groups[1])};
IteratorAttributeGroups key_4_expected_attribute_groups{
IteratorAttributeGroup(key_4_attribute_groups[0]),
IteratorAttributeGroup(key_4_attribute_groups[1])};
// Test for iteration over CF default->1->2->3
std::vector<ColumnFamilyHandle*> cfhs_order_0_1_2_3 = {
handles_[0], handles_[1], handles_[2], handles_[3]};
{
std::vector<Slice> expected_keys = {key_1, key_2, key_3, key_4};
std::vector<IteratorAttributeGroups> expected_attribute_groups{
key_1_expected_attribute_groups, key_2_expected_attribute_groups,
key_3_expected_attribute_groups, key_4_expected_attribute_groups};
VerifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
std::vector<AttributeGroups> expected_attribute_groups = {
key_1_attribute_groups, key_2_attribute_groups, key_3_attribute_groups,
key_4_attribute_groups};
verifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
expected_attribute_groups);
}
Slice lb = Slice("key_2");
Slice ub = Slice("key_4");
// Test for lower bound only
{
std::vector<Slice> expected_keys = {key_2, key_3, key_4};
std::vector<IteratorAttributeGroups> expected_attribute_groups{
key_2_expected_attribute_groups, key_3_expected_attribute_groups,
key_4_expected_attribute_groups};
VerifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
std::vector<AttributeGroups> expected_attribute_groups = {
key_2_attribute_groups, key_3_attribute_groups, key_4_attribute_groups};
verifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
expected_attribute_groups, &lb);
}
// Test for upper bound only
{
std::vector<Slice> expected_keys = {key_1, key_2, key_3};
std::vector<IteratorAttributeGroups> expected_attribute_groups{
key_1_expected_attribute_groups, key_2_expected_attribute_groups,
key_3_expected_attribute_groups};
VerifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
std::vector<AttributeGroups> expected_attribute_groups = {
key_1_attribute_groups, key_2_attribute_groups, key_3_attribute_groups};
verifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
expected_attribute_groups, nullptr, &ub);
}
// Test for lower and upper bound
{
std::vector<Slice> expected_keys = {key_2, key_3};
std::vector<IteratorAttributeGroups> expected_attribute_groups{
key_2_expected_attribute_groups, key_3_expected_attribute_groups};
VerifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
std::vector<AttributeGroups> expected_attribute_groups = {
key_2_attribute_groups, key_3_attribute_groups};
verifyAttributeGroupIterator(cfhs_order_0_1_2_3, expected_keys,
expected_attribute_groups, &lb, &ub);
}
}
TEST_F(AttributeGroupIteratorTest, AllowUnpreparedValue) {
Options options = GetDefaultOptions();
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, options);
constexpr char key_1[] = "key_1";
WideColumns key_1_columns_in_cf_2{
{kDefaultWideColumnName, "cf_2_col_val_0_key_1"},
{"cf_2_col_name_1", "cf_2_col_val_1_key_1"},
{"cf_2_col_name_2", "cf_2_col_val_2_key_1"}};
WideColumns key_1_columns_in_cf_3{
{"cf_3_col_name_1", "cf_3_col_val_1_key_1"},
{"cf_3_col_name_2", "cf_3_col_val_2_key_1"},
{"cf_3_col_name_3", "cf_3_col_val_3_key_1"}};
constexpr char key_2[] = "key_2";
WideColumns key_2_columns_in_cf_1{
{"cf_1_col_name_1", "cf_1_col_val_1_key_2"}};
WideColumns key_2_columns_in_cf_2{
{"cf_2_col_name_1", "cf_2_col_val_1_key_2"},
{"cf_2_col_name_2", "cf_2_col_val_2_key_2"}};
constexpr char key_3[] = "key_3";
WideColumns key_3_columns_in_cf_1{
{"cf_1_col_name_1", "cf_1_col_val_1_key_3"}};
WideColumns key_3_columns_in_cf_3{
{"cf_3_col_name_1", "cf_3_col_val_1_key_3"}};
constexpr char key_4[] = "key_4";
WideColumns key_4_columns_in_cf_0{
{"cf_0_col_name_1", "cf_0_col_val_1_key_4"}};
WideColumns key_4_columns_in_cf_2{
{"cf_2_col_name_1", "cf_2_col_val_1_key_4"}};
AttributeGroups key_1_attribute_groups{
AttributeGroup(handles_[2], key_1_columns_in_cf_2),
AttributeGroup(handles_[3], key_1_columns_in_cf_3)};
AttributeGroups key_2_attribute_groups{
AttributeGroup(handles_[1], key_2_columns_in_cf_1),
AttributeGroup(handles_[2], key_2_columns_in_cf_2)};
AttributeGroups key_3_attribute_groups{
AttributeGroup(handles_[1], key_3_columns_in_cf_1),
AttributeGroup(handles_[3], key_3_columns_in_cf_3)};
AttributeGroups key_4_attribute_groups{
AttributeGroup(handles_[0], key_4_columns_in_cf_0),
AttributeGroup(handles_[2], key_4_columns_in_cf_2)};
ASSERT_OK(db_->PutEntity(WriteOptions(), key_1, key_1_attribute_groups));
ASSERT_OK(db_->PutEntity(WriteOptions(), key_2, key_2_attribute_groups));
ASSERT_OK(db_->PutEntity(WriteOptions(), key_3, key_3_attribute_groups));
ASSERT_OK(db_->PutEntity(WriteOptions(), key_4, key_4_attribute_groups));
IteratorAttributeGroups key_1_expected_attribute_groups{
IteratorAttributeGroup(key_1_attribute_groups[0]),
IteratorAttributeGroup(key_1_attribute_groups[1])};
IteratorAttributeGroups key_2_expected_attribute_groups{
IteratorAttributeGroup(key_2_attribute_groups[0]),
IteratorAttributeGroup(key_2_attribute_groups[1])};
IteratorAttributeGroups key_3_expected_attribute_groups{
IteratorAttributeGroup(key_3_attribute_groups[0]),
IteratorAttributeGroup(key_3_attribute_groups[1])};
IteratorAttributeGroups key_4_expected_attribute_groups{
IteratorAttributeGroup(key_4_attribute_groups[0]),
IteratorAttributeGroup(key_4_attribute_groups[1])};
std::vector<ColumnFamilyHandle*> cfhs_order_0_1_2_3{handles_[0], handles_[1],
handles_[2], handles_[3]};
std::vector<Slice> expected_keys{key_1, key_2, key_3, key_4};
std::vector<IteratorAttributeGroups> expected_attribute_groups{
key_1_expected_attribute_groups, key_2_expected_attribute_groups,
key_3_expected_attribute_groups, key_4_expected_attribute_groups};
VerifyAttributeGroupIterator(
cfhs_order_0_1_2_3, expected_keys, expected_attribute_groups,
/* lower_bound */ nullptr, /* upper_bound */ nullptr,
/* allow_unprepared_value */ true);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+3 -2
View File
@@ -197,10 +197,11 @@ class FragmentedRangeTombstoneIterator : public InternalIterator {
pinned_seq_pos_ = tombstones_->seq_end();
}
RangeTombstone Tombstone() const {
RangeTombstone Tombstone(bool logical_strip_timestamp = false) const {
assert(Valid());
if (icmp_->user_comparator()->timestamp_size()) {
return RangeTombstone(start_key(), end_key(), seq(), timestamp());
return RangeTombstone(start_key(), end_key(), seq(), timestamp(),
logical_strip_timestamp);
}
return RangeTombstone(start_key(), end_key(), seq());
}
+9 -10
View File
@@ -105,7 +105,6 @@ class Repairer {
SanitizeOptions(immutable_db_options_, default_cf_opts)),
default_iopts_(
ImmutableOptions(immutable_db_options_, default_cf_opts_)),
default_mopts_(MutableCFOptions(default_cf_opts_)),
unknown_cf_opts_(
SanitizeOptions(immutable_db_options_, unknown_cf_opts)),
create_unknown_cfs_(create_unknown_cfs),
@@ -262,7 +261,6 @@ class Repairer {
const InternalKeyComparator icmp_;
const ColumnFamilyOptions default_cf_opts_;
const ImmutableOptions default_iopts_; // table_cache_ holds reference
const MutableCFOptions default_mopts_;
const ColumnFamilyOptions unknown_cf_opts_;
const bool create_unknown_cfs_;
std::shared_ptr<Cache> raw_table_cache_;
@@ -413,12 +411,9 @@ class Repairer {
if (record_status.ok()) {
const UnorderedMap<uint32_t, size_t>& record_ts_sz =
reader.GetRecordedTimestampSize();
// Use same value for `seq_per_batch` and `batch_per_txn` as
// WriteBatchInternal::InsertInto does below.
record_status = HandleWriteBatchTimestampSizeDifference(
&batch, running_ts_sz, record_ts_sz,
TimestampSizeConsistencyMode::kVerifyConsistency,
/* seq_per_batch */ false, /* batch_per_txn */ true);
TimestampSizeConsistencyMode::kVerifyConsistency);
if (record_status.ok()) {
record_status =
WriteBatchInternal::InsertInto(&batch, cf_mems, nullptr, nullptr);
@@ -542,7 +537,8 @@ class Repairer {
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
status = table_cache_->GetTableProperties(
file_options_, read_options, icmp_, t->meta, &props, default_mopts_);
file_options_, read_options, icmp_, t->meta, &props,
0 /* block_protection_bytes_per_key */);
}
if (status.ok()) {
auto s =
@@ -606,13 +602,15 @@ class Repairer {
ropts.total_order_seek = true;
InternalIterator* iter = table_cache_->NewIterator(
ropts, file_options_, cfd->internal_comparator(), t->meta,
nullptr /* range_del_agg */, *cfd->GetLatestMutableCFOptions(),
nullptr /* range_del_agg */,
cfd->GetLatestMutableCFOptions()->prefix_extractor,
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
TableReaderCaller::kRepair, /*arena=*/nullptr, /*skip_filters=*/false,
/*level=*/-1, /*max_file_size_for_l0_meta_pin=*/0,
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr,
/*allow_unprepared_value=*/false);
/*allow_unprepared_value=*/false,
cfd->GetLatestMutableCFOptions()->block_protection_bytes_per_key);
ParsedInternalKey parsed;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
Slice key = iter->key();
@@ -653,7 +651,8 @@ class Repairer {
std::unique_ptr<FragmentedRangeTombstoneIterator> r_iter;
status = table_cache_->GetRangeTombstoneIterator(
ropts, cfd->internal_comparator(), t->meta,
*cfd->GetLatestMutableCFOptions(), &r_iter);
cfd->GetLatestMutableCFOptions()->block_protection_bytes_per_key,
&r_iter);
if (r_iter) {
r_iter->SeekToFirst();
-7
View File
@@ -55,11 +55,4 @@ class WritePreparedSnapshotChecker : public SnapshotChecker {
const WritePreparedTxnDB* const txn_db_;
};
bool DataIsDefinitelyInSnapshot(SequenceNumber seqno, SequenceNumber snapshot,
const SnapshotChecker* snapshot_checker);
bool DataIsDefinitelyNotInSnapshot(SequenceNumber seqno,
SequenceNumber snapshot,
const SnapshotChecker* snapshot_checker);
} // namespace ROCKSDB_NAMESPACE
+61 -54
View File
@@ -91,9 +91,10 @@ Status TableCache::GetTableReader(
const ReadOptions& ro, const FileOptions& file_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, bool sequential_mode,
HistogramImpl* file_read_hist, std::unique_ptr<TableReader>* table_reader,
const MutableCFOptions& mutable_cf_options, bool skip_filters, int level,
bool prefetch_index_and_filter_in_cache,
uint8_t block_protection_bytes_per_key, HistogramImpl* file_read_hist,
std::unique_ptr<TableReader>* table_reader,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache,
size_t max_file_size_for_l0_meta_pin, Temperature file_temperature) {
std::string fname = TableFileName(
ioptions_.cf_paths, file_meta.fd.GetNumber(), file_meta.fd.GetPathId());
@@ -145,14 +146,13 @@ Status TableCache::GetTableReader(
} else {
expected_unique_id = kNullUniqueId64x2; // null ID == no verification
}
s = mutable_cf_options.table_factory->NewTableReader(
s = ioptions_.table_factory->NewTableReader(
ro,
TableReaderOptions(
ioptions_, mutable_cf_options.prefix_extractor, file_options,
internal_comparator,
mutable_cf_options.block_protection_bytes_per_key, skip_filters,
immortal_tables_, false /* force_direct_prefetch */, level,
block_cache_tracer_, max_file_size_for_l0_meta_pin, db_session_id_,
ioptions_, prefix_extractor, file_options, internal_comparator,
block_protection_bytes_per_key, skip_filters, immortal_tables_,
false /* force_direct_prefetch */, level, block_cache_tracer_,
max_file_size_for_l0_meta_pin, db_session_id_,
file_meta.fd.GetNumber(), expected_unique_id,
file_meta.fd.largest_seqno, file_meta.tail_size,
file_meta.user_defined_timestamps_persisted),
@@ -164,7 +164,6 @@ Status TableCache::GetTableReader(
}
Cache::Handle* TableCache::Lookup(Cache* cache, uint64_t file_number) {
// NOTE: sharing same Cache with BlobFileCache
Slice key = GetSliceForFileNumber(&file_number);
return cache->Lookup(key);
}
@@ -173,13 +172,13 @@ Status TableCache::FindTable(
const ReadOptions& ro, const FileOptions& file_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, TypedHandle** handle,
const MutableCFOptions& mutable_cf_options, const bool no_io,
HistogramImpl* file_read_hist, bool skip_filters, int level,
bool prefetch_index_and_filter_in_cache,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
const bool no_io, HistogramImpl* file_read_hist, bool skip_filters,
int level, bool prefetch_index_and_filter_in_cache,
size_t max_file_size_for_l0_meta_pin, Temperature file_temperature) {
PERF_TIMER_GUARD_WITH_CLOCK(find_table_nanos, ioptions_.clock);
uint64_t number = file_meta.fd.GetNumber();
// NOTE: sharing same Cache with BlobFileCache
Slice key = GetSliceForFileNumber(&number);
*handle = cache_.Lookup(key);
TEST_SYNC_POINT_CALLBACK("TableCache::FindTable:0",
@@ -198,8 +197,9 @@ Status TableCache::FindTable(
std::unique_ptr<TableReader> table_reader;
Status s = GetTableReader(ro, file_options, internal_comparator, file_meta,
false /* sequential mode */, file_read_hist,
&table_reader, mutable_cf_options, skip_filters,
false /* sequential mode */,
block_protection_bytes_per_key, file_read_hist,
&table_reader, prefix_extractor, skip_filters,
level, prefetch_index_and_filter_in_cache,
max_file_size_for_l0_meta_pin, file_temperature);
if (!s.ok()) {
@@ -223,12 +223,13 @@ InternalIterator* TableCache::NewIterator(
const ReadOptions& options, const FileOptions& file_options,
const InternalKeyComparator& icomparator, const FileMetaData& file_meta,
RangeDelAggregator* range_del_agg,
const MutableCFOptions& mutable_cf_options, TableReader** table_reader_ptr,
HistogramImpl* file_read_hist, TableReaderCaller caller, Arena* arena,
bool skip_filters, int level, size_t max_file_size_for_l0_meta_pin,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
TableReader** table_reader_ptr, HistogramImpl* file_read_hist,
TableReaderCaller caller, Arena* arena, bool skip_filters, int level,
size_t max_file_size_for_l0_meta_pin,
const InternalKey* smallest_compaction_key,
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
const SequenceNumber* read_seqno,
uint8_t block_protection_bytes_per_key, const SequenceNumber* read_seqno,
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter) {
PERF_TIMER_GUARD(new_table_iterator_nanos);
@@ -243,7 +244,7 @@ InternalIterator* TableCache::NewIterator(
table_reader = fd.table_reader;
if (table_reader == nullptr) {
s = FindTable(options, file_options, icomparator, file_meta, &handle,
mutable_cf_options,
block_protection_bytes_per_key, prefix_extractor,
options.read_tier == kBlockCacheTier /* no_io */,
file_read_hist, skip_filters, level,
true /* prefetch_index_and_filter_in_cache */,
@@ -259,9 +260,8 @@ InternalIterator* TableCache::NewIterator(
result = NewEmptyInternalIterator<Slice>(arena);
} else {
result = table_reader->NewIterator(
options, mutable_cf_options.prefix_extractor.get(), arena,
skip_filters, caller, file_options.compaction_readahead_size,
allow_unprepared_value);
options, prefix_extractor.get(), arena, skip_filters, caller,
file_options.compaction_readahead_size, allow_unprepared_value);
}
if (handle != nullptr) {
cache_.RegisterReleaseAsCleanup(handle, *result);
@@ -328,7 +328,7 @@ InternalIterator* TableCache::NewIterator(
Status TableCache::GetRangeTombstoneIterator(
const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MutableCFOptions& mutable_cf_options,
const FileMetaData& file_meta, uint8_t block_protection_bytes_per_key,
std::unique_ptr<FragmentedRangeTombstoneIterator>* out_iter) {
assert(out_iter);
const FileDescriptor& fd = file_meta.fd;
@@ -337,7 +337,7 @@ Status TableCache::GetRangeTombstoneIterator(
TypedHandle* handle = nullptr;
if (t == nullptr) {
s = FindTable(options, file_options_, internal_comparator, file_meta,
&handle, mutable_cf_options);
&handle, block_protection_bytes_per_key);
if (s.ok()) {
t = cache_.Value(handle);
}
@@ -429,13 +429,14 @@ bool TableCache::GetFromRowCache(const Slice& user_key, IterKey& row_cache_key,
return found;
}
Status TableCache::Get(const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const Slice& k,
GetContext* get_context,
const MutableCFOptions& mutable_cf_options,
HistogramImpl* file_read_hist, bool skip_filters,
int level, size_t max_file_size_for_l0_meta_pin) {
Status TableCache::Get(
const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const Slice& k, GetContext* get_context,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
HistogramImpl* file_read_hist, bool skip_filters, int level,
size_t max_file_size_for_l0_meta_pin) {
auto& fd = file_meta.fd;
std::string* row_cache_entry = nullptr;
bool done = false;
@@ -460,7 +461,7 @@ Status TableCache::Get(const ReadOptions& options,
if (s.ok() && !done) {
if (t == nullptr) {
s = FindTable(options, file_options_, internal_comparator, file_meta,
&handle, mutable_cf_options,
&handle, block_protection_bytes_per_key, prefix_extractor,
options.read_tier == kBlockCacheTier /* no_io */,
file_read_hist, skip_filters, level,
true /* prefetch_index_and_filter_in_cache */,
@@ -489,8 +490,7 @@ Status TableCache::Get(const ReadOptions& options,
}
if (s.ok()) {
get_context->SetReplayLog(row_cache_entry); // nullptr if no cache.
s = t->Get(options, k, get_context,
mutable_cf_options.prefix_extractor.get(), skip_filters);
s = t->Get(options, k, get_context, prefix_extractor.get(), skip_filters);
get_context->SetReplayLog(nullptr);
} else if (options.read_tier == kBlockCacheTier && s.IsIncomplete()) {
// Couldn't find table in cache and couldn't open it because of no_io.
@@ -543,9 +543,11 @@ void TableCache::UpdateRangeTombstoneSeqnums(
Status TableCache::MultiGetFilter(
const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MutableCFOptions& mutable_cf_options,
const FileMetaData& file_meta,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
HistogramImpl* file_read_hist, int level,
MultiGetContext::Range* mget_range, TypedHandle** table_handle) {
MultiGetContext::Range* mget_range, TypedHandle** table_handle,
uint8_t block_protection_bytes_per_key) {
auto& fd = file_meta.fd;
IterKey row_cache_key;
std::string row_cache_entry_buffer;
@@ -564,7 +566,7 @@ Status TableCache::MultiGetFilter(
mget_range->end());
if (t == nullptr) {
s = FindTable(options, file_options_, internal_comparator, file_meta,
&handle, mutable_cf_options,
&handle, block_protection_bytes_per_key, prefix_extractor,
options.read_tier == kBlockCacheTier /* no_io */,
file_read_hist,
/*skip_filters=*/false, level,
@@ -576,8 +578,7 @@ Status TableCache::MultiGetFilter(
*table_handle = handle;
}
if (s.ok()) {
s = t->MultiGetFilter(options, mutable_cf_options.prefix_extractor.get(),
mget_range);
s = t->MultiGetFilter(options, prefix_extractor.get(), mget_range);
}
if (s.ok() && !options.ignore_range_deletions) {
// Update the range tombstone sequence numbers for the keys here
@@ -598,7 +599,8 @@ Status TableCache::GetTableProperties(
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
std::shared_ptr<const TableProperties>* properties,
const MutableCFOptions& mutable_cf_options, bool no_io) {
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor, bool no_io) {
auto table_reader = file_meta.fd.table_reader;
// table already been pre-loaded?
if (table_reader) {
@@ -609,7 +611,8 @@ Status TableCache::GetTableProperties(
TypedHandle* table_handle = nullptr;
Status s = FindTable(read_options, file_options, internal_comparator,
file_meta, &table_handle, mutable_cf_options, no_io);
file_meta, &table_handle, block_protection_bytes_per_key,
prefix_extractor, no_io);
if (!s.ok()) {
return s;
}
@@ -622,15 +625,14 @@ Status TableCache::GetTableProperties(
Status TableCache::ApproximateKeyAnchors(
const ReadOptions& ro, const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MutableCFOptions& mutable_cf_options,
const FileMetaData& file_meta, uint8_t block_protection_bytes_per_key,
std::vector<TableReader::Anchor>& anchors) {
Status s;
TableReader* t = file_meta.fd.table_reader;
TypedHandle* handle = nullptr;
if (t == nullptr) {
s = FindTable(ro, file_options_, internal_comparator, file_meta, &handle,
mutable_cf_options);
block_protection_bytes_per_key);
if (s.ok()) {
t = cache_.Value(handle);
}
@@ -647,7 +649,8 @@ Status TableCache::ApproximateKeyAnchors(
size_t TableCache::GetMemoryUsageByTableReader(
const FileOptions& file_options, const ReadOptions& read_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MutableCFOptions& mutable_cf_options) {
const FileMetaData& file_meta, uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor) {
auto table_reader = file_meta.fd.table_reader;
// table already been pre-loaded?
if (table_reader) {
@@ -655,9 +658,9 @@ size_t TableCache::GetMemoryUsageByTableReader(
}
TypedHandle* table_handle = nullptr;
Status s =
FindTable(read_options, file_options, internal_comparator, file_meta,
&table_handle, mutable_cf_options, true /* no_io */);
Status s = FindTable(read_options, file_options, internal_comparator,
file_meta, &table_handle, block_protection_bytes_per_key,
prefix_extractor, true /* no_io */);
if (!s.ok()) {
return 0;
}
@@ -676,14 +679,16 @@ uint64_t TableCache::ApproximateOffsetOf(
const ReadOptions& read_options, const Slice& key,
const FileMetaData& file_meta, TableReaderCaller caller,
const InternalKeyComparator& internal_comparator,
const MutableCFOptions& mutable_cf_options) {
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor) {
uint64_t result = 0;
TableReader* table_reader = file_meta.fd.table_reader;
TypedHandle* table_handle = nullptr;
if (table_reader == nullptr) {
Status s =
FindTable(read_options, file_options_, internal_comparator, file_meta,
&table_handle, mutable_cf_options, false /* no_io */);
&table_handle, block_protection_bytes_per_key,
prefix_extractor, false /* no_io */);
if (s.ok()) {
table_reader = cache_.Value(table_handle);
}
@@ -703,14 +708,16 @@ uint64_t TableCache::ApproximateSize(
const ReadOptions& read_options, const Slice& start, const Slice& end,
const FileMetaData& file_meta, TableReaderCaller caller,
const InternalKeyComparator& internal_comparator,
const MutableCFOptions& mutable_cf_options) {
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor) {
uint64_t result = 0;
TableReader* table_reader = file_meta.fd.table_reader;
TypedHandle* table_handle = nullptr;
if (table_reader == nullptr) {
Status s =
FindTable(read_options, file_options_, internal_comparator, file_meta,
&table_handle, mutable_cf_options, false /* no_io */);
&table_handle, block_protection_bytes_per_key,
prefix_extractor, false /* no_io */);
if (s.ok()) {
table_reader = cache_.Value(table_handle);
}
+73 -65
View File
@@ -92,12 +92,13 @@ class TableCache {
const ReadOptions& options, const FileOptions& toptions,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, RangeDelAggregator* range_del_agg,
const MutableCFOptions& mutable_cf_options,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
TableReader** table_reader_ptr, HistogramImpl* file_read_hist,
TableReaderCaller caller, Arena* arena, bool skip_filters, int level,
size_t max_file_size_for_l0_meta_pin,
const InternalKey* smallest_compaction_key,
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
uint8_t protection_bytes_per_key,
const SequenceNumber* range_del_read_seqno = nullptr,
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter = nullptr);
@@ -111,20 +112,21 @@ class TableCache {
// recorded
// @param skip_filters Disables loading/accessing the filter block
// @param level The level this table is at, -1 for "not set / don't know"
Status Get(const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const Slice& k,
GetContext* get_context,
const MutableCFOptions& mutable_cf_options,
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
int level = -1, size_t max_file_size_for_l0_meta_pin = 0);
Status Get(
const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const Slice& k, GetContext* get_context,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
int level = -1, size_t max_file_size_for_l0_meta_pin = 0);
// Return the range delete tombstone iterator of the file specified by
// `file_meta`.
Status GetRangeTombstoneIterator(
const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MutableCFOptions& mutable_cf_options,
const FileMetaData& file_meta, uint8_t block_protection_bytes_per_key,
std::unique_ptr<FragmentedRangeTombstoneIterator>* out_iter);
// Call table reader's MultiGetFilter to use the bloom filter to filter out
@@ -132,13 +134,14 @@ class TableCache {
// If the table cache is looked up to get the table reader, the cache handle
// is returned in table_handle. This handle should be passed back to
// MultiGet() so it can be released.
Status MultiGetFilter(const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
const MutableCFOptions& mutable_cf_options,
HistogramImpl* file_read_hist, int level,
MultiGetContext::Range* mget_range,
TypedHandle** table_handle);
Status MultiGetFilter(
const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
HistogramImpl* file_read_hist, int level,
MultiGetContext::Range* mget_range, TypedHandle** table_handle,
uint8_t block_protection_bytes_per_key);
// If a seek to internal key "k" in specified file finds an entry,
// call get_context->SaveValue() repeatedly until
@@ -149,15 +152,15 @@ class TableCache {
// in the embedded GetContext
// @param skip_filters Disables loading/accessing the filter block
// @param level The level this table is at, -1 for "not set / don't know"
DECLARE_SYNC_AND_ASYNC(Status, MultiGet, const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
const MultiGetContext::Range* mget_range,
const MutableCFOptions& mutable_cf_options,
HistogramImpl* file_read_hist = nullptr,
bool skip_filters = false,
bool skip_range_deletions = false, int level = -1,
TypedHandle* table_handle = nullptr);
DECLARE_SYNC_AND_ASYNC(
Status, MultiGet, const ReadOptions& options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MultiGetContext::Range* mget_range,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
bool skip_range_deletions = false, int level = -1,
TypedHandle* table_handle = nullptr);
// Evict any entry for the specified file number
static void Evict(Cache* cache, uint64_t file_number);
@@ -173,16 +176,17 @@ class TableCache {
// Find table reader
// @param skip_filters Disables loading/accessing the filter block
// @param level == -1 means not specified
Status FindTable(const ReadOptions& ro, const FileOptions& toptions,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, TypedHandle**,
const MutableCFOptions& mutable_cf_options,
const bool no_io = false,
HistogramImpl* file_read_hist = nullptr,
bool skip_filters = false, int level = -1,
bool prefetch_index_and_filter_in_cache = true,
size_t max_file_size_for_l0_meta_pin = 0,
Temperature file_temperature = Temperature::kUnknown);
Status FindTable(
const ReadOptions& ro, const FileOptions& toptions,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, TypedHandle**,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
const bool no_io = false, HistogramImpl* file_read_hist = nullptr,
bool skip_filters = false, int level = -1,
bool prefetch_index_and_filter_in_cache = true,
size_t max_file_size_for_l0_meta_pin = 0,
Temperature file_temperature = Temperature::kUnknown);
// Get the table properties of a given table.
// @no_io: indicates if we should load table to the cache if it is not present
@@ -190,18 +194,19 @@ class TableCache {
// @returns: `properties` will be reset on success. Please note that we will
// return Status::Incomplete() if table is not present in cache and
// we set `no_io` to be true.
Status GetTableProperties(const FileOptions& toptions,
const ReadOptions& read_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
std::shared_ptr<const TableProperties>* properties,
const MutableCFOptions& mutable_cf_options,
bool no_io = false);
Status GetTableProperties(
const FileOptions& toptions, const ReadOptions& read_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
std::shared_ptr<const TableProperties>* properties,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
bool no_io = false);
Status ApproximateKeyAnchors(const ReadOptions& ro,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
const MutableCFOptions& mutable_cf_options,
uint8_t block_protection_bytes_per_key,
std::vector<TableReader::Anchor>& anchors);
// Return total memory usage of the table reader of the file.
@@ -209,23 +214,25 @@ class TableCache {
size_t GetMemoryUsageByTableReader(
const FileOptions& toptions, const ReadOptions& read_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta,
const MutableCFOptions& mutable_cf_options);
const FileMetaData& file_meta, uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr);
// Returns approximated offset of a key in a file represented by fd.
uint64_t ApproximateOffsetOf(const ReadOptions& read_options,
const Slice& key, const FileMetaData& file_meta,
TableReaderCaller caller,
const InternalKeyComparator& internal_comparator,
const MutableCFOptions& mutable_cf_options);
uint64_t ApproximateOffsetOf(
const ReadOptions& read_options, const Slice& key,
const FileMetaData& file_meta, TableReaderCaller caller,
const InternalKeyComparator& internal_comparator,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr);
// Returns approximated data size between start and end keys in a file
// represented by fd (the start key must not be greater than the end key).
uint64_t ApproximateSize(const ReadOptions& read_options, const Slice& start,
const Slice& end, const FileMetaData& file_meta,
TableReaderCaller caller,
const InternalKeyComparator& internal_comparator,
const MutableCFOptions& mutable_cf_options);
uint64_t ApproximateSize(
const ReadOptions& read_options, const Slice& start, const Slice& end,
const FileMetaData& file_meta, TableReaderCaller caller,
const InternalKeyComparator& internal_comparator,
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr);
CacheInterface& get_cache() { return cache_; }
@@ -243,16 +250,17 @@ class TableCache {
private:
// Build a table reader
Status GetTableReader(const ReadOptions& ro, const FileOptions& file_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, bool sequential_mode,
HistogramImpl* file_read_hist,
std::unique_ptr<TableReader>* table_reader,
const MutableCFOptions& mutable_cf_options,
bool skip_filters = false, int level = -1,
bool prefetch_index_and_filter_in_cache = true,
size_t max_file_size_for_l0_meta_pin = 0,
Temperature file_temperature = Temperature::kUnknown);
Status GetTableReader(
const ReadOptions& ro, const FileOptions& file_options,
const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, bool sequential_mode,
uint8_t block_protection_bytes_per_key, HistogramImpl* file_read_hist,
std::unique_ptr<TableReader>* table_reader,
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
bool skip_filters = false, int level = -1,
bool prefetch_index_and_filter_in_cache = true,
size_t max_file_size_for_l0_meta_pin = 0,
Temperature file_temperature = Temperature::kUnknown);
// Update the max_covering_tombstone_seq in the GetContext for each key based
// on the range deletions in the table
+6 -5
View File
@@ -17,8 +17,10 @@ namespace ROCKSDB_NAMESPACE {
DEFINE_SYNC_AND_ASYNC(Status, TableCache::MultiGet)
(const ReadOptions& options, const InternalKeyComparator& internal_comparator,
const FileMetaData& file_meta, const MultiGetContext::Range* mget_range,
const MutableCFOptions& mutable_cf_options, HistogramImpl* file_read_hist,
bool skip_filters, bool skip_range_deletions, int level, TypedHandle* handle) {
uint8_t block_protection_bytes_per_key,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
HistogramImpl* file_read_hist, bool skip_filters, bool skip_range_deletions,
int level, TypedHandle* handle) {
auto& fd = file_meta.fd;
Status s;
TableReader* t = fd.table_reader;
@@ -70,7 +72,7 @@ DEFINE_SYNC_AND_ASYNC(Status, TableCache::MultiGet)
if (t == nullptr) {
assert(handle == nullptr);
s = FindTable(options, file_options_, internal_comparator, file_meta,
&handle, mutable_cf_options,
&handle, block_protection_bytes_per_key, prefix_extractor,
options.read_tier == kBlockCacheTier /* no_io */,
file_read_hist, skip_filters, level,
true /* prefetch_index_and_filter_in_cache */,
@@ -86,8 +88,7 @@ DEFINE_SYNC_AND_ASYNC(Status, TableCache::MultiGet)
}
if (s.ok()) {
CO_AWAIT(t->MultiGet)
(options, &table_range, mutable_cf_options.prefix_extractor.get(),
skip_filters);
(options, &table_range, prefix_extractor.get(), skip_filters);
} else if (options.read_tier == kBlockCacheTier && s.IsIncomplete()) {
for (auto iter = table_range.begin(); iter != table_range.end(); ++iter) {
Status* status = iter->s;
+26 -25
View File
@@ -24,7 +24,6 @@
#include <vector>
#include "cache/cache_reservation_manager.h"
#include "db/blob/blob_file_cache.h"
#include "db/blob/blob_file_meta.h"
#include "db/dbformat.h"
#include "db/internal_stats.h"
@@ -745,9 +744,12 @@ class VersionBuilder::Rep {
return Status::Corruption("VersionBuilder", oss.str());
}
auto deleter = [vs = version_set_, ioptions = ioptions_,
bc = cfd_ ? cfd_->blob_file_cache()
: nullptr](SharedBlobFileMetaData* shared_meta) {
// Note: we use C++11 for now but in C++14, this could be done in a more
// elegant way using generalized lambda capture.
VersionSet* const vs = version_set_;
const ImmutableCFOptions* const ioptions = ioptions_;
auto deleter = [vs, ioptions](SharedBlobFileMetaData* shared_meta) {
if (vs) {
assert(ioptions);
assert(!ioptions->cf_paths.empty());
@@ -756,9 +758,6 @@ class VersionBuilder::Rep {
vs->AddObsoleteBlobFile(shared_meta->GetBlobFileNumber(),
ioptions->cf_paths.front().path);
}
if (bc) {
bc->Evict(shared_meta->GetBlobFileNumber());
}
delete shared_meta;
};
@@ -767,7 +766,7 @@ class VersionBuilder::Rep {
blob_file_number, blob_file_addition.GetTotalBlobCount(),
blob_file_addition.GetTotalBlobBytes(),
blob_file_addition.GetChecksumMethod(),
blob_file_addition.GetChecksumValue(), std::move(deleter));
blob_file_addition.GetChecksumValue(), deleter);
mutable_blob_file_metas_.emplace(
blob_file_number, MutableBlobFileMetaData(std::move(shared_meta)));
@@ -1637,12 +1636,12 @@ class VersionBuilder::Rep {
return s;
}
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache,
bool is_initial_load,
const MutableCFOptions& mutable_cf_options,
size_t max_file_size_for_l0_meta_pin,
const ReadOptions& read_options) {
Status LoadTableHandlers(
InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options,
uint8_t block_protection_bytes_per_key) {
assert(table_cache_ != nullptr);
assert(!track_found_and_missing_files_ || valid_version_available_);
@@ -1717,7 +1716,7 @@ class VersionBuilder::Rep {
statuses[file_idx] = table_cache_->FindTable(
read_options, file_options_,
*(base_vstorage_->InternalComparator()), *file_meta, &handle,
mutable_cf_options, false /*no_io */,
block_protection_bytes_per_key, prefix_extractor, false /*no_io */,
internal_stats->GetFileReadHist(level), false, level,
prefetch_index_and_filter_in_cache, max_file_size_for_l0_meta_pin,
file_meta->temperature);
@@ -1778,12 +1777,13 @@ Status VersionBuilder::SaveTo(VersionStorageInfo* vstorage) const {
Status VersionBuilder::LoadTableHandlers(
InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
const MutableCFOptions& mutable_cf_options,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options) {
return rep_->LoadTableHandlers(internal_stats, max_threads,
prefetch_index_and_filter_in_cache,
is_initial_load, mutable_cf_options,
max_file_size_for_l0_meta_pin, read_options);
const std::shared_ptr<const SliceTransform>& prefix_extractor,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options,
uint8_t block_protection_bytes_per_key) {
return rep_->LoadTableHandlers(
internal_stats, max_threads, prefetch_index_and_filter_in_cache,
is_initial_load, prefix_extractor, max_file_size_for_l0_meta_pin,
read_options, block_protection_bytes_per_key);
}
void VersionBuilder::CreateOrReplaceSavePoint() {
@@ -1814,15 +1814,16 @@ Status VersionBuilder::SaveSavePointTo(VersionStorageInfo* vstorage) const {
Status VersionBuilder::LoadSavePointTableHandlers(
InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
const MutableCFOptions& mutable_cf_options,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options) {
const std::shared_ptr<const SliceTransform>& prefix_extractor,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options,
uint8_t block_protection_bytes_per_key) {
if (!savepoint_ || !savepoint_->ValidVersionAvailable()) {
return Status::InvalidArgument();
}
return savepoint_->LoadTableHandlers(
internal_stats, max_threads, prefetch_index_and_filter_in_cache,
is_initial_load, mutable_cf_options, max_file_size_for_l0_meta_pin,
read_options);
is_initial_load, prefix_extractor, max_file_size_for_l0_meta_pin,
read_options, block_protection_bytes_per_key);
}
void VersionBuilder::ClearSavePoint() { savepoint_.reset(nullptr); }
+12 -13
View File
@@ -54,12 +54,12 @@ class VersionBuilder {
Status SaveTo(VersionStorageInfo* vstorage) const;
// Load all the table handlers for the current Version in the builder.
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache,
bool is_initial_load,
const MutableCFOptions& mutable_cf_options,
size_t max_file_size_for_l0_meta_pin,
const ReadOptions& read_options);
Status LoadTableHandlers(
InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options,
uint8_t block_protection_bytes_per_key);
//============APIs only used by VersionEditHandlerPointInTime ============//
@@ -99,13 +99,12 @@ class VersionBuilder {
// Load all the table handlers for the Version in the save point.
// Non-OK status will be returned if there is not a valid save point.
Status LoadSavePointTableHandlers(InternalStats* internal_stats,
int max_threads,
bool prefetch_index_and_filter_in_cache,
bool is_initial_load,
const MutableCFOptions& mutable_cf_options,
size_t max_file_size_for_l0_meta_pin,
const ReadOptions& read_options);
Status LoadSavePointTableHandlers(
InternalStats* internal_stats, int max_threads,
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
size_t max_file_size_for_l0_meta_pin, const ReadOptions& read_options,
uint8_t block_protection_bytes_per_key);
void ClearSavePoint();
-8
View File
@@ -350,14 +350,6 @@ struct FileMetaData {
file_checksum_func_name.size();
return usage;
}
// Returns whether this file is one with just one range tombstone. These type
// of file should always be marked for compaction.
bool FileIsStandAloneRangeTombstone() const {
bool res = num_range_deletions == 1 && num_entries == num_range_deletions;
assert(!res || fd.smallest_seqno == fd.largest_seqno);
return res;
}
};
// A compressed copy of file meta data that just contain minimum data needed
+5 -3
View File
@@ -545,8 +545,9 @@ Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
Status s = builder->LoadTableHandlers(
cfd->internal_stats(),
version_set_->db_options_->max_file_opening_threads,
prefetch_index_and_filter_in_cache, is_initial_load, *moptions,
MaxFileSizeForL0MetaPin(*moptions), read_options_);
prefetch_index_and_filter_in_cache, is_initial_load,
moptions->prefix_extractor, MaxFileSizeForL0MetaPin(*moptions),
read_options_, moptions->block_protection_bytes_per_key);
if ((s.IsPathNotFound() || s.IsCorruption()) && no_error_if_files_missing_) {
s = Status::OK();
}
@@ -869,7 +870,8 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit(
s = builder->LoadSavePointTableHandlers(
cfd->internal_stats(),
version_set_->db_options_->max_file_opening_threads, false, true,
*cf_opts_ptr, MaxFileSizeForL0MetaPin(*cf_opts_ptr), read_options_);
cf_opts_ptr->prefix_extractor, MaxFileSizeForL0MetaPin(*cf_opts_ptr),
read_options_, cf_opts_ptr->block_protection_bytes_per_key);
if (!s.ok()) {
delete version;
if (s.IsCorruption()) {
+76 -72
View File
@@ -964,15 +964,15 @@ namespace {
class LevelIterator final : public InternalIterator {
public:
// NOTE: many of the const& parameters are saved in this object (so
// must outlive this object)
// @param read_options Must outlive this iterator.
LevelIterator(
TableCache* table_cache, const ReadOptions& read_options,
const FileOptions& file_options, const InternalKeyComparator& icomparator,
const LevelFilesBrief* flevel, const MutableCFOptions& mutable_cf_options,
const LevelFilesBrief* flevel,
const std::shared_ptr<const SliceTransform>& prefix_extractor,
bool should_sample, HistogramImpl* file_read_hist,
TableReaderCaller caller, bool skip_filters, int level,
RangeDelAggregator* range_del_agg,
uint8_t block_protection_bytes_per_key, RangeDelAggregator* range_del_agg,
const std::vector<AtomicCompactionUnitBoundary>* compaction_boundaries =
nullptr,
bool allow_unprepared_value = false,
@@ -984,8 +984,7 @@ class LevelIterator final : public InternalIterator {
icomparator_(icomparator),
user_comparator_(icomparator.user_comparator()),
flevel_(flevel),
mutable_cf_options_(mutable_cf_options),
prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
prefix_extractor_(prefix_extractor),
file_read_hist_(file_read_hist),
caller_(caller),
file_index_(flevel_->num_files),
@@ -997,6 +996,7 @@ class LevelIterator final : public InternalIterator {
? read_options.snapshot->GetSequenceNumber()
: kMaxSequenceNumber),
level_(level),
block_protection_bytes_per_key_(block_protection_bytes_per_key),
should_sample_(should_sample),
skip_filters_(skip_filters),
allow_unprepared_value_(allow_unprepared_value),
@@ -1147,12 +1147,12 @@ class LevelIterator final : public InternalIterator {
ClearRangeTombstoneIter();
return table_cache_->NewIterator(
read_options_, file_options_, icomparator_, *file_meta.file_metadata,
range_del_agg_, mutable_cf_options_,
range_del_agg_, prefix_extractor_,
nullptr /* don't need reference to table */, file_read_hist_, caller_,
/*arena=*/nullptr, skip_filters_, level_,
/*max_file_size_for_l0_meta_pin=*/0, smallest_compaction_key,
largest_compaction_key, allow_unprepared_value_, &read_seq_,
range_tombstone_iter_);
largest_compaction_key, allow_unprepared_value_,
block_protection_bytes_per_key_, &read_seq_, range_tombstone_iter_);
}
// Check if current file being fully within iterate_lower_bound.
@@ -1176,8 +1176,10 @@ class LevelIterator final : public InternalIterator {
const UserComparatorWrapper user_comparator_;
const LevelFilesBrief* flevel_;
mutable FileDescriptor current_value_;
const MutableCFOptions& mutable_cf_options_;
const SliceTransform* prefix_extractor_;
// `prefix_extractor_` may be non-null even for total order seek. Checking
// this variable is not the right way to identify whether prefix iterator
// is used.
const std::shared_ptr<const SliceTransform>& prefix_extractor_;
HistogramImpl* file_read_hist_;
TableReaderCaller caller_;
@@ -1211,6 +1213,7 @@ class LevelIterator final : public InternalIterator {
SequenceNumber read_seq_;
int level_;
uint8_t block_protection_bytes_per_key_;
bool should_sample_;
bool skip_filters_;
bool allow_unprepared_value_;
@@ -1577,7 +1580,8 @@ Status Version::GetTableProperties(const ReadOptions& read_options,
auto ioptions = cfd_->ioptions();
Status s = table_cache->GetTableProperties(
file_options_, read_options, cfd_->internal_comparator(), *file_meta, tp,
mutable_cf_options_, true /* no io */);
mutable_cf_options_.block_protection_bytes_per_key,
mutable_cf_options_.prefix_extractor, true /* no io */);
if (s.ok()) {
return s;
}
@@ -1663,7 +1667,8 @@ Status Version::TablesRangeTombstoneSummary(int max_entries_to_print,
Status s = table_cache->GetRangeTombstoneIterator(
read_options, cfd_->internal_comparator(), *file_meta,
mutable_cf_options_, &tombstone_iter);
cfd_->GetLatestMutableCFOptions()->block_protection_bytes_per_key,
&tombstone_iter);
if (!s.ok()) {
return s;
}
@@ -1780,7 +1785,9 @@ size_t Version::GetMemoryUsageByTableReaders(const ReadOptions& read_options) {
for (size_t i = 0; i < file_level.num_files; i++) {
total_usage += cfd_->table_cache()->GetMemoryUsageByTableReader(
file_options_, read_options, cfd_->internal_comparator(),
*file_level.files[i].file_metadata, mutable_cf_options_);
*file_level.files[i].file_metadata,
mutable_cf_options_.block_protection_bytes_per_key,
mutable_cf_options_.prefix_extractor);
}
}
return total_usage;
@@ -1929,9 +1936,10 @@ InternalIterator* Version::TEST_GetLevelIterator(
auto level_iter = new (mem) LevelIterator(
cfd_->table_cache(), read_options, file_options_,
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
mutable_cf_options_, should_sample_file_read(),
mutable_cf_options_.prefix_extractor, should_sample_file_read(),
cfd_->internal_stats()->GetFileReadHist(level),
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
mutable_cf_options_.block_protection_bytes_per_key,
nullptr /* range_del_agg */, nullptr /* compaction_boundaries */,
allow_unprepared_value, &tombstone_iter_ptr);
if (read_options.ignore_range_deletions) {
@@ -1965,16 +1973,9 @@ uint64_t VersionStorageInfo::GetEstimatedActiveKeys() const {
}
if (current_num_samples_ < file_count) {
assert(current_num_samples_ != 0);
assert(est != 0);
double multiplier = static_cast<double>(file_count) / current_num_samples_;
double maximum_multiplier =
static_cast<double>(std::numeric_limits<uint64_t>::max()) / est;
// If it can overflow, we return the maximum unsigned long.
if (multiplier >= maximum_multiplier) {
return std::numeric_limits<uint64_t>::max();
}
return static_cast<uint64_t>(est * multiplier);
// casting to avoid overflowing
return static_cast<uint64_t>(
(est * static_cast<double>(file_count) / current_num_samples_));
} else {
return est;
}
@@ -2036,12 +2037,14 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
const auto& file = storage_info_.LevelFilesBrief(0).files[i];
auto table_iter = cfd_->table_cache()->NewIterator(
read_options, soptions, cfd_->internal_comparator(),
*file.file_metadata, /*range_del_agg=*/nullptr, mutable_cf_options_,
nullptr, cfd_->internal_stats()->GetFileReadHist(0),
*file.file_metadata, /*range_del_agg=*/nullptr,
mutable_cf_options_.prefix_extractor, nullptr,
cfd_->internal_stats()->GetFileReadHist(0),
TableReaderCaller::kUserIterator, arena,
/*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr, allow_unprepared_value,
mutable_cf_options_.block_protection_bytes_per_key,
/*range_del_read_seqno=*/nullptr, &tombstone_iter);
if (read_options.ignore_range_deletions) {
merge_iter_builder->AddIterator(table_iter);
@@ -2068,9 +2071,10 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
auto level_iter = new (mem) LevelIterator(
cfd_->table_cache(), read_options, soptions,
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
mutable_cf_options_, should_sample_file_read(),
mutable_cf_options_.prefix_extractor, should_sample_file_read(),
cfd_->internal_stats()->GetFileReadHist(level),
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
mutable_cf_options_.block_protection_bytes_per_key,
/*range_del_agg=*/nullptr,
/*compaction_boundaries=*/nullptr, allow_unprepared_value,
&tombstone_iter_ptr);
@@ -2109,13 +2113,15 @@ Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
}
ScopedArenaPtr<InternalIterator> iter(cfd_->table_cache()->NewIterator(
read_options, file_options, cfd_->internal_comparator(),
*file->file_metadata, &range_del_agg, mutable_cf_options_, nullptr,
*file->file_metadata, &range_del_agg,
mutable_cf_options_.prefix_extractor, nullptr,
cfd_->internal_stats()->GetFileReadHist(0),
TableReaderCaller::kUserIterator, &arena,
/*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr,
/*allow_unprepared_value=*/false));
/*allow_unprepared_value=*/false,
mutable_cf_options_.block_protection_bytes_per_key));
status = OverlapWithIterator(ucmp, smallest_user_key, largest_user_key,
iter.get(), overlap);
if (!status.ok() || *overlap) {
@@ -2127,10 +2133,11 @@ Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
ScopedArenaPtr<InternalIterator> iter(new (mem) LevelIterator(
cfd_->table_cache(), read_options, file_options,
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
mutable_cf_options_, should_sample_file_read(),
mutable_cf_options_.prefix_extractor, should_sample_file_read(),
cfd_->internal_stats()->GetFileReadHist(level),
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
&range_del_agg, nullptr, false));
mutable_cf_options_.block_protection_bytes_per_key, &range_del_agg,
nullptr, false));
status = OverlapWithIterator(ucmp, smallest_user_key, largest_user_key,
iter.get(), overlap);
}
@@ -2443,7 +2450,8 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
StopWatchNano timer(clock_, timer_enabled /* auto_start */);
*status = table_cache_->Get(
read_options, *internal_comparator(), *f->file_metadata, ikey,
&get_context, mutable_cf_options_,
&get_context, mutable_cf_options_.block_protection_bytes_per_key,
mutable_cf_options_.prefix_extractor,
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
fp.IsHitFileLastInLevel()),
@@ -2678,9 +2686,10 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
if (!skip_filters) {
Status status = table_cache_->MultiGetFilter(
read_options, *internal_comparator(), *f->file_metadata,
mutable_cf_options_,
mutable_cf_options_.prefix_extractor,
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
fp.GetHitFileLevel(), &file_range, &table_handle);
fp.GetHitFileLevel(), &file_range, &table_handle,
mutable_cf_options_.block_protection_bytes_per_key);
skip_range_deletions = true;
if (status.ok()) {
skip_filters = true;
@@ -2864,9 +2873,10 @@ Status Version::ProcessBatch(
if (!skip_filters) {
Status status = table_cache_->MultiGetFilter(
read_options, *internal_comparator(), *f->file_metadata,
mutable_cf_options_,
mutable_cf_options_.prefix_extractor,
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
fp.GetHitFileLevel(), &file_range, &table_handle);
fp.GetHitFileLevel(), &file_range, &table_handle,
mutable_cf_options_.block_protection_bytes_per_key);
if (status.ok()) {
skip_filters = true;
skip_range_deletions = true;
@@ -3630,7 +3640,6 @@ void VersionStorageInfo::ComputeCompactionScore(
void VersionStorageInfo::ComputeFilesMarkedForCompaction(int last_level) {
files_marked_for_compaction_.clear();
int last_qualify_level = 0;
standalone_range_tombstone_files_mark_threshold_ = kMaxSequenceNumber;
// Do not include files from the last level with data
// If table properties collector suggests a file on the last level,
@@ -3646,11 +3655,6 @@ void VersionStorageInfo::ComputeFilesMarkedForCompaction(int last_level) {
for (auto* f : files_[level]) {
if (!f->being_compacted && f->marked_for_compaction) {
files_marked_for_compaction_.emplace_back(level, f);
if (f->FileIsStandAloneRangeTombstone()) {
standalone_range_tombstone_files_mark_threshold_ =
std::min(standalone_range_tombstone_files_mark_threshold_,
f->fd.smallest_seqno);
}
}
}
}
@@ -5534,8 +5538,10 @@ Status VersionSet::ProcessManifestWrites(
s = builder_guards[i]->version_builder()->LoadTableHandlers(
cfd->internal_stats(), 1 /* max_threads */,
true /* prefetch_index_and_filter_in_cache */,
false /* is_initial_load */, *mutable_cf_options_ptrs[i],
MaxFileSizeForL0MetaPin(*mutable_cf_options_ptrs[i]), read_options);
false /* is_initial_load */,
mutable_cf_options_ptrs[i]->prefix_extractor,
MaxFileSizeForL0MetaPin(*mutable_cf_options_ptrs[i]), read_options,
mutable_cf_options_ptrs[i]->block_protection_bytes_per_key);
if (!s.ok()) {
if (db_options_->paranoid_checks) {
break;
@@ -6014,19 +6020,15 @@ Status VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
}
Status VersionSet::GetCurrentManifestPath(const std::string& dbname,
FileSystem* fs, bool is_retry,
FileSystem* fs,
std::string* manifest_path,
uint64_t* manifest_file_number) {
assert(fs != nullptr);
assert(manifest_path != nullptr);
assert(manifest_file_number != nullptr);
IOOptions opts;
std::string fname;
if (is_retry) {
opts.verify_and_reconstruct_read = true;
}
Status s = ReadFileToString(fs, CurrentFileName(dbname), opts, &fname);
Status s = ReadFileToString(fs, CurrentFileName(dbname), &fname);
if (!s.ok()) {
return s;
}
@@ -6056,8 +6058,8 @@ Status VersionSet::Recover(
// Read "CURRENT" file, which contains a pointer to the current manifest
// file
std::string manifest_path;
Status s = GetCurrentManifestPath(dbname_, fs_.get(), is_retry,
&manifest_path, &manifest_file_number_);
Status s = GetCurrentManifestPath(dbname_, fs_.get(), &manifest_path,
&manifest_file_number_);
if (!s.ok()) {
return s;
}
@@ -6302,8 +6304,8 @@ Status VersionSet::ListColumnFamilies(std::vector<std::string>* column_families,
// Read "CURRENT" file, which contains a pointer to the current manifest file
std::string manifest_path;
uint64_t manifest_file_number;
Status s = GetCurrentManifestPath(dbname, fs, /*is_retry=*/false,
&manifest_path, &manifest_file_number);
Status s =
GetCurrentManifestPath(dbname, fs, &manifest_path, &manifest_file_number);
if (!s.ok()) {
return s;
}
@@ -6926,7 +6928,8 @@ uint64_t VersionSet::ApproximateOffsetOf(const ReadOptions& read_options,
const MutableCFOptions& cf_opts = v->GetMutableCFOptions();
if (table_cache != nullptr) {
result = table_cache->ApproximateOffsetOf(
read_options, key, *f.file_metadata, caller, icmp, cf_opts);
read_options, key, *f.file_metadata, caller, icmp,
cf_opts.block_protection_bytes_per_key, cf_opts.prefix_extractor);
}
}
return result;
@@ -6967,8 +6970,9 @@ uint64_t VersionSet::ApproximateSize(const ReadOptions& read_options,
return 0;
}
const MutableCFOptions& cf_opts = v->GetMutableCFOptions();
return table_cache->ApproximateSize(read_options, start, end,
*f.file_metadata, caller, icmp, cf_opts);
return table_cache->ApproximateSize(
read_options, start, end, *f.file_metadata, caller, icmp,
cf_opts.block_protection_bytes_per_key, cf_opts.prefix_extractor);
}
void VersionSet::RemoveLiveFiles(
@@ -7094,12 +7098,10 @@ InternalIterator* VersionSet::MakeInputIterator(
std::unique_ptr<TruncatedRangeDelIterator>**>>
range_tombstones;
size_t num = 0;
[[maybe_unused]] size_t num_input_files = 0;
for (size_t which = 0; which < c->num_input_levels(); which++) {
const LevelFilesBrief* flevel = c->input_levels(which);
num_input_files += flevel->num_files;
if (flevel->num_files != 0) {
if (c->input_levels(which)->num_files != 0) {
if (c->level(which) == 0) {
const LevelFilesBrief* flevel = c->input_levels(which);
for (size_t i = 0; i < flevel->num_files; i++) {
const FileMetaData& fmd = *flevel->files[i].file_metadata;
if (start.has_value() &&
@@ -7120,7 +7122,7 @@ InternalIterator* VersionSet::MakeInputIterator(
list[num++] = cfd->table_cache()->NewIterator(
read_options, file_options_compactions,
cfd->internal_comparator(), fmd, range_del_agg,
*c->mutable_cf_options(),
c->mutable_cf_options()->prefix_extractor,
/*table_reader_ptr=*/nullptr,
/*file_read_hist=*/nullptr, TableReaderCaller::kCompaction,
/*arena=*/nullptr,
@@ -7130,6 +7132,7 @@ InternalIterator* VersionSet::MakeInputIterator(
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr,
/*allow_unprepared_value=*/false,
c->mutable_cf_options()->block_protection_bytes_per_key,
/*range_del_read_seqno=*/nullptr,
/*range_del_iter=*/&range_tombstone_iter);
range_tombstones.emplace_back(std::move(range_tombstone_iter),
@@ -7141,19 +7144,18 @@ InternalIterator* VersionSet::MakeInputIterator(
nullptr;
list[num++] = new LevelIterator(
cfd->table_cache(), read_options, file_options_compactions,
cfd->internal_comparator(), flevel, *c->mutable_cf_options(),
cfd->internal_comparator(), c->input_levels(which),
c->mutable_cf_options()->prefix_extractor,
/*should_sample=*/false,
/*no per level latency histogram=*/nullptr,
TableReaderCaller::kCompaction, /*skip_filters=*/false,
/*level=*/static_cast<int>(c->level(which)), range_del_agg,
c->boundaries(which), false, &tombstone_iter_ptr);
/*level=*/static_cast<int>(c->level(which)),
c->mutable_cf_options()->block_protection_bytes_per_key,
range_del_agg, c->boundaries(which), false, &tombstone_iter_ptr);
range_tombstones.emplace_back(nullptr, tombstone_iter_ptr);
}
}
}
TEST_SYNC_POINT_CALLBACK(
"VersionSet::MakeInputIterator:NewCompactionMergingIterator",
&num_input_files);
assert(num <= space);
InternalIterator* result = NewCompactionMergingIterator(
&c->column_family_data()->internal_comparator(), list,
@@ -7394,6 +7396,7 @@ Status VersionSet::VerifyFileMetadata(const ReadOptions& read_options,
const MutableCFOptions* const cf_opts = cfd->GetLatestMutableCFOptions();
assert(cf_opts);
std::shared_ptr<const SliceTransform> pe = cf_opts->prefix_extractor;
size_t max_sz_for_l0_meta_pin = MaxFileSizeForL0MetaPin(*cf_opts);
const FileOptions& file_opts = file_options();
@@ -7409,7 +7412,8 @@ Status VersionSet::VerifyFileMetadata(const ReadOptions& read_options,
TableCache::TypedHandle* handle = nullptr;
FileMetaData meta_copy = meta;
status = table_cache->FindTable(
read_options, file_opts, *icmp, meta_copy, &handle, *cf_opts,
read_options, file_opts, *icmp, meta_copy, &handle,
cf_opts->block_protection_bytes_per_key, pe,
/*no_io=*/false, internal_stats->GetFileReadHist(level), false, level,
/*prefetch_index_and_filter_in_cache*/ false, max_sz_for_l0_meta_pin,
meta_copy.temperature);
@@ -7501,8 +7505,8 @@ Status ReactiveVersionSet::MaybeSwitchManifest(
assert(manifest_reader != nullptr);
Status s;
std::string manifest_path;
s = GetCurrentManifestPath(dbname_, fs_.get(), /*is_retry=*/false,
&manifest_path, &manifest_file_number_);
s = GetCurrentManifestPath(dbname_, fs_.get(), &manifest_path,
&manifest_file_number_);
if (!s.ok()) {
return s;
}
+2 -13
View File
@@ -616,10 +616,6 @@ class VersionStorageInfo {
return bottommost_files_mark_threshold_;
}
SequenceNumber standalone_range_tombstone_files_mark_threshold() const {
return standalone_range_tombstone_files_mark_threshold_;
}
// Returns whether any key in [`smallest_key`, `largest_key`] could appear in
// an older L0 file than `last_l0_idx` or in a greater level than `last_level`
//
@@ -632,8 +628,6 @@ class VersionStorageInfo {
Env::WriteLifeTimeHint CalculateSSTWriteHint(int level) const;
const Comparator* user_comparator() const { return user_comparator_; }
private:
void ComputeCompensatedSizes();
void UpdateNumNonEmptyLevels();
@@ -738,12 +732,6 @@ class VersionStorageInfo {
// seqnums of unmarked bottommost files.
SequenceNumber bottommost_files_mark_threshold_ = kMaxSequenceNumber;
// The minimum sequence number among all the standalone range tombstone files
// that are marked for compaction. A standalone range tombstone file is one
// with just one range tombstone.
SequenceNumber standalone_range_tombstone_files_mark_threshold_ =
kMaxSequenceNumber;
// Monotonically increases as we release old snapshots. Zero indicates no
// snapshots have been released yet. When no snapshots remain we set it to the
// current seqnum, which needs to be protected as a snapshot can still be
@@ -1278,7 +1266,7 @@ class VersionSet {
{});
static Status GetCurrentManifestPath(const std::string& dbname,
FileSystem* fs, bool is_retry,
FileSystem* fs,
std::string* manifest_filename,
uint64_t* manifest_file_number);
void WakeUpWaitingManifestWriters();
@@ -1526,6 +1514,7 @@ class VersionSet {
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata);
void AddObsoleteBlobFile(uint64_t blob_file_number, std::string path) {
// TODO: Erase file from BlobFileCache?
obsolete_blob_files_.emplace_back(blob_file_number, std::move(path));
}
+2 -1
View File
@@ -25,7 +25,8 @@ DEFINE_SYNC_AND_ASYNC(Status, Version::MultiGetFromSST)
StopWatchNano timer(clock_, timer_enabled /* auto_start */);
s = CO_AWAIT(table_cache_->MultiGet)(
read_options, *internal_comparator(), *f->file_metadata, &file_range,
mutable_cf_options_,
mutable_cf_options_.block_protection_bytes_per_key,
mutable_cf_options_.prefix_extractor,
cfd_->internal_stats()->GetFileReadHist(hit_file_level), skip_filters,
skip_range_deletions, hit_file_level, table_handle);
// TODO: examine the behavior for corrupted key
+5 -10
View File
@@ -1182,9 +1182,6 @@ class VersionSetTestBase {
immutable_options_.fs = fs_;
immutable_options_.clock = env_->GetSystemClock().get();
cf_options_.table_factory = table_factory_;
mutable_cf_options_.table_factory = table_factory_;
versions_.reset(new VersionSet(
dbname_, &db_options_, env_options_, table_cache_.get(),
&write_buffer_manager_, &write_controller_,
@@ -1223,7 +1220,7 @@ class VersionSetTestBase {
tmp_db_options.env = env_;
std::unique_ptr<DBImpl> impl(new DBImpl(tmp_db_options, dbname_));
std::string db_id;
ASSERT_OK(impl->GetDbIdentityFromIdentityFile(IOOptions(), &db_id));
ASSERT_OK(impl->GetDbIdentityFromIdentityFile(&db_id));
new_db.SetDBId(db_id);
}
new_db.SetLogNumber(0);
@@ -1391,8 +1388,7 @@ class VersionSetTestBase {
assert(manifest_path != nullptr);
uint64_t manifest_file_number = 0;
Status s = versions_->GetCurrentManifestPath(
dbname_, fs_.get(), /*is_retry=*/false, manifest_path,
&manifest_file_number);
dbname_, fs_.get(), manifest_path, &manifest_file_number);
ASSERT_OK(s);
}
@@ -1400,8 +1396,7 @@ class VersionSetTestBase {
assert(manifest_path != nullptr);
uint64_t manifest_file_number = 0;
Status s = versions_->GetCurrentManifestPath(
dbname_, fs_.get(), /*is_retry=*/false, manifest_path,
&manifest_file_number);
dbname_, fs_.get(), manifest_path, &manifest_file_number);
ASSERT_OK(s);
ASSERT_EQ(1, manifest_file_number);
}
@@ -3517,7 +3512,7 @@ class VersionSetTestEmptyDb
tmp_db_options.env = env_;
std::unique_ptr<DBImpl> impl(new DBImpl(tmp_db_options, dbname_));
std::string db_id;
ASSERT_OK(impl->GetDbIdentityFromIdentityFile(IOOptions(), &db_id));
ASSERT_OK(impl->GetDbIdentityFromIdentityFile(&db_id));
new_db.SetDBId(db_id);
}
const std::string manifest_path = DescriptorFileName(dbname_, 1);
@@ -3841,7 +3836,7 @@ class VersionSetTestMissingFiles : public VersionSetTestBase,
tmp_db_options.env = env_;
std::unique_ptr<DBImpl> impl(new DBImpl(tmp_db_options, dbname_));
std::string db_id;
ASSERT_OK(impl->GetDbIdentityFromIdentityFile(IOOptions(), &db_id));
ASSERT_OK(impl->GetDbIdentityFromIdentityFile(&db_id));
new_db.SetDBId(db_id);
}
{
+12 -38
View File
@@ -502,9 +502,7 @@ Status ReadRecordFromWriteBatch(Slice* input, char* tag,
break;
}
default:
return Status::Corruption(
"unknown WriteBatch tag",
std::to_string(static_cast<unsigned int>(*tag)));
return Status::Corruption("unknown WriteBatch tag");
}
return Status::OK();
}
@@ -752,9 +750,7 @@ Status WriteBatchInternal::Iterate(const WriteBatch* wb,
}
break;
default:
return Status::Corruption(
"unknown WriteBatch tag",
std::to_string(static_cast<unsigned int>(tag)));
return Status::Corruption("unknown WriteBatch tag");
}
}
if (!s.ok()) {
@@ -1160,34 +1156,6 @@ Status WriteBatchInternal::InsertNoop(WriteBatch* b) {
return Status::OK();
}
ValueType WriteBatchInternal::GetBeginPrepareType(bool write_after_commit,
bool unprepared_batch) {
return write_after_commit
? kTypeBeginPrepareXID
: (unprepared_batch ? kTypeBeginUnprepareXID
: kTypeBeginPersistedPrepareXID);
}
Status WriteBatchInternal::InsertBeginPrepare(WriteBatch* b,
bool write_after_commit,
bool unprepared_batch) {
b->rep_.push_back(static_cast<char>(
GetBeginPrepareType(write_after_commit, unprepared_batch)));
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
ContentFlags::HAS_BEGIN_PREPARE,
std::memory_order_relaxed);
return Status::OK();
}
Status WriteBatchInternal::InsertEndPrepare(WriteBatch* b, const Slice& xid) {
b->rep_.push_back(static_cast<char>(kTypeEndPrepareXID));
PutLengthPrefixedSlice(&b->rep_, xid);
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
ContentFlags::HAS_END_PREPARE,
std::memory_order_relaxed);
return Status::OK();
}
Status WriteBatchInternal::MarkEndPrepare(WriteBatch* b, const Slice& xid,
bool write_after_commit,
bool unprepared_batch) {
@@ -1203,8 +1171,13 @@ Status WriteBatchInternal::MarkEndPrepare(WriteBatch* b, const Slice& xid,
// rewrite noop as begin marker
b->rep_[12] = static_cast<char>(
GetBeginPrepareType(write_after_commit, unprepared_batch));
write_after_commit ? kTypeBeginPrepareXID
: (unprepared_batch ? kTypeBeginUnprepareXID
: kTypeBeginPersistedPrepareXID));
b->rep_.push_back(static_cast<char>(kTypeEndPrepareXID));
PutLengthPrefixedSlice(&b->rep_, xid);
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
ContentFlags::HAS_END_PREPARE |
ContentFlags::HAS_BEGIN_PREPARE,
std::memory_order_relaxed);
if (unprepared_batch) {
@@ -1212,7 +1185,7 @@ Status WriteBatchInternal::MarkEndPrepare(WriteBatch* b, const Slice& xid,
ContentFlags::HAS_BEGIN_UNPREPARE,
std::memory_order_relaxed);
}
return WriteBatchInternal::InsertEndPrepare(b, xid);
return Status::OK();
}
Status WriteBatchInternal::MarkCommit(WriteBatch* b, const Slice& xid) {
@@ -2647,8 +2620,9 @@ class MemTableInserter : public WriteBatch::Handler {
// TODO(ajkr): refactor `SeekToColumnFamily()` so it returns a `Status`.
ret_status.PermitUncheckedError();
return Status::NotSupported(
std::string("CF " + cfd->GetName() +
" reports it does not support DeleteRange"));
std::string("DeleteRange not supported for table type ") +
cfd->ioptions()->table_factory->Name() + " in CF " +
cfd->GetName());
}
int cmp =
cfd->user_comparator()->CompareWithoutTimestamp(begin_key, end_key);
-9
View File
@@ -122,15 +122,6 @@ class WriteBatchInternal {
static Status PutBlobIndex(WriteBatch* batch, uint32_t column_family_id,
const Slice& key, const Slice& value);
static ValueType GetBeginPrepareType(bool write_after_commit,
bool unprepared_batch);
static Status InsertBeginPrepare(WriteBatch* batch,
const bool write_after_commit = true,
bool unprepared_batch = false);
static Status InsertEndPrepare(WriteBatch* batch, const Slice& xid);
static Status MarkEndPrepare(WriteBatch* batch, const Slice& xid,
const bool write_after_commit = true,
const bool unprepared_batch = false);
+2 -3
View File
@@ -1031,9 +1031,8 @@ DEFINE_int32(continuous_verification_interval, 1000,
"disables continuous verification.");
DEFINE_int32(approximate_size_one_in, 64,
"If non-zero, DB::GetApproximateSizes() and "
"DB::GetApproximateMemTableStats() will be called against "
"random key ranges.");
"If non-zero, DB::GetApproximateSizes() will be called against"
" random key ranges.");
DEFINE_int32(read_fault_one_in, 1000,
"On non-zero, enables fault injection on read");
+17 -39
View File
@@ -274,8 +274,6 @@ bool StressTest::BuildOptionsTable() {
return true;
}
bool keepRibbonFilterPolicyOnly = FLAGS_bloom_before_level != INT_MAX;
std::unordered_map<std::string, std::vector<std::string>> options_tbl = {
{"write_buffer_size",
{std::to_string(options_.write_buffer_size),
@@ -341,17 +339,6 @@ bool StressTest::BuildOptionsTable() {
"2",
}},
{"max_sequential_skip_in_iterations", {"4", "8", "12"}},
{"block_based_table_factory",
{
keepRibbonFilterPolicyOnly ? "{filter_policy=ribbonfilter:2.35}"
: "{filter_policy=bloomfilter:2.34}",
"{filter_policy=ribbonfilter:5.67:-1}",
keepRibbonFilterPolicyOnly ? "{filter_policy=ribbonfilter:8.9:3}"
: "{filter_policy=nullptr}",
"{block_size=" + std::to_string(FLAGS_block_size) + "}",
"{block_size=" +
std::to_string(FLAGS_block_size + (FLAGS_seed & 0xFFFU)) + "}",
}},
};
if (FLAGS_compaction_style == kCompactionStyleUniversal &&
FLAGS_universal_max_read_amp > 0) {
@@ -406,7 +393,7 @@ bool StressTest::BuildOptionsTable() {
std::vector<std::string>{"kDisable", "kFlushOnly"});
}
if (keepRibbonFilterPolicyOnly) {
if (FLAGS_bloom_before_level != INT_MAX) {
// Can modify RibbonFilterPolicy field
options_tbl.emplace("table_factory.filter_policy.bloom_before_level",
std::vector<std::string>{"-1", "0", "1", "2",
@@ -2440,31 +2427,22 @@ Status StressTest::TestApproximateSize(
std::string key1_str = Key(key1);
std::string key2_str = Key(key2);
Range range{Slice(key1_str), Slice(key2_str)};
if (thread->rand.OneIn(3)) {
// Call GetApproximateMemTableStats instead
uint64_t count, size;
db_->GetApproximateMemTableStats(column_families_[rand_column_families[0]],
range, &count, &size);
return Status::OK();
} else {
// Call GetApproximateSizes
SizeApproximationOptions sao;
sao.include_memtables = thread->rand.OneIn(2);
if (sao.include_memtables) {
sao.include_files = thread->rand.OneIn(2);
}
if (thread->rand.OneIn(2)) {
if (thread->rand.OneIn(2)) {
sao.files_size_error_margin = 0.0;
} else {
sao.files_size_error_margin =
static_cast<double>(thread->rand.Uniform(3));
}
}
uint64_t result;
return db_->GetApproximateSizes(
sao, column_families_[rand_column_families[0]], &range, 1, &result);
SizeApproximationOptions sao;
sao.include_memtables = thread->rand.OneIn(2);
if (sao.include_memtables) {
sao.include_files = thread->rand.OneIn(2);
}
if (thread->rand.OneIn(2)) {
if (thread->rand.OneIn(2)) {
sao.files_size_error_margin = 0.0;
} else {
sao.files_size_error_margin =
static_cast<double>(thread->rand.Uniform(3));
}
}
uint64_t result;
return db_->GetApproximateSizes(
sao, column_families_[rand_column_families[0]], &range, 1, &result);
}
Status StressTest::TestCheckpoint(ThreadState* thread,
@@ -3696,7 +3674,7 @@ void StressTest::Reopen(ThreadState* thread) {
// crash-recovery verification does. Therefore it always expects no data loss
// and we should ensure no data loss in testing.
// TODO(hx235): eliminate the FlushWAL(true /* sync */)/SyncWAL() below
if (!FLAGS_disable_wal) {
if (!FLAGS_disable_wal && FLAGS_avoid_flush_during_shutdown) {
Status s;
if (FLAGS_manual_wal_flush_one_in > 0) {
s = db_->FlushWAL(/*sync=*/true);
Loaded 100 of 180 files, more files were not shown because too many files have changed in this diff. Show more