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_work_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/sync_point.h"
26 #include "test_util/testutil.h"
27 #include "util/random.h"
29 namespace ROCKSDB_NAMESPACE
{
32 class StatsHistoryTest
: public DBTestBase
{
35 : DBTestBase("/stats_history_test", /*env_do_fsync=*/true) {
36 mock_env_
.reset(new MockTimeEnv(env_
));
40 std::unique_ptr
<MockTimeEnv
> mock_env_
;
42 void SetUp() override
{
43 mock_env_
->InstallTimedWaitFixCallback();
44 SyncPoint::GetInstance()->SetCallBack(
45 "DBImpl::StartPeriodicWorkScheduler:Init", [&](void* arg
) {
46 auto* periodic_work_scheduler_ptr
=
47 reinterpret_cast<PeriodicWorkScheduler
**>(arg
);
48 *periodic_work_scheduler_ptr
=
49 PeriodicWorkTestScheduler::Default(mock_env_
.get());
54 TEST_F(StatsHistoryTest
, RunStatsDumpPeriodSec
) {
55 constexpr int kPeriodSec
= 5;
57 options
.create_if_missing
= true;
58 options
.stats_dump_period_sec
= kPeriodSec
;
59 options
.env
= mock_env_
.get();
61 SyncPoint::GetInstance()->SetCallBack("DBImpl::DumpStats:1",
62 [&](void* /*arg*/) { counter
++; });
64 ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec
);
66 // Wait for the first stats persist to finish, as the initial delay could be
68 dbfull()->TEST_WaitForStatsDumpRun(
69 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
71 dbfull()->TEST_WaitForStatsDumpRun(
72 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
73 ASSERT_GE(counter
, 1);
75 // Test cancel job through SetOptions
76 ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
77 int old_val
= counter
;
78 for (int i
= 1; i
< 20; ++i
) {
79 mock_env_
->MockSleepForSeconds(kPeriodSec
);
81 ASSERT_EQ(counter
, old_val
);
85 // Test persistent stats background thread scheduling and cancelling
86 TEST_F(StatsHistoryTest
, StatsPersistScheduling
) {
87 constexpr int kPeriodSec
= 5;
89 options
.create_if_missing
= true;
90 options
.stats_persist_period_sec
= kPeriodSec
;
91 options
.env
= mock_env_
.get();
93 SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
94 [&](void* /*arg*/) { counter
++; });
96 ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec
);
98 // Wait for the first stats persist to finish, as the initial delay could be
100 dbfull()->TEST_WaitForStatsDumpRun(
101 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
103 dbfull()->TEST_WaitForStatsDumpRun(
104 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
105 ASSERT_GE(counter
, 1);
107 // Test cancel job through SetOptions
108 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
109 int old_val
= counter
;
110 dbfull()->TEST_WaitForStatsDumpRun(
111 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
* 2); });
112 ASSERT_EQ(counter
, old_val
);
117 // Test enabling persistent stats for the first time
118 TEST_F(StatsHistoryTest
, PersistentStatsFreshInstall
) {
119 constexpr unsigned int kPeriodSec
= 5;
121 options
.create_if_missing
= true;
122 options
.stats_persist_period_sec
= 0;
123 options
.env
= mock_env_
.get();
125 SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
126 [&](void* /*arg*/) { counter
++; });
128 ASSERT_OK(dbfull()->SetDBOptions(
129 {{"stats_persist_period_sec", std::to_string(kPeriodSec
)}}));
130 ASSERT_EQ(kPeriodSec
, dbfull()->GetDBOptions().stats_persist_period_sec
);
132 dbfull()->TEST_WaitForStatsDumpRun(
133 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
134 ASSERT_GE(counter
, 1);
138 // TODO(Zhongyi): Move persistent stats related tests to a separate file
139 TEST_F(StatsHistoryTest
, GetStatsHistoryInMemory
) {
140 constexpr int kPeriodSec
= 5;
142 options
.create_if_missing
= true;
143 options
.stats_persist_period_sec
= kPeriodSec
;
144 options
.statistics
= CreateDBStatistics();
145 options
.env
= mock_env_
.get();
146 CreateColumnFamilies({"pikachu"}, options
);
147 ASSERT_OK(Put("foo", "bar"));
148 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
150 // make sure the first stats persist to finish
151 dbfull()->TEST_WaitForStatsDumpRun(
152 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
154 // Wait for stats persist to finish
155 dbfull()->TEST_WaitForStatsDumpRun(
156 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
158 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
159 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
160 ASSERT_TRUE(stats_iter
!= nullptr);
161 // disabled stats snapshots
162 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
163 size_t stats_count
= 0;
164 for (; stats_iter
->Valid(); stats_iter
->Next()) {
165 auto stats_map
= stats_iter
->GetStatsMap();
166 ASSERT_EQ(stats_iter
->GetStatsTime(), mock_env_
->NowSeconds());
167 stats_count
+= stats_map
.size();
169 ASSERT_GT(stats_count
, 0);
170 // Wait a bit and verify no more stats are found
171 for (int i
= 0; i
< 10; ++i
) {
172 dbfull()->TEST_WaitForStatsDumpRun(
173 [&] { mock_env_
->MockSleepForSeconds(1); });
175 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds(), &stats_iter
));
176 ASSERT_TRUE(stats_iter
!= nullptr);
177 size_t stats_count_new
= 0;
178 for (; stats_iter
->Valid(); stats_iter
->Next()) {
179 stats_count_new
+= stats_iter
->GetStatsMap().size();
181 ASSERT_EQ(stats_count_new
, stats_count
);
185 TEST_F(StatsHistoryTest
, InMemoryStatsHistoryPurging
) {
186 constexpr int kPeriodSec
= 1;
188 options
.create_if_missing
= true;
189 options
.statistics
= CreateDBStatistics();
190 options
.stats_persist_period_sec
= kPeriodSec
;
191 options
.env
= mock_env_
.get();
193 CreateColumnFamilies({"pikachu"}, options
);
194 ASSERT_OK(Put("foo", "bar"));
195 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
196 // some random operation to populate statistics
197 ASSERT_OK(Delete("foo"));
198 ASSERT_OK(Put("sol", "sol"));
199 ASSERT_OK(Put("epic", "epic"));
200 ASSERT_OK(Put("ltd", "ltd"));
201 ASSERT_EQ("sol", Get("sol"));
202 ASSERT_EQ("epic", Get("epic"));
203 ASSERT_EQ("ltd", Get("ltd"));
204 Iterator
* iterator
= db_
->NewIterator(ReadOptions());
205 for (iterator
->SeekToFirst(); iterator
->Valid(); iterator
->Next()) {
206 ASSERT_TRUE(iterator
->key() == iterator
->value());
210 ASSERT_OK(Delete("sol"));
211 ASSERT_OK(db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr));
213 // second round of ops
214 ASSERT_OK(Put("saigon", "saigon"));
215 ASSERT_OK(Put("noodle talk", "noodle talk"));
216 ASSERT_OK(Put("ping bistro", "ping bistro"));
217 iterator
= db_
->NewIterator(ReadOptions());
218 for (iterator
->SeekToFirst(); iterator
->Valid(); iterator
->Next()) {
219 ASSERT_TRUE(iterator
->key() == iterator
->value());
223 ASSERT_OK(db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr));
225 const int kIterations
= 10;
226 for (int i
= 0; i
< kIterations
; ++i
) {
227 dbfull()->TEST_WaitForStatsDumpRun(
228 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
231 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
232 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
233 ASSERT_TRUE(stats_iter
!= nullptr);
234 size_t stats_count
= 0;
236 for (; stats_iter
->Valid(); stats_iter
->Next()) {
238 auto stats_map
= stats_iter
->GetStatsMap();
239 stats_count
+= stats_map
.size();
241 size_t stats_history_size
= dbfull()->TEST_EstimateInMemoryStatsHistorySize();
242 ASSERT_GE(slice_count
, kIterations
- 1);
243 ASSERT_GE(stats_history_size
, 13000);
244 // capping memory cost at 13000 bytes since one slice is around 10000~13000
245 ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "13000"}}));
246 ASSERT_EQ(13000, dbfull()->GetDBOptions().stats_history_buffer_size
);
248 // Wait for stats persist to finish
249 for (int i
= 0; i
< kIterations
; ++i
) {
250 dbfull()->TEST_WaitForStatsDumpRun(
251 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
254 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
255 ASSERT_TRUE(stats_iter
!= nullptr);
256 size_t stats_count_reopen
= 0;
258 for (; stats_iter
->Valid(); stats_iter
->Next()) {
260 auto stats_map
= stats_iter
->GetStatsMap();
261 stats_count_reopen
+= stats_map
.size();
263 size_t stats_history_size_reopen
=
264 dbfull()->TEST_EstimateInMemoryStatsHistorySize();
265 // only one slice can fit under the new stats_history_buffer_size
266 ASSERT_LT(slice_count
, 2);
267 ASSERT_TRUE(stats_history_size_reopen
< 13000 &&
268 stats_history_size_reopen
> 0);
269 ASSERT_TRUE(stats_count_reopen
< stats_count
&& stats_count_reopen
> 0);
271 // TODO: may also want to verify stats timestamp to make sure we are purging
272 // the correct stats snapshot
275 int countkeys(Iterator
* iter
) {
277 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
283 TEST_F(StatsHistoryTest
, GetStatsHistoryFromDisk
) {
284 constexpr int kPeriodSec
= 5;
286 options
.create_if_missing
= true;
287 options
.stats_persist_period_sec
= kPeriodSec
;
288 options
.statistics
= CreateDBStatistics();
289 options
.persist_stats_to_disk
= true;
290 options
.env
= mock_env_
.get();
291 CreateColumnFamilies({"pikachu"}, options
);
292 ASSERT_OK(Put("foo", "bar"));
293 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
294 ASSERT_EQ(Get("foo"), "bar");
296 // Wait for the first stats persist to finish, as the initial delay could be
298 dbfull()->TEST_WaitForStatsDumpRun(
299 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
301 // Wait for stats persist to finish
302 dbfull()->TEST_WaitForStatsDumpRun(
303 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
306 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
307 int key_count1
= countkeys(iter
);
310 dbfull()->TEST_WaitForStatsDumpRun(
311 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
313 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
314 int key_count2
= countkeys(iter
);
317 dbfull()->TEST_WaitForStatsDumpRun(
318 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
320 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
321 int key_count3
= countkeys(iter
);
323 ASSERT_GE(key_count2
, key_count1
);
324 ASSERT_GE(key_count3
, key_count2
);
325 ASSERT_EQ(key_count3
- key_count2
, key_count2
- key_count1
);
326 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
327 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
328 ASSERT_TRUE(stats_iter
!= nullptr);
329 size_t stats_count
= 0;
331 int non_zero_count
= 0;
332 for (int i
= 2; stats_iter
->Valid(); stats_iter
->Next(), i
++) {
334 auto stats_map
= stats_iter
->GetStatsMap();
335 ASSERT_EQ(stats_iter
->GetStatsTime(), kPeriodSec
* i
- 1);
336 for (auto& stat
: stats_map
) {
337 if (stat
.second
!= 0) {
341 stats_count
+= stats_map
.size();
343 ASSERT_EQ(slice_count
, 3);
344 // 2 extra keys for format version
345 ASSERT_EQ(stats_count
, key_count3
- 2);
346 // verify reopen will not cause data loss
347 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
348 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
349 ASSERT_TRUE(stats_iter
!= nullptr);
350 size_t stats_count_reopen
= 0;
351 int slice_count_reopen
= 0;
352 int non_zero_count_recover
= 0;
353 for (; stats_iter
->Valid(); stats_iter
->Next()) {
354 slice_count_reopen
++;
355 auto stats_map
= stats_iter
->GetStatsMap();
356 for (auto& stat
: stats_map
) {
357 if (stat
.second
!= 0) {
358 non_zero_count_recover
++;
361 stats_count_reopen
+= stats_map
.size();
364 ASSERT_EQ(non_zero_count
, non_zero_count_recover
);
365 ASSERT_EQ(slice_count
, slice_count_reopen
);
366 ASSERT_EQ(stats_count
, stats_count_reopen
);
370 // Test persisted stats matches the value found in options.statistics and
371 // the stats value retains after DB reopen
372 TEST_F(StatsHistoryTest
, PersitentStatsVerifyValue
) {
373 constexpr int kPeriodSec
= 5;
375 options
.create_if_missing
= true;
376 options
.stats_persist_period_sec
= kPeriodSec
;
377 options
.statistics
= CreateDBStatistics();
378 options
.persist_stats_to_disk
= true;
379 std::map
<std::string
, uint64_t> stats_map_before
;
380 ASSERT_TRUE(options
.statistics
->getTickerMap(&stats_map_before
));
381 options
.env
= mock_env_
.get();
382 CreateColumnFamilies({"pikachu"}, options
);
383 ASSERT_OK(Put("foo", "bar"));
384 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
385 ASSERT_EQ(Get("foo"), "bar");
387 // Wait for the first stats persist to finish, as the initial delay could be
389 dbfull()->TEST_WaitForStatsDumpRun(
390 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
392 // Wait for stats persist to finish
393 dbfull()->TEST_WaitForStatsDumpRun(
394 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
396 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
400 dbfull()->TEST_WaitForStatsDumpRun(
401 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
403 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
407 dbfull()->TEST_WaitForStatsDumpRun(
408 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
410 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
414 dbfull()->TEST_WaitForStatsDumpRun(
415 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
417 std::map
<std::string
, uint64_t> stats_map_after
;
418 ASSERT_TRUE(options
.statistics
->getTickerMap(&stats_map_after
));
419 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
420 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
421 ASSERT_TRUE(stats_iter
!= nullptr);
422 std::string sample
= "rocksdb.num.iterator.deleted";
423 uint64_t recovered_value
= 0;
424 for (int i
= 2; stats_iter
->Valid(); stats_iter
->Next(), ++i
) {
425 auto stats_map
= stats_iter
->GetStatsMap();
426 ASSERT_EQ(stats_iter
->GetStatsTime(), kPeriodSec
* i
- 1);
427 for (const auto& stat
: stats_map
) {
428 if (sample
.compare(stat
.first
) == 0) {
429 recovered_value
+= stat
.second
;
433 ASSERT_EQ(recovered_value
, stats_map_after
[sample
]);
435 // test stats value retains after recovery
436 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
437 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds() + 1, &stats_iter
));
438 ASSERT_TRUE(stats_iter
!= nullptr);
439 uint64_t new_recovered_value
= 0;
440 for (int i
= 2; stats_iter
->Valid(); stats_iter
->Next(), i
++) {
441 auto stats_map
= stats_iter
->GetStatsMap();
442 ASSERT_EQ(stats_iter
->GetStatsTime(), kPeriodSec
* i
- 1);
443 for (const auto& stat
: stats_map
) {
444 if (sample
.compare(stat
.first
) == 0) {
445 new_recovered_value
+= stat
.second
;
449 ASSERT_EQ(recovered_value
, new_recovered_value
);
451 // TODO(Zhongyi): also add test to read raw values from disk and verify
456 // TODO(Zhongyi): add test for different format versions
458 TEST_F(StatsHistoryTest
, PersistentStatsCreateColumnFamilies
) {
459 constexpr int kPeriodSec
= 5;
461 options
.create_if_missing
= true;
462 options
.stats_persist_period_sec
= kPeriodSec
;
463 options
.statistics
= CreateDBStatistics();
464 options
.persist_stats_to_disk
= true;
465 options
.env
= mock_env_
.get();
466 ASSERT_OK(TryReopen(options
));
467 CreateColumnFamilies({"one", "two", "three"}, options
);
468 ASSERT_OK(Put(1, "foo", "bar"));
469 ReopenWithColumnFamilies({"default", "one", "two", "three"}, options
);
470 ASSERT_EQ(Get(2, "foo"), "bar");
471 CreateColumnFamilies({"four"}, options
);
472 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options
);
473 ASSERT_EQ(Get(2, "foo"), "bar");
475 // make sure the first stats persist to finish
476 dbfull()->TEST_WaitForStatsDumpRun(
477 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
479 dbfull()->TEST_WaitForStatsDumpRun(
480 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
482 db_
->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
483 int key_count
= countkeys(iter
);
485 ASSERT_GE(key_count
, 0);
486 uint64_t num_write_wal
= 0;
487 std::string sample
= "rocksdb.write.wal";
488 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
489 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds(), &stats_iter
));
490 ASSERT_TRUE(stats_iter
!= nullptr);
491 for (; stats_iter
->Valid(); stats_iter
->Next()) {
492 auto stats_map
= stats_iter
->GetStatsMap();
493 for (const auto& stat
: stats_map
) {
494 if (sample
.compare(stat
.first
) == 0) {
495 num_write_wal
+= stat
.second
;
500 ASSERT_EQ(num_write_wal
, 2);
502 options
.persist_stats_to_disk
= false;
503 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options
);
505 for (auto cfd
: *dbfull()->versions_
->GetColumnFamilySet()) {
509 // persistent stats cf will be implicitly opened even if
510 // persist_stats_to_disk is false
511 ASSERT_EQ(cf_count
, 6);
512 ASSERT_EQ(Get(2, "foo"), "bar");
514 // attempt to create column family using same name, should fail
515 ColumnFamilyOptions
cf_opts(options
);
516 ColumnFamilyHandle
* handle
;
517 ASSERT_NOK(db_
->CreateColumnFamily(cf_opts
, kPersistentStatsColumnFamilyName
,
520 options
.persist_stats_to_disk
= true;
521 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options
);
522 ASSERT_NOK(db_
->CreateColumnFamily(cf_opts
, kPersistentStatsColumnFamilyName
,
524 // verify stats is not affected by prior failed CF creation
525 ASSERT_OK(db_
->GetStatsHistory(0, mock_env_
->NowSeconds(), &stats_iter
));
526 ASSERT_TRUE(stats_iter
!= nullptr);
528 for (; stats_iter
->Valid(); stats_iter
->Next()) {
529 auto stats_map
= stats_iter
->GetStatsMap();
530 for (const auto& stat
: stats_map
) {
531 if (sample
.compare(stat
.first
) == 0) {
532 num_write_wal
+= stat
.second
;
536 ASSERT_EQ(num_write_wal
, 2);
542 TEST_F(StatsHistoryTest
, PersistentStatsReadOnly
) {
543 ASSERT_OK(Put("bar", "v2"));
546 auto options
= CurrentOptions();
547 options
.stats_persist_period_sec
= 5;
548 options
.persist_stats_to_disk
= true;
549 assert(options
.env
== env_
);
550 ASSERT_OK(ReadOnlyReopen(options
));
551 ASSERT_EQ("v2", Get("bar"));
554 // Reopen and flush memtable.
555 ASSERT_OK(TryReopen(options
));
558 // Now check keys in read only mode.
559 ASSERT_OK(ReadOnlyReopen(options
));
562 TEST_F(StatsHistoryTest
, ForceManualFlushStatsCF
) {
563 constexpr int kPeriodSec
= 5;
565 options
.create_if_missing
= true;
566 options
.write_buffer_size
= 1024 * 1024 * 10; // 10 Mb
567 options
.stats_persist_period_sec
= kPeriodSec
;
568 options
.statistics
= CreateDBStatistics();
569 options
.persist_stats_to_disk
= true;
570 options
.env
= mock_env_
.get();
571 CreateColumnFamilies({"pikachu"}, options
);
572 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
574 // Wait for the first stats persist to finish, as the initial delay could be
576 dbfull()->TEST_WaitForStatsDumpRun(
577 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
- 1); });
579 ColumnFamilyData
* cfd_default
=
580 static_cast<ColumnFamilyHandleImpl
*>(dbfull()->DefaultColumnFamily())
582 ColumnFamilyData
* cfd_stats
= static_cast<ColumnFamilyHandleImpl
*>(
583 dbfull()->PersistentStatsColumnFamily())
585 ColumnFamilyData
* cfd_test
=
586 static_cast<ColumnFamilyHandleImpl
*>(handles_
[1])->cfd();
588 ASSERT_OK(Put("foo", "v0"));
589 ASSERT_OK(Put("bar", "v0"));
590 ASSERT_EQ("v0", Get("bar"));
591 ASSERT_EQ("v0", Get("foo"));
592 ASSERT_OK(Put(1, "Eevee", "v0"));
593 ASSERT_EQ("v0", Get(1, "Eevee"));
595 dbfull()->TEST_WaitForStatsDumpRun(
596 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
597 // writing to all three cf, flush default cf
598 // LogNumbers: default: 14, stats: 4, pikachu: 4
600 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
601 ASSERT_LT(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
603 ASSERT_OK(Put("foo1", "v1"));
604 ASSERT_OK(Put("bar1", "v1"));
605 ASSERT_EQ("v1", Get("bar1"));
606 ASSERT_EQ("v1", Get("foo1"));
607 ASSERT_OK(Put(1, "Vaporeon", "v1"));
608 ASSERT_EQ("v1", Get(1, "Vaporeon"));
609 // writing to default and test cf, flush test cf
610 // LogNumbers: default: 14, stats: 16, pikachu: 16
612 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
613 ASSERT_GT(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
615 ASSERT_OK(Put("foo2", "v2"));
616 ASSERT_OK(Put("bar2", "v2"));
617 ASSERT_EQ("v2", Get("bar2"));
618 ASSERT_EQ("v2", Get("foo2"));
620 dbfull()->TEST_WaitForStatsDumpRun(
621 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
622 // writing to default and stats cf, flushing default cf
623 // LogNumbers: default: 19, stats: 19, pikachu: 19
625 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
626 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
628 ASSERT_OK(Put("foo3", "v3"));
629 ASSERT_OK(Put("bar3", "v3"));
630 ASSERT_EQ("v3", Get("bar3"));
631 ASSERT_EQ("v3", Get("foo3"));
632 ASSERT_OK(Put(1, "Jolteon", "v3"));
633 ASSERT_EQ("v3", Get(1, "Jolteon"));
635 dbfull()->TEST_WaitForStatsDumpRun(
636 [&] { mock_env_
->MockSleepForSeconds(kPeriodSec
); });
637 // writing to all three cf, flushing test cf
638 // LogNumbers: default: 19, stats: 19, pikachu: 22
640 ASSERT_LT(cfd_stats
->GetLogNumber(), cfd_test
->GetLogNumber());
641 ASSERT_EQ(cfd_stats
->GetLogNumber(), cfd_default
->GetLogNumber());
645 #endif // !ROCKSDB_LITE
646 } // namespace ROCKSDB_NAMESPACE
648 int main(int argc
, char** argv
) {
649 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
650 ::testing::InitGoogleTest(&argc
, argv
);
651 return RUN_ALL_TESTS();