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).
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"
13 #include <unordered_map>
15 #include "db/column_family.h"
16 #include "db/db_impl/db_impl.h"
17 #include "db/db_test_util.h"
18 #include "db/periodic_task_scheduler.h"
19 #include "monitoring/persistent_stats_history.h"
20 #include "options/options_helper.h"
21 #include "port/stack_trace.h"
22 #include "rocksdb/cache.h"
23 #include "rocksdb/convenience.h"
24 #include "rocksdb/rate_limiter.h"
25 #include "test_util/mock_time_env.h"
26 #include "test_util/sync_point.h"
27 #include "test_util/testutil.h"
28 #include "util/random.h"
30 namespace ROCKSDB_NAMESPACE
{
33 class StatsHistoryTest
: public DBTestBase
{
35 StatsHistoryTest() : DBTestBase("stats_history_test", /*env_do_fsync=*/true) {
36 mock_clock_
= std::make_shared
<MockSystemClock
>(env_
->GetSystemClock());
37 mock_env_
.reset(new CompositeEnvWrapper(env_
, mock_clock_
));
41 std::shared_ptr
<MockSystemClock
> mock_clock_
;
42 std::unique_ptr
<Env
> mock_env_
;
44 void SetUp() override
{
45 mock_clock_
->InstallTimedWaitFixCallback();
46 SyncPoint::GetInstance()->SetCallBack(
47 "DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg
) {
48 auto periodic_task_scheduler_ptr
=
49 reinterpret_cast<PeriodicTaskScheduler
*>(arg
);
50 periodic_task_scheduler_ptr
->TEST_OverrideTimer(mock_clock_
.get());
55 TEST_F(StatsHistoryTest
, RunStatsDumpPeriodSec
) {
56 constexpr int kPeriodSec
= 5;
58 options
.create_if_missing
= true;
59 options
.stats_dump_period_sec
= kPeriodSec
;
60 options
.env
= mock_env_
.get();
62 SyncPoint::GetInstance()->SetCallBack("DBImpl::DumpStats:1",
63 [&](void* /*arg*/) { counter
++; });
65 ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec
);
67 // Wait for the first stats persist to finish, as the initial delay could be
69 dbfull()->TEST_WaitForPeridicTaskRun(
70 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
72 dbfull()->TEST_WaitForPeridicTaskRun(
73 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
74 ASSERT_GE(counter
, 1);
76 // Test cancel job through SetOptions
77 ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
78 int old_val
= counter
;
79 for (int i
= 1; i
< 20; ++i
) {
80 mock_clock_
->MockSleepForSeconds(kPeriodSec
);
82 ASSERT_EQ(counter
, old_val
);
86 // Test persistent stats background thread scheduling and cancelling
87 TEST_F(StatsHistoryTest
, StatsPersistScheduling
) {
88 constexpr int kPeriodSec
= 5;
90 options
.create_if_missing
= true;
91 options
.stats_persist_period_sec
= kPeriodSec
;
92 options
.env
= mock_env_
.get();
94 SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
95 [&](void* /*arg*/) { counter
++; });
97 ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec
);
99 // Wait for the first stats persist to finish, as the initial delay could be
101 dbfull()->TEST_WaitForPeridicTaskRun(
102 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
104 dbfull()->TEST_WaitForPeridicTaskRun(
105 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
106 ASSERT_GE(counter
, 1);
108 // Test cancel job through SetOptions
109 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
110 int old_val
= counter
;
111 dbfull()->TEST_WaitForPeridicTaskRun(
112 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
* 2); });
113 ASSERT_EQ(counter
, old_val
);
118 // Test enabling persistent stats for the first time
119 TEST_F(StatsHistoryTest
, PersistentStatsFreshInstall
) {
120 constexpr unsigned int kPeriodSec
= 5;
122 options
.create_if_missing
= true;
123 options
.stats_persist_period_sec
= 0;
124 options
.env
= mock_env_
.get();
126 SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
127 [&](void* /*arg*/) { counter
++; });
129 ASSERT_OK(dbfull()->SetDBOptions(
130 {{"stats_persist_period_sec", std::to_string(kPeriodSec
)}}));
131 ASSERT_EQ(kPeriodSec
, dbfull()->GetDBOptions().stats_persist_period_sec
);
133 dbfull()->TEST_WaitForPeridicTaskRun(
134 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
135 ASSERT_GE(counter
, 1);
139 // TODO(Zhongyi): Move persistent stats related tests to a separate file
140 TEST_F(StatsHistoryTest
, GetStatsHistoryInMemory
) {
141 constexpr int kPeriodSec
= 5;
143 options
.create_if_missing
= true;
144 options
.stats_persist_period_sec
= kPeriodSec
;
145 options
.statistics
= CreateDBStatistics();
146 options
.env
= mock_env_
.get();
147 CreateColumnFamilies({"pikachu"}, options
);
148 ASSERT_OK(Put("foo", "bar"));
149 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
151 // make sure the first stats persist to finish
152 dbfull()->TEST_WaitForPeridicTaskRun(
153 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
155 // Wait for stats persist to finish
156 dbfull()->TEST_WaitForPeridicTaskRun(
157 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
159 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
161 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &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(), mock_clock_
->NowSeconds());
169 stats_count
+= stats_map
.size();
171 ASSERT_GT(stats_count
, 0);
172 // Wait a bit and verify no more stats are found
173 for (int i
= 0; i
< 10; ++i
) {
174 dbfull()->TEST_WaitForPeridicTaskRun(
175 [&] { mock_clock_
->MockSleepForSeconds(1); });
177 ASSERT_OK(db_
->GetStatsHistory(0, mock_clock_
->NowSeconds(), &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();
183 ASSERT_EQ(stats_count_new
, stats_count
);
187 TEST_F(StatsHistoryTest
, InMemoryStatsHistoryPurging
) {
188 constexpr int kPeriodSec
= 1;
190 options
.create_if_missing
= true;
191 options
.statistics
= CreateDBStatistics();
192 options
.stats_persist_period_sec
= kPeriodSec
;
193 options
.env
= mock_env_
.get();
195 CreateColumnFamilies({"pikachu"}, options
);
196 ASSERT_OK(Put("foo", "bar"));
197 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
198 // some random operation to populate statistics
199 ASSERT_OK(Delete("foo"));
200 ASSERT_OK(Put("sol", "sol"));
201 ASSERT_OK(Put("epic", "epic"));
202 ASSERT_OK(Put("ltd", "ltd"));
203 ASSERT_EQ("sol", Get("sol"));
204 ASSERT_EQ("epic", Get("epic"));
205 ASSERT_EQ("ltd", Get("ltd"));
206 Iterator
* iterator
= db_
->NewIterator(ReadOptions());
207 for (iterator
->SeekToFirst(); iterator
->Valid(); iterator
->Next()) {
208 ASSERT_TRUE(iterator
->key() == iterator
->value());
212 ASSERT_OK(Delete("sol"));
213 ASSERT_OK(db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr));
215 // second round of ops
216 ASSERT_OK(Put("saigon", "saigon"));
217 ASSERT_OK(Put("noodle talk", "noodle talk"));
218 ASSERT_OK(Put("ping bistro", "ping bistro"));
219 iterator
= db_
->NewIterator(ReadOptions());
220 for (iterator
->SeekToFirst(); iterator
->Valid(); iterator
->Next()) {
221 ASSERT_TRUE(iterator
->key() == iterator
->value());
225 ASSERT_OK(db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr));
227 const int kIterations
= 10;
228 for (int i
= 0; i
< kIterations
; ++i
) {
229 dbfull()->TEST_WaitForPeridicTaskRun(
230 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
233 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
235 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &stats_iter
));
236 ASSERT_TRUE(stats_iter
!= nullptr);
237 size_t stats_count
= 0;
239 for (; stats_iter
->Valid(); stats_iter
->Next()) {
241 auto stats_map
= stats_iter
->GetStatsMap();
242 stats_count
+= stats_map
.size();
244 size_t stats_history_size
= dbfull()->TEST_EstimateInMemoryStatsHistorySize();
245 ASSERT_GE(slice_count
, kIterations
- 1);
246 ASSERT_GE(stats_history_size
, 15000);
247 // capping memory cost at 15000 bytes since one slice is around 10000~15000
248 ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "15000"}}));
249 ASSERT_EQ(15000, dbfull()->GetDBOptions().stats_history_buffer_size
);
251 // Wait for stats persist to finish
252 for (int i
= 0; i
< kIterations
; ++i
) {
253 dbfull()->TEST_WaitForPeridicTaskRun(
254 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
258 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &stats_iter
));
259 ASSERT_TRUE(stats_iter
!= nullptr);
260 size_t stats_count_reopen
= 0;
262 for (; stats_iter
->Valid(); stats_iter
->Next()) {
264 auto stats_map
= stats_iter
->GetStatsMap();
265 stats_count_reopen
+= stats_map
.size();
267 size_t stats_history_size_reopen
=
268 dbfull()->TEST_EstimateInMemoryStatsHistorySize();
269 // only one slice can fit under the new stats_history_buffer_size
270 ASSERT_LT(slice_count
, 2);
271 ASSERT_TRUE(stats_history_size_reopen
< 15000 &&
272 stats_history_size_reopen
> 0);
273 ASSERT_TRUE(stats_count_reopen
< stats_count
&& stats_count_reopen
> 0);
275 // TODO: may also want to verify stats timestamp to make sure we are purging
276 // the correct stats snapshot
279 int countkeys(Iterator
* iter
) {
281 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
287 TEST_F(StatsHistoryTest
, GetStatsHistoryFromDisk
) {
288 constexpr int kPeriodSec
= 5;
290 options
.create_if_missing
= true;
291 options
.stats_persist_period_sec
= kPeriodSec
;
292 options
.statistics
= CreateDBStatistics();
293 options
.persist_stats_to_disk
= true;
294 options
.env
= mock_env_
.get();
295 CreateColumnFamilies({"pikachu"}, options
);
296 ASSERT_OK(Put("foo", "bar"));
297 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
298 ASSERT_EQ(Get("foo"), "bar");
300 // Wait for the first stats persist to finish, as the initial delay could be
302 dbfull()->TEST_WaitForPeridicTaskRun(
303 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
305 // Wait for stats persist to finish
306 dbfull()->TEST_WaitForPeridicTaskRun(
307 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
310 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
311 int key_count1
= countkeys(iter
);
314 dbfull()->TEST_WaitForPeridicTaskRun(
315 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
317 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
318 int key_count2
= countkeys(iter
);
321 dbfull()->TEST_WaitForPeridicTaskRun(
322 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
324 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
325 int key_count3
= countkeys(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
;
332 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &stats_iter
));
333 ASSERT_TRUE(stats_iter
!= nullptr);
334 size_t stats_count
= 0;
336 int non_zero_count
= 0;
337 for (int i
= 2; stats_iter
->Valid(); stats_iter
->Next(), i
++) {
339 auto stats_map
= stats_iter
->GetStatsMap();
340 ASSERT_EQ(stats_iter
->GetStatsTime(), kPeriodSec
* i
- 1);
341 for (auto& stat
: stats_map
) {
342 if (stat
.second
!= 0) {
346 stats_count
+= stats_map
.size();
348 ASSERT_EQ(slice_count
, 3);
349 // 2 extra keys for format version
350 ASSERT_EQ(stats_count
, key_count3
- 2);
351 // verify reopen will not cause data loss
352 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
354 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &stats_iter
));
355 ASSERT_TRUE(stats_iter
!= nullptr);
356 size_t stats_count_reopen
= 0;
357 int slice_count_reopen
= 0;
358 int non_zero_count_recover
= 0;
359 for (; stats_iter
->Valid(); stats_iter
->Next()) {
360 slice_count_reopen
++;
361 auto stats_map
= stats_iter
->GetStatsMap();
362 for (auto& stat
: stats_map
) {
363 if (stat
.second
!= 0) {
364 non_zero_count_recover
++;
367 stats_count_reopen
+= stats_map
.size();
370 ASSERT_EQ(non_zero_count
, non_zero_count_recover
);
371 ASSERT_EQ(slice_count
, slice_count_reopen
);
372 ASSERT_EQ(stats_count
, stats_count_reopen
);
376 // Test persisted stats matches the value found in options.statistics and
377 // the stats value retains after DB reopen
378 TEST_F(StatsHistoryTest
, PersitentStatsVerifyValue
) {
379 constexpr int kPeriodSec
= 5;
381 options
.create_if_missing
= true;
382 options
.stats_persist_period_sec
= kPeriodSec
;
383 options
.statistics
= CreateDBStatistics();
384 options
.persist_stats_to_disk
= true;
385 std::map
<std::string
, uint64_t> stats_map_before
;
386 ASSERT_TRUE(options
.statistics
->getTickerMap(&stats_map_before
));
387 options
.env
= mock_env_
.get();
388 CreateColumnFamilies({"pikachu"}, options
);
389 ASSERT_OK(Put("foo", "bar"));
390 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
391 ASSERT_EQ(Get("foo"), "bar");
393 // Wait for the first stats persist to finish, as the initial delay could be
395 dbfull()->TEST_WaitForPeridicTaskRun(
396 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
398 // Wait for stats persist to finish
399 dbfull()->TEST_WaitForPeridicTaskRun(
400 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
402 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
406 dbfull()->TEST_WaitForPeridicTaskRun(
407 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
409 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
413 dbfull()->TEST_WaitForPeridicTaskRun(
414 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
416 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
420 dbfull()->TEST_WaitForPeridicTaskRun(
421 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
423 std::map
<std::string
, uint64_t> stats_map_after
;
424 ASSERT_TRUE(options
.statistics
->getTickerMap(&stats_map_after
));
425 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
427 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &stats_iter
));
428 ASSERT_TRUE(stats_iter
!= nullptr);
429 std::string sample
= "rocksdb.num.iterator.deleted";
430 uint64_t recovered_value
= 0;
431 for (int i
= 2; stats_iter
->Valid(); stats_iter
->Next(), ++i
) {
432 auto stats_map
= stats_iter
->GetStatsMap();
433 ASSERT_EQ(stats_iter
->GetStatsTime(), kPeriodSec
* i
- 1);
434 for (const auto& stat
: stats_map
) {
435 if (sample
.compare(stat
.first
) == 0) {
436 recovered_value
+= stat
.second
;
440 ASSERT_EQ(recovered_value
, stats_map_after
[sample
]);
442 // test stats value retains after recovery
443 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
445 db_
->GetStatsHistory(0, mock_clock_
->NowSeconds() + 1, &stats_iter
));
446 ASSERT_TRUE(stats_iter
!= nullptr);
447 uint64_t new_recovered_value
= 0;
448 for (int i
= 2; stats_iter
->Valid(); stats_iter
->Next(), i
++) {
449 auto stats_map
= stats_iter
->GetStatsMap();
450 ASSERT_EQ(stats_iter
->GetStatsTime(), kPeriodSec
* i
- 1);
451 for (const auto& stat
: stats_map
) {
452 if (sample
.compare(stat
.first
) == 0) {
453 new_recovered_value
+= stat
.second
;
457 ASSERT_EQ(recovered_value
, new_recovered_value
);
459 // TODO(Zhongyi): also add test to read raw values from disk and verify
464 // TODO(Zhongyi): add test for different format versions
466 TEST_F(StatsHistoryTest
, PersistentStatsCreateColumnFamilies
) {
467 constexpr int kPeriodSec
= 5;
469 options
.create_if_missing
= true;
470 options
.stats_persist_period_sec
= kPeriodSec
;
471 options
.statistics
= CreateDBStatistics();
472 options
.persist_stats_to_disk
= true;
473 options
.env
= mock_env_
.get();
474 ASSERT_OK(TryReopen(options
));
475 CreateColumnFamilies({"one", "two", "three"}, options
);
476 ASSERT_OK(Put(1, "foo", "bar"));
477 ReopenWithColumnFamilies({"default", "one", "two", "three"}, options
);
478 ASSERT_EQ(Get(2, "foo"), "bar");
479 CreateColumnFamilies({"four"}, options
);
480 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options
);
481 ASSERT_EQ(Get(2, "foo"), "bar");
483 // make sure the first stats persist to finish
484 dbfull()->TEST_WaitForPeridicTaskRun(
485 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
487 dbfull()->TEST_WaitForPeridicTaskRun(
488 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
490 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
491 int key_count
= countkeys(iter
);
493 ASSERT_GE(key_count
, 0);
494 uint64_t num_write_wal
= 0;
495 std::string sample
= "rocksdb.write.wal";
496 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
497 ASSERT_OK(db_
->GetStatsHistory(0, mock_clock_
->NowSeconds(), &stats_iter
));
498 ASSERT_TRUE(stats_iter
!= nullptr);
499 for (; stats_iter
->Valid(); stats_iter
->Next()) {
500 auto stats_map
= stats_iter
->GetStatsMap();
501 for (const auto& stat
: stats_map
) {
502 if (sample
.compare(stat
.first
) == 0) {
503 num_write_wal
+= stat
.second
;
508 ASSERT_EQ(num_write_wal
, 1);
510 options
.persist_stats_to_disk
= false;
511 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options
);
513 for (auto cfd
: *dbfull()->versions_
->GetColumnFamilySet()) {
517 // persistent stats cf will be implicitly opened even if
518 // persist_stats_to_disk is false
519 ASSERT_EQ(cf_count
, 6);
520 ASSERT_EQ(Get(2, "foo"), "bar");
522 // attempt to create column family using same name, should fail
523 ColumnFamilyOptions
cf_opts(options
);
524 ColumnFamilyHandle
* handle
;
525 ASSERT_NOK(db_
->CreateColumnFamily(cf_opts
, kPersistentStatsColumnFamilyName
,
528 options
.persist_stats_to_disk
= true;
529 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options
);
530 ASSERT_NOK(db_
->CreateColumnFamily(cf_opts
, kPersistentStatsColumnFamilyName
,
532 // verify stats is not affected by prior failed CF creation
533 ASSERT_OK(db_
->GetStatsHistory(0, mock_clock_
->NowSeconds(), &stats_iter
));
534 ASSERT_TRUE(stats_iter
!= nullptr);
536 for (; stats_iter
->Valid(); stats_iter
->Next()) {
537 auto stats_map
= stats_iter
->GetStatsMap();
538 for (const auto& stat
: stats_map
) {
539 if (sample
.compare(stat
.first
) == 0) {
540 num_write_wal
+= stat
.second
;
544 ASSERT_EQ(num_write_wal
, 1);
550 TEST_F(StatsHistoryTest
, PersistentStatsReadOnly
) {
551 ASSERT_OK(Put("bar", "v2"));
554 auto options
= CurrentOptions();
555 options
.stats_persist_period_sec
= 5;
556 options
.persist_stats_to_disk
= true;
557 assert(options
.env
== env_
);
558 ASSERT_OK(ReadOnlyReopen(options
));
559 ASSERT_EQ("v2", Get("bar"));
562 // Reopen and flush memtable.
563 ASSERT_OK(TryReopen(options
));
566 // Now check keys in read only mode.
567 ASSERT_OK(ReadOnlyReopen(options
));
570 TEST_F(StatsHistoryTest
, ForceManualFlushStatsCF
) {
571 constexpr int kPeriodSec
= 5;
573 options
.create_if_missing
= true;
574 options
.write_buffer_size
= 1024 * 1024 * 10; // 10 Mb
575 options
.stats_persist_period_sec
= kPeriodSec
;
576 options
.statistics
= CreateDBStatistics();
577 options
.persist_stats_to_disk
= true;
578 options
.env
= mock_env_
.get();
579 CreateColumnFamilies({"pikachu"}, options
);
580 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
582 // Wait for the first stats persist to finish, as the initial delay could be
584 dbfull()->TEST_WaitForPeridicTaskRun(
585 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
- 1); });
587 ColumnFamilyData
* cfd_default
=
588 static_cast<ColumnFamilyHandleImpl
*>(dbfull()->DefaultColumnFamily())
590 ColumnFamilyData
* cfd_stats
= static_cast<ColumnFamilyHandleImpl
*>(
591 dbfull()->PersistentStatsColumnFamily())
593 ColumnFamilyData
* cfd_test
=
594 static_cast<ColumnFamilyHandleImpl
*>(handles_
[1])->cfd();
596 ASSERT_OK(Put("foo", "v0"));
597 ASSERT_OK(Put("bar", "v0"));
598 ASSERT_EQ("v0", Get("bar"));
599 ASSERT_EQ("v0", Get("foo"));
600 ASSERT_OK(Put(1, "Eevee", "v0"));
601 ASSERT_EQ("v0", Get(1, "Eevee"));
603 dbfull()->TEST_WaitForPeridicTaskRun(
604 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
605 // writing to all three cf, flush default cf
606 // LogNumbers: default: 16, stats: 10, pikachu: 5
607 // Since in recovery process, cfd_stats column is created after WAL is
608 // created, synced and MANIFEST is persisted, its log number which depends on
609 // logfile_number_ will be different. Since "pikachu" is never flushed, thus
610 // its log_number should be the smallest of the three.
612 ASSERT_LT(cfd_test
->GetLogNumber(), cfd_stats
->GetLogNumber());
613 ASSERT_LT(cfd_test
->GetLogNumber(), cfd_default
->GetLogNumber());
615 ASSERT_OK(Put("foo1", "v1"));
616 ASSERT_OK(Put("bar1", "v1"));
617 ASSERT_EQ("v1", Get("bar1"));
618 ASSERT_EQ("v1", Get("foo1"));
619 ASSERT_OK(Put(1, "Vaporeon", "v1"));
620 ASSERT_EQ("v1", Get(1, "Vaporeon"));
621 // writing to default and test cf, flush test cf
622 // LogNumbers: default: 14, stats: 16, pikachu: 16
624 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
625 ASSERT_GT(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
627 ASSERT_OK(Put("foo2", "v2"));
628 ASSERT_OK(Put("bar2", "v2"));
629 ASSERT_EQ("v2", Get("bar2"));
630 ASSERT_EQ("v2", Get("foo2"));
632 dbfull()->TEST_WaitForPeridicTaskRun(
633 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
634 // writing to default and stats cf, flushing default cf
635 // LogNumbers: default: 19, stats: 19, pikachu: 19
637 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
638 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
640 ASSERT_OK(Put("foo3", "v3"));
641 ASSERT_OK(Put("bar3", "v3"));
642 ASSERT_EQ("v3", Get("bar3"));
643 ASSERT_EQ("v3", Get("foo3"));
644 ASSERT_OK(Put(1, "Jolteon", "v3"));
645 ASSERT_EQ("v3", Get(1, "Jolteon"));
647 dbfull()->TEST_WaitForPeridicTaskRun(
648 [&] { mock_clock_
->MockSleepForSeconds(kPeriodSec
); });
649 // writing to all three cf, flushing test cf
650 // LogNumbers: default: 19, stats: 19, pikachu: 22
652 ASSERT_LT(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
653 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
657 #endif // !ROCKSDB_LITE
658 } // namespace ROCKSDB_NAMESPACE
660 int main(int argc
, char** argv
) {
661 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
662 ::testing::InitGoogleTest(&argc
, argv
);
663 return RUN_ALL_TESTS();