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.
11 #include <unordered_map>
13 #include "db/column_family.h"
14 #include "db/db_impl.h"
15 #include "db/db_test_util.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 "util/random.h"
23 #include "util/sync_point.h"
24 #include "util/testutil.h"
28 const int kMicrosInSec
= 1000000;
30 class DBOptionsTest
: public DBTestBase
{
32 DBOptionsTest() : DBTestBase("/db_options_test") {}
35 std::unordered_map
<std::string
, std::string
> GetMutableDBOptionsMap(
36 const DBOptions
& options
) {
37 std::string options_str
;
38 GetStringFromDBOptions(&options_str
, options
);
39 std::unordered_map
<std::string
, std::string
> options_map
;
40 StringToMap(options_str
, &options_map
);
41 std::unordered_map
<std::string
, std::string
> mutable_map
;
42 for (const auto opt
: db_options_type_info
) {
43 if (opt
.second
.is_mutable
&&
44 opt
.second
.verification
!= OptionVerificationType::kDeprecated
) {
45 mutable_map
[opt
.first
] = options_map
[opt
.first
];
51 std::unordered_map
<std::string
, std::string
> GetMutableCFOptionsMap(
52 const ColumnFamilyOptions
& options
) {
53 std::string options_str
;
54 GetStringFromColumnFamilyOptions(&options_str
, options
);
55 std::unordered_map
<std::string
, std::string
> options_map
;
56 StringToMap(options_str
, &options_map
);
57 std::unordered_map
<std::string
, std::string
> mutable_map
;
58 for (const auto opt
: cf_options_type_info
) {
59 if (opt
.second
.is_mutable
&&
60 opt
.second
.verification
!= OptionVerificationType::kDeprecated
) {
61 mutable_map
[opt
.first
] = options_map
[opt
.first
];
67 std::unordered_map
<std::string
, std::string
> GetRandomizedMutableCFOptionsMap(
71 ImmutableDBOptions
db_options(options
);
72 test::RandomInitCFOptions(&options
, rnd
);
73 auto sanitized_options
= SanitizeOptions(db_options
, options
);
74 auto opt_map
= GetMutableCFOptionsMap(sanitized_options
);
75 delete options
.compaction_filter
;
79 std::unordered_map
<std::string
, std::string
> GetRandomizedMutableDBOptionsMap(
82 test::RandomInitDBOptions(&db_options
, rnd
);
83 auto sanitized_options
= SanitizeOptions(dbname_
, db_options
);
84 return GetMutableDBOptionsMap(sanitized_options
);
86 #endif // ROCKSDB_LITE
89 // RocksDB lite don't support dynamic options.
92 TEST_F(DBOptionsTest
, GetLatestDBOptions
) {
93 // GetOptions should be able to get latest option changed by SetOptions.
95 options
.create_if_missing
= true;
99 auto new_options
= GetRandomizedMutableDBOptionsMap(&rnd
);
100 ASSERT_OK(dbfull()->SetDBOptions(new_options
));
101 ASSERT_EQ(new_options
, GetMutableDBOptionsMap(dbfull()->GetDBOptions()));
104 TEST_F(DBOptionsTest
, GetLatestCFOptions
) {
105 // GetOptions should be able to get latest option changed by SetOptions.
107 options
.create_if_missing
= true;
111 CreateColumnFamilies({"foo"}, options
);
112 ReopenWithColumnFamilies({"default", "foo"}, options
);
113 auto options_default
= GetRandomizedMutableCFOptionsMap(&rnd
);
114 auto options_foo
= GetRandomizedMutableCFOptionsMap(&rnd
);
115 ASSERT_OK(dbfull()->SetOptions(handles_
[0], options_default
));
116 ASSERT_OK(dbfull()->SetOptions(handles_
[1], options_foo
));
117 ASSERT_EQ(options_default
,
118 GetMutableCFOptionsMap(dbfull()->GetOptions(handles_
[0])));
119 ASSERT_EQ(options_foo
,
120 GetMutableCFOptionsMap(dbfull()->GetOptions(handles_
[1])));
123 TEST_F(DBOptionsTest
, SetBytesPerSync
) {
124 const size_t kValueSize
= 1024 * 1024; // 1MB
126 options
.create_if_missing
= true;
127 options
.bytes_per_sync
= 1024 * 1024;
128 options
.use_direct_reads
= false;
129 options
.write_buffer_size
= 400 * kValueSize
;
130 options
.disable_auto_compactions
= true;
131 options
.compression
= kNoCompression
;
135 int low_bytes_per_sync
= 0;
137 const std::string
kValue(kValueSize
, 'v');
138 ASSERT_EQ(options
.bytes_per_sync
, dbfull()->GetDBOptions().bytes_per_sync
);
139 rocksdb::SyncPoint::GetInstance()->SetCallBack(
140 "WritableFileWriter::RangeSync:0", [&](void* /*arg*/) {
144 WriteOptions write_opts
;
145 // should sync approximately 40MB/1MB ~= 40 times.
146 for (i
= 0; i
< 40; i
++) {
147 Put(Key(i
), kValue
, write_opts
);
149 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
150 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
151 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
152 low_bytes_per_sync
= counter
;
153 ASSERT_GT(low_bytes_per_sync
, 35);
154 ASSERT_LT(low_bytes_per_sync
, 45);
157 // 8388608 = 8 * 1024 * 1024
158 ASSERT_OK(dbfull()->SetDBOptions({{"bytes_per_sync", "8388608"}}));
159 ASSERT_EQ(8388608, dbfull()->GetDBOptions().bytes_per_sync
);
160 // should sync approximately 40MB*2/8MB ~= 10 times.
161 // data will be 40*2MB because of previous Puts too.
162 for (i
= 0; i
< 40; i
++) {
163 Put(Key(i
), kValue
, write_opts
);
165 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
166 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
167 ASSERT_GT(counter
, 5);
168 ASSERT_LT(counter
, 15);
170 // Redundant assert. But leaving it here just to get the point across that
171 // low_bytes_per_sync > counter.
172 ASSERT_GT(low_bytes_per_sync
, counter
);
175 TEST_F(DBOptionsTest
, SetWalBytesPerSync
) {
176 const size_t kValueSize
= 1024 * 1024 * 3;
178 options
.create_if_missing
= true;
179 options
.wal_bytes_per_sync
= 512;
180 options
.write_buffer_size
= 100 * kValueSize
;
181 options
.disable_auto_compactions
= true;
182 options
.compression
= kNoCompression
;
185 ASSERT_EQ(512, dbfull()->GetDBOptions().wal_bytes_per_sync
);
187 int low_bytes_per_sync
= 0;
188 rocksdb::SyncPoint::GetInstance()->SetCallBack(
189 "WritableFileWriter::RangeSync:0", [&](void* /*arg*/) {
192 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
193 const std::string
kValue(kValueSize
, 'v');
195 for (; i
< 10; i
++) {
198 // Do not flush. If we flush here, SwitchWAL will reuse old WAL file since its
199 // empty and will not get the new wal_bytes_per_sync value.
200 low_bytes_per_sync
= counter
;
201 //5242880 = 1024 * 1024 * 5
202 ASSERT_OK(dbfull()->SetDBOptions({{"wal_bytes_per_sync", "5242880"}}));
203 ASSERT_EQ(5242880, dbfull()->GetDBOptions().wal_bytes_per_sync
);
206 for (; i
< 10; i
++) {
209 ASSERT_GT(counter
, 0);
210 ASSERT_GT(low_bytes_per_sync
, 0);
211 ASSERT_GT(low_bytes_per_sync
, counter
);
214 TEST_F(DBOptionsTest
, WritableFileMaxBufferSize
) {
216 options
.create_if_missing
= true;
217 options
.writable_file_max_buffer_size
= 1024 * 1024;
218 options
.level0_file_num_compaction_trigger
= 3;
219 options
.max_manifest_file_size
= 1;
221 int buffer_size
= 1024 * 1024;
223 ASSERT_EQ(buffer_size
,
224 dbfull()->GetDBOptions().writable_file_max_buffer_size
);
226 std::atomic
<int> match_cnt(0);
227 std::atomic
<int> unmatch_cnt(0);
228 rocksdb::SyncPoint::GetInstance()->SetCallBack(
229 "WritableFileWriter::WritableFileWriter:0", [&](void* arg
) {
230 int value
= static_cast<int>(reinterpret_cast<uintptr_t>(arg
));
231 if (value
== buffer_size
) {
237 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
240 ASSERT_OK(Put("foo", ToString(i
)));
241 ASSERT_OK(Put("bar", ToString(i
)));
244 dbfull()->TEST_WaitForCompact();
245 ASSERT_EQ(unmatch_cnt
, 0);
246 ASSERT_GE(match_cnt
, 11);
249 dbfull()->SetDBOptions({{"writable_file_max_buffer_size", "524288"}}));
250 buffer_size
= 512 * 1024;
252 unmatch_cnt
= 0; // SetDBOptions() will create a WriteableFileWriter
254 ASSERT_EQ(buffer_size
,
255 dbfull()->GetDBOptions().writable_file_max_buffer_size
);
258 ASSERT_OK(Put("foo", ToString(i
)));
259 ASSERT_OK(Put("bar", ToString(i
)));
262 dbfull()->TEST_WaitForCompact();
263 ASSERT_EQ(unmatch_cnt
, 0);
264 ASSERT_GE(match_cnt
, 11);
267 TEST_F(DBOptionsTest
, SetOptionsAndReopen
) {
269 auto rand_opts
= GetRandomizedMutableCFOptionsMap(&rnd
);
270 ASSERT_OK(dbfull()->SetOptions(rand_opts
));
271 // Verify if DB can be reopen after setting options.
274 ASSERT_OK(TryReopen(options
));
277 TEST_F(DBOptionsTest
, EnableAutoCompactionAndTriggerStall
) {
278 const std::string
kValue(1024, 'v');
279 for (int method_type
= 0; method_type
< 2; method_type
++) {
280 for (int option_type
= 0; option_type
< 4; option_type
++) {
282 options
.create_if_missing
= true;
283 options
.disable_auto_compactions
= true;
284 options
.write_buffer_size
= 1024 * 1024 * 10;
285 options
.compression
= CompressionType::kNoCompression
;
286 options
.level0_file_num_compaction_trigger
= 1;
287 options
.level0_stop_writes_trigger
= std::numeric_limits
<int>::max();
288 options
.level0_slowdown_writes_trigger
= std::numeric_limits
<int>::max();
289 options
.hard_pending_compaction_bytes_limit
=
290 std::numeric_limits
<uint64_t>::max();
291 options
.soft_pending_compaction_bytes_limit
=
292 std::numeric_limits
<uint64_t>::max();
295 DestroyAndReopen(options
);
297 for (; i
< 1024; i
++) {
301 for (; i
< 1024 * 2; i
++) {
305 dbfull()->TEST_WaitForFlushMemTable();
306 ASSERT_EQ(2, NumTableFilesAtLevel(0));
307 uint64_t l0_size
= SizeAtLevel(0);
309 switch (option_type
) {
311 // test with level0_stop_writes_trigger
312 options
.level0_stop_writes_trigger
= 2;
313 options
.level0_slowdown_writes_trigger
= 2;
316 options
.level0_slowdown_writes_trigger
= 2;
319 options
.hard_pending_compaction_bytes_limit
= l0_size
;
320 options
.soft_pending_compaction_bytes_limit
= l0_size
;
323 options
.soft_pending_compaction_bytes_limit
= l0_size
;
327 dbfull()->TEST_WaitForCompact();
328 ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
329 ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
331 SyncPoint::GetInstance()->LoadDependency(
332 {{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:1",
333 "BackgroundCallCompaction:0"},
334 {"DBImpl::BackgroundCompaction():BeforePickCompaction",
335 "DBOptionsTest::EnableAutoCompactionAndTriggerStall:2"},
336 {"DBOptionsTest::EnableAutoCompactionAndTriggerStall:3",
337 "DBImpl::BackgroundCompaction():AfterPickCompaction"}});
338 // Block background compaction.
339 SyncPoint::GetInstance()->EnableProcessing();
341 switch (method_type
) {
344 dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
347 ASSERT_OK(dbfull()->EnableAutoCompaction(
348 {dbfull()->DefaultColumnFamily()}));
351 TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:1");
352 // Wait for stall condition recalculate.
353 TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:2");
355 switch (option_type
) {
357 ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
360 ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
361 ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
364 ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
367 ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
368 ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
371 TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:3");
373 // Background compaction executed.
374 dbfull()->TEST_WaitForCompact();
375 ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
376 ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
381 TEST_F(DBOptionsTest
, SetOptionsMayTriggerCompaction
) {
383 options
.create_if_missing
= true;
384 options
.level0_file_num_compaction_trigger
= 1000;
387 for (int i
= 0; i
< 3; i
++) {
388 // Need to insert two keys to avoid trivial move.
389 ASSERT_OK(Put("foo", ToString(i
)));
390 ASSERT_OK(Put("bar", ToString(i
)));
393 ASSERT_EQ("3", FilesPerLevel());
395 dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "3"}}));
396 dbfull()->TEST_WaitForCompact();
397 ASSERT_EQ("0,1", FilesPerLevel());
400 TEST_F(DBOptionsTest
, SetBackgroundCompactionThreads
) {
402 options
.create_if_missing
= true;
403 options
.max_background_compactions
= 1; // default value
406 ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
407 ASSERT_OK(dbfull()->SetDBOptions({{"max_background_compactions", "3"}}));
408 ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
409 auto stop_token
= dbfull()->TEST_write_controler().GetStopToken();
410 ASSERT_EQ(3, dbfull()->TEST_BGCompactionsAllowed());
413 TEST_F(DBOptionsTest
, SetBackgroundJobs
) {
415 options
.create_if_missing
= true;
416 options
.max_background_jobs
= 8;
420 for (int i
= 0; i
< 2; ++i
) {
422 options
.max_background_jobs
= 12;
423 ASSERT_OK(dbfull()->SetDBOptions(
424 {{"max_background_jobs",
425 std::to_string(options
.max_background_jobs
)}}));
428 ASSERT_EQ(options
.max_background_jobs
/ 4,
429 dbfull()->TEST_BGFlushesAllowed());
430 ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
432 auto stop_token
= dbfull()->TEST_write_controler().GetStopToken();
434 ASSERT_EQ(options
.max_background_jobs
/ 4,
435 dbfull()->TEST_BGFlushesAllowed());
436 ASSERT_EQ(3 * options
.max_background_jobs
/ 4,
437 dbfull()->TEST_BGCompactionsAllowed());
441 TEST_F(DBOptionsTest
, AvoidFlushDuringShutdown
) {
443 options
.create_if_missing
= true;
444 options
.disable_auto_compactions
= true;
446 WriteOptions write_without_wal
;
447 write_without_wal
.disableWAL
= true;
449 ASSERT_FALSE(options
.avoid_flush_during_shutdown
);
450 DestroyAndReopen(options
);
451 ASSERT_OK(Put("foo", "v1", write_without_wal
));
453 ASSERT_EQ("v1", Get("foo"));
454 ASSERT_EQ("1", FilesPerLevel());
456 DestroyAndReopen(options
);
457 ASSERT_OK(Put("foo", "v2", write_without_wal
));
458 ASSERT_OK(dbfull()->SetDBOptions({{"avoid_flush_during_shutdown", "true"}}));
460 ASSERT_EQ("NOT_FOUND", Get("foo"));
461 ASSERT_EQ("", FilesPerLevel());
464 TEST_F(DBOptionsTest
, SetDelayedWriteRateOption
) {
466 options
.create_if_missing
= true;
467 options
.delayed_write_rate
= 2 * 1024U * 1024U;
470 ASSERT_EQ(2 * 1024U * 1024U, dbfull()->TEST_write_controler().max_delayed_write_rate());
472 ASSERT_OK(dbfull()->SetDBOptions({{"delayed_write_rate", "20000"}}));
473 ASSERT_EQ(20000, dbfull()->TEST_write_controler().max_delayed_write_rate());
476 TEST_F(DBOptionsTest
, MaxTotalWalSizeChange
) {
478 const auto value_size
= size_t(1024);
480 test::RandomString(&rnd
, value_size
, &value
);
483 options
.create_if_missing
= true;
485 CreateColumnFamilies({"1", "2", "3"}, options
);
486 ReopenWithColumnFamilies({"default", "1", "2", "3"}, options
);
488 WriteOptions write_options
;
490 const int key_count
= 100;
491 for (int i
= 0; i
< key_count
; ++i
) {
492 for (size_t cf
= 0; cf
< handles_
.size(); ++cf
) {
493 ASSERT_OK(Put(static_cast<int>(cf
), Key(i
), value
));
496 ASSERT_OK(dbfull()->SetDBOptions({{"max_total_wal_size", "10"}}));
498 for (size_t cf
= 0; cf
< handles_
.size(); ++cf
) {
499 dbfull()->TEST_WaitForFlushMemTable(handles_
[cf
]);
500 ASSERT_EQ("1", FilesPerLevel(static_cast<int>(cf
)));
504 TEST_F(DBOptionsTest
, SetStatsDumpPeriodSec
) {
506 options
.create_if_missing
= true;
507 options
.stats_dump_period_sec
= 5;
510 ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec
);
512 for (int i
= 0; i
< 20; i
++) {
513 int num
= rand() % 5000 + 1;
515 dbfull()->SetDBOptions({{"stats_dump_period_sec", ToString(num
)}}));
516 ASSERT_EQ(num
, dbfull()->GetDBOptions().stats_dump_period_sec
);
521 TEST_F(DBOptionsTest
, RunStatsDumpPeriodSec
) {
523 options
.create_if_missing
= true;
524 options
.stats_dump_period_sec
= 5;
525 std::unique_ptr
<rocksdb::MockTimeEnv
> mock_env
;
526 mock_env
.reset(new rocksdb::MockTimeEnv(env_
));
527 mock_env
->set_current_time(0); // in seconds
528 options
.env
= mock_env
.get();
530 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
531 rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
532 #if defined(OS_MACOSX) && !defined(NDEBUG)
533 rocksdb::SyncPoint::GetInstance()->SetCallBack(
534 "InstrumentedCondVar::TimedWaitInternal", [&](void* arg
) {
535 uint64_t time_us
= *reinterpret_cast<uint64_t*>(arg
);
536 if (time_us
< mock_env
->RealNowMicros()) {
537 *reinterpret_cast<uint64_t*>(arg
) = mock_env
->RealNowMicros() + 1000;
540 #endif // OS_MACOSX && !NDEBUG
541 rocksdb::SyncPoint::GetInstance()->SetCallBack(
542 "DBImpl::DumpStats:1", [&](void* /*arg*/) {
545 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
547 ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec
);
548 dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env
->set_current_time(5); });
549 ASSERT_GE(counter
, 1);
551 // Test cacel job through SetOptions
552 ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
553 int old_val
= counter
;
554 for (int i
= 6; i
< 20; ++i
) {
555 dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env
->set_current_time(i
); });
557 ASSERT_EQ(counter
, old_val
);
561 // Test persistent stats background thread scheduling and cancelling
562 TEST_F(DBOptionsTest
, StatsPersistScheduling
) {
564 options
.create_if_missing
= true;
565 options
.stats_persist_period_sec
= 5;
566 std::unique_ptr
<rocksdb::MockTimeEnv
> mock_env
;
567 mock_env
.reset(new rocksdb::MockTimeEnv(env_
));
568 mock_env
->set_current_time(0); // in seconds
569 options
.env
= mock_env
.get();
570 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
571 rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
572 #if defined(OS_MACOSX) && !defined(NDEBUG)
573 rocksdb::SyncPoint::GetInstance()->SetCallBack(
574 "InstrumentedCondVar::TimedWaitInternal", [&](void* arg
) {
575 uint64_t time_us
= *reinterpret_cast<uint64_t*>(arg
);
576 if (time_us
< mock_env
->RealNowMicros()) {
577 *reinterpret_cast<uint64_t*>(arg
) = mock_env
->RealNowMicros() + 1000;
580 #endif // OS_MACOSX && !NDEBUG
582 rocksdb::SyncPoint::GetInstance()->SetCallBack(
583 "DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter
++; });
584 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
586 ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec
);
587 dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env
->set_current_time(5); });
588 ASSERT_GE(counter
, 1);
590 // Test cacel job through SetOptions
591 ASSERT_TRUE(dbfull()->TEST_IsPersistentStatsEnabled());
592 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
593 ASSERT_FALSE(dbfull()->TEST_IsPersistentStatsEnabled());
597 // Test enabling persistent stats for the first time
598 TEST_F(DBOptionsTest
, PersistentStatsFreshInstall
) {
600 options
.create_if_missing
= true;
601 options
.stats_persist_period_sec
= 0;
602 std::unique_ptr
<rocksdb::MockTimeEnv
> mock_env
;
603 mock_env
.reset(new rocksdb::MockTimeEnv(env_
));
604 mock_env
->set_current_time(0); // in seconds
605 options
.env
= mock_env
.get();
606 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
607 rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
608 #if defined(OS_MACOSX) && !defined(NDEBUG)
609 rocksdb::SyncPoint::GetInstance()->SetCallBack(
610 "InstrumentedCondVar::TimedWaitInternal", [&](void* arg
) {
611 uint64_t time_us
= *reinterpret_cast<uint64_t*>(arg
);
612 if (time_us
< mock_env
->RealNowMicros()) {
613 *reinterpret_cast<uint64_t*>(arg
) = mock_env
->RealNowMicros() + 1000;
616 #endif // OS_MACOSX && !NDEBUG
618 rocksdb::SyncPoint::GetInstance()->SetCallBack(
619 "DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter
++; });
620 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
622 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "5"}}));
623 ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec
);
624 dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env
->set_current_time(5); });
625 ASSERT_GE(counter
, 1);
629 TEST_F(DBOptionsTest
, SetOptionsStatsPersistPeriodSec
) {
631 options
.create_if_missing
= true;
632 options
.stats_persist_period_sec
= 5;
635 ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec
);
637 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "12345"}}));
638 ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec
);
639 ASSERT_NOK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "abcde"}}));
640 ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec
);
643 TEST_F(DBOptionsTest
, GetStatsHistory
) {
645 options
.create_if_missing
= true;
646 options
.stats_persist_period_sec
= 5;
647 options
.statistics
= rocksdb::CreateDBStatistics();
648 std::unique_ptr
<rocksdb::MockTimeEnv
> mock_env
;
649 mock_env
.reset(new rocksdb::MockTimeEnv(env_
));
650 mock_env
->set_current_time(0); // in seconds
651 options
.env
= mock_env
.get();
652 #if defined(OS_MACOSX) && !defined(NDEBUG)
653 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
654 rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
655 rocksdb::SyncPoint::GetInstance()->SetCallBack(
656 "InstrumentedCondVar::TimedWaitInternal", [&](void* arg
) {
657 uint64_t time_us
= *reinterpret_cast<uint64_t*>(arg
);
658 if (time_us
< mock_env
->RealNowMicros()) {
659 *reinterpret_cast<uint64_t*>(arg
) = mock_env
->RealNowMicros() + 1000;
662 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
663 #endif // OS_MACOSX && !NDEBUG
665 CreateColumnFamilies({"pikachu"}, options
);
666 ASSERT_OK(Put("foo", "bar"));
667 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
670 // Wait for stats persist to finish
671 dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env
->set_current_time(5); });
672 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
673 db_
->GetStatsHistory(0, 6 * kMicrosInSec
, &stats_iter
);
674 ASSERT_TRUE(stats_iter
!= nullptr);
675 // disabled stats snapshots
676 ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
677 size_t stats_count
= 0;
678 for (; stats_iter
->Valid(); stats_iter
->Next()) {
679 auto stats_map
= stats_iter
->GetStatsMap();
680 stats_count
+= stats_map
.size();
682 ASSERT_GT(stats_count
, 0);
683 // Wait a bit and verify no more stats are found
684 for (mock_time
= 6; mock_time
< 20; ++mock_time
) {
685 dbfull()->TEST_WaitForPersistStatsRun(
686 [&] { mock_env
->set_current_time(mock_time
); });
688 db_
->GetStatsHistory(0, 20 * kMicrosInSec
, &stats_iter
);
689 ASSERT_TRUE(stats_iter
!= nullptr);
690 size_t stats_count_new
= 0;
691 for (; stats_iter
->Valid(); stats_iter
->Next()) {
692 stats_count_new
+= stats_iter
->GetStatsMap().size();
694 ASSERT_EQ(stats_count_new
, stats_count
);
698 TEST_F(DBOptionsTest
, InMemoryStatsHistoryPurging
) {
700 options
.create_if_missing
= true;
701 options
.statistics
= rocksdb::CreateDBStatistics();
702 options
.stats_persist_period_sec
= 1;
703 std::unique_ptr
<rocksdb::MockTimeEnv
> mock_env
;
704 mock_env
.reset(new rocksdb::MockTimeEnv(env_
));
705 mock_env
->set_current_time(0); // in seconds
706 options
.env
= mock_env
.get();
707 #if defined(OS_MACOSX) && !defined(NDEBUG)
708 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
709 rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
710 rocksdb::SyncPoint::GetInstance()->SetCallBack(
711 "InstrumentedCondVar::TimedWaitInternal", [&](void* arg
) {
712 uint64_t time_us
= *reinterpret_cast<uint64_t*>(arg
);
713 if (time_us
< mock_env
->RealNowMicros()) {
714 *reinterpret_cast<uint64_t*>(arg
) = mock_env
->RealNowMicros() + 1000;
717 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
718 #endif // OS_MACOSX && !NDEBUG
720 CreateColumnFamilies({"pikachu"}, options
);
721 ASSERT_OK(Put("foo", "bar"));
722 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
723 // some random operation to populate statistics
724 ASSERT_OK(Delete("foo"));
725 ASSERT_OK(Put("sol", "sol"));
726 ASSERT_OK(Put("epic", "epic"));
727 ASSERT_OK(Put("ltd", "ltd"));
728 ASSERT_EQ("sol", Get("sol"));
729 ASSERT_EQ("epic", Get("epic"));
730 ASSERT_EQ("ltd", Get("ltd"));
731 Iterator
* iterator
= db_
->NewIterator(ReadOptions());
732 for (iterator
->SeekToFirst(); iterator
->Valid(); iterator
->Next()) {
733 ASSERT_TRUE(iterator
->key() == iterator
->value());
737 ASSERT_OK(Delete("sol"));
738 db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr);
740 // Wait for stats persist to finish
741 for (; mock_time
< 5; ++mock_time
) {
742 dbfull()->TEST_WaitForPersistStatsRun(
743 [&] { mock_env
->set_current_time(mock_time
); });
746 // second round of ops
747 ASSERT_OK(Put("saigon", "saigon"));
748 ASSERT_OK(Put("noodle talk", "noodle talk"));
749 ASSERT_OK(Put("ping bistro", "ping bistro"));
750 iterator
= db_
->NewIterator(ReadOptions());
751 for (iterator
->SeekToFirst(); iterator
->Valid(); iterator
->Next()) {
752 ASSERT_TRUE(iterator
->key() == iterator
->value());
756 db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr);
757 for (; mock_time
< 10; ++mock_time
) {
758 dbfull()->TEST_WaitForPersistStatsRun(
759 [&] { mock_env
->set_current_time(mock_time
); });
761 std::unique_ptr
<StatsHistoryIterator
> stats_iter
;
762 db_
->GetStatsHistory(0, 10 * kMicrosInSec
, &stats_iter
);
763 ASSERT_TRUE(stats_iter
!= nullptr);
764 size_t stats_count
= 0;
766 for (; stats_iter
->Valid(); stats_iter
->Next()) {
768 auto stats_map
= stats_iter
->GetStatsMap();
769 stats_count
+= stats_map
.size();
771 size_t stats_history_size
= dbfull()->TEST_EstiamteStatsHistorySize();
772 ASSERT_GE(slice_count
, 9);
773 ASSERT_GE(stats_history_size
, 12000);
774 // capping memory cost at 12000 bytes since one slice is around 10000~12000
775 ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "12000"}}));
776 ASSERT_EQ(12000, dbfull()->GetDBOptions().stats_history_buffer_size
);
777 // Wait for stats persist to finish
778 for (; mock_time
< 20; ++mock_time
) {
779 dbfull()->TEST_WaitForPersistStatsRun(
780 [&] { mock_env
->set_current_time(mock_time
); });
782 db_
->GetStatsHistory(0, 20 * kMicrosInSec
, &stats_iter
);
783 ASSERT_TRUE(stats_iter
!= nullptr);
784 size_t stats_count_reopen
= 0;
786 for (; stats_iter
->Valid(); stats_iter
->Next()) {
788 auto stats_map
= stats_iter
->GetStatsMap();
789 stats_count_reopen
+= stats_map
.size();
791 size_t stats_history_size_reopen
= dbfull()->TEST_EstiamteStatsHistorySize();
792 // only one slice can fit under the new stats_history_buffer_size
793 ASSERT_LT(slice_count
, 2);
794 ASSERT_TRUE(stats_history_size_reopen
< 12000 &&
795 stats_history_size_reopen
> 0);
796 ASSERT_TRUE(stats_count_reopen
< stats_count
&& stats_count_reopen
> 0);
800 static void assert_candidate_files_empty(DBImpl
* dbfull
, const bool empty
) {
801 dbfull
->TEST_LockMutex();
802 JobContext
job_context(0);
803 dbfull
->FindObsoleteFiles(&job_context
, false);
804 ASSERT_EQ(empty
, job_context
.full_scan_candidate_files
.empty());
805 dbfull
->TEST_UnlockMutex();
806 if (job_context
.HaveSomethingToDelete()) {
807 // fulfill the contract of FindObsoleteFiles by calling PurgeObsoleteFiles
808 // afterwards; otherwise the test may hang on shutdown
809 dbfull
->PurgeObsoleteFiles(job_context
);
814 TEST_F(DBOptionsTest
, DeleteObsoleteFilesPeriodChange
) {
815 SpecialEnv
env(env_
);
816 env
.time_elapse_only_sleep_
= true;
819 options
.create_if_missing
= true;
820 ASSERT_OK(TryReopen(options
));
822 // Verify that candidate files set is empty when no full scan requested.
823 assert_candidate_files_empty(dbfull(), true);
826 dbfull()->SetDBOptions({{"delete_obsolete_files_period_micros", "0"}}));
828 // After delete_obsolete_files_period_micros updated to 0, the next call
829 // to FindObsoleteFiles should make a full scan
830 assert_candidate_files_empty(dbfull(), false);
833 dbfull()->SetDBOptions({{"delete_obsolete_files_period_micros", "20"}}));
835 assert_candidate_files_empty(dbfull(), true);
837 env
.addon_time_
.store(20);
838 assert_candidate_files_empty(dbfull(), true);
840 env
.addon_time_
.store(21);
841 assert_candidate_files_empty(dbfull(), false);
846 TEST_F(DBOptionsTest
, MaxOpenFilesChange
) {
847 SpecialEnv
env(env_
);
849 options
.env
= CurrentOptions().env
;
850 options
.max_open_files
= -1;
854 Cache
* tc
= dbfull()->TEST_table_cache();
856 ASSERT_EQ(-1, dbfull()->GetDBOptions().max_open_files
);
857 ASSERT_LT(2000, tc
->GetCapacity());
858 ASSERT_OK(dbfull()->SetDBOptions({{"max_open_files", "1024"}}));
859 ASSERT_EQ(1024, dbfull()->GetDBOptions().max_open_files
);
860 // examine the table cache (actual size should be 1014)
861 ASSERT_GT(1500, tc
->GetCapacity());
865 TEST_F(DBOptionsTest
, SanitizeDelayedWriteRate
) {
867 options
.delayed_write_rate
= 0;
869 ASSERT_EQ(16 * 1024 * 1024, dbfull()->GetDBOptions().delayed_write_rate
);
871 options
.rate_limiter
.reset(NewGenericRateLimiter(31 * 1024 * 1024));
873 ASSERT_EQ(31 * 1024 * 1024, dbfull()->GetDBOptions().delayed_write_rate
);
876 TEST_F(DBOptionsTest
, SetFIFOCompactionOptions
) {
878 options
.compaction_style
= kCompactionStyleFIFO
;
879 options
.write_buffer_size
= 10 << 10; // 10KB
880 options
.arena_block_size
= 4096;
881 options
.compression
= kNoCompression
;
882 options
.create_if_missing
= true;
883 options
.compaction_options_fifo
.allow_compaction
= false;
884 env_
->time_elapse_only_sleep_
= false;
887 // Test dynamically changing ttl.
888 env_
->addon_time_
.store(0);
889 options
.ttl
= 1 * 60 * 60; // 1 hour
890 ASSERT_OK(TryReopen(options
));
893 for (int i
= 0; i
< 10; i
++) {
894 // Generate and flush a file about 10KB.
895 for (int j
= 0; j
< 10; j
++) {
896 ASSERT_OK(Put(ToString(i
* 20 + j
), RandomString(&rnd
, 980)));
900 ASSERT_OK(dbfull()->TEST_WaitForCompact());
901 ASSERT_EQ(NumTableFilesAtLevel(0), 10);
903 // Add 61 seconds to the time.
904 env_
->addon_time_
.fetch_add(61);
906 // No files should be compacted as ttl is set to 1 hour.
907 ASSERT_EQ(dbfull()->GetOptions().ttl
, 3600);
908 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
909 ASSERT_EQ(NumTableFilesAtLevel(0), 10);
911 // Set ttl to 1 minute. So all files should get deleted.
912 ASSERT_OK(dbfull()->SetOptions({{"ttl", "60"}}));
913 ASSERT_EQ(dbfull()->GetOptions().ttl
, 60);
914 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
915 ASSERT_OK(dbfull()->TEST_WaitForCompact());
916 ASSERT_EQ(NumTableFilesAtLevel(0), 0);
918 // Test dynamically changing compaction_options_fifo.max_table_files_size
919 env_
->addon_time_
.store(0);
920 options
.compaction_options_fifo
.max_table_files_size
= 500 << 10; // 00KB
922 DestroyAndReopen(options
);
924 for (int i
= 0; i
< 10; i
++) {
925 // Generate and flush a file about 10KB.
926 for (int j
= 0; j
< 10; j
++) {
927 ASSERT_OK(Put(ToString(i
* 20 + j
), RandomString(&rnd
, 980)));
931 ASSERT_OK(dbfull()->TEST_WaitForCompact());
932 ASSERT_EQ(NumTableFilesAtLevel(0), 10);
934 // No files should be compacted as max_table_files_size is set to 500 KB.
935 ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo
.max_table_files_size
,
937 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
938 ASSERT_EQ(NumTableFilesAtLevel(0), 10);
940 // Set max_table_files_size to 12 KB. So only 1 file should remain now.
941 ASSERT_OK(dbfull()->SetOptions(
942 {{"compaction_options_fifo", "{max_table_files_size=12288;}"}}));
943 ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo
.max_table_files_size
,
945 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
946 ASSERT_OK(dbfull()->TEST_WaitForCompact());
947 ASSERT_EQ(NumTableFilesAtLevel(0), 1);
949 // Test dynamically changing compaction_options_fifo.allow_compaction
950 options
.compaction_options_fifo
.max_table_files_size
= 500 << 10; // 500KB
952 options
.compaction_options_fifo
.allow_compaction
= false;
953 options
.level0_file_num_compaction_trigger
= 6;
954 DestroyAndReopen(options
);
956 for (int i
= 0; i
< 10; i
++) {
957 // Generate and flush a file about 10KB.
958 for (int j
= 0; j
< 10; j
++) {
959 ASSERT_OK(Put(ToString(i
* 20 + j
), RandomString(&rnd
, 980)));
963 ASSERT_OK(dbfull()->TEST_WaitForCompact());
964 ASSERT_EQ(NumTableFilesAtLevel(0), 10);
966 // No files should be compacted as max_table_files_size is set to 500 KB and
967 // allow_compaction is false
968 ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo
.allow_compaction
,
970 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
971 ASSERT_EQ(NumTableFilesAtLevel(0), 10);
973 // Set allow_compaction to true. So number of files should be between 1 and 5.
974 ASSERT_OK(dbfull()->SetOptions(
975 {{"compaction_options_fifo", "{allow_compaction=true;}"}}));
976 ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo
.allow_compaction
,
978 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
979 ASSERT_OK(dbfull()->TEST_WaitForCompact());
980 ASSERT_GE(NumTableFilesAtLevel(0), 1);
981 ASSERT_LE(NumTableFilesAtLevel(0), 5);
984 TEST_F(DBOptionsTest
, CompactionReadaheadSizeChange
) {
985 SpecialEnv
env(env_
);
989 options
.compaction_readahead_size
= 0;
990 options
.new_table_reader_for_compaction_inputs
= true;
991 options
.level0_file_num_compaction_trigger
= 2;
992 const std::string
kValue(1024, 'v');
995 ASSERT_EQ(0, dbfull()->GetDBOptions().compaction_readahead_size
);
996 ASSERT_OK(dbfull()->SetDBOptions({{"compaction_readahead_size", "256"}}));
997 ASSERT_EQ(256, dbfull()->GetDBOptions().compaction_readahead_size
);
998 for (int i
= 0; i
< 1024; i
++) {
1002 for (int i
= 0; i
< 1024 * 2; i
++) {
1003 Put(Key(i
), kValue
);
1006 dbfull()->TEST_WaitForCompact();
1007 ASSERT_EQ(256, env_
->compaction_readahead_size_
);
1010 #endif // ROCKSDB_LITE
1012 } // namespace rocksdb
1014 int main(int argc
, char** argv
) {
1015 rocksdb::port::InstallStackTraceHandler();
1016 ::testing::InitGoogleTest(&argc
, argv
);
1017 return RUN_ALL_TESTS();