2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// 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).
|
2015-07-14 23:41:08 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
// Introduction of SyncPoint effectively disabled building and running this test
|
|
|
|
// in Release build.
|
|
|
|
// which is a pity, it is a good test
|
|
|
|
|
2015-10-12 20:05:42 +00:00
|
|
|
#include "db/db_test_util.h"
|
2015-08-25 20:40:58 +00:00
|
|
|
#include "db/forward_iterator.h"
|
2015-07-14 23:41:08 +00:00
|
|
|
#include "port/stack_trace.h"
|
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
namespace {
|
|
|
|
static bool enable_io_uring = true;
|
|
|
|
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
|
|
|
} // namespace
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
namespace ROCKSDB_NAMESPACE {
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
class DBTestTailingIterator : public DBTestBase,
|
|
|
|
public ::testing::WithParamInterface<bool> {
|
2015-07-14 23:41:08 +00:00
|
|
|
public:
|
2020-08-18 01:41:20 +00:00
|
|
|
DBTestTailingIterator()
|
2021-07-23 15:37:27 +00:00
|
|
|
: DBTestBase("db_tailing_iterator_test", /*env_do_fsync=*/true) {}
|
2015-07-14 23:41:08 +00:00
|
|
|
};
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
INSTANTIATE_TEST_CASE_P(DBTestTailingIterator, DBTestTailingIterator,
|
|
|
|
::testing::Bool());
|
|
|
|
|
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorSingle) {
|
2015-07-14 23:41:08 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
|
|
|
iter->SeekToFirst();
|
|
|
|
ASSERT_TRUE(!iter->Valid());
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2015-07-14 23:41:08 +00:00
|
|
|
|
|
|
|
// add a record and check that iter can see it
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "mirko", "fodor"));
|
|
|
|
iter->SeekToFirst();
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().ToString(), "mirko");
|
|
|
|
|
|
|
|
iter->Next();
|
|
|
|
ASSERT_TRUE(!iter->Valid());
|
2023-10-18 16:38:38 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorKeepAdding) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.env = env.get();
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2015-07-14 23:41:08 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
|
|
|
ASSERT_OK(iter->status());
|
|
|
|
std::string value(1024, 'a');
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
const int num_records = 10000;
|
|
|
|
for (int i = 0; i < num_records; ++i) {
|
|
|
|
char buf[32];
|
|
|
|
snprintf(buf, sizeof(buf), "%016d", i);
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
Slice key(buf, 16);
|
|
|
|
ASSERT_OK(Put(1, key, value));
|
|
|
|
|
|
|
|
iter->Seek(key);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().compare(key), 0);
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
Close();
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorSeekToNext) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.env = env.get();
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2015-07-14 23:41:08 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
|
|
|
ASSERT_OK(iter->status());
|
|
|
|
std::unique_ptr<Iterator> itern(
|
|
|
|
db_->NewIterator(read_options, handles_[1]));
|
|
|
|
ASSERT_OK(itern->status());
|
|
|
|
std::string value(1024, 'a');
|
|
|
|
|
|
|
|
const int num_records = 1000;
|
|
|
|
for (int i = 1; i < num_records; ++i) {
|
|
|
|
char buf1[32];
|
|
|
|
char buf2[32];
|
|
|
|
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
|
|
|
|
|
|
|
Slice key(buf1, 20);
|
|
|
|
ASSERT_OK(Put(1, key, value));
|
|
|
|
|
|
|
|
if (i % 100 == 99) {
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
|
|
|
Slice target(buf2, 20);
|
|
|
|
iter->Seek(target);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().compare(key), 0);
|
|
|
|
if (i == 1) {
|
|
|
|
itern->SeekToFirst();
|
|
|
|
} else {
|
|
|
|
itern->Next();
|
|
|
|
}
|
|
|
|
ASSERT_TRUE(itern->Valid());
|
|
|
|
ASSERT_EQ(itern->key().compare(key), 0);
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
for (int i = 2 * num_records; i > 0; --i) {
|
|
|
|
char buf1[32];
|
|
|
|
char buf2[32];
|
|
|
|
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
|
|
|
|
|
|
|
Slice key(buf1, 20);
|
|
|
|
ASSERT_OK(Put(1, key, value));
|
|
|
|
|
|
|
|
if (i % 100 == 99) {
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
|
|
|
Slice target(buf2, 20);
|
|
|
|
iter->Seek(target);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().compare(key), 0);
|
2015-08-18 21:40:06 +00:00
|
|
|
}
|
2015-08-19 23:05:51 +00:00
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
Close();
|
2015-08-19 23:05:51 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
2015-09-04 21:28:45 +00:00
|
|
|
const uint64_t k150KB = 150 * 1024;
|
2023-06-23 18:48:49 +00:00
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
2015-08-28 18:07:07 +00:00
|
|
|
Options options;
|
2023-06-23 18:48:49 +00:00
|
|
|
options.env = env.get();
|
2015-09-04 21:28:45 +00:00
|
|
|
options.write_buffer_size = k150KB;
|
|
|
|
options.max_write_buffer_number = 3;
|
|
|
|
options.min_write_buffer_number_to_merge = 2;
|
2017-03-13 16:41:30 +00:00
|
|
|
options.env = env_;
|
2015-08-28 18:07:07 +00:00
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2015-08-19 23:05:51 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2015-09-04 21:28:45 +00:00
|
|
|
int num_iters, deleted_iters;
|
2015-08-19 23:05:51 +00:00
|
|
|
|
|
|
|
char bufe[32];
|
|
|
|
snprintf(bufe, sizeof(bufe), "00b0%016d", 0);
|
|
|
|
Slice keyu(bufe, 20);
|
|
|
|
read_options.iterate_upper_bound = &keyu;
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2015-08-19 23:05:51 +00:00
|
|
|
std::unique_ptr<Iterator> itern(db_->NewIterator(read_options, handles_[1]));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(itern->status());
|
2015-08-19 23:05:51 +00:00
|
|
|
std::unique_ptr<Iterator> iterh(db_->NewIterator(read_options, handles_[1]));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iterh->status());
|
2015-08-19 23:05:51 +00:00
|
|
|
std::string value(1024, 'a');
|
2015-08-25 20:40:58 +00:00
|
|
|
bool file_iters_deleted = false;
|
Reuse file iterators in tailing iterator when memtable is flushed
Summary:
Under a tailing workload, there were increased block cache
misses when a memtable was flushed because we were rebuilding iterators
in that case since the version set changed. This was exacerbated in the
case of iterate_upper_bound, since file iterators which were over the
iterate_upper_bound would have been deleted and are now brought back as
part of the Rebuild, only to be deleted again. We now renew the iterators
and only build iterators for files which are added and delete file
iterators for files which are deleted.
Refer to https://reviews.facebook.net/D50463 for previous version
Test Plan: DBTestTailingIterator.TailingIteratorTrimSeekToNext
Reviewers: anthony, IslamAbdelRahman, igor, tnovak, yhchiang, sdong
Reviewed By: sdong
Subscribers: yhchiang, march, dhruba, leveldb, lovro
Differential Revision: https://reviews.facebook.net/D50679
2015-11-13 23:50:59 +00:00
|
|
|
bool file_iters_renewed_null = false;
|
|
|
|
bool file_iters_renewed_copy = false;
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
2015-08-25 20:40:58 +00:00
|
|
|
"ForwardIterator::SeekInternal:Return", [&](void* arg) {
|
Prefer static_cast in place of most reinterpret_cast (#12308)
Summary:
The following are risks associated with pointer-to-pointer reinterpret_cast:
* Can produce the "wrong result" (crash or memory corruption). IIRC, in theory this can happen for any up-cast or down-cast for a non-standard-layout type, though in practice would only happen for multiple inheritance cases (where the base class pointer might be "inside" the derived object). We don't use multiple inheritance a lot, but we do.
* Can mask useful compiler errors upon code change, including converting between unrelated pointer types that you are expecting to be related, and converting between pointer and scalar types unintentionally.
I can only think of some obscure cases where static_cast could be troublesome when it compiles as a replacement:
* Going through `void*` could plausibly cause unnecessary or broken pointer arithmetic. Suppose we have
`struct Derived: public Base1, public Base2`. If we have `Derived*` -> `void*` -> `Base2*` -> `Derived*` through reinterpret casts, this could plausibly work (though technical UB) assuming the `Base2*` is not dereferenced. Changing to static cast could introduce breaking pointer arithmetic.
* Unnecessary (but safe) pointer arithmetic could arise in a case like `Derived*` -> `Base2*` -> `Derived*` where before the Base2 pointer might not have been dereferenced. This could potentially affect performance.
With some light scripting, I tried replacing pointer-to-pointer reinterpret_casts with static_cast and kept the cases that still compile. Most occurrences of reinterpret_cast have successfully been changed (except for java/ and third-party/). 294 changed, 257 remain.
A couple of related interventions included here:
* Previously Cache::Handle was not actually derived from in the implementations and just used as a `void*` stand-in with reinterpret_cast. Now there is a relationship to allow static_cast. In theory, this could introduce pointer arithmetic (as described above) but is unlikely without multiple inheritance AND non-empty Cache::Handle.
* Remove some unnecessary casts to void* as this is allowed to be implicit (for better or worse).
Most of the remaining reinterpret_casts are for converting to/from raw bytes of objects. We could consider better idioms for these patterns in follow-up work.
I wish there were a way to implement a template variant of static_cast that would only compile if no pointer arithmetic is generated, but best I can tell, this is not possible. AFAIK the best you could do is a dynamic check that the void* conversion after the static cast is unchanged.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12308
Test Plan: existing tests, CI
Reviewed By: ltamasi
Differential Revision: D53204947
Pulled By: pdillinger
fbshipit-source-id: 9de23e618263b0d5b9820f4e15966876888a16e2
2024-02-07 18:44:11 +00:00
|
|
|
ForwardIterator* fiter = static_cast<ForwardIterator*>(arg);
|
2015-09-04 21:28:45 +00:00
|
|
|
ASSERT_TRUE(!file_iters_deleted ||
|
|
|
|
fiter->TEST_CheckDeletedIters(&deleted_iters, &num_iters));
|
2015-08-25 20:40:58 +00:00
|
|
|
});
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
2015-08-25 20:40:58 +00:00
|
|
|
"ForwardIterator::Next:Return", [&](void* arg) {
|
Prefer static_cast in place of most reinterpret_cast (#12308)
Summary:
The following are risks associated with pointer-to-pointer reinterpret_cast:
* Can produce the "wrong result" (crash or memory corruption). IIRC, in theory this can happen for any up-cast or down-cast for a non-standard-layout type, though in practice would only happen for multiple inheritance cases (where the base class pointer might be "inside" the derived object). We don't use multiple inheritance a lot, but we do.
* Can mask useful compiler errors upon code change, including converting between unrelated pointer types that you are expecting to be related, and converting between pointer and scalar types unintentionally.
I can only think of some obscure cases where static_cast could be troublesome when it compiles as a replacement:
* Going through `void*` could plausibly cause unnecessary or broken pointer arithmetic. Suppose we have
`struct Derived: public Base1, public Base2`. If we have `Derived*` -> `void*` -> `Base2*` -> `Derived*` through reinterpret casts, this could plausibly work (though technical UB) assuming the `Base2*` is not dereferenced. Changing to static cast could introduce breaking pointer arithmetic.
* Unnecessary (but safe) pointer arithmetic could arise in a case like `Derived*` -> `Base2*` -> `Derived*` where before the Base2 pointer might not have been dereferenced. This could potentially affect performance.
With some light scripting, I tried replacing pointer-to-pointer reinterpret_casts with static_cast and kept the cases that still compile. Most occurrences of reinterpret_cast have successfully been changed (except for java/ and third-party/). 294 changed, 257 remain.
A couple of related interventions included here:
* Previously Cache::Handle was not actually derived from in the implementations and just used as a `void*` stand-in with reinterpret_cast. Now there is a relationship to allow static_cast. In theory, this could introduce pointer arithmetic (as described above) but is unlikely without multiple inheritance AND non-empty Cache::Handle.
* Remove some unnecessary casts to void* as this is allowed to be implicit (for better or worse).
Most of the remaining reinterpret_casts are for converting to/from raw bytes of objects. We could consider better idioms for these patterns in follow-up work.
I wish there were a way to implement a template variant of static_cast that would only compile if no pointer arithmetic is generated, but best I can tell, this is not possible. AFAIK the best you could do is a dynamic check that the void* conversion after the static cast is unchanged.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12308
Test Plan: existing tests, CI
Reviewed By: ltamasi
Differential Revision: D53204947
Pulled By: pdillinger
fbshipit-source-id: 9de23e618263b0d5b9820f4e15966876888a16e2
2024-02-07 18:44:11 +00:00
|
|
|
ForwardIterator* fiter = static_cast<ForwardIterator*>(arg);
|
2015-09-04 21:28:45 +00:00
|
|
|
ASSERT_TRUE(!file_iters_deleted ||
|
|
|
|
fiter->TEST_CheckDeletedIters(&deleted_iters, &num_iters));
|
2015-08-25 20:40:58 +00:00
|
|
|
});
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
Reuse file iterators in tailing iterator when memtable is flushed
Summary:
Under a tailing workload, there were increased block cache
misses when a memtable was flushed because we were rebuilding iterators
in that case since the version set changed. This was exacerbated in the
case of iterate_upper_bound, since file iterators which were over the
iterate_upper_bound would have been deleted and are now brought back as
part of the Rebuild, only to be deleted again. We now renew the iterators
and only build iterators for files which are added and delete file
iterators for files which are deleted.
Refer to https://reviews.facebook.net/D50463 for previous version
Test Plan: DBTestTailingIterator.TailingIteratorTrimSeekToNext
Reviewers: anthony, IslamAbdelRahman, igor, tnovak, yhchiang, sdong
Reviewed By: sdong
Subscribers: yhchiang, march, dhruba, leveldb, lovro
Differential Revision: https://reviews.facebook.net/D50679
2015-11-13 23:50:59 +00:00
|
|
|
"ForwardIterator::RenewIterators:Null",
|
2018-04-13 00:55:14 +00:00
|
|
|
[&](void* /*arg*/) { file_iters_renewed_null = true; });
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
Reuse file iterators in tailing iterator when memtable is flushed
Summary:
Under a tailing workload, there were increased block cache
misses when a memtable was flushed because we were rebuilding iterators
in that case since the version set changed. This was exacerbated in the
case of iterate_upper_bound, since file iterators which were over the
iterate_upper_bound would have been deleted and are now brought back as
part of the Rebuild, only to be deleted again. We now renew the iterators
and only build iterators for files which are added and delete file
iterators for files which are deleted.
Refer to https://reviews.facebook.net/D50463 for previous version
Test Plan: DBTestTailingIterator.TailingIteratorTrimSeekToNext
Reviewers: anthony, IslamAbdelRahman, igor, tnovak, yhchiang, sdong
Reviewed By: sdong
Subscribers: yhchiang, march, dhruba, leveldb, lovro
Differential Revision: https://reviews.facebook.net/D50679
2015-11-13 23:50:59 +00:00
|
|
|
"ForwardIterator::RenewIterators:Copy",
|
2018-04-13 00:55:14 +00:00
|
|
|
[&](void* /*arg*/) { file_iters_renewed_copy = true; });
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
2015-08-19 23:05:51 +00:00
|
|
|
const int num_records = 1000;
|
|
|
|
for (int i = 1; i < num_records; ++i) {
|
|
|
|
char buf1[32];
|
|
|
|
char buf2[32];
|
|
|
|
char buf3[32];
|
|
|
|
char buf4[32];
|
|
|
|
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
2015-12-15 23:26:20 +00:00
|
|
|
snprintf(buf3, sizeof(buf3), "00b0%016d", i * 5);
|
2015-08-19 23:05:51 +00:00
|
|
|
|
|
|
|
Slice key(buf1, 20);
|
|
|
|
ASSERT_OK(Put(1, key, value));
|
|
|
|
Slice keyn(buf3, 20);
|
|
|
|
ASSERT_OK(Put(1, keyn, value));
|
|
|
|
|
|
|
|
if (i % 100 == 99) {
|
|
|
|
ASSERT_OK(Flush(1));
|
2020-12-23 07:44:44 +00:00
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-08-25 20:40:58 +00:00
|
|
|
if (i == 299) {
|
|
|
|
file_iters_deleted = true;
|
|
|
|
}
|
2015-12-15 23:26:20 +00:00
|
|
|
snprintf(buf4, sizeof(buf4), "00a0%016d", i * 5 / 2);
|
2015-08-19 23:05:51 +00:00
|
|
|
Slice target(buf4, 20);
|
|
|
|
iterh->Seek(target);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
for (int j = (i + 1) * 5 / 2; j < i * 5; j += 5) {
|
|
|
|
iterh->Next();
|
|
|
|
ASSERT_TRUE(iterh->Valid());
|
|
|
|
}
|
2015-08-25 20:40:58 +00:00
|
|
|
if (i == 299) {
|
|
|
|
file_iters_deleted = false;
|
|
|
|
}
|
2015-08-19 23:05:51 +00:00
|
|
|
}
|
|
|
|
|
2015-09-04 21:28:45 +00:00
|
|
|
file_iters_deleted = true;
|
2015-08-19 23:05:51 +00:00
|
|
|
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
|
|
|
Slice target(buf2, 20);
|
|
|
|
iter->Seek(target);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().compare(key), 0);
|
2015-09-04 21:28:45 +00:00
|
|
|
ASSERT_LE(num_iters, 1);
|
2015-08-19 23:05:51 +00:00
|
|
|
if (i == 1) {
|
|
|
|
itern->SeekToFirst();
|
|
|
|
} else {
|
|
|
|
itern->Next();
|
|
|
|
}
|
|
|
|
ASSERT_TRUE(itern->Valid());
|
2015-08-18 21:40:06 +00:00
|
|
|
ASSERT_EQ(itern->key().compare(key), 0);
|
2015-09-04 21:28:45 +00:00
|
|
|
ASSERT_LE(num_iters, 1);
|
2015-08-25 21:02:03 +00:00
|
|
|
file_iters_deleted = false;
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
Reuse file iterators in tailing iterator when memtable is flushed
Summary:
Under a tailing workload, there were increased block cache
misses when a memtable was flushed because we were rebuilding iterators
in that case since the version set changed. This was exacerbated in the
case of iterate_upper_bound, since file iterators which were over the
iterate_upper_bound would have been deleted and are now brought back as
part of the Rebuild, only to be deleted again. We now renew the iterators
and only build iterators for files which are added and delete file
iterators for files which are deleted.
Refer to https://reviews.facebook.net/D50463 for previous version
Test Plan: DBTestTailingIterator.TailingIteratorTrimSeekToNext
Reviewers: anthony, IslamAbdelRahman, igor, tnovak, yhchiang, sdong
Reviewed By: sdong
Subscribers: yhchiang, march, dhruba, leveldb, lovro
Differential Revision: https://reviews.facebook.net/D50679
2015-11-13 23:50:59 +00:00
|
|
|
ASSERT_TRUE(file_iters_renewed_null);
|
|
|
|
ASSERT_TRUE(file_iters_renewed_copy);
|
2018-03-07 20:39:19 +00:00
|
|
|
iter = nullptr;
|
|
|
|
itern = nullptr;
|
|
|
|
iterh = nullptr;
|
2015-09-04 21:28:45 +00:00
|
|
|
BlockBasedTableOptions table_options;
|
|
|
|
table_options.no_block_cache = true;
|
|
|
|
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
|
|
|
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
2015-08-25 20:38:35 +00:00
|
|
|
read_options.read_tier = kBlockCacheTier;
|
|
|
|
std::unique_ptr<Iterator> iteri(db_->NewIterator(read_options, handles_[1]));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iteri->status());
|
2015-08-25 20:38:35 +00:00
|
|
|
char buf5[32];
|
|
|
|
snprintf(buf5, sizeof(buf5), "00a0%016d", (num_records / 2) * 5 - 2);
|
|
|
|
Slice target1(buf5, 20);
|
|
|
|
iteri->Seek(target1);
|
2015-09-04 21:28:45 +00:00
|
|
|
ASSERT_TRUE(iteri->status().IsIncomplete());
|
2018-03-07 20:39:19 +00:00
|
|
|
iteri = nullptr;
|
2015-08-25 20:38:35 +00:00
|
|
|
|
2015-09-04 21:28:45 +00:00
|
|
|
read_options.read_tier = kReadAllTier;
|
|
|
|
options.table_factory.reset(NewBlockBasedTableFactory());
|
|
|
|
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
|
|
|
iter.reset(db_->NewIterator(read_options, handles_[1]));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2015-07-14 23:41:08 +00:00
|
|
|
for (int i = 2 * num_records; i > 0; --i) {
|
|
|
|
char buf1[32];
|
|
|
|
char buf2[32];
|
|
|
|
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
|
|
|
|
|
|
|
Slice key(buf1, 20);
|
|
|
|
ASSERT_OK(Put(1, key, value));
|
|
|
|
|
|
|
|
if (i % 100 == 99) {
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
|
|
|
Slice target(buf2, 20);
|
|
|
|
iter->Seek(target);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().compare(key), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorDeletes) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.env = env.get();
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2015-07-14 23:41:08 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
|
|
|
ASSERT_OK(iter->status());
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// write a single record, read it using the iterator, then delete it
|
|
|
|
ASSERT_OK(Put(1, "0test", "test"));
|
|
|
|
iter->SeekToFirst();
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().ToString(), "0test");
|
|
|
|
ASSERT_OK(Delete(1, "0test"));
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// write many more records
|
|
|
|
const int num_records = 10000;
|
|
|
|
std::string value(1024, 'A');
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
for (int i = 0; i < num_records; ++i) {
|
|
|
|
char buf[32];
|
|
|
|
snprintf(buf, sizeof(buf), "1%015d", i);
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
Slice key(buf, 16);
|
|
|
|
ASSERT_OK(Put(1, key, value));
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// force a flush to make sure that no records are read from memtable
|
|
|
|
ASSERT_OK(Flush(1));
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// skip "0test"
|
|
|
|
iter->Next();
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// make sure we can read all new records using the existing iterator
|
|
|
|
int count = 0;
|
2024-01-05 19:53:57 +00:00
|
|
|
for (; iter->Valid(); iter->Next(), ++count) {
|
2023-06-23 18:48:49 +00:00
|
|
|
;
|
2024-01-05 19:53:57 +00:00
|
|
|
}
|
2023-10-18 16:38:38 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2023-06-23 18:48:49 +00:00
|
|
|
ASSERT_EQ(count, num_records);
|
|
|
|
}
|
|
|
|
Close();
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
2015-07-14 23:41:08 +00:00
|
|
|
Options options = CurrentOptions();
|
2023-06-23 18:48:49 +00:00
|
|
|
options.env = env.get();
|
2015-07-14 23:41:08 +00:00
|
|
|
options.create_if_missing = true;
|
|
|
|
options.disable_auto_compactions = true;
|
|
|
|
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
|
|
|
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
2016-11-16 17:24:52 +00:00
|
|
|
options.allow_concurrent_memtable_write = false;
|
2015-07-14 23:41:08 +00:00
|
|
|
DestroyAndReopen(options);
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
|
|
|
ASSERT_OK(iter->status());
|
|
|
|
ASSERT_OK(Put(1, "0101", "test"));
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
ASSERT_OK(Flush(1));
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
ASSERT_OK(Put(1, "0202", "test"));
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// Seek(0102) shouldn't find any records since 0202 has a different prefix
|
|
|
|
iter->Seek("0102");
|
|
|
|
ASSERT_TRUE(!iter->Valid());
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
iter->Seek("0202");
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().ToString(), "0202");
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
iter->Next();
|
|
|
|
ASSERT_TRUE(!iter->Valid());
|
2023-10-18 16:38:38 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2023-06-23 18:48:49 +00:00
|
|
|
}
|
|
|
|
Close();
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorIncomplete) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.env = env.get();
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2015-07-14 23:41:08 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
read_options.read_tier = kBlockCacheTier;
|
|
|
|
|
|
|
|
std::string key("key");
|
|
|
|
std::string value("value");
|
|
|
|
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
|
|
|
ASSERT_OK(iter->status());
|
|
|
|
iter->SeekToFirst();
|
|
|
|
// we either see the entry or it's not in cache
|
|
|
|
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
|
|
|
|
|
|
|
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
|
|
|
iter->SeekToFirst();
|
|
|
|
// should still be true after compaction
|
|
|
|
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
|
|
|
}
|
|
|
|
Close();
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
2015-07-14 23:41:08 +00:00
|
|
|
Options options = CurrentOptions();
|
2023-06-23 18:48:49 +00:00
|
|
|
options.env = env.get();
|
2015-07-14 23:41:08 +00:00
|
|
|
options.compaction_style = kCompactionStyleUniversal;
|
|
|
|
options.write_buffer_size = 1000;
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
|
|
|
|
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2015-07-14 23:41:08 +00:00
|
|
|
const int NROWS = 10000;
|
|
|
|
// Write rows with keys 00000, 00002, 00004 etc.
|
|
|
|
for (int i = 0; i < NROWS; ++i) {
|
|
|
|
char buf[100];
|
2022-11-02 21:34:24 +00:00
|
|
|
snprintf(buf, sizeof(buf), "%05d", 2 * i);
|
2015-07-14 23:41:08 +00:00
|
|
|
std::string key(buf);
|
|
|
|
std::string value("value");
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
|
|
|
}
|
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
|
|
|
ASSERT_OK(iter->status());
|
|
|
|
// Seek to 00001. We expect to find 00002.
|
|
|
|
std::string start_key = "00001";
|
|
|
|
iter->Seek(start_key);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
std::string found = iter->key().ToString();
|
|
|
|
ASSERT_EQ("00002", found);
|
2015-07-14 23:41:08 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
// Now seek to the same key. The iterator should remain in the same
|
|
|
|
// position.
|
|
|
|
iter->Seek(found);
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(found, iter->key().ToString());
|
|
|
|
}
|
|
|
|
Close();
|
2015-07-14 23:41:08 +00:00
|
|
|
}
|
|
|
|
|
2015-08-31 23:44:34 +00:00
|
|
|
// Sets iterate_upper_bound and verifies that ForwardIterator doesn't call
|
|
|
|
// Seek() on immutable iterators when target key is >= prev_key and all
|
|
|
|
// iterators, including the memtable iterator, are over the upper bound.
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorUpperBound) {
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.env = env.get();
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2015-08-31 23:44:34 +00:00
|
|
|
|
|
|
|
const Slice upper_bound("20", 3);
|
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
|
|
|
read_options.iterate_upper_bound = &upper_bound;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2015-08-31 23:44:34 +00:00
|
|
|
ASSERT_OK(Put(1, "11", "11"));
|
|
|
|
ASSERT_OK(Put(1, "12", "12"));
|
|
|
|
ASSERT_OK(Put(1, "22", "22"));
|
|
|
|
ASSERT_OK(Flush(1)); // flush all those keys to an immutable SST file
|
|
|
|
|
|
|
|
// Add another key to the memtable.
|
|
|
|
ASSERT_OK(Put(1, "21", "21"));
|
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
bool read_async_called = false;
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"UpdateResults::io_uring_result",
|
|
|
|
[&](void* /*arg*/) { read_async_called = true; });
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
|
|
|
auto it =
|
|
|
|
std::unique_ptr<Iterator>(db_->NewIterator(read_options, handles_[1]));
|
|
|
|
ASSERT_OK(it->status());
|
|
|
|
it->Seek("12");
|
|
|
|
ASSERT_TRUE(it->Valid());
|
|
|
|
ASSERT_EQ("12", it->key().ToString());
|
|
|
|
|
|
|
|
it->Next();
|
|
|
|
// Not valid since "21" is over the upper bound.
|
|
|
|
ASSERT_FALSE(it->Valid());
|
|
|
|
ASSERT_OK(it->status());
|
|
|
|
// This keeps track of the number of times NeedToSeekImmutable() was true.
|
|
|
|
int immutable_seeks = 0;
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"ForwardIterator::SeekInternal:Immutable",
|
|
|
|
[&](void* /*arg*/) { ++immutable_seeks; });
|
|
|
|
|
|
|
|
// Seek to 13. This should not require any immutable seeks.
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
it->Seek("13");
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->SetCallBack(
|
|
|
|
"UpdateResults::io_uring_result",
|
|
|
|
[&](void* /*arg*/) { read_async_called = true; });
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
|
|
|
|
|
|
|
ASSERT_FALSE(it->Valid());
|
|
|
|
ASSERT_OK(it->status());
|
|
|
|
if (GetParam() && read_async_called) {
|
|
|
|
ASSERT_EQ(1, immutable_seeks);
|
|
|
|
} else {
|
|
|
|
ASSERT_EQ(0, immutable_seeks);
|
|
|
|
}
|
2022-10-10 22:48:48 +00:00
|
|
|
}
|
2023-06-23 18:48:49 +00:00
|
|
|
Close();
|
2015-08-31 23:44:34 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
2016-10-29 01:17:17 +00:00
|
|
|
// level 1: [20, 25] [35, 40]
|
|
|
|
// level 2: [10 - 15] [45 - 50]
|
|
|
|
// level 3: [20, 30, 40]
|
|
|
|
// Previously there is a bug in tailing_iterator that if there is a gap in
|
|
|
|
// lower level, the key will be skipped if it is within the range between
|
|
|
|
// the largest key of index n file and the smallest key of index n+1 file
|
|
|
|
// if both file fit in that gap. In this example, 25 < key < 35
|
|
|
|
// https://github.com/facebook/rocksdb/issues/1372
|
2023-06-23 18:48:49 +00:00
|
|
|
if (mem_env_ || encrypted_env_) {
|
|
|
|
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::unique_ptr<Env> env(
|
|
|
|
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
|
|
|
Options options = CurrentOptions();
|
|
|
|
options.env = env.get();
|
|
|
|
CreateAndReopenWithCF({"pikachu"}, options);
|
2016-10-29 01:17:17 +00:00
|
|
|
|
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2016-10-29 01:17:17 +00:00
|
|
|
ASSERT_OK(Put(1, "20", "20"));
|
|
|
|
ASSERT_OK(Put(1, "30", "30"));
|
|
|
|
ASSERT_OK(Put(1, "40", "40"));
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
MoveFilesToLevel(3, 1);
|
|
|
|
|
|
|
|
ASSERT_OK(Put(1, "10", "10"));
|
|
|
|
ASSERT_OK(Put(1, "15", "15"));
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
ASSERT_OK(Put(1, "45", "45"));
|
|
|
|
ASSERT_OK(Put(1, "50", "50"));
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
MoveFilesToLevel(2, 1);
|
|
|
|
|
|
|
|
ASSERT_OK(Put(1, "20", "20"));
|
|
|
|
ASSERT_OK(Put(1, "25", "25"));
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
ASSERT_OK(Put(1, "35", "35"));
|
|
|
|
ASSERT_OK(Put(1, "40", "40"));
|
|
|
|
ASSERT_OK(Flush(1));
|
|
|
|
MoveFilesToLevel(1, 1);
|
|
|
|
|
|
|
|
ColumnFamilyMetaData meta;
|
|
|
|
db_->GetColumnFamilyMetaData(handles_[1], &meta);
|
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
|
|
|
it->Seek("30");
|
|
|
|
ASSERT_TRUE(it->Valid());
|
|
|
|
ASSERT_EQ("30", it->key().ToString());
|
2016-10-29 01:17:17 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
it->Next();
|
|
|
|
ASSERT_TRUE(it->Valid());
|
|
|
|
ASSERT_EQ("35", it->key().ToString());
|
2016-10-29 01:17:17 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
it->Next();
|
|
|
|
ASSERT_TRUE(it->Valid());
|
|
|
|
ASSERT_EQ("40", it->key().ToString());
|
2021-01-06 22:14:01 +00:00
|
|
|
|
2023-06-23 18:48:49 +00:00
|
|
|
ASSERT_OK(it->status());
|
|
|
|
}
|
|
|
|
Close();
|
2016-10-29 01:17:17 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
2016-11-08 21:44:38 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2016-11-08 21:44:38 +00:00
|
|
|
const Slice upper_bound("cc", 3);
|
|
|
|
read_options.iterate_upper_bound = &upper_bound;
|
|
|
|
|
|
|
|
// 1st L0 file
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
// 2nd L0 file
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2016-11-08 21:44:38 +00:00
|
|
|
|
|
|
|
iter->Seek("aa");
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().ToString(), "aa");
|
|
|
|
}
|
|
|
|
|
2022-10-10 22:48:48 +00:00
|
|
|
TEST_P(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
2016-11-08 21:44:38 +00:00
|
|
|
ReadOptions read_options;
|
|
|
|
read_options.tailing = true;
|
2022-10-10 22:48:48 +00:00
|
|
|
if (GetParam()) {
|
|
|
|
read_options.async_io = true;
|
|
|
|
}
|
2016-11-08 21:44:38 +00:00
|
|
|
const Slice upper_bound("cc", 3);
|
|
|
|
read_options.iterate_upper_bound = &upper_bound;
|
|
|
|
|
|
|
|
// 1st L0 file
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
// 2nd L0 file
|
|
|
|
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
|
|
|
ASSERT_OK(Flush());
|
|
|
|
|
|
|
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(iter->status());
|
2016-11-08 21:44:38 +00:00
|
|
|
|
|
|
|
iter->SeekToFirst();
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().ToString(), "aa");
|
|
|
|
|
|
|
|
iter->Next();
|
|
|
|
ASSERT_FALSE(iter->Valid());
|
|
|
|
|
|
|
|
iter->SeekToFirst();
|
|
|
|
ASSERT_TRUE(iter->Valid());
|
|
|
|
ASSERT_EQ(iter->key().ToString(), "aa");
|
|
|
|
}
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
2015-07-14 23:41:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
2015-07-14 23:41:08 +00:00
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|