mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c007dd1da6 | ||
|
|
0d79386197 | ||
|
|
621dbd54fa | ||
|
|
03cfaf3931 | ||
|
|
1061f74b7d | ||
|
|
6a83adcea8 | ||
|
|
ab42881ce7 | ||
|
|
27c544fb29 | ||
|
|
11a61ca109 | ||
|
|
462388a434 |
No files matched your search
@@ -182,7 +182,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported built-in compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux No Test Runs ======================= #
|
||||
build-linux-release:
|
||||
|
||||
+21
@@ -1,6 +1,27 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 10.5.1 (08/04/2025)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in remote compaction that may mistakenly delete live SST file(s) during the cleanup phase when no keys survive the compaction (all expired)
|
||||
|
||||
## 10.5.0 (07/21/2025)
|
||||
### Public API Changes
|
||||
* DB option skip_checking_sst_file_sizes_on_db_open is deprecated, in favor of validating file size in parallel in a thread pool, when db is opened. When DB is opened, with paranoid check enabled, a file with the wrong size would fail the DB open. With paranoid check disabled, the DB open would succeed, the column family with the corrupted file would not be read or write, while the other healthy column families could be read and write normally. When max_open_files option is not set to -1, only a subset of the files will be opened and checked. The rest of the files will be opened and checked when they are accessed.
|
||||
* GetTtl() API is now available in TTL DB
|
||||
|
||||
### Behavior Changes
|
||||
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occured.
|
||||
* A new API GetFileSize is added to FSRandomAccessFile interface class. It uses fstat vs stat on the posix implementation which is more efficient. Caller could use it to get file size faster. This function might be required in the future for FileSystem implementation outside of the RocksDB code base.
|
||||
* RocksDB now triggers eligible compactions every 12 hours when periodic compaction is configured. This solves a limitation of the compaction trigger mechanism, which would only trigger compaction after specific events like flush, compaction, or SetOptions.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in BackupEngine that can crash backup due to a null FSWritableFile passed to WritableFileWriter.
|
||||
* Fix DB::NewMultiScan iterator to respect the scan upper bound specified in ScanOptions
|
||||
|
||||
### Performance Improvements
|
||||
* Optimized MultiScan using BlockBasedTable to coalesce I/Os and prefetch all data blocks.
|
||||
|
||||
## 10.4.0 (06/20/2025)
|
||||
### New Features
|
||||
* Add a new CF option `memtable_avg_op_scan_flush_trigger` that supports triggering memtable flush when an iterator scans through an expensive range of keys, with the average number of skipped keys from the active memtable exceeding the threshold.
|
||||
|
||||
@@ -1736,14 +1736,11 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
if (s.ok()) {
|
||||
tp = outputs.GetTableProperties();
|
||||
}
|
||||
|
||||
if (s.ok() && current_entries == 0 && tp.num_range_deletions == 0) {
|
||||
// If there is nothing to output, no necessary to generate a sst file.
|
||||
// This happens when the output level is bottom level, at the same time
|
||||
// the sub_compact output nothing.
|
||||
std::string fname =
|
||||
TableFileName(sub_compact->compaction->immutable_options().cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
std::string fname = GetTableFileName(meta->fd.GetNumber());
|
||||
|
||||
// TODO(AR) it is not clear if there are any larger implications if
|
||||
// DeleteFile fails here
|
||||
@@ -1942,6 +1939,10 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
|
||||
// no need to lock because VersionSet::next_file_number_ is atomic
|
||||
uint64_t file_number = versions_->NewFileNumber();
|
||||
#ifndef NDEBUG
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::OpenCompactionOutputFile::NewFileNumber", &file_number);
|
||||
#endif
|
||||
std::string fname = GetTableFileName(file_number);
|
||||
// Fire events.
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
|
||||
@@ -337,7 +337,9 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files, int output_level,
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, uint32_t output_path_id) {
|
||||
const MutableDBOptions& mutable_db_options, uint32_t output_path_id,
|
||||
std::optional<SequenceNumber> earliest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker) {
|
||||
#ifndef NDEBUG
|
||||
assert(input_files.size());
|
||||
// This compaction output should not overlap with a running compaction as
|
||||
@@ -380,8 +382,8 @@ 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, CompactionReason::kManualCompaction);
|
||||
/* grandparents */ {}, earliest_snapshot, snapshot_checker,
|
||||
CompactionReason::kManualCompaction);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -117,12 +117,17 @@ class CompactionPicker {
|
||||
// Caller must provide a set of input files that has been passed through
|
||||
// `SanitizeAndConvertCompactionInputFiles` earlier. The lock should not be
|
||||
// released between that call and this one.
|
||||
Compaction* CompactFiles(const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files,
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
uint32_t output_path_id);
|
||||
//
|
||||
// TODO - Remove default values for earliest_snapshot and snapshot_checker
|
||||
// and require all callers to pass them in so that DB::CompactFiles() can
|
||||
// also benefit from Standalone Range Tombstone Optimization
|
||||
Compaction* CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files, int output_level,
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, uint32_t output_path_id,
|
||||
std::optional<SequenceNumber> earliest_snapshot = std::nullopt,
|
||||
const SnapshotChecker* snapshot_checker = nullptr);
|
||||
|
||||
// Converts a set of compaction input file numbers into
|
||||
// a list of CompactionInputFiles.
|
||||
|
||||
@@ -461,6 +461,97 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
ASSERT_EQ(handles_[1]->GetName(), info.cf_name);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, StandaloneDeleteRangeTombstoneOptimization) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
size_t num_files_after_filtered = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::MakeInputIterator:NewCompactionMergingIterator",
|
||||
[&](void* arg) {
|
||||
num_files_after_filtered = *static_cast<size_t*>(arg);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> files;
|
||||
{
|
||||
// Writes first version of data in range partitioned files.
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::string file1 = dbname_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
ASSERT_OK(sst_file_writer.Put("a", "a1"));
|
||||
ASSERT_OK(sst_file_writer.Put("b", "b1"));
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
files.push_back(std::move(file1));
|
||||
|
||||
std::string file2 = dbname_ + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
ASSERT_OK(sst_file_writer.Put("x", "x1"));
|
||||
ASSERT_OK(sst_file_writer.Put("y", "y1"));
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
files.push_back(std::move(file2));
|
||||
}
|
||||
|
||||
IngestExternalFileOptions ifo;
|
||||
ASSERT_OK(db_->IngestExternalFile(files, ifo));
|
||||
ASSERT_EQ(Get("a"), "a1");
|
||||
ASSERT_EQ(Get("b"), "b1");
|
||||
ASSERT_EQ(Get("x"), "x1");
|
||||
ASSERT_EQ(Get("y"), "y1");
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(6));
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
{
|
||||
// Atomically delete old version of data with one range delete file.
|
||||
// And a new batch of range partitioned files with new version of data.
|
||||
files.clear();
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::string file2 = dbname_ + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
ASSERT_OK(sst_file_writer.DeleteRange("a", "z"));
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
files.push_back(std::move(file2));
|
||||
|
||||
std::string file3 = dbname_ + "file3.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file3));
|
||||
ASSERT_OK(sst_file_writer.Put("a", "a2"));
|
||||
ASSERT_OK(sst_file_writer.Put("b", "b2"));
|
||||
ExternalSstFileInfo file3_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file3_info));
|
||||
files.push_back(std::move(file3));
|
||||
|
||||
std::string file4 = dbname_ + "file4.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file4));
|
||||
ASSERT_OK(sst_file_writer.Put("x", "x2"));
|
||||
ASSERT_OK(sst_file_writer.Put("y", "y2"));
|
||||
ExternalSstFileInfo file4_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file4_info));
|
||||
files.push_back(std::move(file4));
|
||||
}
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile(files, ifo));
|
||||
ASSERT_OK(db_->WaitForCompact(WaitForCompactOptions()));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
|
||||
ASSERT_EQ(num_files_after_filtered, 1);
|
||||
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CompactionOutputFileIOError) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -793,6 +884,79 @@ TEST_F(CompactionServiceTest, VerifyInputRecordCount) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, EmptyResult) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
// Delete range to cover entire range
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "key", "keyz"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// In this unit test, both remote compaction and primary db instance are
|
||||
// running in the same process, so NewFileNumber will never have a collision.
|
||||
// In the real-world remote compactions, when the compaction is indeed running
|
||||
// in another process, this is not going to be the case.
|
||||
// To simulate the SST file with the same name created in the tmp directory,
|
||||
// override the file number in remote compaction to re-use old SST file
|
||||
// number.
|
||||
bool need_to_override_file_number = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
|
||||
[&](void*) { need_to_override_file_number = true; });
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile::NewFileNumber",
|
||||
[&](void* file_number) {
|
||||
if (need_to_override_file_number) {
|
||||
auto n = static_cast<uint64_t*>(file_number);
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta);
|
||||
for (const auto& level : cf_meta.levels) {
|
||||
for (const auto& file : level.files) {
|
||||
// Use one of the existing file name
|
||||
*n = test::GetFileNumber(file.name);
|
||||
need_to_override_file_number = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Inject failure, so that the remote compaction fails after
|
||||
// ProcessKeyValueCompaction()
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::CompactWithoutInstallation::End", [&](void* status) {
|
||||
// override job status
|
||||
auto s = static_cast<Status*>(status);
|
||||
*s = Status::Aborted("MyTestCompactionService failed to compact!");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Compaction should fail and SST files in the primary db should exist
|
||||
{
|
||||
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(&meta);
|
||||
for (const auto& level : meta.levels) {
|
||||
for (const auto& file : level.files) {
|
||||
std::string fname = file.db_path + "/" + file.name;
|
||||
ASSERT_OK(db_->GetEnv()->FileExists(fname));
|
||||
}
|
||||
}
|
||||
}
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CorruptedOutput) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
@@ -848,7 +848,6 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
|
||||
VersionStorageInfo* vstorage = version->storage_info();
|
||||
|
||||
// Use comp_options to reuse some CompactFiles functions
|
||||
CompactionOptions comp_options;
|
||||
comp_options.compression = kDisableCompressionOption;
|
||||
comp_options.output_file_size_limit = MaxFileSizeForLevel(
|
||||
@@ -867,13 +866,27 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
return s;
|
||||
}
|
||||
|
||||
const int job_id = next_job_id_.fetch_add(1);
|
||||
JobContext job_context(job_id, true /*create_superversion*/);
|
||||
std::vector<SequenceNumber> snapshots = input.snapshots;
|
||||
|
||||
// TODO - snapshot_checker support in Remote Compaction
|
||||
job_context.InitSnapshotContext(/*checker=*/nullptr,
|
||||
/*managed_snapshot=*/nullptr,
|
||||
kMaxSequenceNumber, std::move(snapshots));
|
||||
|
||||
// TODO - consider serializing the entire Compaction object and using it as
|
||||
// input instead of recreating it in the remote worker
|
||||
std::unique_ptr<Compaction> c;
|
||||
assert(cfd->compaction_picker());
|
||||
c.reset(cfd->compaction_picker()->CompactFiles(
|
||||
comp_options, input_files, input.output_level, vstorage,
|
||||
cfd->GetLatestMutableCFOptions(), mutable_db_options_, 0));
|
||||
cfd->GetLatestMutableCFOptions(), mutable_db_options_, 0,
|
||||
/*earliest_snapshot=*/job_context.snapshot_seqs.empty()
|
||||
? kMaxSequenceNumber
|
||||
: job_context.snapshot_seqs.front(),
|
||||
job_context.snapshot_checker));
|
||||
assert(c != nullptr);
|
||||
|
||||
c->FinalizeInputInfo(version);
|
||||
|
||||
// Create output directory if it's not existed yet
|
||||
@@ -886,11 +899,6 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
|
||||
const int job_id = next_job_id_.fetch_add(1);
|
||||
JobContext job_context(0, true /*create_superversion*/);
|
||||
std::vector<SequenceNumber> snapshots = input.snapshots;
|
||||
job_context.InitSnapshotContext(nullptr, nullptr, kMaxSequenceNumber,
|
||||
std::move(snapshots));
|
||||
// use primary host's db_id for running the compaction, but db_session_id is
|
||||
// using the local one, which is to make sure the unique id is unique from
|
||||
// the remote compactors. Because the id is generated from db_id,
|
||||
|
||||
@@ -156,10 +156,6 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// It is unsafe to assume application had sync the file and file
|
||||
// directory before ingest the file. For integrity of RocksDB we need
|
||||
// to sync the file.
|
||||
// Use FSRandomRWFile instead of FSWritableFile, as in encrypted file
|
||||
// system the FSWritableFile will append a new prefix to the end of the
|
||||
// file when the file exists, which causes file corruption. On the
|
||||
// contrary, FSRandomRWFile handles an existing file correctly.
|
||||
|
||||
// TODO(xingbo), We should in general be moving away from production
|
||||
// uses of ReuseWritableFile (except explicitly for WAL recycling),
|
||||
@@ -168,9 +164,9 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// re-open+sync+close combo but can (a) be reused easily, and (b) be
|
||||
// overridden to do that more cleanly, e.g. in EncryptedEnv.
|
||||
// https://github.com/facebook/rocksdb/issues/13741
|
||||
std::unique_ptr<FSRandomRWFile> file_to_sync;
|
||||
Status s = fs_->NewRandomRWFile(path_inside_db, env_options_,
|
||||
&file_to_sync, nullptr);
|
||||
std::unique_ptr<FSWritableFile> file_to_sync;
|
||||
Status s = fs_->ReopenWritableFile(path_inside_db, env_options_,
|
||||
&file_to_sync, nullptr);
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Prepare:Reopen",
|
||||
&s);
|
||||
// Some file systems (especially remote/distributed) don't support
|
||||
|
||||
Vendored
+38
-3
@@ -665,17 +665,52 @@ class EncryptedFileSystemImpl : public EncryptedFileSystem {
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
result->reset();
|
||||
if (options.use_mmap_writes) {
|
||||
if (options.use_mmap_reads || options.use_mmap_writes) {
|
||||
return IOStatus::InvalidArgument();
|
||||
}
|
||||
|
||||
size_t prefix_length = 0;
|
||||
std::unique_ptr<BlockAccessCipherStream> stream;
|
||||
|
||||
// Open file using underlying Env implementation
|
||||
std::unique_ptr<FSWritableFile> underlying;
|
||||
IOStatus status =
|
||||
auto status =
|
||||
FileSystemWrapper::ReopenWritableFile(fname, options, &underlying, dbg);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
|
||||
|
||||
if (underlying->GetFileSize(options.io_options, dbg) != 0) {
|
||||
// read the cipher stream from file for non-empty file
|
||||
std::unique_ptr<FSRandomAccessFile> underlying_file_reader;
|
||||
status = FileSystemWrapper::NewRandomAccessFile(
|
||||
fname, options, &underlying_file_reader, dbg);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = CreateRandomReadCipherStream(
|
||||
fname, underlying_file_reader, options, &prefix_length, &stream, dbg);
|
||||
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
// create cipher stream for new or empty file
|
||||
status = CreateWritableCipherStream(fname, underlying, options,
|
||||
&prefix_length, &stream, dbg);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream) {
|
||||
result->reset(new EncryptedWritableFile(
|
||||
std::move(underlying), std::move(stream), prefix_length));
|
||||
} else {
|
||||
result->reset(underlying.release());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
IOStatus ReuseWritableFile(const std::string& fname,
|
||||
|
||||
@@ -63,6 +63,8 @@ class DBWithTTL : public StackableDB {
|
||||
|
||||
virtual void SetTtl(ColumnFamilyHandle* h, int32_t ttl) = 0;
|
||||
|
||||
virtual Status GetTtl(ColumnFamilyHandle* h, int32_t* ttl) = 0;
|
||||
|
||||
protected:
|
||||
explicit DBWithTTL(DB* db) : StackableDB(db) {}
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// minor or major version number planned for release.
|
||||
#define ROCKSDB_MAJOR 10
|
||||
#define ROCKSDB_MINOR 5
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_PATCH 1
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -72,6 +72,9 @@ std::unique_ptr<Configurable> CFOptionsAsConfigurable(
|
||||
Status StringToMap(const std::string& opts_str,
|
||||
std::unordered_map<std::string, std::string>* opts_map);
|
||||
|
||||
Status GetStringFromCompressionType(std::string* compression_str,
|
||||
CompressionType compression_type);
|
||||
|
||||
struct OptionsHelper {
|
||||
static const std::string kCFOptionsName /*= "ColumnFamilyOptions"*/;
|
||||
static const std::string kDBOptionsName /*= "DBOptions" */;
|
||||
|
||||
@@ -260,16 +260,21 @@ Status SstFileDumper::CalculateCompressedTableSize(
|
||||
}
|
||||
|
||||
Status SstFileDumper::ShowAllCompressionSizes(
|
||||
size_t block_size,
|
||||
const std::vector<std::pair<CompressionType, const char*>>&
|
||||
compression_types,
|
||||
size_t block_size, const std::vector<CompressionType>& compression_types,
|
||||
int32_t compress_level_from, int32_t compress_level_to,
|
||||
uint32_t max_dict_bytes, uint32_t zstd_max_train_bytes,
|
||||
uint64_t max_dict_buffer_bytes, bool use_zstd_dict_trainer) {
|
||||
fprintf(stdout, "Block Size: %" ROCKSDB_PRIszt "\n", block_size);
|
||||
for (auto& i : compression_types) {
|
||||
if (CompressionTypeSupported(i.first)) {
|
||||
fprintf(stdout, "Compression: %-24s\n", i.second);
|
||||
for (CompressionType ctype : compression_types) {
|
||||
std::string cname;
|
||||
if (!GetStringFromCompressionType(&cname, ctype).ok()) {
|
||||
// Can produce names like "Reserved4F" for unrecognized values
|
||||
cname = CompressionTypeToString(ctype);
|
||||
}
|
||||
if (options_.compression_manager
|
||||
? options_.compression_manager->SupportsCompressionType(ctype)
|
||||
: CompressionTypeSupported(ctype)) {
|
||||
fprintf(stdout, "Compression: %-24s\n", cname.c_str());
|
||||
CompressionOptions compress_opt;
|
||||
compress_opt.max_dict_bytes = max_dict_bytes;
|
||||
compress_opt.zstd_max_train_bytes = zstd_max_train_bytes;
|
||||
@@ -278,13 +283,13 @@ Status SstFileDumper::ShowAllCompressionSizes(
|
||||
for (int32_t j = compress_level_from; j <= compress_level_to; j++) {
|
||||
fprintf(stdout, "Compression level: %d", j);
|
||||
compress_opt.level = j;
|
||||
Status s = ShowCompressionSize(block_size, i.first, compress_opt);
|
||||
Status s = ShowCompressionSize(block_size, ctype, compress_opt);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stdout, "Unsupported compression type: %s.\n", i.second);
|
||||
fprintf(stdout, "Unsupported compression type: %s.\n", cname.c_str());
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
|
||||
@@ -43,9 +43,7 @@ class SstFileDumper {
|
||||
Status getStatus() { return init_result_; }
|
||||
|
||||
Status ShowAllCompressionSizes(
|
||||
size_t block_size,
|
||||
const std::vector<std::pair<CompressionType, const char*>>&
|
||||
compression_types,
|
||||
size_t block_size, const std::vector<CompressionType>& compression_types,
|
||||
int32_t compress_level_from, int32_t compress_level_to,
|
||||
uint32_t max_dict_bytes, uint32_t zstd_max_train_bytes,
|
||||
uint64_t max_dict_buffer_bytes, bool use_zstd_dict_trainer);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/sst_dump_tool.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/sst_file_dumper.h"
|
||||
#include "table/table_builder.h"
|
||||
@@ -346,6 +347,76 @@ TEST_F(SSTDumpToolTest, CompressedSizes) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
using Compressor8A = test::CompressorCustomAlg<kCustomCompression8A>;
|
||||
class MyManager : public CompressionManager {
|
||||
public:
|
||||
static constexpr const char* kCompatibilityName = "SSTDumpToolTest:MyManager";
|
||||
const char* Name() const override { return kCompatibilityName; }
|
||||
const char* CompatibilityName() const override { return kCompatibilityName; }
|
||||
|
||||
bool SupportsCompressionType(CompressionType type) const override {
|
||||
return type == kCustomCompression8A;
|
||||
}
|
||||
|
||||
std::unique_ptr<Compressor> GetCompressor(const CompressionOptions& /*opts*/,
|
||||
CompressionType type) override {
|
||||
switch (static_cast<unsigned char>(type)) {
|
||||
case kCustomCompression8A:
|
||||
return std::make_unique<Compressor8A>();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Decompressor> GetDecompressor() override {
|
||||
return std::make_shared<test::DecompressorCustomAlg>();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_F(SSTDumpToolTest, CompressionManager) {
|
||||
if (!Compressor8A::Supported()) {
|
||||
fprintf(stderr,
|
||||
"Prerequisite compression library not supported. Skipping\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Registery in ObjectLibrary to check that sst_dump can use named
|
||||
// CompressionManagers with dependency injection
|
||||
auto& library = *ObjectLibrary::Default();
|
||||
library.AddFactory<CompressionManager>(
|
||||
MyManager::kCompatibilityName,
|
||||
[](const std::string& /*uri*/, std::unique_ptr<CompressionManager>* guard,
|
||||
std::string* /*errmsg*/) {
|
||||
*guard = std::make_unique<MyManager>();
|
||||
return guard->get();
|
||||
});
|
||||
|
||||
Options opts;
|
||||
opts.env = env();
|
||||
BlockBasedTableOptions table_opts;
|
||||
table_opts.filter_policy.reset(
|
||||
ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
|
||||
opts.table_factory.reset(new BlockBasedTableFactory(table_opts));
|
||||
std::string file_path = MakeFilePath("rocksdb_sst_test.sst");
|
||||
createSST(opts, file_path, 10);
|
||||
|
||||
char* usage[5];
|
||||
PopulateCommandArgs(file_path, "--command=recompress", usage);
|
||||
snprintf(usage[3], kOptLength, "--compression_manager=%s",
|
||||
MyManager::kCompatibilityName);
|
||||
snprintf(usage[4], kOptLength, "--compression_types=kCustomCompression8A");
|
||||
|
||||
ROCKSDB_NAMESPACE::SSTDumpTool tool;
|
||||
ASSERT_TRUE(!tool.Run(5, usage, opts));
|
||||
|
||||
cleanup(opts, file_path);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
delete[] usage[i];
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SSTDumpToolTest, MemEnv) {
|
||||
std::unique_ptr<Env> mem_env(NewMemEnv(env()));
|
||||
Options opts;
|
||||
|
||||
+37
-22
@@ -17,17 +17,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
static const std::vector<std::pair<CompressionType, const char*>>
|
||||
kCompressions = {
|
||||
{CompressionType::kNoCompression, "kNoCompression"},
|
||||
{CompressionType::kSnappyCompression, "kSnappyCompression"},
|
||||
{CompressionType::kZlibCompression, "kZlibCompression"},
|
||||
{CompressionType::kBZip2Compression, "kBZip2Compression"},
|
||||
{CompressionType::kLZ4Compression, "kLZ4Compression"},
|
||||
{CompressionType::kLZ4HCCompression, "kLZ4HCCompression"},
|
||||
{CompressionType::kXpressCompression, "kXpressCompression"},
|
||||
{CompressionType::kZSTD, "kZSTD"}};
|
||||
|
||||
namespace {
|
||||
|
||||
void print_help(bool to_stderr) {
|
||||
@@ -98,10 +87,15 @@ void print_help(bool to_stderr) {
|
||||
be used when trying different compression algorithms
|
||||
|
||||
--compression_types=<comma-separated list of CompressionType members, e.g.,
|
||||
kSnappyCompression>
|
||||
kSnappyCompression or kCustomCompressionC4>
|
||||
Can be combined with --command=recompress to run recompression for this
|
||||
list of compression types
|
||||
Supported compression types: %s
|
||||
Supported built-in compression types: %s
|
||||
|
||||
--compression_manager=<compression manager string>
|
||||
Used with --command=recompress to specify a compression manager to use
|
||||
instead of the built-in compression manager, which may support a
|
||||
different set of compression types.
|
||||
|
||||
--parse_internal_key=<0xKEY>
|
||||
Convenience option to parse an internal key on the command line. Dumps the
|
||||
@@ -178,7 +172,8 @@ int SSTDumpTool::Run(int argc, char const* const* argv, Options options) {
|
||||
std::string compression_level_to_str;
|
||||
size_t block_size = 0;
|
||||
size_t readahead_size = 2 * 1024 * 1024;
|
||||
std::vector<std::pair<CompressionType, const char*>> compression_types;
|
||||
std::vector<CompressionType> compression_types;
|
||||
std::shared_ptr<CompressionManager> compression_manager;
|
||||
uint64_t total_num_files = 0;
|
||||
uint64_t total_num_data_blocks = 0;
|
||||
uint64_t total_data_block_size = 0;
|
||||
@@ -244,19 +239,36 @@ int SSTDumpTool::Run(int argc, char const* const* argv, Options options) {
|
||||
std::istringstream iss(compression_types_csv);
|
||||
std::string compression_type;
|
||||
has_specified_compression_types = true;
|
||||
|
||||
while (std::getline(iss, compression_type, ',')) {
|
||||
auto iter = std::find_if(
|
||||
kCompressions.begin(), kCompressions.end(),
|
||||
[&compression_type](std::pair<CompressionType, const char*> curr) {
|
||||
return curr.second == compression_type;
|
||||
});
|
||||
if (iter == kCompressions.end()) {
|
||||
auto iter =
|
||||
OptionsHelper::compression_type_string_map.find(compression_type);
|
||||
if (iter == OptionsHelper::compression_type_string_map.end()) {
|
||||
fprintf(stderr, "%s is not a valid CompressionType\n",
|
||||
compression_type.c_str());
|
||||
exit(1);
|
||||
}
|
||||
compression_types.emplace_back(*iter);
|
||||
compression_types.emplace_back(iter->second);
|
||||
}
|
||||
} else if (strncmp(argv[i], "--compression_manager=", 22) == 0) {
|
||||
std::string compression_manager_str = argv[i] + 22;
|
||||
ConfigOptions config_options;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
Status s = CompressionManager::CreateFromString(
|
||||
config_options, compression_manager_str, &compression_manager);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Failed to create compression manager: %s\n",
|
||||
s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (compression_manager == nullptr) {
|
||||
fprintf(stderr, "No compression manager created: %s\n",
|
||||
compression_manager_str.c_str());
|
||||
exit(1);
|
||||
}
|
||||
options.compression_manager = compression_manager;
|
||||
printf("Using compression manager: %s\n",
|
||||
compression_manager->GetId().c_str());
|
||||
} else if (strncmp(argv[i], "--parse_internal_key=", 21) == 0) {
|
||||
std::string in_key(argv[i] + 21);
|
||||
try {
|
||||
@@ -450,9 +462,12 @@ int SSTDumpTool::Run(int argc, char const* const* argv, Options options) {
|
||||
}
|
||||
|
||||
if (command == "recompress") {
|
||||
// TODO: consider getting supported compressions from the compression
|
||||
// manager
|
||||
st = dumper.ShowAllCompressionSizes(
|
||||
set_block_size ? block_size : 16384,
|
||||
compression_types.empty() ? kCompressions : compression_types,
|
||||
compression_types.empty() ? GetSupportedCompressions()
|
||||
: compression_types,
|
||||
compress_level_from, compress_level_to, compression_max_dict_bytes,
|
||||
compression_zstd_max_train_bytes, compression_max_dict_buffer_bytes,
|
||||
!compression_use_zstd_finalize_dict);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occured.
|
||||
-1
@@ -1 +0,0 @@
|
||||
A new API GetFileSize is added to FSRandomAccessFile interface class. It uses fstat vs stat on the posix implementation which is more efficient. Caller could use it to get file size faster. This function might be required in the future for FileSystem implementation outside of the RocksDB code base.
|
||||
@@ -1 +0,0 @@
|
||||
* RocksDB now triggers eligible compactions every 12 hours when periodic compaction is configured. This solves a limitation of the compaction trigger mechanism, which would only trigger compaction after specific events like flush, compaction, or SetOptions.
|
||||
@@ -1 +0,0 @@
|
||||
* Fix a bug in BackupEngine that can crash backup due to a null FSWritableFile passed to WritableFileWriter.
|
||||
@@ -1 +0,0 @@
|
||||
Fix DB::NewMultiScan iterator to respect the scan upper bound specified in ScanOptions
|
||||
@@ -1 +0,0 @@
|
||||
* Optimized MultiScan using BlockBasedTable to coalesce I/Os and prefetch all data blocks.
|
||||
-1
@@ -1 +0,0 @@
|
||||
DB option skip_checking_sst_file_sizes_on_db_open is deprecated, in favor of validating file size in parallel in a thread pool, when db is opened. When DB is opened, with paranoid check enabled, a file with the wrong size would fail the DB open. With paranoid check disabled, the DB open would succeed, the column family with the corrupted file would not be read or write, while the other healthy column families could be read and write normally. When max_open_files option is not set to -1, only a subset of the files will be opened and checked. The rest of the files will be opened and checked when they are accessed.
|
||||
@@ -635,4 +635,22 @@ void DBWithTTLImpl::SetTtl(ColumnFamilyHandle* h, int32_t ttl) {
|
||||
filter->SetTtl(ttl);
|
||||
}
|
||||
|
||||
Status DBWithTTLImpl::GetTtl(ColumnFamilyHandle* h, int32_t* ttl) {
|
||||
if (h == nullptr || ttl == nullptr) {
|
||||
return Status::InvalidArgument(
|
||||
"column family handle or ttl cannot be null");
|
||||
}
|
||||
std::shared_ptr<TtlCompactionFilterFactory> filter;
|
||||
Options opts;
|
||||
opts = GetOptions(h);
|
||||
filter = std::static_pointer_cast<TtlCompactionFilterFactory>(
|
||||
opts.compaction_filter_factory);
|
||||
if (!filter) {
|
||||
return Status::InvalidArgument(
|
||||
"TTLCompactionFilterFactory is not set for TTLDB");
|
||||
}
|
||||
*ttl = filter->GetTtl();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -100,6 +100,8 @@ class DBWithTTLImpl : public DBWithTTL {
|
||||
|
||||
void SetTtl(ColumnFamilyHandle* h, int32_t ttl) override;
|
||||
|
||||
Status GetTtl(ColumnFamilyHandle* h, int32_t* ttl) override;
|
||||
|
||||
private:
|
||||
// remember whether the Close completes or not
|
||||
bool closed_;
|
||||
@@ -184,6 +186,7 @@ class TtlCompactionFilterFactory : public CompactionFilterFactory {
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override;
|
||||
void SetTtl(int32_t ttl) { ttl_ = ttl; }
|
||||
int32_t GetTtl() { return ttl_; }
|
||||
|
||||
const char* Name() const override { return kClassName(); }
|
||||
static const char* kClassName() { return "TtlCompactionFilterFactory"; }
|
||||
|
||||
@@ -720,6 +720,9 @@ TEST_F(TtlTest, ChangeTtlOnOpenDb) {
|
||||
|
||||
OpenTtl(1); // T=0:Open the db with ttl = 2
|
||||
SetTtl(3);
|
||||
int32_t ttl = 0;
|
||||
ASSERT_OK(db_ttl_->GetTtl(db_ttl_->DefaultColumnFamily(), &ttl));
|
||||
ASSERT_EQ(ttl, 3);
|
||||
PutValues(0, kSampleSize_); // T=0:Insert Set1. Delete at t=2
|
||||
SleepCompactCheck(2, 0, kSampleSize_, true); // T=2:Set1 should be there
|
||||
CloseTtl();
|
||||
|
||||
Reference in new issue
Block a user