Report if unpinnable value encountered during backward iteration (#7618)
Summary: There is an undocumented behavior about a certain combination of options and operations. - inplace_update_support = true, and - call `SeekForPrev()`, `SeekToLast()`, and/or `Prev()` on unflushed data. We should stop the backward iteration and report an error of `Status::NotSupported`. Pull Request resolved: https://github.com/facebook/rocksdb/pull/7618 Test Plan: make check Reviewed By: pdillinger Differential Revision: D24769619 Pulled By: riversand963 fbshipit-source-id: 81d199fa55ed4739ab10e719cc345a992238ccbb
This commit is contained in:
parent
18aee7db7e
commit
bcba372352
@ -811,12 +811,19 @@ bool DBIter::FindValueForCurrentKey() {
|
|||||||
ikey, RangeDelPositioningMode::kBackwardTraversal)) {
|
ikey, RangeDelPositioningMode::kBackwardTraversal)) {
|
||||||
last_key_entry_type = kTypeRangeDeletion;
|
last_key_entry_type = kTypeRangeDeletion;
|
||||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||||
} else {
|
} else if (iter_.iter()->IsValuePinned()) {
|
||||||
assert(iter_.iter()->IsValuePinned());
|
|
||||||
pinned_value_ = iter_.value();
|
pinned_value_ = iter_.value();
|
||||||
|
} else {
|
||||||
|
valid_ = false;
|
||||||
|
status_ = Status::NotSupported(
|
||||||
|
"Backward iteration not supported if underlying iterator's value "
|
||||||
|
"cannot be pinned.");
|
||||||
}
|
}
|
||||||
merge_context_.Clear();
|
merge_context_.Clear();
|
||||||
last_not_merge_type = last_key_entry_type;
|
last_not_merge_type = last_key_entry_type;
|
||||||
|
if (!status_.ok()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case kTypeDeletion:
|
case kTypeDeletion:
|
||||||
case kTypeSingleDeletion:
|
case kTypeSingleDeletion:
|
||||||
|
@ -3021,6 +3021,44 @@ TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
|||||||
delete iter;
|
delete iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DBIteratorTest, BackwardIterationOnInplaceUpdateMemtable) {
|
||||||
|
Options options = CurrentOptions();
|
||||||
|
options.create_if_missing = true;
|
||||||
|
options.inplace_update_support = false;
|
||||||
|
options.env = env_;
|
||||||
|
DestroyAndReopen(options);
|
||||||
|
constexpr int kNumKeys = 10;
|
||||||
|
|
||||||
|
// Write kNumKeys to WAL.
|
||||||
|
for (int i = 0; i < kNumKeys; ++i) {
|
||||||
|
ASSERT_OK(Put(Key(i), "val"));
|
||||||
|
}
|
||||||
|
ReadOptions read_opts;
|
||||||
|
read_opts.total_order_seek = true;
|
||||||
|
{
|
||||||
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||||
|
int count = 0;
|
||||||
|
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(kNumKeys, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen and rebuild the memtable from WAL.
|
||||||
|
options.create_if_missing = false;
|
||||||
|
options.avoid_flush_during_recovery = true;
|
||||||
|
options.inplace_update_support = true;
|
||||||
|
options.allow_concurrent_memtable_write = false;
|
||||||
|
Reopen(options);
|
||||||
|
{
|
||||||
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||||
|
iter->SeekToLast();
|
||||||
|
// Backward iteration not supported due to inplace_update_support = true.
|
||||||
|
ASSERT_TRUE(iter->status().IsNotSupported());
|
||||||
|
ASSERT_FALSE(iter->Valid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
@ -237,6 +237,7 @@ struct AdvancedColumnFamilyOptions {
|
|||||||
// achieve point-in-time consistency using snapshot or iterator (assuming
|
// achieve point-in-time consistency using snapshot or iterator (assuming
|
||||||
// concurrent updates). Hence iterator and multi-get will return results
|
// concurrent updates). Hence iterator and multi-get will return results
|
||||||
// which are not consistent as of any point-in-time.
|
// which are not consistent as of any point-in-time.
|
||||||
|
// Backward iteration on memtables will not work either.
|
||||||
// If inplace_callback function is not set,
|
// If inplace_callback function is not set,
|
||||||
// Put(key, new_value) will update inplace the existing_value iff
|
// Put(key, new_value) will update inplace the existing_value iff
|
||||||
// * key exists in current memtable
|
// * key exists in current memtable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user