mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
6
Commits
6.7.fb
...
siying-patch-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3a8735494 | ||
|
|
4f6c86226c | ||
|
|
7aa66c704f | ||
|
|
5b33cfa1e3 | ||
|
|
f10f135938 | ||
|
|
986df37135 |
No files matched your search
+3
-1
@@ -1,5 +1,7 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Bug Fixes
|
||||
* Fix incorrect results while block-based table uses kHashSearch, together with Prev()/SeekForPrev().
|
||||
|
||||
## 6.7.0 (01/21/2020)
|
||||
### Public API Change
|
||||
@@ -14,10 +16,10 @@
|
||||
* Fix a race condition for cfd->log_number_ between manifest switch and memtable switch (PR 6249) when number of column families is greater than 1.
|
||||
* Fix a bug on fractional cascading index when multiple files at the same level contain the same smallest user key, and those user keys are for merge operands. In this case, Get() the exact key may miss some merge operands.
|
||||
* Delcare kHashSearch index type feature-incompatible with index_block_restart_interval larger than 1.
|
||||
* Fix incorrect results while block-based table uses kHashSearch, together with Prev()/SeekForPrev().
|
||||
* Fixed an issue where the thread pools were not resized upon setting `max_background_jobs` dynamically through the `SetDBOptions` interface.
|
||||
* Fix a bug that can cause write threads to hang when a slowdown/stall happens and there is a mix of writers with WriteOptions::no_slowdown set/unset.
|
||||
* Fixed an issue where an incorrect "number of input records" value was used to compute the "records dropped" statistics for compactions.
|
||||
* Fix a regression bug that causes segfault when hash is used, max_open_files != -1 and total order seek is used and switched back.
|
||||
|
||||
### New Features
|
||||
* It is now possible to enable periodic compactions for the base DB when using BlobDB.
|
||||
|
||||
@@ -1292,7 +1292,7 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
// suboptimal but still correct.
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Flushing column family with largest mem table size. Write buffer is "
|
||||
"Flushing column family with oldest memtable entry. Write buffer is "
|
||||
"using %" ROCKSDB_PRIszt " bytes out of a total of %" ROCKSDB_PRIszt ".",
|
||||
write_buffer_manager_->memory_usage(),
|
||||
write_buffer_manager_->buffer_size());
|
||||
|
||||
@@ -3174,6 +3174,57 @@ TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, BlockBasedTablePrefixIndexTotalOrderSeek) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
options.max_open_files = 10;
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
|
||||
// RocksDB sanitize max open files to at least 20. Modify it back.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = static_cast<int*>(arg);
|
||||
*max_open_files = 11;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
Flush();
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 1;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
// Force evict tables
|
||||
dbfull()->TEST_table_cache()->SetCapacity(0);
|
||||
// Make table cache to keep one entry.
|
||||
dbfull()->TEST_table_cache()->SetCapacity(1);
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.total_order_seek = true;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->Seek("k1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("k1", iter->key().ToString());
|
||||
}
|
||||
|
||||
// After total order seek, prefix index should still be used.
|
||||
read_options.total_order_seek = false;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->Seek("k1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("k1", iter->key().ToString());
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ChecksumTest) {
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
|
||||
+3
-2
@@ -382,8 +382,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cfd_->ioptions()->compression_opts,
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
TableFileCreationReason::kFlush, event_logger_, job_context_->job_id,
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */, current_time,
|
||||
oldest_key_time, write_hint, current_time);
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */,
|
||||
meta_.oldest_ancester_time, oldest_key_time, write_hint,
|
||||
current_time);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
|
||||
+6
-3
@@ -867,8 +867,7 @@ class LevelIterator final : public InternalIterator {
|
||||
icomparator_(icomparator),
|
||||
user_comparator_(icomparator.user_comparator()),
|
||||
flevel_(flevel),
|
||||
prefix_extractor_(read_options.total_order_seek ? nullptr
|
||||
: prefix_extractor),
|
||||
prefix_extractor_(prefix_extractor),
|
||||
file_read_hist_(file_read_hist),
|
||||
should_sample_(should_sample),
|
||||
caller_(caller),
|
||||
@@ -1003,6 +1002,9 @@ class LevelIterator final : public InternalIterator {
|
||||
const UserComparatorWrapper user_comparator_;
|
||||
const LevelFilesBrief* flevel_;
|
||||
mutable FileDescriptor current_value_;
|
||||
// `prefix_extractor_` may be non-null even for total order seek. Checking
|
||||
// this variable is not the right way to identify whether prefix iterator
|
||||
// is used.
|
||||
const SliceTransform* prefix_extractor_;
|
||||
|
||||
HistogramImpl* file_read_hist_;
|
||||
@@ -1045,7 +1047,8 @@ void LevelIterator::Seek(const Slice& target) {
|
||||
file_iter_.Seek(target);
|
||||
}
|
||||
if (SkipEmptyFileForward() && prefix_extractor_ != nullptr &&
|
||||
file_iter_.iter() != nullptr && file_iter_.Valid()) {
|
||||
!read_options_.total_order_seek && file_iter_.iter() != nullptr &&
|
||||
file_iter_.Valid()) {
|
||||
// We've skipped the file we initially positioned to. In the prefix
|
||||
// seek case, it is likely that the file is skipped because of
|
||||
// prefix bloom or hash, where more keys are skipped. We then check
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
> Note: Please use Issues only for bug reports. For questions, discussions, feature requests, etc. post to dev group: https://www.facebook.com/groups/rocksdb.dev
|
||||
> Note: Please use Issues only for bug reports. For questions, discussions, feature requests, etc. post to dev group: https://groups.google.com/forum/#!forum/rocksdb or https://www.facebook.com/groups/rocksdb.dev
|
||||
|
||||
### Expected behavior
|
||||
|
||||
|
||||
@@ -718,6 +718,7 @@ class HashIndexReader : public BlockBasedTable::IndexReaderCommon {
|
||||
}
|
||||
|
||||
BlockPrefixIndex* prefix_index = nullptr;
|
||||
assert(rep->internal_prefix_transform.get() != nullptr);
|
||||
s = BlockPrefixIndex::Create(rep->internal_prefix_transform.get(),
|
||||
prefixes_contents.data,
|
||||
prefixes_meta_contents.data, &prefix_index);
|
||||
@@ -1187,8 +1188,10 @@ Status BlockBasedTable::Open(
|
||||
rep->hash_index_allow_collision = table_options.hash_index_allow_collision;
|
||||
// We need to wrap data with internal_prefix_transform to make sure it can
|
||||
// handle prefix correctly.
|
||||
rep->internal_prefix_transform.reset(
|
||||
new InternalKeySliceTransform(prefix_extractor));
|
||||
if (prefix_extractor != nullptr) {
|
||||
rep->internal_prefix_transform.reset(
|
||||
new InternalKeySliceTransform(prefix_extractor));
|
||||
}
|
||||
SetupCacheKeyPrefix(rep);
|
||||
std::unique_ptr<BlockBasedTable> new_table(
|
||||
new BlockBasedTable(rep, block_cache_tracer));
|
||||
@@ -4049,7 +4052,13 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
std::unique_ptr<Block> metaindex_guard;
|
||||
std::unique_ptr<InternalIterator> metaindex_iter_guard;
|
||||
auto meta_index_iter = preloaded_meta_index_iter;
|
||||
if (meta_index_iter == nullptr) {
|
||||
bool should_fallback = false;
|
||||
if (rep_->internal_prefix_transform.get() == nullptr) {
|
||||
ROCKS_LOG_WARN(rep_->ioptions.info_log,
|
||||
"No prefix extractor passed in. Fall back to binary"
|
||||
" search index.");
|
||||
should_fallback = true;
|
||||
} else if (meta_index_iter == nullptr) {
|
||||
auto s = ReadMetaIndexBlock(prefetch_buffer, &metaindex_guard,
|
||||
&metaindex_iter_guard);
|
||||
if (!s.ok()) {
|
||||
@@ -4058,16 +4067,20 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
ROCKS_LOG_WARN(rep_->ioptions.info_log,
|
||||
"Unable to read the metaindex block."
|
||||
" Fall back to binary search index.");
|
||||
return BinarySearchIndexReader::Create(this, prefetch_buffer,
|
||||
use_cache, prefetch, pin,
|
||||
lookup_context, index_reader);
|
||||
should_fallback = true;
|
||||
}
|
||||
meta_index_iter = metaindex_iter_guard.get();
|
||||
}
|
||||
|
||||
return HashIndexReader::Create(this, prefetch_buffer, meta_index_iter,
|
||||
use_cache, prefetch, pin, lookup_context,
|
||||
index_reader);
|
||||
if (should_fallback) {
|
||||
return BinarySearchIndexReader::Create(this, prefetch_buffer, use_cache,
|
||||
prefetch, pin, lookup_context,
|
||||
index_reader);
|
||||
} else {
|
||||
return HashIndexReader::Create(this, prefetch_buffer, meta_index_iter,
|
||||
use_cache, prefetch, pin, lookup_context,
|
||||
index_reader);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
std::string error_message =
|
||||
|
||||
@@ -548,6 +548,7 @@ struct BlockBasedTable::Rep {
|
||||
// module should not be relying on db module. However to make things easier
|
||||
// and compatible with existing code, we introduce a wrapper that allows
|
||||
// block to extract prefix without knowing if a key is internal or not.
|
||||
// null if no prefix_extractor is passed in when opening the table reader.
|
||||
std::unique_ptr<SliceTransform> internal_prefix_transform;
|
||||
std::shared_ptr<const SliceTransform> table_prefix_extractor;
|
||||
|
||||
|
||||
@@ -32,9 +32,8 @@ PartitionedFilterBlockBuilder::PartitionedFilterBlockBuilder(
|
||||
true /*use_delta_encoding*/,
|
||||
use_value_delta_encoding),
|
||||
p_index_builder_(p_index_builder),
|
||||
filters_in_partition_(0),
|
||||
num_added_(0) {
|
||||
filters_per_partition_ =
|
||||
keys_added_to_partition_(0) {
|
||||
keys_per_partition_ =
|
||||
filter_bits_builder_->CalculateNumEntry(partition_size);
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ PartitionedFilterBlockBuilder::~PartitionedFilterBlockBuilder() {}
|
||||
void PartitionedFilterBlockBuilder::MaybeCutAFilterBlock(
|
||||
const Slice* next_key) {
|
||||
// Use == to send the request only once
|
||||
if (filters_in_partition_ == filters_per_partition_) {
|
||||
if (keys_added_to_partition_ == keys_per_partition_) {
|
||||
// Currently only index builder is in charge of cutting a partition. We keep
|
||||
// requesting until it is granted.
|
||||
p_index_builder_->RequestPartitionCut();
|
||||
@@ -65,7 +64,7 @@ void PartitionedFilterBlockBuilder::MaybeCutAFilterBlock(
|
||||
Slice filter = filter_bits_builder_->Finish(&filter_gc.back());
|
||||
std::string& index_key = p_index_builder_->GetPartitionKey();
|
||||
filters.push_back({index_key, filter});
|
||||
filters_in_partition_ = 0;
|
||||
keys_added_to_partition_ = 0;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -75,9 +74,8 @@ void PartitionedFilterBlockBuilder::Add(const Slice& key) {
|
||||
}
|
||||
|
||||
void PartitionedFilterBlockBuilder::AddKey(const Slice& key) {
|
||||
filter_bits_builder_->AddKey(key);
|
||||
filters_in_partition_++;
|
||||
num_added_++;
|
||||
FullFilterBlockBuilder::AddKey(key);
|
||||
keys_added_to_partition_++;
|
||||
}
|
||||
|
||||
Slice PartitionedFilterBlockBuilder::Finish(
|
||||
|
||||
@@ -34,8 +34,6 @@ class PartitionedFilterBlockBuilder : public FullFilterBlockBuilder {
|
||||
void AddKey(const Slice& key) override;
|
||||
void Add(const Slice& key) override;
|
||||
|
||||
size_t NumAdded() const override { return num_added_; }
|
||||
|
||||
virtual Slice Finish(const BlockHandle& last_partition_block_handle,
|
||||
Status* status) override;
|
||||
|
||||
@@ -60,12 +58,10 @@ class PartitionedFilterBlockBuilder : public FullFilterBlockBuilder {
|
||||
// optimizations did not realize we can use different number of partitions and
|
||||
// eliminate p_index_builder_
|
||||
PartitionedIndexBuilder* const p_index_builder_;
|
||||
// The desired number of filters per partition
|
||||
uint32_t filters_per_partition_;
|
||||
// The current number of filters in the last partition
|
||||
uint32_t filters_in_partition_;
|
||||
// Number of keys added
|
||||
size_t num_added_;
|
||||
// The desired number of keys per partition
|
||||
uint32_t keys_per_partition_;
|
||||
// The number of keys added to the last partition so far
|
||||
uint32_t keys_added_to_partition_;
|
||||
BlockHandle last_encoded_handle_;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user