]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/rocksdb/monitoring/stats_history_test.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / monitoring / stats_history_test.cc
index 56b8dc4f5c09648e9ba7f30c0dafcfce1c89eb1c..37e8f6d4add0f522ec8ebb722a0122d85fc327c9 100644 (file)
@@ -6,6 +6,8 @@
 // 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();
@@ -72,120 +84,95 @@ TEST_F(StatsHistoryTest, RunStatsDumpPeriodSec) {
 
 // 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()) {
@@ -196,26 +183,12 @@ TEST_F(StatsHistoryTest, GetStatsHistoryInMemory) {
 }
 
 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"));
@@ -235,13 +208,7 @@ TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
   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"));
@@ -253,13 +220,16 @@ TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
   }
   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;
@@ -269,17 +239,19 @@ TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
     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;
@@ -292,7 +264,7 @@ TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
       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();
@@ -309,34 +281,41 @@ int countkeys(Iterator* iter) {
 }
 
 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);
@@ -345,15 +324,15 @@ TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
   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++;
@@ -366,7 +345,7 @@ TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
   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;
@@ -381,6 +360,7 @@ TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
     }
     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);
@@ -390,53 +370,60 @@ TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
 // 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;
@@ -447,12 +434,12 @@ TEST_F(StatsHistoryTest, PersitentStatsVerifyValue) {
 
   // 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;
@@ -469,15 +456,13 @@ TEST_F(StatsHistoryTest, PersitentStatsVerifyValue) {
 // 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"));
@@ -486,7 +471,13 @@ TEST_F(StatsHistoryTest, PersistentStatsCreateColumnFamilies) {
   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);
@@ -495,7 +486,7 @@ TEST_F(StatsHistoryTest, PersistentStatsCreateColumnFamilies) {
   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();
@@ -531,7 +522,7 @@ TEST_F(StatsHistoryTest, PersistentStatsCreateColumnFamilies) {
   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()) {
@@ -569,18 +560,22 @@ TEST_F(StatsHistoryTest, PersistentStatsReadOnly) {
 }
 
 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();
@@ -596,7 +591,9 @@ TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
   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());
@@ -619,8 +616,9 @@ TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
   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());
@@ -633,8 +631,9 @@ TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
   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));