Add timestamp support to DBImplReadOnly (#10004)
Summary: This PR adds timestamp support to a read only DB instance opened as `DBImplReadOnly`. A follow up PR will add the same support to `CompactedDBImpl`. With this, read only database has these timestamp related APIs: `ReadOptions.timestamp` : read should return the latest data visible to this specified timestamp `Iterator::timestamp()` : returns the timestamp associated with the key, value `DB:Get(..., std::string* timestamp)` : returns the timestamp associated with the key, value in `timestamp` Test plan (on devserver): ``` $COMPILE_WITH_ASAN=1 make -j24 all $./db_with_timestamp_basic_test --gtest_filter=DBBasicTestWithTimestamp.ReadOnlyDB* ``` Pull Request resolved: https://github.com/facebook/rocksdb/pull/10004 Reviewed By: riversand963 Differential Revision: D36434422 Pulled By: jowlyzhang fbshipit-source-id: 5d949e65b1ffb845758000e2b310fdd4aae71cfbmain
parent
57997ddaaf
commit
16bdb1f999
@ -0,0 +1,331 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
|
||||||
|
#include "db/db_with_timestamp_test_util.h" |
||||||
|
#include "test_util/testutil.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
class DBReadOnlyTestWithTimestamp : public DBBasicTestWithTimestampBase { |
||||||
|
public: |
||||||
|
DBReadOnlyTestWithTimestamp() |
||||||
|
: DBBasicTestWithTimestampBase("db_readonly_test_with_timestamp") {} |
||||||
|
}; |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
TEST_F(DBReadOnlyTestWithTimestamp, IteratorAndGetReadTimestampSizeMismatch) { |
||||||
|
const int kNumKeysPerFile = 128; |
||||||
|
const uint64_t kMaxKey = 1024; |
||||||
|
Options options = CurrentOptions(); |
||||||
|
options.env = env_; |
||||||
|
options.create_if_missing = true; |
||||||
|
const size_t kTimestampSize = Timestamp(0, 0).size(); |
||||||
|
TestComparator test_cmp(kTimestampSize); |
||||||
|
options.comparator = &test_cmp; |
||||||
|
options.memtable_factory.reset( |
||||||
|
test::NewSpecialSkipListFactory(kNumKeysPerFile)); |
||||||
|
DestroyAndReopen(options); |
||||||
|
const std::string write_timestamp = Timestamp(1, 0); |
||||||
|
WriteOptions write_opts; |
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
Status s = db_->Put(write_opts, Key1(key), write_timestamp, |
||||||
|
"value" + std::to_string(key)); |
||||||
|
ASSERT_OK(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Reopen the database in read only mode to test its timestamp support.
|
||||||
|
Close(); |
||||||
|
ASSERT_OK(ReadOnlyReopen(options)); |
||||||
|
ReadOptions read_opts; |
||||||
|
std::string different_size_read_timestamp; |
||||||
|
PutFixed32(&different_size_read_timestamp, 2); |
||||||
|
Slice different_size_read_ts = different_size_read_timestamp; |
||||||
|
read_opts.timestamp = &different_size_read_ts; |
||||||
|
{ |
||||||
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts)); |
||||||
|
ASSERT_FALSE(iter->Valid()); |
||||||
|
ASSERT_TRUE(iter->status().IsInvalidArgument()); |
||||||
|
} |
||||||
|
|
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
std::string value_from_get; |
||||||
|
std::string timestamp; |
||||||
|
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp) |
||||||
|
.IsInvalidArgument()); |
||||||
|
} |
||||||
|
|
||||||
|
Close(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DBReadOnlyTestWithTimestamp, |
||||||
|
IteratorAndGetReadTimestampSpecifiedWithoutWriteTimestamp) { |
||||||
|
const int kNumKeysPerFile = 128; |
||||||
|
const uint64_t kMaxKey = 1024; |
||||||
|
Options options = CurrentOptions(); |
||||||
|
options.env = env_; |
||||||
|
options.create_if_missing = true; |
||||||
|
options.memtable_factory.reset( |
||||||
|
test::NewSpecialSkipListFactory(kNumKeysPerFile)); |
||||||
|
DestroyAndReopen(options); |
||||||
|
WriteOptions write_opts; |
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key)); |
||||||
|
ASSERT_OK(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Reopen the database in read only mode to test its timestamp support.
|
||||||
|
Close(); |
||||||
|
ASSERT_OK(ReadOnlyReopen(options)); |
||||||
|
ReadOptions read_opts; |
||||||
|
const std::string read_timestamp = Timestamp(2, 0); |
||||||
|
Slice read_ts = read_timestamp; |
||||||
|
read_opts.timestamp = &read_ts; |
||||||
|
{ |
||||||
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts)); |
||||||
|
ASSERT_FALSE(iter->Valid()); |
||||||
|
ASSERT_TRUE(iter->status().IsInvalidArgument()); |
||||||
|
} |
||||||
|
|
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
std::string value_from_get; |
||||||
|
std::string timestamp; |
||||||
|
ASSERT_TRUE(db_->Get(read_opts, Key1(key), &value_from_get, ×tamp) |
||||||
|
.IsInvalidArgument()); |
||||||
|
} |
||||||
|
|
||||||
|
Close(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DBReadOnlyTestWithTimestamp, IteratorAndGet) { |
||||||
|
const int kNumKeysPerFile = 128; |
||||||
|
const uint64_t kMaxKey = 1024; |
||||||
|
Options options = CurrentOptions(); |
||||||
|
options.env = env_; |
||||||
|
options.create_if_missing = true; |
||||||
|
const size_t kTimestampSize = Timestamp(0, 0).size(); |
||||||
|
TestComparator test_cmp(kTimestampSize); |
||||||
|
options.comparator = &test_cmp; |
||||||
|
options.memtable_factory.reset( |
||||||
|
test::NewSpecialSkipListFactory(kNumKeysPerFile)); |
||||||
|
DestroyAndReopen(options); |
||||||
|
const std::vector<uint64_t> start_keys = {1, 0}; |
||||||
|
const std::vector<std::string> write_timestamps = {Timestamp(1, 0), |
||||||
|
Timestamp(3, 0)}; |
||||||
|
const std::vector<std::string> read_timestamps = {Timestamp(2, 0), |
||||||
|
Timestamp(4, 0)}; |
||||||
|
for (size_t i = 0; i < write_timestamps.size(); ++i) { |
||||||
|
WriteOptions write_opts; |
||||||
|
for (uint64_t key = start_keys[i]; key <= kMaxKey; ++key) { |
||||||
|
Status s = db_->Put(write_opts, Key1(key), write_timestamps[i], |
||||||
|
"value" + std::to_string(i)); |
||||||
|
ASSERT_OK(s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Reopen the database in read only mode to test its timestamp support.
|
||||||
|
Close(); |
||||||
|
ASSERT_OK(ReadOnlyReopen(options)); |
||||||
|
|
||||||
|
auto get_value_and_check = [](DB* db, ReadOptions read_opts, Slice key, |
||||||
|
Slice expected_value, std::string expected_ts) { |
||||||
|
std::string value_from_get; |
||||||
|
std::string timestamp; |
||||||
|
ASSERT_OK(db->Get(read_opts, key.ToString(), &value_from_get, ×tamp)); |
||||||
|
ASSERT_EQ(expected_value, value_from_get); |
||||||
|
ASSERT_EQ(expected_ts, timestamp); |
||||||
|
}; |
||||||
|
for (size_t i = 0; i < read_timestamps.size(); ++i) { |
||||||
|
ReadOptions read_opts; |
||||||
|
Slice read_ts = read_timestamps[i]; |
||||||
|
read_opts.timestamp = &read_ts; |
||||||
|
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts)); |
||||||
|
int count = 0; |
||||||
|
uint64_t key = 0; |
||||||
|
// Forward iterate.
|
||||||
|
for (it->Seek(Key1(0)), key = start_keys[i]; it->Valid(); |
||||||
|
it->Next(), ++count, ++key) { |
||||||
|
CheckIterUserEntry(it.get(), Key1(key), kTypeValue, |
||||||
|
"value" + std::to_string(i), write_timestamps[i]); |
||||||
|
get_value_and_check(db_, read_opts, it->key(), it->value(), |
||||||
|
write_timestamps[i]); |
||||||
|
} |
||||||
|
size_t expected_count = kMaxKey - start_keys[i] + 1; |
||||||
|
ASSERT_EQ(expected_count, count); |
||||||
|
|
||||||
|
// Backward iterate.
|
||||||
|
count = 0; |
||||||
|
for (it->SeekForPrev(Key1(kMaxKey)), key = kMaxKey; it->Valid(); |
||||||
|
it->Prev(), ++count, --key) { |
||||||
|
CheckIterUserEntry(it.get(), Key1(key), kTypeValue, |
||||||
|
"value" + std::to_string(i), write_timestamps[i]); |
||||||
|
get_value_and_check(db_, read_opts, it->key(), it->value(), |
||||||
|
write_timestamps[i]); |
||||||
|
} |
||||||
|
ASSERT_EQ(static_cast<size_t>(kMaxKey) - start_keys[i] + 1, count); |
||||||
|
|
||||||
|
// SeekToFirst()/SeekToLast() with lower/upper bounds.
|
||||||
|
// Then iter with lower and upper bounds.
|
||||||
|
uint64_t l = 0; |
||||||
|
uint64_t r = kMaxKey + 1; |
||||||
|
while (l < r) { |
||||||
|
std::string lb_str = Key1(l); |
||||||
|
Slice lb = lb_str; |
||||||
|
std::string ub_str = Key1(r); |
||||||
|
Slice ub = ub_str; |
||||||
|
read_opts.iterate_lower_bound = &lb; |
||||||
|
read_opts.iterate_upper_bound = &ub; |
||||||
|
it.reset(db_->NewIterator(read_opts)); |
||||||
|
for (it->SeekToFirst(), key = std::max(l, start_keys[i]), count = 0; |
||||||
|
it->Valid(); it->Next(), ++key, ++count) { |
||||||
|
CheckIterUserEntry(it.get(), Key1(key), kTypeValue, |
||||||
|
"value" + std::to_string(i), write_timestamps[i]); |
||||||
|
get_value_and_check(db_, read_opts, it->key(), it->value(), |
||||||
|
write_timestamps[i]); |
||||||
|
} |
||||||
|
ASSERT_EQ(r - std::max(l, start_keys[i]), count); |
||||||
|
|
||||||
|
for (it->SeekToLast(), key = std::min(r, kMaxKey + 1), count = 0; |
||||||
|
it->Valid(); it->Prev(), --key, ++count) { |
||||||
|
CheckIterUserEntry(it.get(), Key1(key - 1), kTypeValue, |
||||||
|
"value" + std::to_string(i), write_timestamps[i]); |
||||||
|
get_value_and_check(db_, read_opts, it->key(), it->value(), |
||||||
|
write_timestamps[i]); |
||||||
|
} |
||||||
|
l += (kMaxKey / 100); |
||||||
|
r -= (kMaxKey / 100); |
||||||
|
} |
||||||
|
} |
||||||
|
Close(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DBReadOnlyTestWithTimestamp, Iterators) { |
||||||
|
const int kNumKeysPerFile = 128; |
||||||
|
const uint64_t kMaxKey = 1024; |
||||||
|
Options options = CurrentOptions(); |
||||||
|
options.env = env_; |
||||||
|
options.create_if_missing = true; |
||||||
|
const size_t kTimestampSize = Timestamp(0, 0).size(); |
||||||
|
TestComparator test_cmp(kTimestampSize); |
||||||
|
options.comparator = &test_cmp; |
||||||
|
options.memtable_factory.reset( |
||||||
|
test::NewSpecialSkipListFactory(kNumKeysPerFile)); |
||||||
|
DestroyAndReopen(options); |
||||||
|
const std::string write_timestamp = Timestamp(1, 0); |
||||||
|
const std::string read_timestamp = Timestamp(2, 0); |
||||||
|
WriteOptions write_opts; |
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
Status s = db_->Put(write_opts, Key1(key), write_timestamp, |
||||||
|
"value" + std::to_string(key)); |
||||||
|
ASSERT_OK(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Reopen the database in read only mode to test its timestamp support.
|
||||||
|
Close(); |
||||||
|
ASSERT_OK(ReadOnlyReopen(options)); |
||||||
|
ReadOptions read_opts; |
||||||
|
Slice read_ts = read_timestamp; |
||||||
|
read_opts.timestamp = &read_ts; |
||||||
|
std::vector<Iterator*> iters; |
||||||
|
ASSERT_OK(db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters)); |
||||||
|
ASSERT_EQ(static_cast<uint64_t>(1), iters.size()); |
||||||
|
|
||||||
|
int count = 0; |
||||||
|
uint64_t key = 0; |
||||||
|
// Forward iterate.
|
||||||
|
for (iters[0]->Seek(Key1(0)), key = 0; iters[0]->Valid(); |
||||||
|
iters[0]->Next(), ++count, ++key) { |
||||||
|
CheckIterUserEntry(iters[0], Key1(key), kTypeValue, |
||||||
|
"value" + std::to_string(key), write_timestamp); |
||||||
|
} |
||||||
|
|
||||||
|
size_t expected_count = kMaxKey - 0 + 1; |
||||||
|
ASSERT_EQ(expected_count, count); |
||||||
|
delete iters[0]; |
||||||
|
|
||||||
|
Close(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DBReadOnlyTestWithTimestamp, IteratorsReadTimestampSizeMismatch) { |
||||||
|
const int kNumKeysPerFile = 128; |
||||||
|
const uint64_t kMaxKey = 1024; |
||||||
|
Options options = CurrentOptions(); |
||||||
|
options.env = env_; |
||||||
|
options.create_if_missing = true; |
||||||
|
const size_t kTimestampSize = Timestamp(0, 0).size(); |
||||||
|
TestComparator test_cmp(kTimestampSize); |
||||||
|
options.comparator = &test_cmp; |
||||||
|
options.memtable_factory.reset( |
||||||
|
test::NewSpecialSkipListFactory(kNumKeysPerFile)); |
||||||
|
DestroyAndReopen(options); |
||||||
|
const std::string write_timestamp = Timestamp(1, 0); |
||||||
|
WriteOptions write_opts; |
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
Status s = db_->Put(write_opts, Key1(key), write_timestamp, |
||||||
|
"value" + std::to_string(key)); |
||||||
|
ASSERT_OK(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Reopen the database in read only mode to test its timestamp support.
|
||||||
|
Close(); |
||||||
|
ASSERT_OK(ReadOnlyReopen(options)); |
||||||
|
ReadOptions read_opts; |
||||||
|
std::string different_size_read_timestamp; |
||||||
|
PutFixed32(&different_size_read_timestamp, 2); |
||||||
|
Slice different_size_read_ts = different_size_read_timestamp; |
||||||
|
read_opts.timestamp = &different_size_read_ts; |
||||||
|
{ |
||||||
|
std::vector<Iterator*> iters; |
||||||
|
ASSERT_TRUE( |
||||||
|
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters) |
||||||
|
.IsInvalidArgument()); |
||||||
|
} |
||||||
|
|
||||||
|
Close(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DBReadOnlyTestWithTimestamp, |
||||||
|
IteratorsReadTimestampSpecifiedWithoutWriteTimestamp) { |
||||||
|
const int kNumKeysPerFile = 128; |
||||||
|
const uint64_t kMaxKey = 1024; |
||||||
|
Options options = CurrentOptions(); |
||||||
|
options.env = env_; |
||||||
|
options.create_if_missing = true; |
||||||
|
options.memtable_factory.reset( |
||||||
|
test::NewSpecialSkipListFactory(kNumKeysPerFile)); |
||||||
|
DestroyAndReopen(options); |
||||||
|
WriteOptions write_opts; |
||||||
|
for (uint64_t key = 0; key <= kMaxKey; ++key) { |
||||||
|
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key)); |
||||||
|
ASSERT_OK(s); |
||||||
|
} |
||||||
|
|
||||||
|
// Reopen the database in read only mode to test its timestamp support.
|
||||||
|
Close(); |
||||||
|
ASSERT_OK(ReadOnlyReopen(options)); |
||||||
|
ReadOptions read_opts; |
||||||
|
const std::string read_timestamp = Timestamp(2, 0); |
||||||
|
Slice read_ts = read_timestamp; |
||||||
|
read_opts.timestamp = &read_ts; |
||||||
|
{ |
||||||
|
std::vector<Iterator*> iters; |
||||||
|
ASSERT_TRUE( |
||||||
|
db_->NewIterators(read_opts, {db_->DefaultColumnFamily()}, &iters) |
||||||
|
.IsInvalidArgument()); |
||||||
|
} |
||||||
|
|
||||||
|
Close(); |
||||||
|
} |
||||||
|
#endif // !ROCKSDB_LITE
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
RegisterCustomObjects(argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
|
||||||
|
#include "db/db_with_timestamp_test_util.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
std::string DBBasicTestWithTimestampBase::Key1(uint64_t k) { |
||||||
|
std::string ret; |
||||||
|
PutFixed64(&ret, k); |
||||||
|
std::reverse(ret.begin(), ret.end()); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DBBasicTestWithTimestampBase::KeyWithPrefix(std::string prefix, |
||||||
|
uint64_t k) { |
||||||
|
std::string ret; |
||||||
|
PutFixed64(&ret, k); |
||||||
|
std::reverse(ret.begin(), ret.end()); |
||||||
|
return prefix + ret; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<Slice> DBBasicTestWithTimestampBase::ConvertStrToSlice( |
||||||
|
std::vector<std::string>& strings) { |
||||||
|
std::vector<Slice> ret; |
||||||
|
for (const auto& s : strings) { |
||||||
|
ret.emplace_back(s); |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
std::string DBBasicTestWithTimestampBase::Timestamp(uint64_t low, |
||||||
|
uint64_t high) { |
||||||
|
std::string ts; |
||||||
|
PutFixed64(&ts, low); |
||||||
|
PutFixed64(&ts, high); |
||||||
|
return ts; |
||||||
|
} |
||||||
|
|
||||||
|
void DBBasicTestWithTimestampBase::CheckIterUserEntry( |
||||||
|
const Iterator* it, const Slice& expected_key, |
||||||
|
ValueType expected_value_type, const Slice& expected_value, |
||||||
|
const Slice& expected_ts) const { |
||||||
|
ASSERT_TRUE(it->Valid()); |
||||||
|
ASSERT_OK(it->status()); |
||||||
|
ASSERT_EQ(expected_key, it->key()); |
||||||
|
if (kTypeValue == expected_value_type) { |
||||||
|
ASSERT_EQ(expected_value, it->value()); |
||||||
|
} |
||||||
|
ASSERT_EQ(expected_ts, it->timestamp()); |
||||||
|
} |
||||||
|
|
||||||
|
void DBBasicTestWithTimestampBase::CheckIterEntry( |
||||||
|
const Iterator* it, const Slice& expected_ukey, SequenceNumber expected_seq, |
||||||
|
ValueType expected_val_type, const Slice& expected_value, |
||||||
|
const Slice& expected_ts) const { |
||||||
|
ASSERT_TRUE(it->Valid()); |
||||||
|
ASSERT_OK(it->status()); |
||||||
|
std::string ukey_and_ts; |
||||||
|
ukey_and_ts.assign(expected_ukey.data(), expected_ukey.size()); |
||||||
|
ukey_and_ts.append(expected_ts.data(), expected_ts.size()); |
||||||
|
ParsedInternalKey parsed_ikey; |
||||||
|
ASSERT_OK(ParseInternalKey(it->key(), &parsed_ikey, true /* log_err_key */)); |
||||||
|
ASSERT_EQ(ukey_and_ts, parsed_ikey.user_key); |
||||||
|
ASSERT_EQ(expected_val_type, parsed_ikey.type); |
||||||
|
ASSERT_EQ(expected_seq, parsed_ikey.sequence); |
||||||
|
if (expected_val_type == kTypeValue) { |
||||||
|
ASSERT_EQ(expected_value, it->value()); |
||||||
|
} |
||||||
|
ASSERT_EQ(expected_ts, it->timestamp()); |
||||||
|
} |
||||||
|
|
||||||
|
void DBBasicTestWithTimestampBase::CheckIterEntry( |
||||||
|
const Iterator* it, const Slice& expected_ukey, ValueType expected_val_type, |
||||||
|
const Slice& expected_value, const Slice& expected_ts) const { |
||||||
|
ASSERT_TRUE(it->Valid()); |
||||||
|
ASSERT_OK(it->status()); |
||||||
|
std::string ukey_and_ts; |
||||||
|
ukey_and_ts.assign(expected_ukey.data(), expected_ukey.size()); |
||||||
|
ukey_and_ts.append(expected_ts.data(), expected_ts.size()); |
||||||
|
|
||||||
|
ParsedInternalKey parsed_ikey; |
||||||
|
ASSERT_OK(ParseInternalKey(it->key(), &parsed_ikey, true /* log_err_key */)); |
||||||
|
ASSERT_EQ(expected_val_type, parsed_ikey.type); |
||||||
|
ASSERT_EQ(Slice(ukey_and_ts), parsed_ikey.user_key); |
||||||
|
if (expected_val_type == kTypeValue) { |
||||||
|
ASSERT_EQ(expected_value, it->value()); |
||||||
|
} |
||||||
|
ASSERT_EQ(expected_ts, it->timestamp()); |
||||||
|
} |
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,126 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "db/db_test_util.h" |
||||||
|
#include "port/stack_trace.h" |
||||||
|
#include "test_util/testutil.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
class DBBasicTestWithTimestampBase : public DBTestBase { |
||||||
|
public: |
||||||
|
explicit DBBasicTestWithTimestampBase(const std::string& dbname) |
||||||
|
: DBTestBase(dbname, /*env_do_fsync=*/true) {} |
||||||
|
|
||||||
|
protected: |
||||||
|
static std::string Key1(uint64_t k); |
||||||
|
|
||||||
|
static std::string KeyWithPrefix(std::string prefix, uint64_t k); |
||||||
|
|
||||||
|
static std::vector<Slice> ConvertStrToSlice( |
||||||
|
std::vector<std::string>& strings); |
||||||
|
|
||||||
|
class TestComparator : public Comparator { |
||||||
|
private: |
||||||
|
const Comparator* cmp_without_ts_; |
||||||
|
|
||||||
|
public: |
||||||
|
explicit TestComparator(size_t ts_sz) |
||||||
|
: Comparator(ts_sz), cmp_without_ts_(nullptr) { |
||||||
|
cmp_without_ts_ = BytewiseComparator(); |
||||||
|
} |
||||||
|
|
||||||
|
const char* Name() const override { return "TestComparator"; } |
||||||
|
|
||||||
|
void FindShortSuccessor(std::string*) const override {} |
||||||
|
|
||||||
|
void FindShortestSeparator(std::string*, const Slice&) const override {} |
||||||
|
|
||||||
|
int Compare(const Slice& a, const Slice& b) const override { |
||||||
|
int r = CompareWithoutTimestamp(a, b); |
||||||
|
if (r != 0 || 0 == timestamp_size()) { |
||||||
|
return r; |
||||||
|
} |
||||||
|
return -CompareTimestamp( |
||||||
|
Slice(a.data() + a.size() - timestamp_size(), timestamp_size()), |
||||||
|
Slice(b.data() + b.size() - timestamp_size(), timestamp_size())); |
||||||
|
} |
||||||
|
|
||||||
|
using Comparator::CompareWithoutTimestamp; |
||||||
|
int CompareWithoutTimestamp(const Slice& a, bool a_has_ts, const Slice& b, |
||||||
|
bool b_has_ts) const override { |
||||||
|
if (a_has_ts) { |
||||||
|
assert(a.size() >= timestamp_size()); |
||||||
|
} |
||||||
|
if (b_has_ts) { |
||||||
|
assert(b.size() >= timestamp_size()); |
||||||
|
} |
||||||
|
Slice lhs = a_has_ts ? StripTimestampFromUserKey(a, timestamp_size()) : a; |
||||||
|
Slice rhs = b_has_ts ? StripTimestampFromUserKey(b, timestamp_size()) : b; |
||||||
|
return cmp_without_ts_->Compare(lhs, rhs); |
||||||
|
} |
||||||
|
|
||||||
|
int CompareTimestamp(const Slice& ts1, const Slice& ts2) const override { |
||||||
|
if (!ts1.data() && !ts2.data()) { |
||||||
|
return 0; |
||||||
|
} else if (ts1.data() && !ts2.data()) { |
||||||
|
return 1; |
||||||
|
} else if (!ts1.data() && ts2.data()) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
assert(ts1.size() == ts2.size()); |
||||||
|
uint64_t low1 = 0; |
||||||
|
uint64_t low2 = 0; |
||||||
|
uint64_t high1 = 0; |
||||||
|
uint64_t high2 = 0; |
||||||
|
const size_t kSize = ts1.size(); |
||||||
|
std::unique_ptr<char[]> ts1_buf(new char[kSize]); |
||||||
|
memcpy(ts1_buf.get(), ts1.data(), ts1.size()); |
||||||
|
std::unique_ptr<char[]> ts2_buf(new char[kSize]); |
||||||
|
memcpy(ts2_buf.get(), ts2.data(), ts2.size()); |
||||||
|
Slice ts1_copy = Slice(ts1_buf.get(), kSize); |
||||||
|
Slice ts2_copy = Slice(ts2_buf.get(), kSize); |
||||||
|
auto* ptr1 = const_cast<Slice*>(&ts1_copy); |
||||||
|
auto* ptr2 = const_cast<Slice*>(&ts2_copy); |
||||||
|
if (!GetFixed64(ptr1, &low1) || !GetFixed64(ptr1, &high1) || |
||||||
|
!GetFixed64(ptr2, &low2) || !GetFixed64(ptr2, &high2)) { |
||||||
|
assert(false); |
||||||
|
} |
||||||
|
if (high1 < high2) { |
||||||
|
return -1; |
||||||
|
} else if (high1 > high2) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (low1 < low2) { |
||||||
|
return -1; |
||||||
|
} else if (low1 > low2) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
std::string Timestamp(uint64_t low, uint64_t high); |
||||||
|
|
||||||
|
void CheckIterUserEntry(const Iterator* it, const Slice& expected_key, |
||||||
|
ValueType expected_value_type, |
||||||
|
const Slice& expected_value, |
||||||
|
const Slice& expected_ts) const; |
||||||
|
|
||||||
|
void CheckIterEntry(const Iterator* it, const Slice& expected_ukey, |
||||||
|
SequenceNumber expected_seq, ValueType expected_val_type, |
||||||
|
const Slice& expected_value, |
||||||
|
const Slice& expected_ts) const; |
||||||
|
|
||||||
|
void CheckIterEntry(const Iterator* it, const Slice& expected_ukey, |
||||||
|
ValueType expected_val_type, const Slice& expected_value, |
||||||
|
const Slice& expected_ts) const; |
||||||
|
}; |
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
Loading…
Reference in new issue