stats_history_test.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. #include "rocksdb/stats_history.h"
  10. #include <limits>
  11. #include <string>
  12. #include <unordered_map>
  13. #include "db/column_family.h"
  14. #include "db/db_impl/db_impl.h"
  15. #include "db/db_test_util.h"
  16. #include "db/periodic_task_scheduler.h"
  17. #include "monitoring/persistent_stats_history.h"
  18. #include "options/options_helper.h"
  19. #include "port/stack_trace.h"
  20. #include "rocksdb/cache.h"
  21. #include "rocksdb/convenience.h"
  22. #include "rocksdb/rate_limiter.h"
  23. #include "test_util/mock_time_env.h"
  24. #include "test_util/sync_point.h"
  25. #include "test_util/testutil.h"
  26. #include "util/random.h"
  27. namespace ROCKSDB_NAMESPACE {
  28. class StatsHistoryTest : public DBTestBase {
  29. public:
  30. StatsHistoryTest() : DBTestBase("stats_history_test", /*env_do_fsync=*/true) {
  31. mock_clock_ = std::make_shared<MockSystemClock>(env_->GetSystemClock());
  32. mock_env_.reset(new CompositeEnvWrapper(env_, mock_clock_));
  33. }
  34. protected:
  35. std::shared_ptr<MockSystemClock> mock_clock_;
  36. std::unique_ptr<Env> mock_env_;
  37. void SetUp() override {
  38. mock_clock_->InstallTimedWaitFixCallback();
  39. SyncPoint::GetInstance()->SetCallBack(
  40. "DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg) {
  41. auto periodic_task_scheduler_ptr =
  42. static_cast<PeriodicTaskScheduler*>(arg);
  43. periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock_.get());
  44. });
  45. }
  46. };
  47. TEST_F(StatsHistoryTest, RunStatsDumpPeriodSec) {
  48. constexpr int kPeriodSec = 5;
  49. Options options;
  50. options.create_if_missing = true;
  51. options.stats_dump_period_sec = kPeriodSec;
  52. options.env = mock_env_.get();
  53. int counter = 0;
  54. SyncPoint::GetInstance()->SetCallBack("DBImpl::DumpStats:1",
  55. [&](void* /*arg*/) { counter++; });
  56. Reopen(options);
  57. ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec);
  58. // Wait for the first stats persist to finish, as the initial delay could be
  59. // different.
  60. dbfull()->TEST_WaitForPeriodicTaskRun(
  61. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  62. dbfull()->TEST_WaitForPeriodicTaskRun(
  63. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  64. ASSERT_GE(counter, 1);
  65. // Test cancel job through SetOptions
  66. ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
  67. int old_val = counter;
  68. for (int i = 1; i < 20; ++i) {
  69. mock_clock_->MockSleepForSeconds(kPeriodSec);
  70. }
  71. ASSERT_EQ(counter, old_val);
  72. Close();
  73. }
  74. // Test persistent stats background thread scheduling and cancelling
  75. TEST_F(StatsHistoryTest, StatsPersistScheduling) {
  76. constexpr int kPeriodSec = 5;
  77. Options options;
  78. options.create_if_missing = true;
  79. options.stats_persist_period_sec = kPeriodSec;
  80. options.env = mock_env_.get();
  81. int counter = 0;
  82. SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
  83. [&](void* /*arg*/) { counter++; });
  84. Reopen(options);
  85. ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec);
  86. // Wait for the first stats persist to finish, as the initial delay could be
  87. // different.
  88. dbfull()->TEST_WaitForPeriodicTaskRun(
  89. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  90. dbfull()->TEST_WaitForPeriodicTaskRun(
  91. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  92. ASSERT_GE(counter, 1);
  93. // Test cancel job through SetOptions
  94. ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
  95. int old_val = counter;
  96. dbfull()->TEST_WaitForPeriodicTaskRun(
  97. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec * 2); });
  98. ASSERT_EQ(counter, old_val);
  99. Close();
  100. }
  101. // Test enabling persistent stats for the first time
  102. TEST_F(StatsHistoryTest, PersistentStatsFreshInstall) {
  103. constexpr unsigned int kPeriodSec = 5;
  104. Options options;
  105. options.create_if_missing = true;
  106. options.stats_persist_period_sec = 0;
  107. options.env = mock_env_.get();
  108. int counter = 0;
  109. SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
  110. [&](void* /*arg*/) { counter++; });
  111. Reopen(options);
  112. ASSERT_OK(dbfull()->SetDBOptions(
  113. {{"stats_persist_period_sec", std::to_string(kPeriodSec)}}));
  114. ASSERT_EQ(kPeriodSec, dbfull()->GetDBOptions().stats_persist_period_sec);
  115. dbfull()->TEST_WaitForPeriodicTaskRun(
  116. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  117. ASSERT_GE(counter, 1);
  118. Close();
  119. }
  120. // TODO(Zhongyi): Move persistent stats related tests to a separate file
  121. TEST_F(StatsHistoryTest, GetStatsHistoryInMemory) {
  122. constexpr int kPeriodSec = 5;
  123. Options options;
  124. options.create_if_missing = true;
  125. options.stats_persist_period_sec = kPeriodSec;
  126. options.statistics = CreateDBStatistics();
  127. options.env = mock_env_.get();
  128. CreateColumnFamilies({"pikachu"}, options);
  129. ASSERT_OK(Put("foo", "bar"));
  130. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  131. // make sure the first stats persist to finish
  132. dbfull()->TEST_WaitForPeriodicTaskRun(
  133. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  134. // Wait for stats persist to finish
  135. dbfull()->TEST_WaitForPeriodicTaskRun(
  136. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  137. std::unique_ptr<StatsHistoryIterator> stats_iter;
  138. ASSERT_OK(
  139. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  140. ASSERT_TRUE(stats_iter != nullptr);
  141. // disabled stats snapshots
  142. ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
  143. size_t stats_count = 0;
  144. for (; stats_iter->Valid(); stats_iter->Next()) {
  145. auto stats_map = stats_iter->GetStatsMap();
  146. ASSERT_EQ(stats_iter->GetStatsTime(), mock_clock_->NowSeconds());
  147. stats_count += stats_map.size();
  148. }
  149. ASSERT_GT(stats_count, 0);
  150. // Wait a bit and verify no more stats are found
  151. for (int i = 0; i < 10; ++i) {
  152. dbfull()->TEST_WaitForPeriodicTaskRun(
  153. [&] { mock_clock_->MockSleepForSeconds(1); });
  154. }
  155. ASSERT_OK(db_->GetStatsHistory(0, mock_clock_->NowSeconds(), &stats_iter));
  156. ASSERT_TRUE(stats_iter != nullptr);
  157. size_t stats_count_new = 0;
  158. for (; stats_iter->Valid(); stats_iter->Next()) {
  159. stats_count_new += stats_iter->GetStatsMap().size();
  160. }
  161. ASSERT_EQ(stats_count_new, stats_count);
  162. Close();
  163. }
  164. TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
  165. constexpr int kPeriodSec = 1;
  166. constexpr int kEstimatedOneSliceSize = 16000;
  167. Options options;
  168. options.create_if_missing = true;
  169. options.statistics = CreateDBStatistics();
  170. options.stats_persist_period_sec = kPeriodSec;
  171. options.env = mock_env_.get();
  172. CreateColumnFamilies({"pikachu"}, options);
  173. ASSERT_OK(Put("foo", "bar"));
  174. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  175. // some random operation to populate statistics
  176. ASSERT_OK(Delete("foo"));
  177. ASSERT_OK(Put("sol", "sol"));
  178. ASSERT_OK(Put("epic", "epic"));
  179. ASSERT_OK(Put("ltd", "ltd"));
  180. ASSERT_EQ("sol", Get("sol"));
  181. ASSERT_EQ("epic", Get("epic"));
  182. ASSERT_EQ("ltd", Get("ltd"));
  183. Iterator* iterator = db_->NewIterator(ReadOptions());
  184. for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
  185. ASSERT_TRUE(iterator->key() == iterator->value());
  186. }
  187. ASSERT_OK(iterator->status());
  188. delete iterator;
  189. ASSERT_OK(Flush());
  190. ASSERT_OK(Delete("sol"));
  191. ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  192. // second round of ops
  193. ASSERT_OK(Put("saigon", "saigon"));
  194. ASSERT_OK(Put("noodle talk", "noodle talk"));
  195. ASSERT_OK(Put("ping bistro", "ping bistro"));
  196. iterator = db_->NewIterator(ReadOptions());
  197. for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
  198. ASSERT_TRUE(iterator->key() == iterator->value());
  199. }
  200. ASSERT_OK(iterator->status());
  201. delete iterator;
  202. ASSERT_OK(Flush());
  203. ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  204. const int kIterations = 10;
  205. for (int i = 0; i < kIterations; ++i) {
  206. dbfull()->TEST_WaitForPeriodicTaskRun(
  207. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  208. }
  209. std::unique_ptr<StatsHistoryIterator> stats_iter;
  210. ASSERT_OK(
  211. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  212. ASSERT_TRUE(stats_iter != nullptr);
  213. size_t stats_count = 0;
  214. int slice_count = 0;
  215. for (; stats_iter->Valid(); stats_iter->Next()) {
  216. slice_count++;
  217. auto stats_map = stats_iter->GetStatsMap();
  218. stats_count += stats_map.size();
  219. }
  220. size_t stats_history_size = dbfull()->TEST_EstimateInMemoryStatsHistorySize();
  221. ASSERT_GE(slice_count, kIterations - 1);
  222. ASSERT_GE(stats_history_size, kEstimatedOneSliceSize);
  223. // capping memory cost to roughly one slice's size
  224. ASSERT_OK(dbfull()->SetDBOptions(
  225. {{"stats_history_buffer_size", std::to_string(kEstimatedOneSliceSize)}}));
  226. ASSERT_EQ(kEstimatedOneSliceSize,
  227. dbfull()->GetDBOptions().stats_history_buffer_size);
  228. // Wait for stats persist to finish
  229. for (int i = 0; i < kIterations; ++i) {
  230. dbfull()->TEST_WaitForPeriodicTaskRun(
  231. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  232. }
  233. ASSERT_OK(
  234. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  235. ASSERT_TRUE(stats_iter != nullptr);
  236. size_t stats_count_reopen = 0;
  237. slice_count = 0;
  238. for (; stats_iter->Valid(); stats_iter->Next()) {
  239. slice_count++;
  240. auto stats_map = stats_iter->GetStatsMap();
  241. stats_count_reopen += stats_map.size();
  242. }
  243. size_t stats_history_size_reopen =
  244. dbfull()->TEST_EstimateInMemoryStatsHistorySize();
  245. // Only one slice can fit under the new stats_history_buffer_size
  246. //
  247. // If `slice_count == 0` when new statistics are added, consider increasing
  248. // `kEstimatedOneSliceSize`
  249. ASSERT_EQ(slice_count, 1);
  250. ASSERT_TRUE(stats_history_size_reopen < 16000 &&
  251. stats_history_size_reopen > 0);
  252. ASSERT_TRUE(stats_count_reopen < stats_count && stats_count_reopen > 0);
  253. Close();
  254. // TODO: may also want to verify stats timestamp to make sure we are purging
  255. // the correct stats snapshot
  256. }
  257. int countkeys(Iterator* iter) {
  258. int count = 0;
  259. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  260. count++;
  261. }
  262. EXPECT_OK(iter->status());
  263. return count;
  264. }
  265. TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
  266. constexpr int kPeriodSec = 5;
  267. Options options;
  268. options.create_if_missing = true;
  269. options.stats_persist_period_sec = kPeriodSec;
  270. options.statistics = CreateDBStatistics();
  271. options.persist_stats_to_disk = true;
  272. options.env = mock_env_.get();
  273. CreateColumnFamilies({"pikachu"}, options);
  274. ASSERT_OK(Put("foo", "bar"));
  275. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  276. ASSERT_EQ(Get("foo"), "bar");
  277. // Wait for the first stats persist to finish, as the initial delay could be
  278. // different.
  279. dbfull()->TEST_WaitForPeriodicTaskRun(
  280. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  281. // Wait for stats persist to finish
  282. dbfull()->TEST_WaitForPeriodicTaskRun(
  283. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  284. auto iter =
  285. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  286. int key_count1 = countkeys(iter);
  287. delete iter;
  288. dbfull()->TEST_WaitForPeriodicTaskRun(
  289. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  290. iter =
  291. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  292. int key_count2 = countkeys(iter);
  293. delete iter;
  294. dbfull()->TEST_WaitForPeriodicTaskRun(
  295. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  296. iter =
  297. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  298. int key_count3 = countkeys(iter);
  299. delete iter;
  300. ASSERT_GE(key_count2, key_count1);
  301. ASSERT_GE(key_count3, key_count2);
  302. ASSERT_EQ(key_count3 - key_count2, key_count2 - key_count1);
  303. std::unique_ptr<StatsHistoryIterator> stats_iter;
  304. ASSERT_OK(
  305. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  306. ASSERT_TRUE(stats_iter != nullptr);
  307. size_t stats_count = 0;
  308. int slice_count = 0;
  309. int non_zero_count = 0;
  310. for (int i = 2; stats_iter->Valid(); stats_iter->Next(), i++) {
  311. slice_count++;
  312. auto stats_map = stats_iter->GetStatsMap();
  313. ASSERT_EQ(stats_iter->GetStatsTime(), kPeriodSec * i - 1);
  314. for (auto& stat : stats_map) {
  315. if (stat.second != 0) {
  316. non_zero_count++;
  317. }
  318. }
  319. stats_count += stats_map.size();
  320. }
  321. ASSERT_EQ(slice_count, 3);
  322. // 2 extra keys for format version
  323. ASSERT_EQ(stats_count, key_count3 - 2);
  324. // verify reopen will not cause data loss
  325. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  326. ASSERT_OK(
  327. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  328. ASSERT_TRUE(stats_iter != nullptr);
  329. size_t stats_count_reopen = 0;
  330. int slice_count_reopen = 0;
  331. int non_zero_count_recover = 0;
  332. for (; stats_iter->Valid(); stats_iter->Next()) {
  333. slice_count_reopen++;
  334. auto stats_map = stats_iter->GetStatsMap();
  335. for (auto& stat : stats_map) {
  336. if (stat.second != 0) {
  337. non_zero_count_recover++;
  338. }
  339. }
  340. stats_count_reopen += stats_map.size();
  341. }
  342. ASSERT_EQ(non_zero_count, non_zero_count_recover);
  343. ASSERT_EQ(slice_count, slice_count_reopen);
  344. ASSERT_EQ(stats_count, stats_count_reopen);
  345. Close();
  346. }
  347. // Test persisted stats matches the value found in options.statistics and
  348. // the stats value retains after DB reopen
  349. TEST_F(StatsHistoryTest, PersitentStatsVerifyValue) {
  350. constexpr int kPeriodSec = 5;
  351. Options options;
  352. options.create_if_missing = true;
  353. options.stats_persist_period_sec = kPeriodSec;
  354. options.statistics = CreateDBStatistics();
  355. options.persist_stats_to_disk = true;
  356. std::map<std::string, uint64_t> stats_map_before;
  357. ASSERT_TRUE(options.statistics->getTickerMap(&stats_map_before));
  358. options.env = mock_env_.get();
  359. CreateColumnFamilies({"pikachu"}, options);
  360. ASSERT_OK(Put("foo", "bar"));
  361. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  362. ASSERT_EQ(Get("foo"), "bar");
  363. // Wait for the first stats persist to finish, as the initial delay could be
  364. // different.
  365. dbfull()->TEST_WaitForPeriodicTaskRun(
  366. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  367. // Wait for stats persist to finish
  368. dbfull()->TEST_WaitForPeriodicTaskRun(
  369. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  370. auto iter =
  371. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  372. countkeys(iter);
  373. delete iter;
  374. dbfull()->TEST_WaitForPeriodicTaskRun(
  375. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  376. iter =
  377. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  378. countkeys(iter);
  379. delete iter;
  380. dbfull()->TEST_WaitForPeriodicTaskRun(
  381. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  382. iter =
  383. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  384. countkeys(iter);
  385. delete iter;
  386. dbfull()->TEST_WaitForPeriodicTaskRun(
  387. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  388. std::map<std::string, uint64_t> stats_map_after;
  389. ASSERT_TRUE(options.statistics->getTickerMap(&stats_map_after));
  390. std::unique_ptr<StatsHistoryIterator> stats_iter;
  391. ASSERT_OK(
  392. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  393. ASSERT_TRUE(stats_iter != nullptr);
  394. std::string sample = "rocksdb.num.iterator.deleted";
  395. uint64_t recovered_value = 0;
  396. for (int i = 2; stats_iter->Valid(); stats_iter->Next(), ++i) {
  397. auto stats_map = stats_iter->GetStatsMap();
  398. ASSERT_EQ(stats_iter->GetStatsTime(), kPeriodSec * i - 1);
  399. for (const auto& stat : stats_map) {
  400. if (sample.compare(stat.first) == 0) {
  401. recovered_value += stat.second;
  402. }
  403. }
  404. }
  405. ASSERT_EQ(recovered_value, stats_map_after[sample]);
  406. // test stats value retains after recovery
  407. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  408. ASSERT_OK(
  409. db_->GetStatsHistory(0, mock_clock_->NowSeconds() + 1, &stats_iter));
  410. ASSERT_TRUE(stats_iter != nullptr);
  411. uint64_t new_recovered_value = 0;
  412. for (int i = 2; stats_iter->Valid(); stats_iter->Next(), i++) {
  413. auto stats_map = stats_iter->GetStatsMap();
  414. ASSERT_EQ(stats_iter->GetStatsTime(), kPeriodSec * i - 1);
  415. for (const auto& stat : stats_map) {
  416. if (sample.compare(stat.first) == 0) {
  417. new_recovered_value += stat.second;
  418. }
  419. }
  420. }
  421. ASSERT_EQ(recovered_value, new_recovered_value);
  422. // TODO(Zhongyi): also add test to read raw values from disk and verify
  423. // correctness
  424. Close();
  425. }
  426. // TODO(Zhongyi): add test for different format versions
  427. TEST_F(StatsHistoryTest, PersistentStatsCreateColumnFamilies) {
  428. constexpr int kPeriodSec = 5;
  429. Options options;
  430. options.create_if_missing = true;
  431. options.stats_persist_period_sec = kPeriodSec;
  432. options.statistics = CreateDBStatistics();
  433. options.persist_stats_to_disk = true;
  434. options.env = mock_env_.get();
  435. ASSERT_OK(TryReopen(options));
  436. CreateColumnFamilies({"one", "two", "three"}, options);
  437. ASSERT_OK(Put(1, "foo", "bar"));
  438. ReopenWithColumnFamilies({"default", "one", "two", "three"}, options);
  439. ASSERT_EQ(Get(2, "foo"), "bar");
  440. CreateColumnFamilies({"four"}, options);
  441. ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options);
  442. ASSERT_EQ(Get(2, "foo"), "bar");
  443. // make sure the first stats persist to finish
  444. dbfull()->TEST_WaitForPeriodicTaskRun(
  445. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  446. dbfull()->TEST_WaitForPeriodicTaskRun(
  447. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  448. auto iter =
  449. db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
  450. int key_count = countkeys(iter);
  451. delete iter;
  452. ASSERT_GE(key_count, 0);
  453. uint64_t num_write_wal = 0;
  454. std::string sample = "rocksdb.write.wal";
  455. std::unique_ptr<StatsHistoryIterator> stats_iter;
  456. ASSERT_OK(db_->GetStatsHistory(0, mock_clock_->NowSeconds(), &stats_iter));
  457. ASSERT_TRUE(stats_iter != nullptr);
  458. for (; stats_iter->Valid(); stats_iter->Next()) {
  459. auto stats_map = stats_iter->GetStatsMap();
  460. for (const auto& stat : stats_map) {
  461. if (sample.compare(stat.first) == 0) {
  462. num_write_wal += stat.second;
  463. }
  464. }
  465. }
  466. stats_iter.reset();
  467. ASSERT_EQ(num_write_wal, 1);
  468. options.persist_stats_to_disk = false;
  469. ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options);
  470. int cf_count = 0;
  471. for (auto cfd : *dbfull()->versions_->GetColumnFamilySet()) {
  472. (void)cfd;
  473. cf_count++;
  474. }
  475. // persistent stats cf will be implicitly opened even if
  476. // persist_stats_to_disk is false
  477. ASSERT_EQ(cf_count, 6);
  478. ASSERT_EQ(Get(2, "foo"), "bar");
  479. // attempt to create column family using same name, should fail
  480. ColumnFamilyOptions cf_opts(options);
  481. ColumnFamilyHandle* handle;
  482. ASSERT_NOK(db_->CreateColumnFamily(cf_opts, kPersistentStatsColumnFamilyName,
  483. &handle));
  484. options.persist_stats_to_disk = true;
  485. ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options);
  486. ASSERT_NOK(db_->CreateColumnFamily(cf_opts, kPersistentStatsColumnFamilyName,
  487. &handle));
  488. // verify stats is not affected by prior failed CF creation
  489. ASSERT_OK(db_->GetStatsHistory(0, mock_clock_->NowSeconds(), &stats_iter));
  490. ASSERT_TRUE(stats_iter != nullptr);
  491. num_write_wal = 0;
  492. for (; stats_iter->Valid(); stats_iter->Next()) {
  493. auto stats_map = stats_iter->GetStatsMap();
  494. for (const auto& stat : stats_map) {
  495. if (sample.compare(stat.first) == 0) {
  496. num_write_wal += stat.second;
  497. }
  498. }
  499. }
  500. ASSERT_EQ(num_write_wal, 1);
  501. Close();
  502. Destroy(options);
  503. }
  504. TEST_F(StatsHistoryTest, PersistentStatsReadOnly) {
  505. ASSERT_OK(Put("bar", "v2"));
  506. Close();
  507. auto options = CurrentOptions();
  508. options.stats_persist_period_sec = 5;
  509. options.persist_stats_to_disk = true;
  510. assert(options.env == env_);
  511. ASSERT_OK(ReadOnlyReopen(options));
  512. ASSERT_EQ("v2", Get("bar"));
  513. Close();
  514. // Reopen and flush memtable.
  515. ASSERT_OK(TryReopen(options));
  516. ASSERT_OK(Flush());
  517. Close();
  518. // Now check keys in read only mode.
  519. ASSERT_OK(ReadOnlyReopen(options));
  520. }
  521. TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
  522. constexpr int kPeriodSec = 5;
  523. Options options;
  524. options.create_if_missing = true;
  525. options.write_buffer_size = 1024 * 1024 * 10; // 10 Mb
  526. options.stats_persist_period_sec = kPeriodSec;
  527. options.statistics = CreateDBStatistics();
  528. options.persist_stats_to_disk = true;
  529. options.env = mock_env_.get();
  530. CreateColumnFamilies({"pikachu"}, options);
  531. ReopenWithColumnFamilies({"default", "pikachu"}, options);
  532. // Wait for the first stats persist to finish, as the initial delay could be
  533. // different.
  534. dbfull()->TEST_WaitForPeriodicTaskRun(
  535. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
  536. ColumnFamilyData* cfd_default =
  537. static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily())
  538. ->cfd();
  539. ColumnFamilyData* cfd_stats = static_cast<ColumnFamilyHandleImpl*>(
  540. dbfull()->PersistentStatsColumnFamily())
  541. ->cfd();
  542. ColumnFamilyData* cfd_test =
  543. static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
  544. ASSERT_OK(Put("foo", "v0"));
  545. ASSERT_OK(Put("bar", "v0"));
  546. ASSERT_EQ("v0", Get("bar"));
  547. ASSERT_EQ("v0", Get("foo"));
  548. ASSERT_OK(Put(1, "Eevee", "v0"));
  549. ASSERT_EQ("v0", Get(1, "Eevee"));
  550. dbfull()->TEST_WaitForPeriodicTaskRun(
  551. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  552. // writing to all three cf, flush default cf
  553. // LogNumbers: default: 16, stats: 10, pikachu: 5
  554. // Since in recovery process, cfd_stats column is created after WAL is
  555. // created, synced and MANIFEST is persisted, its log number which depends on
  556. // cur_wal_number_ will be different. Since "pikachu" is never flushed, thus
  557. // its log_number should be the smallest of the three.
  558. ASSERT_OK(Flush());
  559. ASSERT_LT(cfd_test->GetLogNumber(), cfd_stats->GetLogNumber());
  560. ASSERT_LT(cfd_test->GetLogNumber(), cfd_default->GetLogNumber());
  561. ASSERT_OK(Put("foo1", "v1"));
  562. ASSERT_OK(Put("bar1", "v1"));
  563. ASSERT_EQ("v1", Get("bar1"));
  564. ASSERT_EQ("v1", Get("foo1"));
  565. ASSERT_OK(Put(1, "Vaporeon", "v1"));
  566. ASSERT_EQ("v1", Get(1, "Vaporeon"));
  567. // writing to default and test cf, flush test cf
  568. // LogNumbers: default: 14, stats: 16, pikachu: 16
  569. ASSERT_OK(Flush(1));
  570. ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
  571. ASSERT_GT(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
  572. ASSERT_OK(Put("foo2", "v2"));
  573. ASSERT_OK(Put("bar2", "v2"));
  574. ASSERT_EQ("v2", Get("bar2"));
  575. ASSERT_EQ("v2", Get("foo2"));
  576. dbfull()->TEST_WaitForPeriodicTaskRun(
  577. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  578. // writing to default and stats cf, flushing default cf
  579. // LogNumbers: default: 19, stats: 19, pikachu: 19
  580. ASSERT_OK(Flush());
  581. ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
  582. ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
  583. ASSERT_OK(Put("foo3", "v3"));
  584. ASSERT_OK(Put("bar3", "v3"));
  585. ASSERT_EQ("v3", Get("bar3"));
  586. ASSERT_EQ("v3", Get("foo3"));
  587. ASSERT_OK(Put(1, "Jolteon", "v3"));
  588. ASSERT_EQ("v3", Get(1, "Jolteon"));
  589. dbfull()->TEST_WaitForPeriodicTaskRun(
  590. [&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
  591. // writing to all three cf, flushing test cf
  592. // LogNumbers: default: 19, stats: 19, pikachu: 22
  593. ASSERT_OK(Flush(1));
  594. ASSERT_LT(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
  595. ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
  596. Close();
  597. }
  598. } // namespace ROCKSDB_NAMESPACE
  599. int main(int argc, char** argv) {
  600. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  601. ::testing::InitGoogleTest(&argc, argv);
  602. return RUN_ALL_TESTS();
  603. }