// 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 "rocksdb/stats_history.h"
+
#include <limits>
#include <string>
#include <unordered_map>
#include "db/column_family.h"
#include "db/db_impl/db_impl.h"
#include "db/db_test_util.h"
+#include "db/periodic_work_scheduler.h"
#include "monitoring/persistent_stats_history.h"
#include "options/options_helper.h"
#include "port/stack_trace.h"
#include "rocksdb/cache.h"
#include "rocksdb/convenience.h"
#include "rocksdb/rate_limiter.h"
-#include "rocksdb/stats_history.h"
#include "test_util/sync_point.h"
#include "test_util/testutil.h"
#include "util/random.h"
namespace ROCKSDB_NAMESPACE {
+#ifndef ROCKSDB_LITE
class StatsHistoryTest : public DBTestBase {
public:
- StatsHistoryTest() : DBTestBase("/stats_history_test") {}
+ StatsHistoryTest()
+ : DBTestBase("/stats_history_test", /*env_do_fsync=*/true) {
+ mock_env_.reset(new MockTimeEnv(env_));
+ }
+
+ protected:
+ std::unique_ptr<MockTimeEnv> mock_env_;
+
+ void SetUp() override {
+ mock_env_->InstallTimedWaitFixCallback();
+ SyncPoint::GetInstance()->SetCallBack(
+ "DBImpl::StartPeriodicWorkScheduler:Init", [&](void* arg) {
+ auto* periodic_work_scheduler_ptr =
+ reinterpret_cast<PeriodicWorkScheduler**>(arg);
+ *periodic_work_scheduler_ptr =
+ PeriodicWorkTestScheduler::Default(mock_env_.get());
+ });
+ }
};
-#ifndef ROCKSDB_LITE
TEST_F(StatsHistoryTest, RunStatsDumpPeriodSec) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
- options.stats_dump_period_sec = 5;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
+ options.stats_dump_period_sec = kPeriodSec;
+ options.env = mock_env_.get();
int counter = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
-#if defined(OS_MACOSX) && !defined(NDEBUG)
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
- uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
- if (time_us < mock_env->RealNowMicros()) {
- *reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
- }
- });
-#endif // OS_MACOSX && !NDEBUG
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::DumpStats:1", [&](void* /*arg*/) { counter++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
+ SyncPoint::GetInstance()->SetCallBack("DBImpl::DumpStats:1",
+ [&](void* /*arg*/) { counter++; });
Reopen(options);
ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec);
- dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env->set_current_time(5); });
+
+ // Wait for the first stats persist to finish, as the initial delay could be
+ // different.
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
ASSERT_GE(counter, 1);
- // Test cacel job through SetOptions
+ // Test cancel job through SetOptions
ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
int old_val = counter;
- for (int i = 6; i < 20; ++i) {
- dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env->set_current_time(i); });
+ for (int i = 1; i < 20; ++i) {
+ mock_env_->MockSleepForSeconds(kPeriodSec);
}
ASSERT_EQ(counter, old_val);
Close();
// Test persistent stats background thread scheduling and cancelling
TEST_F(StatsHistoryTest, StatsPersistScheduling) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
- options.stats_persist_period_sec = 5;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
-#if defined(OS_MACOSX) && !defined(NDEBUG)
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
- uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
- if (time_us < mock_env->RealNowMicros()) {
- *reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
- }
- });
-#endif // OS_MACOSX && !NDEBUG
+ options.stats_persist_period_sec = kPeriodSec;
+ options.env = mock_env_.get();
int counter = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
+ SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
+ [&](void* /*arg*/) { counter++; });
Reopen(options);
ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec);
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+
+ // Wait for the first stats persist to finish, as the initial delay could be
+ // different.
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
ASSERT_GE(counter, 1);
- // Test cacel job through SetOptions
- ASSERT_TRUE(dbfull()->TEST_IsPersistentStatsEnabled());
+ // Test cancel job through SetOptions
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
- ASSERT_FALSE(dbfull()->TEST_IsPersistentStatsEnabled());
+ int old_val = counter;
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec * 2); });
+ ASSERT_EQ(counter, old_val);
+
Close();
}
// Test enabling persistent stats for the first time
TEST_F(StatsHistoryTest, PersistentStatsFreshInstall) {
+ constexpr unsigned int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
options.stats_persist_period_sec = 0;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
-#if defined(OS_MACOSX) && !defined(NDEBUG)
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
- uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
- if (time_us < mock_env->RealNowMicros()) {
- *reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
- }
- });
-#endif // OS_MACOSX && !NDEBUG
+ options.env = mock_env_.get();
int counter = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
+ SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
+ [&](void* /*arg*/) { counter++; });
Reopen(options);
- ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "5"}}));
- ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec);
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+ ASSERT_OK(dbfull()->SetDBOptions(
+ {{"stats_persist_period_sec", std::to_string(kPeriodSec)}}));
+ ASSERT_EQ(kPeriodSec, dbfull()->GetDBOptions().stats_persist_period_sec);
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
ASSERT_GE(counter, 1);
Close();
}
// TODO(Zhongyi): Move persistent stats related tests to a separate file
TEST_F(StatsHistoryTest, GetStatsHistoryInMemory) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
- options.stats_persist_period_sec = 5;
- options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
-#if defined(OS_MACOSX) && !defined(NDEBUG)
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
- uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
- if (time_us < mock_env->RealNowMicros()) {
- *reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
-#endif // OS_MACOSX && !NDEBUG
-
+ options.stats_persist_period_sec = kPeriodSec;
+ options.statistics = CreateDBStatistics();
+ options.env = mock_env_.get();
CreateColumnFamilies({"pikachu"}, options);
ASSERT_OK(Put("foo", "bar"));
ReopenWithColumnFamilies({"default", "pikachu"}, options);
- int mock_time = 1;
+ // make sure the first stats persist to finish
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
// Wait for stats persist to finish
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
+
std::unique_ptr<StatsHistoryIterator> stats_iter;
- db_->GetStatsHistory(0 /*start_time*/, 6 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
// disabled stats snapshots
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
size_t stats_count = 0;
for (; stats_iter->Valid(); stats_iter->Next()) {
auto stats_map = stats_iter->GetStatsMap();
- ASSERT_EQ(stats_iter->GetStatsTime(), 5);
+ ASSERT_EQ(stats_iter->GetStatsTime(), mock_env_->NowSeconds());
stats_count += stats_map.size();
}
ASSERT_GT(stats_count, 0);
// Wait a bit and verify no more stats are found
- for (mock_time = 6; mock_time < 20; ++mock_time) {
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(mock_time); });
+ for (int i = 0; i < 10; ++i) {
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(1); });
}
- db_->GetStatsHistory(0 /*start_time*/, 20 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds(), &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
size_t stats_count_new = 0;
for (; stats_iter->Valid(); stats_iter->Next()) {
}
TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
+ constexpr int kPeriodSec = 1;
Options options;
options.create_if_missing = true;
- options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
- options.stats_persist_period_sec = 1;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
-#if defined(OS_MACOSX) && !defined(NDEBUG)
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
- uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
- if (time_us < mock_env->RealNowMicros()) {
- *reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
-#endif // OS_MACOSX && !NDEBUG
+ options.statistics = CreateDBStatistics();
+ options.stats_persist_period_sec = kPeriodSec;
+ options.env = mock_env_.get();
CreateColumnFamilies({"pikachu"}, options);
ASSERT_OK(Put("foo", "bar"));
delete iterator;
ASSERT_OK(Flush());
ASSERT_OK(Delete("sol"));
- db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
- int mock_time = 1;
- // Wait for stats persist to finish
- for (; mock_time < 5; ++mock_time) {
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(mock_time); });
- }
+ ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
// second round of ops
ASSERT_OK(Put("saigon", "saigon"));
}
delete iterator;
ASSERT_OK(Flush());
- db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
- for (; mock_time < 10; ++mock_time) {
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(mock_time); });
+ ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
+
+ const int kIterations = 10;
+ for (int i = 0; i < kIterations; ++i) {
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
}
+
std::unique_ptr<StatsHistoryIterator> stats_iter;
- db_->GetStatsHistory(0 /*start_time*/, 10 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
size_t stats_count = 0;
int slice_count = 0;
stats_count += stats_map.size();
}
size_t stats_history_size = dbfull()->TEST_EstimateInMemoryStatsHistorySize();
- ASSERT_GE(slice_count, 9);
- ASSERT_GE(stats_history_size, 12000);
- // capping memory cost at 12000 bytes since one slice is around 10000~12000
- ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "12000"}}));
- ASSERT_EQ(12000, dbfull()->GetDBOptions().stats_history_buffer_size);
+ ASSERT_GE(slice_count, kIterations - 1);
+ ASSERT_GE(stats_history_size, 13000);
+ // capping memory cost at 13000 bytes since one slice is around 10000~13000
+ ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "13000"}}));
+ ASSERT_EQ(13000, dbfull()->GetDBOptions().stats_history_buffer_size);
+
// Wait for stats persist to finish
- for (; mock_time < 20; ++mock_time) {
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(mock_time); });
+ for (int i = 0; i < kIterations; ++i) {
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
}
- db_->GetStatsHistory(0 /*start_time*/, 20 /*end_time*/, &stats_iter);
+
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
size_t stats_count_reopen = 0;
slice_count = 0;
dbfull()->TEST_EstimateInMemoryStatsHistorySize();
// only one slice can fit under the new stats_history_buffer_size
ASSERT_LT(slice_count, 2);
- ASSERT_TRUE(stats_history_size_reopen < 12000 &&
+ ASSERT_TRUE(stats_history_size_reopen < 13000 &&
stats_history_size_reopen > 0);
ASSERT_TRUE(stats_count_reopen < stats_count && stats_count_reopen > 0);
Close();
}
TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
- options.stats_persist_period_sec = 5;
- options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
+ options.stats_persist_period_sec = kPeriodSec;
+ options.statistics = CreateDBStatistics();
options.persist_stats_to_disk = true;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
+ options.env = mock_env_.get();
CreateColumnFamilies({"pikachu"}, options);
ASSERT_OK(Put("foo", "bar"));
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_EQ(Get("foo"), "bar");
+ // Wait for the first stats persist to finish, as the initial delay could be
+ // different.
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
// Wait for stats persist to finish
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
+
auto iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
int key_count1 = countkeys(iter);
delete iter;
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(10); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
int key_count2 = countkeys(iter);
delete iter;
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(15); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
int key_count3 = countkeys(iter);
ASSERT_GE(key_count3, key_count2);
ASSERT_EQ(key_count3 - key_count2, key_count2 - key_count1);
std::unique_ptr<StatsHistoryIterator> stats_iter;
- db_->GetStatsHistory(0 /*start_time*/, 16 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
size_t stats_count = 0;
int slice_count = 0;
int non_zero_count = 0;
- for (int i = 1; stats_iter->Valid(); stats_iter->Next(), i++) {
+ for (int i = 2; stats_iter->Valid(); stats_iter->Next(), i++) {
slice_count++;
auto stats_map = stats_iter->GetStatsMap();
- ASSERT_EQ(stats_iter->GetStatsTime(), 5 * i);
+ ASSERT_EQ(stats_iter->GetStatsTime(), kPeriodSec * i - 1);
for (auto& stat : stats_map) {
if (stat.second != 0) {
non_zero_count++;
ASSERT_EQ(stats_count, key_count3 - 2);
// verify reopen will not cause data loss
ReopenWithColumnFamilies({"default", "pikachu"}, options);
- db_->GetStatsHistory(0 /*start_time*/, 16 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
size_t stats_count_reopen = 0;
int slice_count_reopen = 0;
}
stats_count_reopen += stats_map.size();
}
+
ASSERT_EQ(non_zero_count, non_zero_count_recover);
ASSERT_EQ(slice_count, slice_count_reopen);
ASSERT_EQ(stats_count, stats_count_reopen);
// Test persisted stats matches the value found in options.statistics and
// the stats value retains after DB reopen
TEST_F(StatsHistoryTest, PersitentStatsVerifyValue) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
- options.stats_persist_period_sec = 5;
- options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
+ options.stats_persist_period_sec = kPeriodSec;
+ options.statistics = CreateDBStatistics();
options.persist_stats_to_disk = true;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
std::map<std::string, uint64_t> stats_map_before;
ASSERT_TRUE(options.statistics->getTickerMap(&stats_map_before));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
+ options.env = mock_env_.get();
CreateColumnFamilies({"pikachu"}, options);
ASSERT_OK(Put("foo", "bar"));
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_EQ(Get("foo"), "bar");
+ // Wait for the first stats persist to finish, as the initial delay could be
+ // different.
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
// Wait for stats persist to finish
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
auto iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
countkeys(iter);
delete iter;
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(10); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
countkeys(iter);
delete iter;
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(15); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
countkeys(iter);
delete iter;
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(20); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
std::map<std::string, uint64_t> stats_map_after;
ASSERT_TRUE(options.statistics->getTickerMap(&stats_map_after));
std::unique_ptr<StatsHistoryIterator> stats_iter;
- db_->GetStatsHistory(0 /*start_time*/, 21 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
std::string sample = "rocksdb.num.iterator.deleted";
uint64_t recovered_value = 0;
- for (int i = 1; stats_iter->Valid(); stats_iter->Next(), ++i) {
+ for (int i = 2; stats_iter->Valid(); stats_iter->Next(), ++i) {
auto stats_map = stats_iter->GetStatsMap();
- ASSERT_EQ(stats_iter->GetStatsTime(), 5 * i);
+ ASSERT_EQ(stats_iter->GetStatsTime(), kPeriodSec * i - 1);
for (const auto& stat : stats_map) {
if (sample.compare(stat.first) == 0) {
recovered_value += stat.second;
// test stats value retains after recovery
ReopenWithColumnFamilies({"default", "pikachu"}, options);
- db_->GetStatsHistory(0 /*start_time*/, 21 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds() + 1, &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
uint64_t new_recovered_value = 0;
- for (int i = 1; stats_iter->Valid(); stats_iter->Next(), i++) {
+ for (int i = 2; stats_iter->Valid(); stats_iter->Next(), i++) {
auto stats_map = stats_iter->GetStatsMap();
- ASSERT_EQ(stats_iter->GetStatsTime(), 5 * i);
+ ASSERT_EQ(stats_iter->GetStatsTime(), kPeriodSec * i - 1);
for (const auto& stat : stats_map) {
if (sample.compare(stat.first) == 0) {
new_recovered_value += stat.second;
// TODO(Zhongyi): add test for different format versions
TEST_F(StatsHistoryTest, PersistentStatsCreateColumnFamilies) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
- options.stats_persist_period_sec = 5;
- options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
+ options.stats_persist_period_sec = kPeriodSec;
+ options.statistics = CreateDBStatistics();
options.persist_stats_to_disk = true;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
+ options.env = mock_env_.get();
ASSERT_OK(TryReopen(options));
CreateColumnFamilies({"one", "two", "three"}, options);
ASSERT_OK(Put(1, "foo", "bar"));
CreateColumnFamilies({"four"}, options);
ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options);
ASSERT_EQ(Get(2, "foo"), "bar");
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+
+ // make sure the first stats persist to finish
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
auto iter =
db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
int key_count = countkeys(iter);
uint64_t num_write_wal = 0;
std::string sample = "rocksdb.write.wal";
std::unique_ptr<StatsHistoryIterator> stats_iter;
- db_->GetStatsHistory(0 /*start_time*/, 5 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds(), &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
for (; stats_iter->Valid(); stats_iter->Next()) {
auto stats_map = stats_iter->GetStatsMap();
ASSERT_NOK(db_->CreateColumnFamily(cf_opts, kPersistentStatsColumnFamilyName,
&handle));
// verify stats is not affected by prior failed CF creation
- db_->GetStatsHistory(0 /*start_time*/, 5 /*end_time*/, &stats_iter);
+ ASSERT_OK(db_->GetStatsHistory(0, mock_env_->NowSeconds(), &stats_iter));
ASSERT_TRUE(stats_iter != nullptr);
num_write_wal = 0;
for (; stats_iter->Valid(); stats_iter->Next()) {
}
TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
+ constexpr int kPeriodSec = 5;
Options options;
options.create_if_missing = true;
options.write_buffer_size = 1024 * 1024 * 10; // 10 Mb
- options.stats_persist_period_sec = 5;
- options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
+ options.stats_persist_period_sec = kPeriodSec;
+ options.statistics = CreateDBStatistics();
options.persist_stats_to_disk = true;
- std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env;
- mock_env.reset(new ROCKSDB_NAMESPACE::MockTimeEnv(env_));
- mock_env->set_current_time(0); // in seconds
- options.env = mock_env.get();
+ options.env = mock_env_.get();
CreateColumnFamilies({"pikachu"}, options);
ReopenWithColumnFamilies({"default", "pikachu"}, options);
+
+ // Wait for the first stats persist to finish, as the initial delay could be
+ // different.
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
+
ColumnFamilyData* cfd_default =
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily())
->cfd();
ASSERT_EQ("v0", Get("foo"));
ASSERT_OK(Put(1, "Eevee", "v0"));
ASSERT_EQ("v0", Get(1, "Eevee"));
- dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
// writing to all three cf, flush default cf
// LogNumbers: default: 14, stats: 4, pikachu: 4
ASSERT_OK(Flush());
ASSERT_OK(Put("bar2", "v2"));
ASSERT_EQ("v2", Get("bar2"));
ASSERT_EQ("v2", Get("foo2"));
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(10); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
// writing to default and stats cf, flushing default cf
// LogNumbers: default: 19, stats: 19, pikachu: 19
ASSERT_OK(Flush());
ASSERT_EQ("v3", Get("foo3"));
ASSERT_OK(Put(1, "Jolteon", "v3"));
ASSERT_EQ("v3", Get(1, "Jolteon"));
- dbfull()->TEST_WaitForPersistStatsRun(
- [&] { mock_env->set_current_time(15); });
+
+ dbfull()->TEST_WaitForStatsDumpRun(
+ [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
// writing to all three cf, flushing test cf
// LogNumbers: default: 19, stats: 19, pikachu: 22
ASSERT_OK(Flush(1));