Deprecate CompactionFilterV2

Summary: It has been around for a while and it looks like it never found any uses in the wild. It's also complicating our compaction_job code quite a bit. We're deprecating it in 3.13, but will put it back in 3.14 if we actually find users that need this feature.

Test Plan: make check

Reviewers: noetzli, yhchiang, sdong

Reviewed By: sdong

Subscribers: dhruba, leveldb

Differential Revision: https://reviews.facebook.net/D42405
main
Igor Canadi 9 years ago
parent 1d20fa9d0f
commit a96fcd09b7
  1. 1
      HISTORY.md
  2. 140
      db/c.cc
  3. 117
      db/c_test.c
  4. 17
      db/compaction.cc
  5. 4
      db/compaction.h
  6. 334
      db/compaction_job.cc
  7. 18
      db/compaction_job.h
  8. 304
      db/db_compaction_filter_test.cc
  9. 3
      db/db_impl.cc
  10. 37
      include/rocksdb/c.h
  11. 2
      include/rocksdb/immutable_options.h
  12. 8
      include/rocksdb/options.h
  13. 1
      include/rocksdb/thread_status.h
  14. 6
      util/options.cc
  15. 2
      util/thread_operation.h

@ -6,6 +6,7 @@
* Deprecated WriteOptions::timeout_hint_us. We no longer support write timeout. If you really need this option, talk to us and we might consider returning it.
* Deprecated purge_redundant_kvs_while_flush option.
* Removed BackupEngine::NewBackupEngine() and NewReadOnlyBackupEngine() that were deprecated in RocksDB 3.8. Please use BackupEngine::Open() instead.
* Deprecated Compaction Filter V2. We are not aware of any existing use-cases. If you use this filter, your compile will break with RocksDB 3.13. Please let us know if you use it and we'll put it back in RocksDB 3.14.
## 3.12.0 (7/2/2015)
### New Features

@ -39,8 +39,6 @@ using rocksdb::ColumnFamilyHandle;
using rocksdb::ColumnFamilyOptions;
using rocksdb::CompactionFilter;
using rocksdb::CompactionFilterFactory;
using rocksdb::CompactionFilterV2;
using rocksdb::CompactionFilterFactoryV2;
using rocksdb::CompactionFilterContext;
using rocksdb::CompactionOptionsFIFO;
using rocksdb::Comparator;
@ -172,99 +170,6 @@ struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
virtual const char* Name() const override { return (*name_)(state_); }
};
struct rocksdb_compactionfilterv2_t : public CompactionFilterV2 {
void* state_;
void (*destructor_)(void*);
const char* (*name_)(void*);
void (*filter_)(void*, int level, size_t num_keys,
const char* const* keys_list, const size_t* keys_list_sizes,
const char* const* existing_values_list, const size_t* existing_values_list_sizes,
char** new_values_list, size_t* new_values_list_sizes,
unsigned char* to_delete_list);
virtual ~rocksdb_compactionfilterv2_t() {
(*destructor_)(state_);
}
virtual const char* Name() const override { return (*name_)(state_); }
virtual std::vector<bool> Filter(
int level, const SliceVector& keys, const SliceVector& existing_values,
std::vector<std::string>* new_values,
std::vector<bool>* values_changed) const override {
// Make a vector pointing to the underlying key data.
size_t num_keys = keys.size();
std::vector<const char*> keys_list(num_keys);
std::vector<size_t> keys_list_sizes(num_keys);
for (size_t i = 0; i < num_keys; ++i) {
keys_list[i] = keys[i].data();
keys_list_sizes[i] = keys[i].size();
}
// Make a vector pointing to the underlying value data.
std::vector<const char*> existing_values_list(num_keys);
std::vector<size_t> existing_values_list_sizes(num_keys);
for (size_t i = 0; i < num_keys; ++i) {
existing_values_list[i] = existing_values[i].data();
existing_values_list_sizes[i] = existing_values[i].size();
}
// Make a vector which will accept newly-allocated char* arrays
// which we will take ownership of and assign to strings in new_values.
new_values->clear();
std::vector<char*> new_values_list(num_keys);
std::vector<size_t> new_values_list_sizes(num_keys);
// Resize values_changed to hold all keys.
values_changed->resize(num_keys);
// Make a vector for bools indicating a value should be deleted
// on compaction (true) or maintained (false).
std::vector<unsigned char> to_delete_list(num_keys);
(*filter_)(
state_, level, num_keys, &keys_list[0], &keys_list_sizes[0],
&existing_values_list[0], &existing_values_list_sizes[0],
&new_values_list[0], &new_values_list_sizes[0], &to_delete_list[0]);
// Now, we transfer any changed values, setting values_changed and
// initializing new_values in the event a value changed.
std::vector<bool> to_delete(num_keys);
for (size_t i = 0; i < num_keys; ++i) {
to_delete[i] = to_delete_list[i];
(*values_changed)[i] = new_values_list[i] != nullptr;
if ((*values_changed)[i]) {
new_values->push_back(std::string(new_values_list[i], new_values_list_sizes[i]));
free(new_values_list[i]);
}
}
return to_delete;
}
};
struct rocksdb_compactionfilterfactoryv2_t : public CompactionFilterFactoryV2 {
void* state_;
void (*destructor_)(void*);
const char* (*name_)(void*);
rocksdb_compactionfilterv2_t* (*create_compaction_filter_v2_)(
void* state, const rocksdb_compactionfiltercontext_t* context);
rocksdb_compactionfilterfactoryv2_t(const SliceTransform* prefix_extractor)
: CompactionFilterFactoryV2(prefix_extractor) {
}
virtual ~rocksdb_compactionfilterfactoryv2_t() {
(*destructor_)(state_);
}
virtual const char* Name() const override { return (*name_)(state_); }
virtual std::unique_ptr<CompactionFilterV2> CreateCompactionFilterV2(
const CompactionFilterContext& context) override {
struct rocksdb_compactionfiltercontext_t c_context;
c_context.rep.is_full_compaction = context.is_full_compaction;
c_context.rep.is_manual_compaction = context.is_manual_compaction;
return std::unique_ptr<CompactionFilterV2>(
(*create_compaction_filter_v2_)(state_, &c_context));
}
};
struct rocksdb_comparator_t : public Comparator {
void* state_;
void (*destructor_)(void*);
@ -1490,11 +1395,6 @@ void rocksdb_options_set_merge_operator(
opt->rep.merge_operator = std::shared_ptr<MergeOperator>(merge_operator);
}
void rocksdb_options_set_compaction_filter_factory_v2(
rocksdb_options_t* opt,
rocksdb_compactionfilterfactoryv2_t* compaction_filter_factory_v2) {
opt->rep.compaction_filter_factory_v2 = std::shared_ptr<CompactionFilterFactoryV2>(compaction_filter_factory_v2);
}
void rocksdb_options_set_create_if_missing(
rocksdb_options_t* opt, unsigned char v) {
@ -2007,46 +1907,6 @@ void rocksdb_compactionfilterfactory_destroy(
delete factory;
}
rocksdb_compactionfilterv2_t* rocksdb_compactionfilterv2_create(
void* state,
void (*destructor)(void*),
void (*filter)(void*, int level, size_t num_keys,
const char* const* keys_list, const size_t* keys_list_sizes,
const char* const* existing_values_list, const size_t* existing_values_list_sizes,
char** new_values_list, size_t* new_values_list_sizes,
unsigned char* to_delete_list),
const char* (*name)(void*)) {
rocksdb_compactionfilterv2_t* result = new rocksdb_compactionfilterv2_t;
result->state_ = state;
result->destructor_ = destructor;
result->filter_ = filter;
result->name_ = name;
return result;
}
void rocksdb_compactionfilterv2_destroy(rocksdb_compactionfilterv2_t* filter) {
delete filter;
}
rocksdb_compactionfilterfactoryv2_t* rocksdb_compactionfilterfactoryv2_create(
void* state,
rocksdb_slicetransform_t* prefix_extractor,
void (*destructor)(void*),
rocksdb_compactionfilterv2_t* (*create_compaction_filter_v2)(
void* state, const rocksdb_compactionfiltercontext_t* context),
const char* (*name)(void*)) {
rocksdb_compactionfilterfactoryv2_t* result = new rocksdb_compactionfilterfactoryv2_t(prefix_extractor);
result->state_ = state;
result->destructor_ = destructor;
result->create_compaction_filter_v2_ = create_compaction_filter_v2;
result->name_ = name;
return result;
}
void rocksdb_compactionfilterfactoryv2_destroy(rocksdb_compactionfilterfactoryv2_t* factory) {
delete factory;
}
rocksdb_comparator_t* rocksdb_comparator_create(
void* state,
void (*destructor)(void*),

@ -251,79 +251,6 @@ static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
return db;
}
// Custom compaction filter V2.
static void CompactionFilterV2Destroy(void* arg) { }
static const char* CompactionFilterV2Name(void* arg) {
return "TestCompactionFilterV2";
}
static void CompactionFilterV2Filter(
void* arg, int level, size_t num_keys,
const char* const* keys_list, const size_t* keys_list_sizes,
const char* const* existing_values_list, const size_t* existing_values_list_sizes,
char** new_values_list, size_t* new_values_list_sizes,
unsigned char* to_delete_list) {
size_t i;
for (i = 0; i < num_keys; i++) {
// If any value is "gc", it's removed.
if (existing_values_list_sizes[i] == 2 && memcmp(existing_values_list[i], "gc", 2) == 0) {
to_delete_list[i] = 1;
} else if (existing_values_list_sizes[i] == 6 && memcmp(existing_values_list[i], "gc all", 6) == 0) {
// If any value is "gc all", all keys are removed.
size_t j;
for (j = 0; j < num_keys; j++) {
to_delete_list[j] = 1;
}
return;
} else if (existing_values_list_sizes[i] == 6 && memcmp(existing_values_list[i], "change", 6) == 0) {
// If value is "change", set changed value to "changed".
size_t len;
len = strlen("changed");
new_values_list[i] = malloc(len);
memcpy(new_values_list[i], "changed", len);
new_values_list_sizes[i] = len;
} else {
// Otherwise, no keys are removed.
}
}
}
// Custom prefix extractor for compaction filter V2 which extracts first 3 characters.
static void CFV2PrefixExtractorDestroy(void* arg) { }
static char* CFV2PrefixExtractorTransform(void* arg, const char* key, size_t length, size_t* dst_length) {
// Verify keys are maximum length 4; this verifies fix for a
// prior bug which was passing the RocksDB-encoded key with
// logical timestamp suffix instead of parsed user key.
if (length > 4) {
fprintf(stderr, "%s:%d: %s: key %s is not user key\n", __FILE__, __LINE__, phase, key);
abort();
}
*dst_length = length < 3 ? length : 3;
return (char*)key;
}
static unsigned char CFV2PrefixExtractorInDomain(void* state, const char* key, size_t length) {
return 1;
}
static unsigned char CFV2PrefixExtractorInRange(void* state, const char* key, size_t length) {
return 1;
}
static const char* CFV2PrefixExtractorName(void* state) {
return "TestCFV2PrefixExtractor";
}
// Custom compaction filter factory V2.
static void CompactionFilterFactoryV2Destroy(void* arg) {
rocksdb_slicetransform_destroy((rocksdb_slicetransform_t*)arg);
}
static const char* CompactionFilterFactoryV2Name(void* arg) {
return "TestCompactionFilterFactoryV2";
}
static rocksdb_compactionfilterv2_t* CompactionFilterFactoryV2Create(
void* state, const rocksdb_compactionfiltercontext_t* context) {
return rocksdb_compactionfilterv2_create(state, CompactionFilterV2Destroy,
CompactionFilterV2Filter,
CompactionFilterV2Name);
}
// Custom merge operator
static void MergeOperatorDestroy(void* arg) { }
static const char* MergeOperatorName(void* arg) {
@ -722,50 +649,6 @@ int main(int argc, char** argv) {
rocksdb_options_destroy(options_with_filter_factory);
}
StartPhase("compaction_filter_v2");
{
rocksdb_compactionfilterfactoryv2_t* factory;
rocksdb_slicetransform_t* prefix_extractor;
prefix_extractor = rocksdb_slicetransform_create(
NULL, CFV2PrefixExtractorDestroy, CFV2PrefixExtractorTransform,
CFV2PrefixExtractorInDomain, CFV2PrefixExtractorInRange,
CFV2PrefixExtractorName);
factory = rocksdb_compactionfilterfactoryv2_create(
prefix_extractor, prefix_extractor, CompactionFilterFactoryV2Destroy,
CompactionFilterFactoryV2Create, CompactionFilterFactoryV2Name);
// Create new database
rocksdb_close(db);
rocksdb_destroy_db(options, dbname, &err);
rocksdb_options_set_compaction_filter_factory_v2(options, factory);
db = rocksdb_open(options, dbname, &err);
CheckNoError(err);
// Only foo2 is GC'd, foo3 is changed.
rocksdb_put(db, woptions, "foo1", 4, "no gc", 5, &err);
CheckNoError(err);
rocksdb_put(db, woptions, "foo2", 4, "gc", 2, &err);
CheckNoError(err);
rocksdb_put(db, woptions, "foo3", 4, "change", 6, &err);
CheckNoError(err);
// All bars are GC'd.
rocksdb_put(db, woptions, "bar1", 4, "no gc", 5, &err);
CheckNoError(err);
rocksdb_put(db, woptions, "bar2", 4, "gc all", 6, &err);
CheckNoError(err);
rocksdb_put(db, woptions, "bar3", 4, "no gc", 5, &err);
CheckNoError(err);
// Compact the DB to garbage collect.
rocksdb_compact_range(db, NULL, 0, NULL, 0);
// Verify foo entries.
CheckGet(db, roptions, "foo1", "no gc");
CheckGet(db, roptions, "foo2", NULL);
CheckGet(db, roptions, "foo3", "changed");
// Verify bar entries were all deleted.
CheckGet(db, roptions, "bar1", NULL);
CheckGet(db, roptions, "bar2", NULL);
CheckGet(db, roptions, "bar3", NULL);
}
StartPhase("merge_operator");
{
rocksdb_mergeoperator_t* merge_operator;

@ -160,8 +160,7 @@ bool Compaction::IsTrivialMove() const {
if (is_manual_compaction_ &&
(cfd_->ioptions()->compaction_filter != nullptr ||
cfd_->ioptions()->compaction_filter_factory != nullptr ||
cfd_->ioptions()->compaction_filter_factory_v2 != nullptr)) {
cfd_->ioptions()->compaction_filter_factory != nullptr)) {
// This is a manual compaction and we have a compaction filter that should
// be executed, we cannot do a trivial move
return false;
@ -376,18 +375,4 @@ std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
context);
}
std::unique_ptr<CompactionFilterV2>
Compaction::CreateCompactionFilterV2() const {
if (!cfd_->ioptions()->compaction_filter_factory_v2) {
return nullptr;
}
CompactionFilterContext context;
context.is_full_compaction = is_full_compaction_;
context.is_manual_compaction = is_manual_compaction_;
return
cfd_->ioptions()->compaction_filter_factory_v2->CreateCompactionFilterV2(
context);
}
} // namespace rocksdb

@ -30,7 +30,6 @@ class Version;
class ColumnFamilyData;
class VersionStorageInfo;
class CompactionFilter;
class CompactionFilterV2;
// A Compaction encapsulates information about a compaction.
class Compaction {
@ -195,9 +194,6 @@ class Compaction {
// Create a CompactionFilter from compaction_filter_factory
std::unique_ptr<CompactionFilter> CreateCompactionFilter() const;
// Create a CompactionFilterV2 from compaction_filter_factory_v2
std::unique_ptr<CompactionFilterV2> CreateCompactionFilterV2() const;
private:
// mark (or clear) all files that are being compacted
void MarkFilesBeingCompacted(bool mark_as_compacted);

@ -84,102 +84,8 @@ struct CompactionJob::CompactionState {
num_input_records(0),
num_output_records(0) {}
std::vector<std::string> key_str_buf_;
std::vector<std::string> existing_value_str_buf_;
// new_value_buf_ will only be appended if a value changes
std::vector<std::string> new_value_buf_;
// if values_changed_buf_[i] is true
// new_value_buf_ will add a new entry with the changed value
std::vector<bool> value_changed_buf_;
// to_delete_buf_[i] is true iff key_buf_[i] is deleted
std::vector<bool> to_delete_buf_;
std::vector<std::string> other_key_str_buf_;
std::vector<std::string> other_value_str_buf_;
std::vector<Slice> combined_key_buf_;
std::vector<Slice> combined_value_buf_;
std::string cur_prefix_;
uint64_t num_input_records;
uint64_t num_output_records;
// Buffers the kv-pair that will be run through compaction filter V2
// in the future.
void BufferKeyValueSlices(const Slice& key, const Slice& value) {
key_str_buf_.emplace_back(key.ToString());
existing_value_str_buf_.emplace_back(value.ToString());
}
// Buffers the kv-pair that will not be run through compaction filter V2
// in the future.
void BufferOtherKeyValueSlices(const Slice& key, const Slice& value) {
other_key_str_buf_.emplace_back(key.ToString());
other_value_str_buf_.emplace_back(value.ToString());
}
// Add a kv-pair to the combined buffer
void AddToCombinedKeyValueSlices(const Slice& key, const Slice& value) {
// The real strings are stored in the batch buffers
combined_key_buf_.emplace_back(key);
combined_value_buf_.emplace_back(value);
}
// Merging the two buffers
void MergeKeyValueSliceBuffer(const InternalKeyComparator* comparator) {
size_t i = 0;
size_t j = 0;
size_t total_size = key_str_buf_.size() + other_key_str_buf_.size();
combined_key_buf_.reserve(total_size);
combined_value_buf_.reserve(total_size);
while (i + j < total_size) {
int comp_res = 0;
if (i < key_str_buf_.size() && j < other_key_str_buf_.size()) {
comp_res = comparator->Compare(key_str_buf_[i], other_key_str_buf_[j]);
} else if (i >= key_str_buf_.size() && j < other_key_str_buf_.size()) {
comp_res = 1;
} else if (j >= other_key_str_buf_.size() && i < key_str_buf_.size()) {
comp_res = -1;
}
if (comp_res > 0) {
AddToCombinedKeyValueSlices(other_key_str_buf_[j],
other_value_str_buf_[j]);
j++;
} else if (comp_res < 0) {
AddToCombinedKeyValueSlices(key_str_buf_[i],
existing_value_str_buf_[i]);
i++;
}
}
}
void CleanupBatchBuffer() {
to_delete_buf_.clear();
key_str_buf_.clear();
existing_value_str_buf_.clear();
new_value_buf_.clear();
value_changed_buf_.clear();
to_delete_buf_.shrink_to_fit();
key_str_buf_.shrink_to_fit();
existing_value_str_buf_.shrink_to_fit();
new_value_buf_.shrink_to_fit();
value_changed_buf_.shrink_to_fit();
other_key_str_buf_.clear();
other_value_str_buf_.clear();
other_key_str_buf_.shrink_to_fit();
other_value_str_buf_.shrink_to_fit();
}
void CleanupMergedBuffer() {
combined_key_buf_.clear();
combined_value_buf_.clear();
combined_key_buf_.shrink_to_fit();
combined_value_buf_.shrink_to_fit();
}
};
CompactionJob::CompactionJob(
@ -271,8 +177,6 @@ void CompactionJob::ReportStartedCompaction(
void CompactionJob::Prepare() {
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_PREPARE);
compact_->CleanupBatchBuffer();
compact_->CleanupMergedBuffer();
// Generate file_levels_ for compaction berfore making Iterator
ColumnFamilyData* cfd __attribute__((unused)) =
@ -316,18 +220,8 @@ Status CompactionJob::Run() {
versions_->MakeInputIterator(compact_->compaction));
input->SeekToFirst();
std::unique_ptr<CompactionFilterV2> compaction_filter_from_factory_v2 =
compact_->compaction->CreateCompactionFilterV2();
auto compaction_filter_v2 = compaction_filter_from_factory_v2.get();
Status status;
int64_t imm_micros = 0; // Micros spent doing imm_ compactions
if (!compaction_filter_v2) {
status = ProcessKeyValueCompaction(&imm_micros, input.get(), false);
} else {
status = ProcessPrefixBatches(cfd, &imm_micros, input.get(),
compaction_filter_v2);
}
auto status = ProcessKeyValueCompaction(&imm_micros, input.get());
if (status.ok() &&
(shutting_down_->load(std::memory_order_acquire) || cfd->IsDropped())) {
@ -418,141 +312,10 @@ void CompactionJob::Install(Status* status,
CleanupCompaction(*status);
}
Status CompactionJob::ProcessPrefixBatches(
ColumnFamilyData* cfd,
int64_t* imm_micros,
Iterator* input,
CompactionFilterV2* compaction_filter_v2) {
// temp_backup_input always point to the start of the current buffer
// temp_backup_input = backup_input;
// iterate through input,
// 1) buffer ineligible keys and value keys into 2 separate buffers;
// 2) send value_buffer to compaction filter and alternate the values;
// 3) merge value_buffer with ineligible_value_buffer;
// 4) run the modified "compaction" using the old for loop.
ParsedInternalKey ikey;
Status status;
bool prefix_initialized = false;
shared_ptr<Iterator> backup_input(
versions_->MakeInputIterator(compact_->compaction));
backup_input->SeekToFirst();
uint64_t total_filter_time = 0;
while (backup_input->Valid() &&
!shutting_down_->load(std::memory_order_acquire) &&
!cfd->IsDropped()) {
// FLUSH preempts compaction
// TODO(icanadi) this currently only checks if flush is necessary on
// compacting column family. we should also check if flush is necessary on
// other column families, too
imm_micros += yield_callback_();
Slice key = backup_input->key();
Slice value = backup_input->value();
if (!ParseInternalKey(key, &ikey)) {
// log error
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
"[%s] [JOB %d] Failed to parse key: %s", cfd->GetName().c_str(),
job_id_, key.ToString().c_str());
continue;
} else {
const SliceTransform* transformer =
cfd->ioptions()->compaction_filter_factory_v2->GetPrefixExtractor();
const auto key_prefix = transformer->Transform(ikey.user_key);
if (!prefix_initialized) {
compact_->cur_prefix_ = key_prefix.ToString();
prefix_initialized = true;
}
// If the prefix remains the same, keep buffering
if (key_prefix.compare(Slice(compact_->cur_prefix_)) == 0) {
// Apply the compaction filter V2 to all the kv pairs sharing
// the same prefix
if (ikey.type == kTypeValue &&
(visible_at_tip_ || ikey.sequence > latest_snapshot_)) {
// Buffer all keys sharing the same prefix for CompactionFilterV2
// Iterate through keys to check prefix
compact_->BufferKeyValueSlices(key, value);
} else {
// buffer ineligible keys
compact_->BufferOtherKeyValueSlices(key, value);
}
backup_input->Next();
continue;
// finish changing values for eligible keys
} else {
// Now prefix changes, this batch is done.
// Call compaction filter on the buffered values to change the value
if (compact_->key_str_buf_.size() > 0) {
uint64_t time = 0;
CallCompactionFilterV2(compaction_filter_v2, &time);
total_filter_time += time;
}
compact_->cur_prefix_ = key_prefix.ToString();
}
}
// Merge this batch of data (values + ineligible keys)
compact_->MergeKeyValueSliceBuffer(&cfd->internal_comparator());
// Done buffering for the current prefix. Spit it out to disk
// Now just iterate through all the kv-pairs
status = ProcessKeyValueCompaction(imm_micros, input, true);
if (!status.ok()) {
break;
}
// After writing the kv-pairs, we can safely remove the reference
// to the string buffer and clean them up
compact_->CleanupBatchBuffer();
compact_->CleanupMergedBuffer();
// Buffer the key that triggers the mismatch in prefix
if (ikey.type == kTypeValue &&
(visible_at_tip_ || ikey.sequence > latest_snapshot_)) {
compact_->BufferKeyValueSlices(key, value);
} else {
compact_->BufferOtherKeyValueSlices(key, value);
}
backup_input->Next();
if (!backup_input->Valid()) {
// If this is the single last value, we need to merge it.
if (compact_->key_str_buf_.size() > 0) {
uint64_t time = 0;
CallCompactionFilterV2(compaction_filter_v2, &time);
total_filter_time += time;
}
compact_->MergeKeyValueSliceBuffer(&cfd->internal_comparator());
status = ProcessKeyValueCompaction(imm_micros, input, true);
if (!status.ok()) {
break;
}
compact_->CleanupBatchBuffer();
compact_->CleanupMergedBuffer();
}
} // done processing all prefix batches
// finish the last batch
if (status.ok()) {
if (compact_->key_str_buf_.size() > 0) {
uint64_t time = 0;
CallCompactionFilterV2(compaction_filter_v2, &time);
total_filter_time += time;
}
compact_->MergeKeyValueSliceBuffer(&cfd->internal_comparator());
status = ProcessKeyValueCompaction(imm_micros, input, true);
}
RecordTick(stats_, FILTER_OPERATION_TOTAL_TIME, total_filter_time);
return status;
}
Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
Iterator* input,
bool is_compaction_v2) {
Iterator* input) {
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
size_t combined_idx = 0;
Status status;
std::string compaction_filter_value;
ParsedInternalKey ikey;
@ -599,26 +362,8 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
// on other column families, too
(*imm_micros) += yield_callback_();
Slice key;
Slice value;
// If is_compaction_v2 is on, kv-pairs are reset to the prefix batch.
// This prefix batch should contain results after calling
// compaction_filter_v2.
//
// If is_compaction_v2 is off, this function will go through all the
// kv-pairs in input.
if (!is_compaction_v2) {
key = input->key();
value = input->value();
} else {
if (combined_idx >= compact_->combined_key_buf_.size()) {
break;
}
key = compact_->combined_key_buf_[combined_idx];
value = compact_->combined_value_buf_[combined_idx];
++combined_idx;
}
Slice key = input->key();
Slice value = input->value();
if (compaction_job_stats_ != nullptr) {
compaction_job_stats_->total_input_raw_key_bytes +=
@ -660,7 +405,7 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
last_sequence_for_key = kMaxSequenceNumber;
visible_in_snapshot = kMaxSequenceNumber;
// apply the compaction filter to the first occurrence of the user key
if (compaction_filter && !is_compaction_v2 && ikey.type == kTypeValue &&
if (compaction_filter && ikey.type == kTypeValue &&
(visible_at_tip_ || ikey.sequence > latest_snapshot_)) {
// If the user has specified a compaction filter and the sequence
// number is greater than any external snapshot, then invoke the
@ -738,11 +483,8 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
// object to minimize change to the existing flow. Turn out this
// logic could also be nicely re-used for memtable flush purge
// optimization in BuildTable.
int steps = 0;
merge.MergeUntil(input, prev_snapshot, bottommost_level_,
db_options_.statistics.get(), &steps, env_);
// Skip the Merge ops
combined_idx = combined_idx - 1 + steps;
db_options_.statistics.get(), nullptr, env_);
current_entry_is_merging = true;
if (merge.IsSuccess()) {
@ -899,70 +641,6 @@ void CompactionJob::RecordDroppedKeys(
}
}
void CompactionJob::CallCompactionFilterV2(
CompactionFilterV2* compaction_filter_v2, uint64_t* time) {
if (compact_ == nullptr || compaction_filter_v2 == nullptr) {
return;
}
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_FILTER_V2);
// Assemble slice vectors for user keys and existing values.
// We also keep track of our parsed internal key structs because
// we may need to access the sequence number in the event that
// keys are garbage collected during the filter process.
std::vector<ParsedInternalKey> ikey_buf;
std::vector<Slice> user_key_buf;
std::vector<Slice> existing_value_buf;
for (const auto& key : compact_->key_str_buf_) {
ParsedInternalKey ikey;
ParseInternalKey(Slice(key), &ikey);
ikey_buf.emplace_back(ikey);
user_key_buf.emplace_back(ikey.user_key);
}
for (const auto& value : compact_->existing_value_str_buf_) {
existing_value_buf.emplace_back(Slice(value));
}
// If the user has specified a compaction filter and the sequence
// number is greater than any external snapshot, then invoke the
// filter.
// If the return value of the compaction filter is true, replace
// the entry with a delete marker.
StopWatchNano timer(env_, stats_ != nullptr);
compact_->to_delete_buf_ = compaction_filter_v2->Filter(
compact_->compaction->level(), user_key_buf, existing_value_buf,
&compact_->new_value_buf_, &compact_->value_changed_buf_);
*time = timer.ElapsedNanos();
// new_value_buf_.size() <= to_delete__buf_.size(). "=" iff all
// kv-pairs in this compaction run needs to be deleted.
assert(compact_->to_delete_buf_.size() == compact_->key_str_buf_.size());
assert(compact_->to_delete_buf_.size() ==
compact_->existing_value_str_buf_.size());
assert(compact_->value_changed_buf_.empty() ||
compact_->to_delete_buf_.size() ==
compact_->value_changed_buf_.size());
int new_value_idx = 0;
for (unsigned int i = 0; i < compact_->to_delete_buf_.size(); ++i) {
if (compact_->to_delete_buf_[i]) {
// update the string buffer directly
// the Slice buffer points to the updated buffer
UpdateInternalKey(&compact_->key_str_buf_[i], ikey_buf[i].sequence,
kTypeDeletion);
// no value associated with delete
compact_->existing_value_str_buf_[i].clear();
RecordTick(stats_, COMPACTION_KEY_DROP_USER);
} else if (!compact_->value_changed_buf_.empty() &&
compact_->value_changed_buf_[i]) {
compact_->existing_value_str_buf_[i] =
compact_->new_value_buf_[new_value_idx++];
}
} // for
}
Status CompactionJob::FinishCompactionOutputFile(const Status& input_status) {
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_SYNC_FILE);

@ -83,23 +83,15 @@ class CompactionJob {
// update the thread status for starting a compaction.
void ReportStartedCompaction(Compaction* compaction);
void AllocateCompactionOutputFileNumbers();
// Processes batches of keys with the same prefixes. This is used for
// CompactionFilterV2.
Status ProcessPrefixBatches(ColumnFamilyData* cfd,
int64_t* imm_micros,
Iterator* input,
CompactionFilterV2* compaction_filter_v2);
// Call compaction filter if is_compaction_v2 is not true. Then iterate
// through input and compact the kv-pairs
Status ProcessKeyValueCompaction(int64_t* imm_micros, Iterator* input,
bool is_compaction_v2);
// Call compaction filter. Then iterate through input and compact the
// kv-pairs
Status ProcessKeyValueCompaction(int64_t* imm_micros, Iterator* input);
Status WriteKeyValue(const Slice& key, const Slice& value,
const ParsedInternalKey& ikey,
const Status& input_status);
// Call compaction_filter_v2->Filter() on kv-pairs in compact
void CallCompactionFilterV2(CompactionFilterV2* compaction_filter_v2,
uint64_t* time);
Status FinishCompactionOutputFile(const Status& input_status);
Status InstallCompactionResults(InstrumentedMutex* db_mutex,
const MutableCFOptions& mutable_cf_options);

@ -538,310 +538,6 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
}
}
class KeepFilterV2 : public CompactionFilterV2 {
public:
virtual std::vector<bool> Filter(int level,
const SliceVector& keys,
const SliceVector& existing_values,
std::vector<std::string>* new_values,
std::vector<bool>* values_changed)
const override {
cfilter_count++;
std::vector<bool> ret;
new_values->clear();
values_changed->clear();
for (unsigned int i = 0; i < keys.size(); ++i) {
values_changed->push_back(false);
ret.push_back(false);
}
return ret;
}
virtual const char* Name() const override {
return "KeepFilterV2";
}
};
class DeleteFilterV2 : public CompactionFilterV2 {
public:
virtual std::vector<bool> Filter(int level,
const SliceVector& keys,
const SliceVector& existing_values,
std::vector<std::string>* new_values,
std::vector<bool>* values_changed)
const override {
cfilter_count++;
new_values->clear();
values_changed->clear();
std::vector<bool> ret;
for (unsigned int i = 0; i < keys.size(); ++i) {
values_changed->push_back(false);
ret.push_back(true);
}
return ret;
}
virtual const char* Name() const override {
return "DeleteFilterV2";
}
};
class ChangeFilterV2 : public CompactionFilterV2 {
public:
virtual std::vector<bool> Filter(int level,
const SliceVector& keys,
const SliceVector& existing_values,
std::vector<std::string>* new_values,
std::vector<bool>* values_changed)
const override {
std::vector<bool> ret;
new_values->clear();
values_changed->clear();
for (unsigned int i = 0; i < keys.size(); ++i) {
values_changed->push_back(true);
new_values->push_back(NEW_VALUE);
ret.push_back(false);
}
return ret;
}
virtual const char* Name() const override {
return "ChangeFilterV2";
}
};
class KeepFilterFactoryV2 : public CompactionFilterFactoryV2 {
public:
explicit KeepFilterFactoryV2(const SliceTransform* prefix_extractor)
: CompactionFilterFactoryV2(prefix_extractor) { }
virtual std::unique_ptr<CompactionFilterV2>
CreateCompactionFilterV2(
const CompactionFilterContext& context) override {
return std::unique_ptr<CompactionFilterV2>(new KeepFilterV2());
}
virtual const char* Name() const override {
return "KeepFilterFactoryV2";
}
};
class DeleteFilterFactoryV2 : public CompactionFilterFactoryV2 {
public:
explicit DeleteFilterFactoryV2(const SliceTransform* prefix_extractor)
: CompactionFilterFactoryV2(prefix_extractor) { }
virtual std::unique_ptr<CompactionFilterV2>
CreateCompactionFilterV2(
const CompactionFilterContext& context) override {
return std::unique_ptr<CompactionFilterV2>(new DeleteFilterV2());
}
virtual const char* Name() const override {
return "DeleteFilterFactoryV2";
}
};
class ChangeFilterFactoryV2 : public CompactionFilterFactoryV2 {
public:
explicit ChangeFilterFactoryV2(const SliceTransform* prefix_extractor)
: CompactionFilterFactoryV2(prefix_extractor) { }
virtual std::unique_ptr<CompactionFilterV2>
CreateCompactionFilterV2(
const CompactionFilterContext& context) override {
return std::unique_ptr<CompactionFilterV2>(new ChangeFilterV2());
}
virtual const char* Name() const override {
return "ChangeFilterFactoryV2";
}
};
TEST_F(DBTestCompactionFilter, CompactionFilterV2) {
Options options = CurrentOptions();
options.num_levels = 3;
options.max_mem_compaction_level = 0;
// extract prefix
std::unique_ptr<const SliceTransform> prefix_extractor;
prefix_extractor.reset(NewFixedPrefixTransform(8));
options.compaction_filter_factory_v2
= std::make_shared<KeepFilterFactoryV2>(prefix_extractor.get());
// In a testing environment, we can only flush the application
// compaction filter buffer using universal compaction
option_config_ = kUniversalCompaction;
options.compaction_style = (rocksdb::CompactionStyle)1;
Reopen(options);
// Write 100K keys, these are written to a few files in L0.
const std::string value(10, 'x');
for (int i = 0; i < 100000; i++) {
char key[100];
snprintf(key, sizeof(key), "B%08d%010d", i , i);
Put(key, value);
}
dbfull()->TEST_FlushMemTable();
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
dbfull()->TEST_CompactRange(1, nullptr, nullptr);
ASSERT_EQ(NumSortedRuns(0), 1);
// All the files are in the lowest level.
int count = 0;
int total = 0;
{
Arena arena;
ScopedArenaIterator iter(dbfull()->TEST_NewInternalIterator(&arena));
iter->SeekToFirst();
ASSERT_OK(iter->status());
while (iter->Valid()) {
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
ikey.sequence = -1;
ASSERT_EQ(ParseInternalKey(iter->key(), &ikey), true);
total++;
if (ikey.sequence != 0) {
count++;
}
iter->Next();
}
}
ASSERT_EQ(total, 100000);
// 1 snapshot only. Since we are using universal compacton,
// the sequence no is cleared for better compression
ASSERT_EQ(count, 1);
// create a new database with the compaction
// filter in such a way that it deletes all keys
options.compaction_filter_factory_v2 =
std::make_shared<DeleteFilterFactoryV2>(prefix_extractor.get());
options.create_if_missing = true;
DestroyAndReopen(options);
// write all the keys once again.
for (int i = 0; i < 100000; i++) {
char key[100];
snprintf(key, sizeof(key), "B%08d%010d", i, i);
Put(key, value);
}
dbfull()->TEST_FlushMemTable();
ASSERT_NE(NumTableFilesAtLevel(0), 0);
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
dbfull()->TEST_CompactRange(1, nullptr, nullptr);
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
// Scan the entire database to ensure that nothing is left
Iterator* iter = db_->NewIterator(ReadOptions());
iter->SeekToFirst();
count = 0;
while (iter->Valid()) {
count++;
iter->Next();
}
ASSERT_EQ(count, 0);
delete iter;
}
TEST_F(DBTestCompactionFilter, CompactionFilterV2WithValueChange) {
Options options = CurrentOptions();
options.num_levels = 3;
options.max_mem_compaction_level = 0;
std::unique_ptr<const SliceTransform> prefix_extractor;
prefix_extractor.reset(NewFixedPrefixTransform(8));
options.compaction_filter_factory_v2 =
std::make_shared<ChangeFilterFactoryV2>(prefix_extractor.get());
// In a testing environment, we can only flush the application
// compaction filter buffer using universal compaction
option_config_ = kUniversalCompaction;
options.compaction_style = (rocksdb::CompactionStyle)1;
options = CurrentOptions(options);
Reopen(options);
// Write 100K+1 keys, these are written to a few files
// in L0. We do this so that the current snapshot points
// to the 100001 key.The compaction filter is not invoked
// on keys that are visible via a snapshot because we
// anyways cannot delete it.
const std::string value(10, 'x');
for (int i = 0; i < 100001; i++) {
char key[100];
snprintf(key, sizeof(key), "B%08d%010d", i, i);
Put(key, value);
}
// push all files to lower levels
dbfull()->TEST_FlushMemTable();
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
dbfull()->TEST_CompactRange(1, nullptr, nullptr);
// verify that all keys now have the new value that
// was set by the compaction process.
for (int i = 0; i < 100001; i++) {
char key[100];
snprintf(key, sizeof(key), "B%08d%010d", i, i);
std::string newvalue = Get(key);
ASSERT_EQ(newvalue.compare(NEW_VALUE), 0);
}
}
TEST_F(DBTestCompactionFilter, CompactionFilterV2NULLPrefix) {
Options options = CurrentOptions();
options.num_levels = 3;
options.max_mem_compaction_level = 0;
std::unique_ptr<const SliceTransform> prefix_extractor;
prefix_extractor.reset(NewFixedPrefixTransform(8));
options.compaction_filter_factory_v2 =
std::make_shared<ChangeFilterFactoryV2>(prefix_extractor.get());
// In a testing environment, we can only flush the application
// compaction filter buffer using universal compaction
option_config_ = kUniversalCompaction;
options.compaction_style = (rocksdb::CompactionStyle)1;
Reopen(options);
// Write 100K+1 keys, these are written to a few files
// in L0. We do this so that the current snapshot points
// to the 100001 key.The compaction filter is not invoked
// on keys that are visible via a snapshot because we
// anyways cannot delete it.
const std::string value(10, 'x');
char first_key[100];
snprintf(first_key, sizeof(first_key), "%s0000%010d", "NULL", 1);
Put(first_key, value);
for (int i = 1; i < 100000; i++) {
char key[100];
snprintf(key, sizeof(key), "%08d%010d", i, i);
Put(key, value);
}
char last_key[100];
snprintf(last_key, sizeof(last_key), "%s0000%010d", "NULL", 2);
Put(last_key, value);
// push all files to lower levels
dbfull()->TEST_FlushMemTable();
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
// verify that all keys now have the new value that
// was set by the compaction process.
std::string newvalue = Get(first_key);
ASSERT_EQ(newvalue.compare(NEW_VALUE), 0);
newvalue = Get(last_key);
ASSERT_EQ(newvalue.compare(NEW_VALUE), 0);
for (int i = 1; i < 100000; i++) {
char key[100];
snprintf(key, sizeof(key), "%08d%010d", i, i);
newvalue = Get(key);
ASSERT_EQ(newvalue.compare(NEW_VALUE), 0);
}
}
} // namespace rocksdb
int main(int argc, char** argv) {
#if !(defined NDEBUG) || !defined(OS_WIN)
rocksdb::port::InstallStackTraceHandler();

@ -1446,8 +1446,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
} else if (options.bottommost_level_compaction ==
BottommostLevelCompaction::kIfHaveCompactionFilter &&
cfd->ioptions()->compaction_filter == nullptr &&
cfd->ioptions()->compaction_filter_factory == nullptr &&
cfd->ioptions()->compaction_filter_factory_v2 == nullptr) {
cfd->ioptions()->compaction_filter_factory == nullptr) {
// Skip bottommost level compaction since we dont have
// compaction filter
continue;

@ -80,10 +80,6 @@ typedef struct rocksdb_compactionfiltercontext_t
rocksdb_compactionfiltercontext_t;
typedef struct rocksdb_compactionfilterfactory_t
rocksdb_compactionfilterfactory_t;
typedef struct rocksdb_compactionfilterv2_t
rocksdb_compactionfilterv2_t;
typedef struct rocksdb_compactionfilterfactoryv2_t
rocksdb_compactionfilterfactoryv2_t;
typedef struct rocksdb_comparator_t rocksdb_comparator_t;
typedef struct rocksdb_env_t rocksdb_env_t;
typedef struct rocksdb_fifo_compaction_options_t rocksdb_fifo_compaction_options_t;
@ -494,9 +490,6 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compaction_filter(
rocksdb_options_t*, rocksdb_compactionfilter_t*);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compaction_filter_factory(
rocksdb_options_t*, rocksdb_compactionfilterfactory_t*);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_compaction_filter_factory_v2(
rocksdb_options_t*, rocksdb_compactionfilterfactoryv2_t*);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_comparator(
rocksdb_options_t*, rocksdb_comparator_t*);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_merge_operator(
@ -740,36 +733,6 @@ rocksdb_compactionfilterfactory_create(
extern ROCKSDB_LIBRARY_API void rocksdb_compactionfilterfactory_destroy(
rocksdb_compactionfilterfactory_t*);
/* Compaction Filter V2 */
extern ROCKSDB_LIBRARY_API rocksdb_compactionfilterv2_t*
rocksdb_compactionfilterv2_create(
void* state, void (*destructor)(void*),
// num_keys specifies the number of array entries in every *list parameter.
// New values added to the new_values_list should be malloc'd and will be
// freed by the caller. Specify true in the to_delete_list to remove an
// entry during compaction; false to keep it.
void (*filter)(void*, int level, size_t num_keys,
const char* const* keys_list, const size_t* keys_list_sizes,
const char* const* existing_values_list,
const size_t* existing_values_list_sizes,
char** new_values_list, size_t* new_values_list_sizes,
unsigned char* to_delete_list),
const char* (*name)(void*));
extern void rocksdb_compactionfilterv2_destroy(rocksdb_compactionfilterv2_t*);
/* Compaction Filter Factory V2 */
extern ROCKSDB_LIBRARY_API rocksdb_compactionfilterfactoryv2_t*
rocksdb_compactionfilterfactoryv2_create(
void* state, rocksdb_slicetransform_t* prefix_extractor,
void (*destructor)(void*),
rocksdb_compactionfilterv2_t* (*create_compaction_filter_v2)(
void*, const rocksdb_compactionfiltercontext_t* context),
const char* (*name)(void*));
extern ROCKSDB_LIBRARY_API void rocksdb_compactionfilterfactoryv2_destroy(
rocksdb_compactionfilterfactoryv2_t*);
/* Comparator */
extern ROCKSDB_LIBRARY_API rocksdb_comparator_t* rocksdb_comparator_create(

@ -35,8 +35,6 @@ struct ImmutableCFOptions {
CompactionFilterFactory* compaction_filter_factory;
CompactionFilterFactoryV2* compaction_filter_factory_v2;
bool inplace_update_support;
UpdateStatus (*inplace_callback)(char* existing_value,

@ -213,11 +213,9 @@ struct ColumnFamilyOptions {
// Default: nullptr
std::shared_ptr<CompactionFilterFactory> compaction_filter_factory;
// Version TWO of the compaction_filter_factory
// It supports rolling compaction
//
// Default: nullptr
std::shared_ptr<CompactionFilterFactoryV2> compaction_filter_factory_v2;
// This is deprecated. Talk to us if you depend on
// compaction_filter_factory_v2 and we'll put it back
// std::shared_ptr<CompactionFilterFactoryV2> compaction_filter_factory_v2;
// -------------------
// Parameters that affect performance

@ -67,7 +67,6 @@ struct ThreadStatus {
STAGE_COMPACTION_PREPARE,
STAGE_COMPACTION_RUN,
STAGE_COMPACTION_PROCESS_KV,
STAGE_COMPACTION_FILTER_V2,
STAGE_COMPACTION_INSTALL,
STAGE_COMPACTION_SYNC_FILE,
STAGE_PICK_MEMTABLES_TO_FLUSH,

@ -44,7 +44,6 @@ ImmutableCFOptions::ImmutableCFOptions(const Options& options)
merge_operator(options.merge_operator.get()),
compaction_filter(options.compaction_filter),
compaction_filter_factory(options.compaction_filter_factory.get()),
compaction_filter_factory_v2(options.compaction_filter_factory_v2.get()),
inplace_update_support(options.inplace_update_support),
inplace_callback(options.inplace_callback),
info_log(options.info_log.get()),
@ -79,7 +78,6 @@ ColumnFamilyOptions::ColumnFamilyOptions()
merge_operator(nullptr),
compaction_filter(nullptr),
compaction_filter_factory(nullptr),
compaction_filter_factory_v2(nullptr),
write_buffer_size(4 << 20),
max_write_buffer_number(2),
min_write_buffer_number_to_merge(1),
@ -132,7 +130,6 @@ ColumnFamilyOptions::ColumnFamilyOptions(const Options& options)
merge_operator(options.merge_operator),
compaction_filter(options.compaction_filter),
compaction_filter_factory(options.compaction_filter_factory),
compaction_filter_factory_v2(options.compaction_filter_factory_v2),
write_buffer_size(options.write_buffer_size),
max_write_buffer_number(options.max_write_buffer_number),
min_write_buffer_number_to_merge(
@ -383,9 +380,6 @@ void ColumnFamilyOptions::Dump(Logger* log) const {
compaction_filter ? compaction_filter->Name() : "None");
Warn(log, " Options.compaction_filter_factory: %s",
compaction_filter_factory ? compaction_filter_factory->Name() : "None");
Warn(log, " Options.compaction_filter_factory_v2: %s",
compaction_filter_factory_v2 ? compaction_filter_factory_v2->Name()
: "None");
Warn(log, " Options.memtable_factory: %s", memtable_factory->Name());
Warn(log, " Options.table_factory: %s", table_factory->Name());
Warn(log, " table_factory options: %s",

@ -61,8 +61,6 @@ static OperationStageInfo global_op_stage_table[] = {
"CompactionJob::Run"},
{ThreadStatus::STAGE_COMPACTION_PROCESS_KV,
"CompactionJob::ProcessKeyValueCompaction"},
{ThreadStatus::STAGE_COMPACTION_FILTER_V2,
"CompactionJob::CallCompactionFilterV2"},
{ThreadStatus::STAGE_COMPACTION_INSTALL,
"CompactionJob::Install"},
{ThreadStatus::STAGE_COMPACTION_SYNC_FILE,

Loading…
Cancel
Save