Publish/support format_version=7, related enhancements (#13713)

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

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

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

Reviewed By: mszeszko-meta, hx235

Differential Revision: D77035482

Pulled By: pdillinger

fbshipit-source-id: 95278de8734a79706a22361bff2184b1edb230ca
This commit is contained in:
Peter Dillinger
2025-06-20 17:39:47 -07:00
committed by Facebook GitHub Bot
parent 190bb0bd24
commit 78c83ac1ec
27 changed files with 238 additions and 98 deletions
+7 -6
View File
@@ -80,15 +80,16 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
ptr = reinterpret_cast<CacheAllocationPtr*>(handle_value); ptr = reinterpret_cast<CacheAllocationPtr*>(handle_value);
handle_value_charge = cache_->GetCharge(lru_handle); handle_value_charge = cache_->GetCharge(lru_handle);
data_ptr = ptr->get(); data_ptr = ptr->get();
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1, const char* limit = ptr->get() + handle_value_charge;
static_cast<uint32_t*>(&type_32)); data_ptr =
GetVarint32Ptr(data_ptr, limit, static_cast<uint32_t*>(&type_32));
type = static_cast<CompressionType>(type_32); type = static_cast<CompressionType>(type_32);
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1, data_ptr =
static_cast<uint32_t*>(&source_32)); GetVarint32Ptr(data_ptr, limit, static_cast<uint32_t*>(&source_32));
source = static_cast<CacheTier>(source_32); source = static_cast<CacheTier>(source_32);
uint64_t data_size = 0; uint64_t data_size = 0;
data_ptr = GetVarint64Ptr(data_ptr, ptr->get() + handle_value_charge, data_ptr =
static_cast<uint64_t*>(&data_size)); GetVarint64Ptr(data_ptr, limit, static_cast<uint64_t*>(&data_size));
assert(handle_value_charge > data_size); assert(handle_value_charge > data_size);
handle_value_charge = data_size; handle_value_charge = data_size;
} }
+33 -17
View File
@@ -110,32 +110,48 @@ void GetInternalTblPropCollFactory(
} }
} }
bool CompressionSupportedWithManager(CompressionType type, Status CheckCompressionSupportedWithManager(
UnownedPtr<CompressionManager> mgr) { CompressionType type, UnownedPtr<CompressionManager> mgr) {
return mgr ? mgr->SupportsCompressionType(type) if (mgr) {
: CompressionTypeSupported(type); if (!mgr->SupportsCompressionType(type)) {
return Status::NotSupported("Compression type " +
CompressionTypeToString(type) +
" is not recognized/supported by this "
"version of CompressionManager " +
mgr->GetId());
}
} else {
if (!CompressionTypeSupported(type)) {
if (type <= kLastBuiltinCompression) {
return Status::InvalidArgument("Compression type " +
CompressionTypeToString(type) +
" is not linked with the binary.");
} else {
return Status::NotSupported(
"Compression type " + CompressionTypeToString(type) +
" is not recognized/supported by built-in CompressionManager.");
}
}
}
return Status::OK();
} }
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) { Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
if (!cf_options.compression_per_level.empty()) { if (!cf_options.compression_per_level.empty()) {
for (size_t level = 0; level < cf_options.compression_per_level.size(); for (size_t level = 0; level < cf_options.compression_per_level.size();
++level) { ++level) {
if (!CompressionSupportedWithManager( Status s = CheckCompressionSupportedWithManager(
cf_options.compression_per_level[level], cf_options.compression_per_level[level],
cf_options.compression_manager.get())) { cf_options.compression_manager.get());
return Status::InvalidArgument( if (!s.ok()) {
"Compression type " + return s;
CompressionTypeToString(cf_options.compression_per_level[level]) +
" is not linked with the binary.");
} }
} }
} else { } else {
if (!CompressionSupportedWithManager( Status s = CheckCompressionSupportedWithManager(
cf_options.compression, cf_options.compression_manager.get())) { cf_options.compression, cf_options.compression_manager.get());
return Status::InvalidArgument( if (!s.ok()) {
"Compression type " + return s;
CompressionTypeToString(cf_options.compression) +
" is not linked with the binary.");
} }
} }
if (cf_options.compression_opts.zstd_max_train_bytes > 0) { if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
-1
View File
@@ -833,7 +833,6 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
GetSupportedDictCompressions(); GetSupportedDictCompressions();
Random rnd(301); Random rnd(301);
// Format version before and after compression handling changes // Format version before and after compression handling changes
TEST_AllowUnsupportedFormatVersion() = true;
for (int format_version : {6, 7}) { for (int format_version : {6, 7}) {
// Test all supported compression types because (at least historically) // Test all supported compression types because (at least historically)
// dictionary compression could be enabled and a dictionary block saved // dictionary compression could be enabled and a dictionary block saved
+1
View File
@@ -6209,6 +6209,7 @@ TEST_F(DBTest, L0L1L2AndUpHitCounter) {
} }
TEST_F(DBTest, EncodeDecompressedBlockSizeTest) { TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
// Allow testing format_version=1
bool& allow_unsupported_fv = TEST_AllowUnsupportedFormatVersion(); bool& allow_unsupported_fv = TEST_AllowUnsupportedFormatVersion();
SaveAndRestore guard(&allow_unsupported_fv); SaveAndRestore guard(&allow_unsupported_fv);
ASSERT_FALSE(allow_unsupported_fv); ASSERT_FALSE(allow_unsupported_fv);
+73 -25
View File
@@ -24,6 +24,7 @@
#include "rocksdb/persistent_cache.h" #include "rocksdb/persistent_cache.h"
#include "rocksdb/trace_record.h" #include "rocksdb/trace_record.h"
#include "rocksdb/trace_record_result.h" #include "rocksdb/trace_record_result.h"
#include "rocksdb/utilities/object_registry.h"
#include "rocksdb/utilities/replayer.h" #include "rocksdb/utilities/replayer.h"
#include "rocksdb/wal_filter.h" #include "rocksdb/wal_filter.h"
#include "test_util/testutil.h" #include "test_util/testutil.h"
@@ -1885,8 +1886,8 @@ TEST_F(DBTest2, CompressionOptions) {
TEST_F(DBTest2, RoundRobinManager) { TEST_F(DBTest2, RoundRobinManager) {
if (ZSTD_Supported()) { if (ZSTD_Supported()) {
auto mgr = std::make_shared<RoundRobinManager>( auto mgr =
GetDefaultBuiltinCompressionManager()); std::make_shared<RoundRobinManager>(GetBuiltinV2CompressionManager());
std::vector<std::string> values; std::vector<std::string> values;
for (bool use_wrapper : {true}) { for (bool use_wrapper : {true}) {
@@ -1934,7 +1935,7 @@ TEST_F(DBTest2, RoundRobinManager) {
TEST_F(DBTest2, RandomMixedCompressionManager) { TEST_F(DBTest2, RandomMixedCompressionManager) {
if (ZSTD_Supported()) { if (ZSTD_Supported()) {
auto mgr = std::make_shared<RandomMixedCompressionManager>( auto mgr = std::make_shared<RandomMixedCompressionManager>(
GetDefaultBuiltinCompressionManager()); GetBuiltinV2CompressionManager());
std::vector<std::string> values; std::vector<std::string> values;
for (bool use_wrapper : {true}) { for (bool use_wrapper : {true}) {
SCOPED_TRACE((use_wrapper ? "With " : "No ") + std::string("wrapper")); SCOPED_TRACE((use_wrapper ? "With " : "No ") + std::string("wrapper"));
@@ -2026,7 +2027,7 @@ TEST_F(DBTest2, CompressionManagerWrapper) {
wrapped_->GetCompressorForSST(context, opts, preferred)); wrapped_->GetCompressorForSST(context, opts, preferred));
} }
}; };
auto mgr = std::make_shared<MyManager>(GetDefaultBuiltinCompressionManager()); auto mgr = std::make_shared<MyManager>(GetBuiltinV2CompressionManager());
for (CompressionType type : GetSupportedCompressions()) { for (CompressionType type : GetSupportedCompressions()) {
for (bool use_wrapper : {false, true}) { for (bool use_wrapper : {false, true}) {
@@ -2117,8 +2118,7 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
bool SupportsCompressionType(CompressionType type) const override { bool SupportsCompressionType(CompressionType type) const override {
return type == kCustomCompression8A || type == kCustomCompression8B || return type == kCustomCompression8A || type == kCustomCompression8B ||
type == kCustomCompression8C || type == kCustomCompression8C ||
GetDefaultBuiltinCompressionManager()->SupportsCompressionType( GetBuiltinV2CompressionManager()->SupportsCompressionType(type);
type);
} }
int used_compressor8A_count_ = 0; int used_compressor8A_count_ = 0;
@@ -2139,8 +2139,7 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
return std::make_unique<Compressor8C>(); return std::make_unique<Compressor8C>();
// Also support built-in compression algorithms // Also support built-in compression algorithms
default: default:
return GetDefaultBuiltinCompressionManager()->GetCompressor(opts, return GetBuiltinV2CompressionManager()->GetCompressor(opts, type);
type);
} }
} }
@@ -2148,13 +2147,14 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
return std::make_shared<test::DecompressorCustomAlg>(); return std::make_shared<test::DecompressorCustomAlg>();
} }
CompressionType last_specific_decompressor_type_ = kNoCompression; RelaxedAtomic<CompressionType> last_specific_decompressor_type_{
kNoCompression};
std::shared_ptr<Decompressor> GetDecompressorForTypes( std::shared_ptr<Decompressor> GetDecompressorForTypes(
const CompressionType* types_begin, const CompressionType* types_begin,
const CompressionType* types_end) override { const CompressionType* types_end) override {
assert(types_end > types_begin); assert(types_end > types_begin);
last_specific_decompressor_type_ = *types_begin; last_specific_decompressor_type_.StoreRelaxed(*types_begin);
auto decomp = std::make_shared<test::DecompressorCustomAlg>(); auto decomp = std::make_shared<test::DecompressorCustomAlg>();
decomp->SetAllowedTypes(types_begin, types_end); decomp->SetAllowedTypes(types_begin, types_end);
return decomp; return decomp;
@@ -2191,9 +2191,11 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
// respect their distinct compatibility names and treat them as incompatible // respect their distinct compatibility names and treat them as incompatible
// (or else risk processing data incorrectly) // (or else risk processing data incorrectly)
// NOTE: these are not registered in ObjectRegistry to test what happens // NOTE: these are not registered in ObjectRegistry to test what happens
// when the original CompressionManager might not be available. // when the original CompressionManager might not be available, but
// mgr_bar will be registered during the test, with different names to
// prevent interference between iterations.
auto mgr_foo = std::make_shared<MyManager>("Foo"); auto mgr_foo = std::make_shared<MyManager>("Foo");
auto mgr_bar = std::make_shared<MyManager>("Bar"); auto mgr_bar = std::make_shared<MyManager>(use_dict ? "Bar1" : "Bar2");
// And this one claims to be fully compatible with the built-in compression // And this one claims to be fully compatible with the built-in compression
// manager when it's not fully compatible (for custom CompressionTypes) // manager when it's not fully compatible (for custom CompressionTypes)
@@ -2256,10 +2258,7 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
options.compression = kLZ4Compression; options.compression = kLZ4Compression;
ASSERT_EQ(TryReopen(options).code(), Status::Code::kInvalidArgument); ASSERT_EQ(TryReopen(options).code(), Status::Code::kInvalidArgument);
// TODO: eliminate this hack when format_version=7 is published // Set format version supporting custom compression
SaveAndRestore guard(&TEST_AllowUnsupportedFormatVersion(), true);
// Set new format version
bbto.format_version = 7; bbto.format_version = 7;
options.table_factory.reset(NewBlockBasedTableFactory(bbto)); options.table_factory.reset(NewBlockBasedTableFactory(bbto));
@@ -2269,8 +2268,13 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
options.compression = kCustomCompression8B; options.compression = kCustomCompression8B;
ASSERT_EQ(TryReopen(options).code(), Status::Code::kInvalidArgument); ASSERT_EQ(TryReopen(options).code(), Status::Code::kInvalidArgument);
// Using a built-in compression type with fv=7 but named custom schema // Custom compression schema, but specifying a custom compression type it
// doesn't support.
options.compression_manager = mgr_foo; options.compression_manager = mgr_foo;
options.compression = kCustomCompressionF0;
ASSERT_EQ(TryReopen(options).code(), Status::Code::kNotSupported);
// Using a built-in compression type with fv=7 but named custom schema
options.compression = kLZ4Compression; options.compression = kLZ4Compression;
Reopen(options); Reopen(options);
ASSERT_OK( ASSERT_OK(
@@ -2279,7 +2283,7 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
ASSERT_EQ(NumTableFilesAtLevel(0), 2); ASSERT_EQ(NumTableFilesAtLevel(0), 2);
ASSERT_EQ(Get("b"), value); ASSERT_EQ(Get("b"), value);
// Verify it was compressed with snappy // Verify it was compressed with LZ4
r = {"b", "b0"}; r = {"b", "b0"};
tables_properties.clear(); tables_properties.clear();
ASSERT_OK(db_->GetPropertiesOfTablesInRange(db_->DefaultColumnFamily(), &r, ASSERT_OK(db_->GetPropertiesOfTablesInRange(db_->DefaultColumnFamily(), &r,
@@ -2288,7 +2292,8 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2); EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2);
// Uses new format for "compression_name" property // Uses new format for "compression_name" property
EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;04;"); EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;04;");
EXPECT_EQ(mgr_foo->last_specific_decompressor_type_, kLZ4Compression); EXPECT_EQ(mgr_foo->last_specific_decompressor_type_.LoadRelaxed(),
kLZ4Compression);
// Custom compression type // Custom compression type
options.compression = kCustomCompression8A; options.compression = kCustomCompression8A;
@@ -2309,7 +2314,8 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
ASSERT_EQ(tables_properties.size(), 1U); ASSERT_EQ(tables_properties.size(), 1U);
EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2); EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2);
EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;8A;"); EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;8A;");
EXPECT_EQ(mgr_foo->last_specific_decompressor_type_, kCustomCompression8A); EXPECT_EQ(mgr_foo->last_specific_decompressor_type_.LoadRelaxed(),
kCustomCompression8A);
// Also dynamically changeable, because the compression manager will respect // Also dynamically changeable, because the compression manager will respect
// the current setting as reported under the legacy logic // the current setting as reported under the legacy logic
@@ -2320,7 +2326,7 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
ASSERT_EQ(NumTableFilesAtLevel(0), 4); ASSERT_EQ(NumTableFilesAtLevel(0), 4);
ASSERT_EQ(Get("d"), value); ASSERT_EQ(Get("d"), value);
// Verify it was compressed with snappy // Verify it was compressed with LZ4
r = {"d", "d0"}; r = {"d", "d0"};
tables_properties.clear(); tables_properties.clear();
ASSERT_OK(db_->GetPropertiesOfTablesInRange(db_->DefaultColumnFamily(), &r, ASSERT_OK(db_->GetPropertiesOfTablesInRange(db_->DefaultColumnFamily(), &r,
@@ -2328,7 +2334,8 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
ASSERT_EQ(tables_properties.size(), 1U); ASSERT_EQ(tables_properties.size(), 1U);
EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2); EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2);
EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;04;"); EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;04;");
EXPECT_EQ(mgr_foo->last_specific_decompressor_type_, kLZ4Compression); EXPECT_EQ(mgr_foo->last_specific_decompressor_type_.LoadRelaxed(),
kLZ4Compression);
// Dynamically changeable to custom compressions also // Dynamically changeable to custom compressions also
ASSERT_OK(dbfull()->SetOptions({{"compression", "kCustomCompression8B"}})); ASSERT_OK(dbfull()->SetOptions({{"compression", "kCustomCompression8B"}}));
@@ -2346,7 +2353,8 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
ASSERT_EQ(tables_properties.size(), 1U); ASSERT_EQ(tables_properties.size(), 1U);
EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2); EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2);
EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;8B;"); EXPECT_EQ(tables_properties.begin()->second->compression_name, "Foo;8B;");
EXPECT_EQ(mgr_foo->last_specific_decompressor_type_, kCustomCompression8B); EXPECT_EQ(mgr_foo->last_specific_decompressor_type_.LoadRelaxed(),
kCustomCompression8B);
// Fails to re-open with incompatible compression manager (can't find // Fails to re-open with incompatible compression manager (can't find
// compression manager Foo because it's not registered nor known by Bar) // compression manager Foo because it's not registered nor known by Bar)
@@ -2365,10 +2373,50 @@ TEST_F(DBTest2, CompressionManagerCustomCompression) {
ASSERT_EQ(Get("d").size(), kValueSize); ASSERT_EQ(Get("d").size(), kValueSize);
ASSERT_EQ(Get("e").size(), kValueSize); ASSERT_EQ(Get("e").size(), kValueSize);
// TODO: mix of compatibility names in same DB // Add a file using mgr_bar
ASSERT_OK(
Put("f", test::CompressibleString(&rnd, 0.1, kValueSize, &value)));
ASSERT_OK(Flush());
ASSERT_EQ(NumTableFilesAtLevel(0), 6);
ASSERT_EQ(Get("f"), value);
// Verify it was compressed appropriately
r = {"f", "f0"};
tables_properties.clear();
ASSERT_OK(db_->GetPropertiesOfTablesInRange(db_->DefaultColumnFamily(), &r,
1, &tables_properties));
ASSERT_EQ(tables_properties.size(), 1U);
EXPECT_LT(tables_properties.begin()->second->data_size, kValueSize / 2);
EXPECT_EQ(mgr_bar->last_specific_decompressor_type_.LoadRelaxed(),
kLZ4Compression);
// Fails to re-open with incompatible compression manager (can't find
// compression manager Bar because it's not registered nor known by Foo)
options.compression_manager = mgr_foo;
ASSERT_EQ(TryReopen(options).code(), Status::Code::kNotSupported);
// Register and re-open
auto& library = *ObjectLibrary::Default();
library.AddFactory<CompressionManager>(
mgr_bar->CompatibilityName(),
[mgr_bar](const std::string& /*uri*/,
std::unique_ptr<CompressionManager>* guard,
std::string* /*errmsg*/) {
*guard = std::make_unique<MyManager>(mgr_bar->CompatibilityName());
return guard->get();
});
Reopen(options);
// Can still read everything
ASSERT_EQ(Get("a").size(), kValueSize);
ASSERT_EQ(Get("b").size(), kValueSize);
ASSERT_EQ(Get("c").size(), kValueSize);
ASSERT_EQ(Get("d").size(), kValueSize);
ASSERT_EQ(Get("e").size(), kValueSize);
ASSERT_EQ(Get("f").size(), kValueSize);
// TODO: test old version of a compression manager unable to read a // TODO: test old version of a compression manager unable to read a
// compression type // compression type
// TODO: test getting compression manager from object registry
} }
} }
@@ -41,7 +41,7 @@ class DbStressCustomCompressionManager : public CompressionManager {
test::CompressorCustomAlg<kCustomCompressionAC>>(); test::CompressorCustomAlg<kCustomCompressionAC>>();
// Also support built-in compression algorithms // Also support built-in compression algorithms
default: default:
return GetDefaultBuiltinCompressionManager()->GetCompressor(opts, type); return GetBuiltinV2CompressionManager()->GetCompressor(opts, type);
} }
} }
@@ -59,7 +59,7 @@ class DbStressCustomCompressionManager : public CompressionManager {
protected: protected:
std::shared_ptr<CompressionManager> default_ = std::shared_ptr<CompressionManager> default_ =
GetDefaultBuiltinCompressionManager(); GetBuiltinV2CompressionManager();
}; };
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
+4 -4
View File
@@ -3435,15 +3435,15 @@ void StressTest::Open(SharedState* shared, bool reopen) {
options_.compression_manager = options_.compression_manager =
std::make_shared<DbStressCustomCompressionManager>(); std::make_shared<DbStressCustomCompressionManager>();
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "mixed")) { } else if (!strcasecmp(FLAGS_compression_manager.c_str(), "mixed")) {
options_.compression_manager = std::make_shared<RoundRobinManager>( options_.compression_manager =
GetDefaultBuiltinCompressionManager()); std::make_shared<RoundRobinManager>(GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "randommixed")) { } else if (!strcasecmp(FLAGS_compression_manager.c_str(), "randommixed")) {
options_.compression_manager = options_.compression_manager =
std::make_shared<RandomMixedCompressionManager>( std::make_shared<RandomMixedCompressionManager>(
GetDefaultBuiltinCompressionManager()); GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "autoskip")) { } else if (!strcasecmp(FLAGS_compression_manager.c_str(), "autoskip")) {
options_.compression_manager = options_.compression_manager =
CreateAutoSkipCompressionManager(GetDefaultBuiltinCompressionManager()); CreateAutoSkipCompressionManager(GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "none")) { } else if (!strcasecmp(FLAGS_compression_manager.c_str(), "none")) {
// Nothing to do using default compression manager // Nothing to do using default compression manager
} else { } else {
+14 -7
View File
@@ -371,8 +371,9 @@ class CompressionManager
const std::string& id, const std::string& id,
std::shared_ptr<CompressionManager>* result); std::shared_ptr<CompressionManager>* result);
// Will this compression type be used if requested in calling // Returns false iff a configuration that would pass the given compression
// GetCompressor/GetCompressorForSST? // type to GetCompressor/GetCompressorForSST should be rejected (not
// supported)
virtual bool SupportsCompressionType(CompressionType type) const = 0; virtual bool SupportsCompressionType(CompressionType type) const = 0;
// TODO: function to check compatibility with or sanitize CompressionOptions // TODO: function to check compatibility with or sanitize CompressionOptions
@@ -577,11 +578,17 @@ class CompressionManagerWrapper : public CompressionManager {
std::shared_ptr<CompressionManager> wrapped_; std::shared_ptr<CompressionManager> wrapped_;
}; };
// Compression manager that implements built-in compression strategy. The // Compression manager that implements the second schema for RocksDB built-in
// behavior of compression_manager=nullptr is essentially equivalent to // compression support. (The first schema is intentionally not provided here.)
// using this compression manager. // *** CURRENT STATE ***
const std::shared_ptr<CompressionManager>& // This is currently the latest schema for built-in compression, and the
GetDefaultBuiltinCompressionManager(); // compression manager used when compression_manager=nullptr.
const std::shared_ptr<CompressionManager>& GetBuiltinV2CompressionManager();
// NOTE: No GetLatestBuiltinCompressionManager() is provided because that could
// lead to unexpected schema changes for user CompressionManagers building on
// the built-in schema, in the unlikely/rare case of a new built-in schema.
// Gets CompressionManager designed for the automated compression strategy. // Gets CompressionManager designed for the automated compression strategy.
// This may include deciding to compress or not. // This may include deciding to compress or not.
// In future should be able to select compression algorithm based on the CPU // In future should be able to select compression algorithm based on the CPU
+2
View File
@@ -32,6 +32,7 @@ enum CompressionType : unsigned char {
// For use by user custom CompressionManagers // For use by user custom CompressionManagers
kCustomCompression80 = 0x80, kCustomCompression80 = 0x80,
kFirstCustomCompression = kCustomCompression80,
kCustomCompression81 = 0x81, kCustomCompression81 = 0x81,
kCustomCompression82 = 0x82, kCustomCompression82 = 0x82,
kCustomCompression83 = 0x83, kCustomCompression83 = 0x83,
@@ -158,6 +159,7 @@ enum CompressionType : unsigned char {
kCustomCompressionFC = 0xFC, kCustomCompressionFC = 0xFC,
kCustomCompressionFD = 0xFD, kCustomCompressionFD = 0xFD,
kCustomCompressionFE = 0xFE, kCustomCompressionFE = 0xFE,
kLastCustomCompression = kCustomCompressionFE,
// kDisableCompressionOption is used to disable some compression options. // kDisableCompressionOption is used to disable some compression options.
kDisableCompressionOption = 0xff, kDisableCompressionOption = 0xff,
+4
View File
@@ -561,6 +561,10 @@ struct BlockBasedTableOptions {
// misplaced within or between files is as likely to fail checksum // misplaced within or between files is as likely to fail checksum
// verification as random corruption. Also checksum-protects SST footer. // verification as random corruption. Also checksum-protects SST footer.
// Can be read by RocksDB versions >= 8.6.0. // Can be read by RocksDB versions >= 8.6.0.
// 7 -- Support for custom compression algorithms with a CompressionManager
// using a non-built-in CompatibilityName(). See `compression_manager` in
// ColumnFamilyOptions. Also changes the format of TableProperties field
// `compression_name`. Can be read by RocksDB versions >= 10.4.0.
// //
// Using the default setting of format_version is strongly recommended, so // Using the default setting of format_version is strongly recommended, so
// that available enhancements are adopted eventually and automatically. The // that available enhancements are adopted eventually and automatically. The
+7 -1
View File
@@ -355,7 +355,13 @@ struct TableProperties {
// behave, this must be set to "ZSTD" if any blocks are compressed // behave, this must be set to "ZSTD" if any blocks are compressed
// with zstd and must NOT be set to "NoCompression" if any blocks are // with zstd and must NOT be set to "NoCompression" if any blocks are
// compressed. // compressed.
// * For format_version >= 7, it is ... // * For format_version >= 7, the format is
// <compatibility_name>;<hex-coded compression types>;<future use>
// where <compatibility_name> is the CompatibilityName() of the
// CompressionManager used for the file, or empty if compression was
// disabled; <hex-coded compression types> represents a sorted set of
// CompressionType values used in the file other than kNoCompression, each
// as 2-digit hex, e.g. 04 for LZ$, 07 for ZSTD, etc.
std::string compression_name; std::string compression_name;
// Compression options used to compress the SST files. // Compression options used to compress the SST files.
+3 -4
View File
@@ -705,10 +705,9 @@ TEST_F(OptionsSettableTest, ColumnFamilyOptionsAllFieldsSettable) {
12345); 12345);
// TODO: try to enhance ObjectLibrary to support singletons // TODO: try to enhance ObjectLibrary to support singletons
// ASSERT_EQ(new_options->compression_manager, // ASSERT_EQ(new_options->compression_manager,
// GetBuiltinCompressionManager(/*compression_format_version*/ 2)); // GetBuiltinV2CompressionManager());
ASSERT_STREQ( ASSERT_STREQ(new_options->compression_manager->Name(),
new_options->compression_manager->Name(), GetBuiltinV2CompressionManager()->Name());
GetBuiltinCompressionManager(/*compression_format_version*/ 2)->Name());
ColumnFamilyOptions rnd_filled_options = *new_options; ColumnFamilyOptions rnd_filled_options = *new_options;
@@ -977,7 +977,10 @@ struct BlockBasedTableBuilder::Rep {
assert(mgr); assert(mgr);
// Use newer compression_name property // Use newer compression_name property
props.compression_name.reserve(32); props.compression_name.reserve(32);
props.compression_name.append(mgr->CompatibilityName()); // If compression is disabled, use empty manager name
if (basic_compressor) {
props.compression_name.append(mgr->CompatibilityName());
}
props.compression_name.push_back(';'); props.compression_name.push_back(';');
// Rest of property to be filled out at the end of building the file // Rest of property to be filled out at the end of building the file
} else { } else {
+11 -4
View File
@@ -606,7 +606,8 @@ Status GetDecompressor(const std::string& compression_name,
if (!s.ok()) { if (!s.ok()) {
return s; return s;
} }
assert(mgr_to_use); assert(mgr_to_use || compatibility_name == kNullptrString ||
compatibility_name.empty());
} }
// Second field is set of compression types actually used in the file // Second field is set of compression types actually used in the file
@@ -632,9 +633,15 @@ Status GetDecompressor(const std::string& compression_name,
} }
ctypes[i] = static_cast<CompressionType>(val); ctypes[i] = static_cast<CompressionType>(val);
} }
*out_decompressor = if (mgr_to_use) {
mgr_to_use->GetDecompressorForTypes(ctypes.get(), ctypes.get() + count); *out_decompressor = mgr_to_use->GetDecompressorForTypes(
assert(*out_decompressor || count == 0); ctypes.get(), ctypes.get() + count);
assert(*out_decompressor || count == 0);
} else {
// Compression/decompression disabled
*out_decompressor = nullptr;
assert(count == 0);
}
// Can ignore possible additional future fields // Can ignore possible additional future fields
} else { } else {
// No explicit CompressionManager, e.g. legacy file support where // No explicit CompressionManager, e.g. legacy file support where
+6 -1
View File
@@ -157,10 +157,15 @@ inline uint32_t GetCompressFormatForVersion(uint32_t format_version) {
// As of format_version 2, we encode compressed block with // As of format_version 2, we encode compressed block with
// compress_format_version == 2. Before that, the version is 1. // compress_format_version == 2. Before that, the version is 1.
// DO NOT CHANGE THIS FUNCTION, it affects disk format // DO NOT CHANGE THIS FUNCTION, it affects disk format
// As of format_version 7 and opening up to custom compression, the
// compression format version is essentially independent of the block-based
// table format version, and encoded in the compression_name table property.
// Thus, this function can go away once we remove support for reading
// format_version=1.
return format_version >= 2 ? 2 : 1; return format_version >= 2 ? 2 : 1;
} }
constexpr uint32_t kLatestFormatVersion = 6; constexpr uint32_t kLatestFormatVersion = 7;
inline bool IsSupportedFormatVersion(uint32_t version) { inline bool IsSupportedFormatVersion(uint32_t version) {
return version <= kLatestFormatVersion; return version <= kLatestFormatVersion;
+15 -4
View File
@@ -1795,18 +1795,23 @@ TEST_P(BlockBasedTableTest, IndexUncompressed) {
#endif // SNAPPY #endif // SNAPPY
TEST_P(BlockBasedTableTest, BlockBasedTableProperties2) { TEST_P(BlockBasedTableTest, BlockBasedTableProperties2) {
TableConstructor c(&reverse_key_comparator); TableConstructor c(&reverse_key_comparator,
true /* convert_to_internal_key_ */);
std::vector<std::string> keys; std::vector<std::string> keys;
stl_wrappers::KVMap kvmap; stl_wrappers::KVMap kvmap;
{ for (CompressionType ct : {kNoCompression, kSnappyCompression}) {
if (!Snappy_Supported() && ct == kSnappyCompression) {
continue;
}
Options options; Options options;
options.compression = CompressionType::kNoCompression; options.compression = ct;
BlockBasedTableOptions table_options = GetBlockBasedTableOptions(); BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
options.table_factory.reset(NewBlockBasedTableFactory(table_options)); options.table_factory.reset(NewBlockBasedTableFactory(table_options));
const ImmutableOptions ioptions(options); const ImmutableOptions ioptions(options);
const MutableCFOptions moptions(options); const MutableCFOptions moptions(options);
c.Add("blah", std::string(200, 'x')); // something to compress
c.Finish(options, ioptions, moptions, table_options, c.Finish(options, ioptions, moptions, table_options,
GetPlainInternalComparator(options.comparator), &keys, &kvmap); GetPlainInternalComparator(options.comparator), &keys, &kvmap);
@@ -1823,7 +1828,13 @@ TEST_P(BlockBasedTableTest, BlockBasedTableProperties2) {
// No filter policy is used // No filter policy is used
ASSERT_EQ("", props.filter_policy_name); ASSERT_EQ("", props.filter_policy_name);
// Compression type == that set: // Compression type == that set:
ASSERT_EQ("NoCompression", props.compression_name); if (FormatVersionUsesCompressionManagerName(table_options.format_version)) {
ASSERT_EQ(ct == kNoCompression ? ";;" : "BuiltinV2;01;",
props.compression_name);
} else {
ASSERT_EQ(ct == kNoCompression ? "NoCompression" : "Snappy",
props.compression_name);
}
c.ResetTableReader(); c.ResetTableReader();
} }
+14 -9
View File
@@ -740,9 +740,9 @@ template <CompressionType kCompression>
struct CompressorCustomAlg : public CompressorWrapper { struct CompressorCustomAlg : public CompressorWrapper {
static bool Supported() { return LZ4_Supported(); } static bool Supported() { return LZ4_Supported(); }
explicit CompressorCustomAlg(std::unique_ptr<Compressor> wrapped = explicit CompressorCustomAlg(
GetDefaultBuiltinCompressionManager() std::unique_ptr<Compressor> wrapped =
->GetCompressor({}, kLZ4Compression)) GetBuiltinV2CompressionManager()->GetCompressor({}, kLZ4Compression))
: CompressorWrapper(std::move(wrapped)), : CompressorWrapper(std::move(wrapped)),
dictionary_hash_(GetSliceHash(wrapped_->GetSerializedDict())) { dictionary_hash_(GetSliceHash(wrapped_->GetSerializedDict())) {
static_assert(kCompression > kLastBuiltinCompression); static_assert(kCompression > kLastBuiltinCompression);
@@ -750,12 +750,16 @@ struct CompressorCustomAlg : public CompressorWrapper {
const char* Name() const override { return "CompressorCustomAlg"; } const char* Name() const override { return "CompressorCustomAlg"; }
CompressionType GetPreferredCompressionType() const override {
return kCompression;
}
Status CompressBlock(Slice uncompressed_data, std::string* compressed_output, Status CompressBlock(Slice uncompressed_data, std::string* compressed_output,
CompressionType* out_compression_type, CompressionType* out_compression_type,
ManagedWorkingArea* working_area) override { ManagedWorkingArea* working_area) override {
Status s = wrapped_->CompressBlock(uncompressed_data, compressed_output, Status s = wrapped_->CompressBlock(uncompressed_data, compressed_output,
out_compression_type, working_area); out_compression_type, working_area);
if (*out_compression_type != kNoCompression) { if (s.ok() && *out_compression_type != kNoCompression) {
assert(*out_compression_type == kLZ4Compression); assert(*out_compression_type == kLZ4Compression);
std::string header(/*size=*/5, 0); std::string header(/*size=*/5, 0);
header[0] = lossless_cast<char>(kCompression); header[0] = lossless_cast<char>(kCompression);
@@ -783,9 +787,8 @@ struct CompressorCustomAlg : public CompressorWrapper {
struct DecompressorCustomAlg : public DecompressorWrapper { struct DecompressorCustomAlg : public DecompressorWrapper {
using TypeSet = SmallEnumSet<CompressionType, kDisableCompressionOption>; using TypeSet = SmallEnumSet<CompressionType, kDisableCompressionOption>;
DecompressorCustomAlg( DecompressorCustomAlg(std::shared_ptr<Decompressor> wrapped =
std::shared_ptr<Decompressor> wrapped = GetBuiltinV2CompressionManager()->GetDecompressor())
GetDefaultBuiltinCompressionManager()->GetDecompressor())
: DecompressorWrapper(std::move(wrapped)), : DecompressorWrapper(std::move(wrapped)),
dictionary_hash_(GetSliceHash(wrapped_->GetSerializedDict())), dictionary_hash_(GetSliceHash(wrapped_->GetSerializedDict())),
allowed_types_(TypeSet::All()) {} allowed_types_(TypeSet::All()) {}
@@ -808,7 +811,8 @@ struct DecompressorCustomAlg : public DecompressorWrapper {
} }
Status ExtractUncompressedSize(Args& args) override { Status ExtractUncompressedSize(Args& args) override {
if (args.compression_type > kLastBuiltinCompression) { if (args.compression_type >= kFirstCustomCompression &&
args.compression_type <= kLastCustomCompression) {
assert(args.compressed_data.size() > 0); assert(args.compressed_data.size() > 0);
assert(args.compressed_data[0] == assert(args.compressed_data[0] ==
lossless_cast<char>(args.compression_type)); lossless_cast<char>(args.compression_type));
@@ -828,7 +832,8 @@ struct DecompressorCustomAlg : public DecompressorWrapper {
} }
Status DecompressBlock(const Args& args, char* uncompressed_output) override { Status DecompressBlock(const Args& args, char* uncompressed_output) override {
if (args.compression_type > kLastBuiltinCompression) { if (args.compression_type >= kFirstCustomCompression &&
args.compression_type <= kLastCustomCompression) {
// Also allowed to copy args and modify // Also allowed to copy args and modify
Args modified_args = args; Args modified_args = args;
modified_args.compression_type = kLZ4Compression; modified_args.compression_type = kLZ4Compression;
+3 -3
View File
@@ -4653,7 +4653,7 @@ class Benchmark {
options.bottommost_compression = kZSTD; options.bottommost_compression = kZSTD;
mgr = std::make_shared<RoundRobinManager>( mgr = std::make_shared<RoundRobinManager>(
GetDefaultBuiltinCompressionManager()); GetBuiltinV2CompressionManager());
} else if (!strcasecmp(FLAGS_compression_manager.c_str(), "autoskip")) { } else if (!strcasecmp(FLAGS_compression_manager.c_str(), "autoskip")) {
options.compression = FLAGS_compression_type_e; options.compression = FLAGS_compression_type_e;
if (FLAGS_compression_type_e == kNoCompression) { if (FLAGS_compression_type_e == kNoCompression) {
@@ -4662,8 +4662,8 @@ class Benchmark {
"autoskip"); "autoskip");
ErrorExit(); ErrorExit();
} }
mgr = CreateAutoSkipCompressionManager( mgr =
GetDefaultBuiltinCompressionManager()); CreateAutoSkipCompressionManager(GetBuiltinV2CompressionManager());
} else { } else {
// not defined -> exit with error // not defined -> exit with error
fprintf(stderr, "Requested compression manager not supported"); fprintf(stderr, "Requested compression manager not supported");
+2 -2
View File
@@ -870,8 +870,8 @@ bool LDBCommand::ParseCompressionTypeOption(
} }
options_.compression = kZSTD; options_.compression = kZSTD;
options_.bottommost_compression = kZSTD; options_.bottommost_compression = kZSTD;
auto mgr = std::make_shared<RoundRobinManager>( auto mgr =
GetDefaultBuiltinCompressionManager()); std::make_shared<RoundRobinManager>(GetBuiltinV2CompressionManager());
options_.compression_manager = mgr; options_.compression_manager = mgr;
// Need to list zstd in the compression_name table property if it's // Need to list zstd in the compression_name table property if it's
@@ -0,0 +1 @@
* Add new `format_version=7` to aid experimental support of custom compression algorithms with CompressionManager and block-based table. This format version includes changing the format of `TableProperties::compression_name`.
+1 -1
View File
@@ -126,6 +126,6 @@ std::unique_ptr<Compressor> AutoSkipCompressorManager::GetCompressorForSST(
std::shared_ptr<CompressionManagerWrapper> CreateAutoSkipCompressionManager( std::shared_ptr<CompressionManagerWrapper> CreateAutoSkipCompressionManager(
std::shared_ptr<CompressionManager> wrapped) { std::shared_ptr<CompressionManager> wrapped) {
return std::make_shared<AutoSkipCompressorManager>( return std::make_shared<AutoSkipCompressorManager>(
wrapped == nullptr ? GetDefaultBuiltinCompressionManager() : wrapped); wrapped == nullptr ? GetBuiltinV2CompressionManager() : wrapped);
} }
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
+1 -2
View File
@@ -1043,8 +1043,7 @@ const std::shared_ptr<CompressionManager>& GetBuiltinCompressionManager(
} }
} }
const std::shared_ptr<CompressionManager>& const std::shared_ptr<CompressionManager>& GetBuiltinV2CompressionManager() {
GetDefaultBuiltinCompressionManager() {
return GetBuiltinCompressionManager(2); return GetBuiltinCompressionManager(2);
} }
+5 -2
View File
@@ -754,8 +754,11 @@ inline std::string CompressionTypeToString(CompressionType compression_type) {
case kDisableCompressionOption: case kDisableCompressionOption:
return "DisableOption"; return "DisableOption";
default: { default: {
char c = lossless_cast<char>(compression_type); bool is_custom = compression_type >= kFirstCustomCompression &&
return "Custom" + Slice(&c, 1).ToString(/*hex=*/true); compression_type <= kLastCustomCompression;
unsigned char c = lossless_cast<unsigned char>(compression_type);
return (is_custom ? "Custom" : "Reserved") +
ToBaseCharsString<16>(2, c, /*uppercase=*/true);
} }
} }
} }
+1 -1
View File
@@ -158,7 +158,7 @@ class DBAutoSkip : public DBTestBase {
rnd_(231), rnd_(231),
key_index_(0) { key_index_(0) {
options.compression_manager = options.compression_manager =
CreateAutoSkipCompressionManager(GetDefaultBuiltinCompressionManager()); CreateAutoSkipCompressionManager(GetBuiltinV2CompressionManager());
auto statistics = ROCKSDB_NAMESPACE::CreateDBStatistics(); auto statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.statistics = statistics; options.statistics = statistics;
options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex); options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
+2 -1
View File
@@ -17,7 +17,8 @@ namespace ROCKSDB_NAMESPACE {
// MultiCompressorWrapper implementation // MultiCompressorWrapper implementation
MultiCompressorWrapper::MultiCompressorWrapper(const CompressionOptions& opts, MultiCompressorWrapper::MultiCompressorWrapper(const CompressionOptions& opts,
CompressionDict&& dict) { CompressionDict&& dict) {
auto builtInManager = GetDefaultBuiltinCompressionManager(); // TODO: make the compression manager a field
auto builtInManager = GetBuiltinV2CompressionManager();
const auto& compressions = GetSupportedCompressions(); const auto& compressions = GetSupportedCompressions();
for (auto type : compressions) { for (auto type : compressions) {
if (type == kNoCompression) { if (type == kNoCompression) {
+12
View File
@@ -14,6 +14,7 @@
#include "test_util/testharness.h" #include "test_util/testharness.h"
#include "test_util/testutil.h" #include "test_util/testutil.h"
#include "util/cast_util.h" #include "util/cast_util.h"
#include "util/string_util.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
@@ -410,6 +411,17 @@ TEST(UnownedPtrTest, Tests) {
} }
} }
TEST(ToBaseCharsStringTest, Tests) {
using ROCKSDB_NAMESPACE::ToBaseCharsString;
// Base 16
ASSERT_EQ(ToBaseCharsString<16>(5, 0, true), "00000");
ASSERT_EQ(ToBaseCharsString<16>(5, 42, true), "0002A");
ASSERT_EQ(ToBaseCharsString<16>(5, 42, false), "0002a");
ASSERT_EQ(ToBaseCharsString<16>(2, 255, false), "ff");
// Base 32
ASSERT_EQ(ToBaseCharsString<32>(2, 255, false), "7v");
}
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {
+10
View File
@@ -40,6 +40,16 @@ inline void PutBaseChars(char** buf, size_t n, uint64_t v, bool uppercase) {
*buf += n; *buf += n;
} }
// Construct a string of n digits from v in base kBase
template <size_t kBase>
inline std::string ToBaseCharsString(size_t n, uint64_t v, bool uppercase) {
std::string result;
result.resize(n);
char* buf = &result[0];
PutBaseChars<kBase>(&buf, n, v, uppercase);
return result;
}
// Parse n digits from *buf in base kBase to *v and advance *buf to the // Parse n digits from *buf in base kBase to *v and advance *buf to the
// position after what was read. On success, true is returned. On failure, // position after what was read. On success, true is returned. On failure,
// false is returned, *buf is placed at the first bad character, and *v // false is returned, *buf is placed at the first bad character, and *v