]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/monitoring/stats_history_test.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / monitoring / stats_history_test.cc
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
11 #include <limits>
12 #include <string>
13 #include <unordered_map>
14
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"
28
29 namespace ROCKSDB_NAMESPACE {
30
31 #ifndef ROCKSDB_LITE
32 class StatsHistoryTest : public DBTestBase {
33 public:
34 StatsHistoryTest()
35 : DBTestBase("/stats_history_test", /*env_do_fsync=*/true) {
36 mock_env_.reset(new MockTimeEnv(env_));
37 }
38
39 protected:
40 std::unique_ptr<MockTimeEnv> mock_env_;
41
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());
50 });
51 }
52 };
53
54 TEST_F(StatsHistoryTest, RunStatsDumpPeriodSec) {
55 constexpr int kPeriodSec = 5;
56 Options options;
57 options.create_if_missing = true;
58 options.stats_dump_period_sec = kPeriodSec;
59 options.env = mock_env_.get();
60 int counter = 0;
61 SyncPoint::GetInstance()->SetCallBack("DBImpl::DumpStats:1",
62 [&](void* /*arg*/) { counter++; });
63 Reopen(options);
64 ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec);
65
66 // Wait for the first stats persist to finish, as the initial delay could be
67 // different.
68 dbfull()->TEST_WaitForStatsDumpRun(
69 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
70
71 dbfull()->TEST_WaitForStatsDumpRun(
72 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
73 ASSERT_GE(counter, 1);
74
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);
80 }
81 ASSERT_EQ(counter, old_val);
82 Close();
83 }
84
85 // Test persistent stats background thread scheduling and cancelling
86 TEST_F(StatsHistoryTest, StatsPersistScheduling) {
87 constexpr int kPeriodSec = 5;
88 Options options;
89 options.create_if_missing = true;
90 options.stats_persist_period_sec = kPeriodSec;
91 options.env = mock_env_.get();
92 int counter = 0;
93 SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
94 [&](void* /*arg*/) { counter++; });
95 Reopen(options);
96 ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec);
97
98 // Wait for the first stats persist to finish, as the initial delay could be
99 // different.
100 dbfull()->TEST_WaitForStatsDumpRun(
101 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
102
103 dbfull()->TEST_WaitForStatsDumpRun(
104 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
105 ASSERT_GE(counter, 1);
106
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);
113
114 Close();
115 }
116
117 // Test enabling persistent stats for the first time
118 TEST_F(StatsHistoryTest, PersistentStatsFreshInstall) {
119 constexpr unsigned int kPeriodSec = 5;
120 Options options;
121 options.create_if_missing = true;
122 options.stats_persist_period_sec = 0;
123 options.env = mock_env_.get();
124 int counter = 0;
125 SyncPoint::GetInstance()->SetCallBack("DBImpl::PersistStats:Entry",
126 [&](void* /*arg*/) { counter++; });
127 Reopen(options);
128 ASSERT_OK(dbfull()->SetDBOptions(
129 {{"stats_persist_period_sec", std::to_string(kPeriodSec)}}));
130 ASSERT_EQ(kPeriodSec, dbfull()->GetDBOptions().stats_persist_period_sec);
131
132 dbfull()->TEST_WaitForStatsDumpRun(
133 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
134 ASSERT_GE(counter, 1);
135 Close();
136 }
137
138 // TODO(Zhongyi): Move persistent stats related tests to a separate file
139 TEST_F(StatsHistoryTest, GetStatsHistoryInMemory) {
140 constexpr int kPeriodSec = 5;
141 Options options;
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);
149
150 // make sure the first stats persist to finish
151 dbfull()->TEST_WaitForStatsDumpRun(
152 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
153
154 // Wait for stats persist to finish
155 dbfull()->TEST_WaitForStatsDumpRun(
156 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
157
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();
168 }
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); });
174 }
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();
180 }
181 ASSERT_EQ(stats_count_new, stats_count);
182 Close();
183 }
184
185 TEST_F(StatsHistoryTest, InMemoryStatsHistoryPurging) {
186 constexpr int kPeriodSec = 1;
187 Options options;
188 options.create_if_missing = true;
189 options.statistics = CreateDBStatistics();
190 options.stats_persist_period_sec = kPeriodSec;
191 options.env = mock_env_.get();
192
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());
207 }
208 delete iterator;
209 ASSERT_OK(Flush());
210 ASSERT_OK(Delete("sol"));
211 ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
212
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());
220 }
221 delete iterator;
222 ASSERT_OK(Flush());
223 ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
224
225 const int kIterations = 10;
226 for (int i = 0; i < kIterations; ++i) {
227 dbfull()->TEST_WaitForStatsDumpRun(
228 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
229 }
230
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;
235 int slice_count = 0;
236 for (; stats_iter->Valid(); stats_iter->Next()) {
237 slice_count++;
238 auto stats_map = stats_iter->GetStatsMap();
239 stats_count += stats_map.size();
240 }
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);
247
248 // Wait for stats persist to finish
249 for (int i = 0; i < kIterations; ++i) {
250 dbfull()->TEST_WaitForStatsDumpRun(
251 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
252 }
253
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;
257 slice_count = 0;
258 for (; stats_iter->Valid(); stats_iter->Next()) {
259 slice_count++;
260 auto stats_map = stats_iter->GetStatsMap();
261 stats_count_reopen += stats_map.size();
262 }
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);
270 Close();
271 // TODO: may also want to verify stats timestamp to make sure we are purging
272 // the correct stats snapshot
273 }
274
275 int countkeys(Iterator* iter) {
276 int count = 0;
277 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
278 count++;
279 }
280 return count;
281 }
282
283 TEST_F(StatsHistoryTest, GetStatsHistoryFromDisk) {
284 constexpr int kPeriodSec = 5;
285 Options options;
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");
295
296 // Wait for the first stats persist to finish, as the initial delay could be
297 // different.
298 dbfull()->TEST_WaitForStatsDumpRun(
299 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
300
301 // Wait for stats persist to finish
302 dbfull()->TEST_WaitForStatsDumpRun(
303 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
304
305 auto iter =
306 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
307 int key_count1 = countkeys(iter);
308 delete iter;
309
310 dbfull()->TEST_WaitForStatsDumpRun(
311 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
312 iter =
313 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
314 int key_count2 = countkeys(iter);
315 delete iter;
316
317 dbfull()->TEST_WaitForStatsDumpRun(
318 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
319 iter =
320 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
321 int key_count3 = countkeys(iter);
322 delete 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;
330 int slice_count = 0;
331 int non_zero_count = 0;
332 for (int i = 2; stats_iter->Valid(); stats_iter->Next(), i++) {
333 slice_count++;
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) {
338 non_zero_count++;
339 }
340 }
341 stats_count += stats_map.size();
342 }
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++;
359 }
360 }
361 stats_count_reopen += stats_map.size();
362 }
363
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);
367 Close();
368 }
369
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;
374 Options options;
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");
386
387 // Wait for the first stats persist to finish, as the initial delay could be
388 // different.
389 dbfull()->TEST_WaitForStatsDumpRun(
390 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
391
392 // Wait for stats persist to finish
393 dbfull()->TEST_WaitForStatsDumpRun(
394 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
395 auto iter =
396 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
397 countkeys(iter);
398 delete iter;
399
400 dbfull()->TEST_WaitForStatsDumpRun(
401 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
402 iter =
403 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
404 countkeys(iter);
405 delete iter;
406
407 dbfull()->TEST_WaitForStatsDumpRun(
408 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
409 iter =
410 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
411 countkeys(iter);
412 delete iter;
413
414 dbfull()->TEST_WaitForStatsDumpRun(
415 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
416
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;
430 }
431 }
432 }
433 ASSERT_EQ(recovered_value, stats_map_after[sample]);
434
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;
446 }
447 }
448 }
449 ASSERT_EQ(recovered_value, new_recovered_value);
450
451 // TODO(Zhongyi): also add test to read raw values from disk and verify
452 // correctness
453 Close();
454 }
455
456 // TODO(Zhongyi): add test for different format versions
457
458 TEST_F(StatsHistoryTest, PersistentStatsCreateColumnFamilies) {
459 constexpr int kPeriodSec = 5;
460 Options options;
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");
474
475 // make sure the first stats persist to finish
476 dbfull()->TEST_WaitForStatsDumpRun(
477 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
478
479 dbfull()->TEST_WaitForStatsDumpRun(
480 [&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
481 auto iter =
482 db_->NewIterator(ReadOptions(), dbfull()->PersistentStatsColumnFamily());
483 int key_count = countkeys(iter);
484 delete 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;
496 }
497 }
498 }
499 stats_iter.reset();
500 ASSERT_EQ(num_write_wal, 2);
501
502 options.persist_stats_to_disk = false;
503 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options);
504 int cf_count = 0;
505 for (auto cfd : *dbfull()->versions_->GetColumnFamilySet()) {
506 (void)cfd;
507 cf_count++;
508 }
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");
513
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,
518 &handle));
519
520 options.persist_stats_to_disk = true;
521 ReopenWithColumnFamilies({"default", "one", "two", "three", "four"}, options);
522 ASSERT_NOK(db_->CreateColumnFamily(cf_opts, kPersistentStatsColumnFamilyName,
523 &handle));
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);
527 num_write_wal = 0;
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;
533 }
534 }
535 }
536 ASSERT_EQ(num_write_wal, 2);
537
538 Close();
539 Destroy(options);
540 }
541
542 TEST_F(StatsHistoryTest, PersistentStatsReadOnly) {
543 ASSERT_OK(Put("bar", "v2"));
544 Close();
545
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"));
552 Close();
553
554 // Reopen and flush memtable.
555 ASSERT_OK(TryReopen(options));
556 Flush();
557 Close();
558 // Now check keys in read only mode.
559 ASSERT_OK(ReadOnlyReopen(options));
560 }
561
562 TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
563 constexpr int kPeriodSec = 5;
564 Options options;
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);
573
574 // Wait for the first stats persist to finish, as the initial delay could be
575 // different.
576 dbfull()->TEST_WaitForStatsDumpRun(
577 [&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
578
579 ColumnFamilyData* cfd_default =
580 static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily())
581 ->cfd();
582 ColumnFamilyData* cfd_stats = static_cast<ColumnFamilyHandleImpl*>(
583 dbfull()->PersistentStatsColumnFamily())
584 ->cfd();
585 ColumnFamilyData* cfd_test =
586 static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
587
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"));
594
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
599 ASSERT_OK(Flush());
600 ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
601 ASSERT_LT(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
602
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
611 ASSERT_OK(Flush(1));
612 ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
613 ASSERT_GT(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
614
615 ASSERT_OK(Put("foo2", "v2"));
616 ASSERT_OK(Put("bar2", "v2"));
617 ASSERT_EQ("v2", Get("bar2"));
618 ASSERT_EQ("v2", Get("foo2"));
619
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
624 ASSERT_OK(Flush());
625 ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
626 ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
627
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"));
634
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
639 ASSERT_OK(Flush(1));
640 ASSERT_LT(cfd_stats->GetLogNumber(), cfd_test->GetLogNumber());
641 ASSERT_EQ(cfd_stats->GetLogNumber(), cfd_default->GetLogNumber());
642 Close();
643 }
644
645 #endif // !ROCKSDB_LITE
646 } // namespace ROCKSDB_NAMESPACE
647
648 int main(int argc, char** argv) {
649 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
650 ::testing::InitGoogleTest(&argc, argv);
651 return RUN_ALL_TESTS();
652 }