stats_history_test.cc 25 KB

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