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.
10 #include "db/db_test_util.h"
11 #include "port/stack_trace.h"
12 #include "rocksdb/perf_context.h"
13 #include "util/fault_injection_test_env.h"
14 #if !defined(ROCKSDB_LITE)
15 #include "util/sync_point.h"
20 class DBBasicTest
: public DBTestBase
{
22 DBBasicTest() : DBTestBase("/db_basic_test") {}
25 TEST_F(DBBasicTest
, OpenWhenOpen
) {
26 Options options
= CurrentOptions();
28 rocksdb::DB
* db2
= nullptr;
29 rocksdb::Status s
= DB::Open(options
, dbname_
, &db2
);
31 ASSERT_EQ(Status::Code::kIOError
, s
.code());
32 ASSERT_EQ(Status::SubCode::kNone
, s
.subcode());
33 ASSERT_TRUE(strstr(s
.getState(), "lock ") != nullptr);
39 TEST_F(DBBasicTest
, ReadOnlyDB
) {
40 ASSERT_OK(Put("foo", "v1"));
41 ASSERT_OK(Put("bar", "v2"));
42 ASSERT_OK(Put("foo", "v3"));
45 auto options
= CurrentOptions();
46 assert(options
.env
== env_
);
47 ASSERT_OK(ReadOnlyReopen(options
));
48 ASSERT_EQ("v3", Get("foo"));
49 ASSERT_EQ("v2", Get("bar"));
50 Iterator
* iter
= db_
->NewIterator(ReadOptions());
52 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
53 ASSERT_OK(iter
->status());
60 // Reopen and flush memtable.
64 // Now check keys in read only mode.
65 ASSERT_OK(ReadOnlyReopen(options
));
66 ASSERT_EQ("v3", Get("foo"));
67 ASSERT_EQ("v2", Get("bar"));
68 ASSERT_TRUE(db_
->SyncWAL().IsNotSupported());
71 TEST_F(DBBasicTest
, CompactedDB
) {
72 const uint64_t kFileSize
= 1 << 20;
73 Options options
= CurrentOptions();
74 options
.disable_auto_compactions
= true;
75 options
.write_buffer_size
= kFileSize
;
76 options
.target_file_size_base
= kFileSize
;
77 options
.max_bytes_for_level_base
= 1 << 30;
78 options
.compression
= kNoCompression
;
80 // 1 L0 file, use CompactedDB if max_open_files = -1
81 ASSERT_OK(Put("aaa", DummyString(kFileSize
/ 2, '1')));
84 ASSERT_OK(ReadOnlyReopen(options
));
85 Status s
= Put("new", "value");
86 ASSERT_EQ(s
.ToString(),
87 "Not implemented: Not supported operation in read only mode.");
88 ASSERT_EQ(DummyString(kFileSize
/ 2, '1'), Get("aaa"));
90 options
.max_open_files
= -1;
91 ASSERT_OK(ReadOnlyReopen(options
));
92 s
= Put("new", "value");
93 ASSERT_EQ(s
.ToString(),
94 "Not implemented: Not supported in compacted db mode.");
95 ASSERT_EQ(DummyString(kFileSize
/ 2, '1'), Get("aaa"));
99 ASSERT_OK(Put("bbb", DummyString(kFileSize
/ 2, '2')));
101 ASSERT_OK(Put("aaa", DummyString(kFileSize
/ 2, 'a')));
103 ASSERT_OK(Put("bbb", DummyString(kFileSize
/ 2, 'b')));
104 ASSERT_OK(Put("eee", DummyString(kFileSize
/ 2, 'e')));
108 ASSERT_OK(ReadOnlyReopen(options
));
109 // Fallback to read-only DB
110 s
= Put("new", "value");
111 ASSERT_EQ(s
.ToString(),
112 "Not implemented: Not supported operation in read only mode.");
118 ASSERT_OK(Put("fff", DummyString(kFileSize
/ 2, 'f')));
119 ASSERT_OK(Put("hhh", DummyString(kFileSize
/ 2, 'h')));
120 ASSERT_OK(Put("iii", DummyString(kFileSize
/ 2, 'i')));
121 ASSERT_OK(Put("jjj", DummyString(kFileSize
/ 2, 'j')));
122 db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr);
123 ASSERT_EQ(3, NumTableFilesAtLevel(1));
127 ASSERT_OK(ReadOnlyReopen(options
));
128 s
= Put("new", "value");
129 ASSERT_EQ(s
.ToString(),
130 "Not implemented: Not supported in compacted db mode.");
131 ASSERT_EQ("NOT_FOUND", Get("abc"));
132 ASSERT_EQ(DummyString(kFileSize
/ 2, 'a'), Get("aaa"));
133 ASSERT_EQ(DummyString(kFileSize
/ 2, 'b'), Get("bbb"));
134 ASSERT_EQ("NOT_FOUND", Get("ccc"));
135 ASSERT_EQ(DummyString(kFileSize
/ 2, 'e'), Get("eee"));
136 ASSERT_EQ(DummyString(kFileSize
/ 2, 'f'), Get("fff"));
137 ASSERT_EQ("NOT_FOUND", Get("ggg"));
138 ASSERT_EQ(DummyString(kFileSize
/ 2, 'h'), Get("hhh"));
139 ASSERT_EQ(DummyString(kFileSize
/ 2, 'i'), Get("iii"));
140 ASSERT_EQ(DummyString(kFileSize
/ 2, 'j'), Get("jjj"));
141 ASSERT_EQ("NOT_FOUND", Get("kkk"));
144 std::vector
<std::string
> values
;
145 std::vector
<Status
> status_list
= dbfull()->MultiGet(
147 std::vector
<Slice
>({Slice("aaa"), Slice("ccc"), Slice("eee"),
148 Slice("ggg"), Slice("iii"), Slice("kkk")}),
150 ASSERT_EQ(status_list
.size(), static_cast<uint64_t>(6));
151 ASSERT_EQ(values
.size(), static_cast<uint64_t>(6));
152 ASSERT_OK(status_list
[0]);
153 ASSERT_EQ(DummyString(kFileSize
/ 2, 'a'), values
[0]);
154 ASSERT_TRUE(status_list
[1].IsNotFound());
155 ASSERT_OK(status_list
[2]);
156 ASSERT_EQ(DummyString(kFileSize
/ 2, 'e'), values
[2]);
157 ASSERT_TRUE(status_list
[3].IsNotFound());
158 ASSERT_OK(status_list
[4]);
159 ASSERT_EQ(DummyString(kFileSize
/ 2, 'i'), values
[4]);
160 ASSERT_TRUE(status_list
[5].IsNotFound());
164 ASSERT_OK(Put("fff", DummyString(kFileSize
/ 2, 'f')));
166 ASSERT_OK(ReadOnlyReopen(options
));
167 s
= Put("new", "value");
168 ASSERT_EQ(s
.ToString(),
169 "Not implemented: Not supported operation in read only mode.");
172 TEST_F(DBBasicTest
, LevelLimitReopen
) {
173 Options options
= CurrentOptions();
174 CreateAndReopenWithCF({"pikachu"}, options
);
176 const std::string
value(1024 * 1024, ' ');
178 while (NumTableFilesAtLevel(2, 1) == 0) {
179 ASSERT_OK(Put(1, Key(i
++), value
));
180 dbfull()->TEST_WaitForFlushMemTable();
181 dbfull()->TEST_WaitForCompact();
184 options
.num_levels
= 1;
185 options
.max_bytes_for_level_multiplier_additional
.resize(1, 1);
186 Status s
= TryReopenWithColumnFamilies({"default", "pikachu"}, options
);
187 ASSERT_EQ(s
.IsInvalidArgument(), true);
188 ASSERT_EQ(s
.ToString(),
189 "Invalid argument: db has more levels than options.num_levels");
191 options
.num_levels
= 10;
192 options
.max_bytes_for_level_multiplier_additional
.resize(10, 1);
193 ASSERT_OK(TryReopenWithColumnFamilies({"default", "pikachu"}, options
));
195 #endif // ROCKSDB_LITE
197 TEST_F(DBBasicTest
, PutDeleteGet
) {
199 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
200 ASSERT_OK(Put(1, "foo", "v1"));
201 ASSERT_EQ("v1", Get(1, "foo"));
202 ASSERT_OK(Put(1, "foo", "v2"));
203 ASSERT_EQ("v2", Get(1, "foo"));
204 ASSERT_OK(Delete(1, "foo"));
205 ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
206 } while (ChangeOptions());
209 TEST_F(DBBasicTest
, PutSingleDeleteGet
) {
211 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
212 ASSERT_OK(Put(1, "foo", "v1"));
213 ASSERT_EQ("v1", Get(1, "foo"));
214 ASSERT_OK(Put(1, "foo2", "v2"));
215 ASSERT_EQ("v2", Get(1, "foo2"));
216 ASSERT_OK(SingleDelete(1, "foo"));
217 ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
218 // Ski FIFO and universal compaction because they do not apply to the test
219 // case. Skip MergePut because single delete does not get removed when it
220 // encounters a merge.
221 } while (ChangeOptions(kSkipFIFOCompaction
| kSkipUniversalCompaction
|
225 TEST_F(DBBasicTest
, EmptyFlush
) {
226 // It is possible to produce empty flushes when using single deletes. Tests
227 // whether empty flushes cause issues.
231 Options options
= CurrentOptions();
232 options
.disable_auto_compactions
= true;
233 CreateAndReopenWithCF({"pikachu"}, options
);
235 Put(1, "a", Slice());
236 SingleDelete(1, "a");
239 ASSERT_EQ("[ ]", AllEntriesFor("a", 1));
240 // Skip FIFO and universal compaction as they do not apply to the test
241 // case. Skip MergePut because merges cannot be combined with single
243 } while (ChangeOptions(kSkipFIFOCompaction
| kSkipUniversalCompaction
|
247 TEST_F(DBBasicTest
, GetFromVersions
) {
249 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
250 ASSERT_OK(Put(1, "foo", "v1"));
252 ASSERT_EQ("v1", Get(1, "foo"));
253 ASSERT_EQ("NOT_FOUND", Get(0, "foo"));
254 } while (ChangeOptions());
258 TEST_F(DBBasicTest
, GetSnapshot
) {
259 anon::OptionsOverride options_override
;
260 options_override
.skip_policy
= kSkipNoSnapshot
;
262 CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override
));
263 // Try with both a short key and a long key
264 for (int i
= 0; i
< 2; i
++) {
265 std::string key
= (i
== 0) ? std::string("foo") : std::string(200, 'x');
266 ASSERT_OK(Put(1, key
, "v1"));
267 const Snapshot
* s1
= db_
->GetSnapshot();
268 ASSERT_OK(Put(1, key
, "v2"));
269 ASSERT_EQ("v2", Get(1, key
));
270 ASSERT_EQ("v1", Get(1, key
, s1
));
272 ASSERT_EQ("v2", Get(1, key
));
273 ASSERT_EQ("v1", Get(1, key
, s1
));
274 db_
->ReleaseSnapshot(s1
);
276 } while (ChangeOptions());
278 #endif // ROCKSDB_LITE
280 TEST_F(DBBasicTest
, CheckLock
) {
283 Options options
= CurrentOptions();
284 ASSERT_OK(TryReopen(options
));
286 // second open should fail
287 ASSERT_TRUE(!(DB::Open(options
, dbname_
, &localdb
)).ok());
288 } while (ChangeCompactOptions());
291 TEST_F(DBBasicTest
, FlushMultipleMemtable
) {
293 Options options
= CurrentOptions();
294 WriteOptions writeOpt
= WriteOptions();
295 writeOpt
.disableWAL
= true;
296 options
.max_write_buffer_number
= 4;
297 options
.min_write_buffer_number_to_merge
= 3;
298 options
.max_write_buffer_number_to_maintain
= -1;
299 CreateAndReopenWithCF({"pikachu"}, options
);
300 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "foo", "v1"));
302 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "bar", "v1"));
304 ASSERT_EQ("v1", Get(1, "foo"));
305 ASSERT_EQ("v1", Get(1, "bar"));
307 } while (ChangeCompactOptions());
310 TEST_F(DBBasicTest
, FlushEmptyColumnFamily
) {
311 // Block flush thread and disable compaction thread
312 env_
->SetBackgroundThreads(1, Env::HIGH
);
313 env_
->SetBackgroundThreads(1, Env::LOW
);
314 test::SleepingBackgroundTask sleeping_task_low
;
315 env_
->Schedule(&test::SleepingBackgroundTask::DoSleepTask
, &sleeping_task_low
,
317 test::SleepingBackgroundTask sleeping_task_high
;
318 env_
->Schedule(&test::SleepingBackgroundTask::DoSleepTask
,
319 &sleeping_task_high
, Env::Priority::HIGH
);
321 Options options
= CurrentOptions();
322 // disable compaction
323 options
.disable_auto_compactions
= true;
324 WriteOptions writeOpt
= WriteOptions();
325 writeOpt
.disableWAL
= true;
326 options
.max_write_buffer_number
= 2;
327 options
.min_write_buffer_number_to_merge
= 1;
328 options
.max_write_buffer_number_to_maintain
= 1;
329 CreateAndReopenWithCF({"pikachu"}, options
);
331 // Compaction can still go through even if no thread can flush the
336 // Insert can go through
337 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[0], "foo", "v1"));
338 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "bar", "v1"));
340 ASSERT_EQ("v1", Get(0, "foo"));
341 ASSERT_EQ("v1", Get(1, "bar"));
343 sleeping_task_high
.WakeUp();
344 sleeping_task_high
.WaitUntilDone();
346 // Flush can still go through.
350 sleeping_task_low
.WakeUp();
351 sleeping_task_low
.WaitUntilDone();
354 TEST_F(DBBasicTest
, FLUSH
) {
356 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
357 WriteOptions writeOpt
= WriteOptions();
358 writeOpt
.disableWAL
= true;
359 SetPerfLevel(kEnableTime
);
360 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "foo", "v1"));
361 // this will now also flush the last 2 writes
363 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "bar", "v1"));
365 get_perf_context()->Reset();
367 ASSERT_TRUE((int)get_perf_context()->get_from_output_files_time
> 0);
368 ASSERT_EQ(2, (int)get_perf_context()->get_read_bytes
);
370 ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
371 ASSERT_EQ("v1", Get(1, "foo"));
372 ASSERT_EQ("v1", Get(1, "bar"));
374 writeOpt
.disableWAL
= true;
375 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "bar", "v2"));
376 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "foo", "v2"));
379 ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
380 ASSERT_EQ("v2", Get(1, "bar"));
381 get_perf_context()->Reset();
382 ASSERT_EQ("v2", Get(1, "foo"));
383 ASSERT_TRUE((int)get_perf_context()->get_from_output_files_time
> 0);
385 writeOpt
.disableWAL
= false;
386 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "bar", "v3"));
387 ASSERT_OK(dbfull()->Put(writeOpt
, handles_
[1], "foo", "v3"));
390 ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
391 // 'foo' should be there because its put
393 ASSERT_EQ("v3", Get(1, "foo"));
394 ASSERT_EQ("v3", Get(1, "bar"));
396 SetPerfLevel(kDisable
);
397 } while (ChangeCompactOptions());
400 TEST_F(DBBasicTest
, ManifestRollOver
) {
403 options
.max_manifest_file_size
= 10; // 10 bytes
404 options
= CurrentOptions(options
);
405 CreateAndReopenWithCF({"pikachu"}, options
);
407 ASSERT_OK(Put(1, "manifest_key1", std::string(1000, '1')));
408 ASSERT_OK(Put(1, "manifest_key2", std::string(1000, '2')));
409 ASSERT_OK(Put(1, "manifest_key3", std::string(1000, '3')));
410 uint64_t manifest_before_flush
= dbfull()->TEST_Current_Manifest_FileNo();
411 ASSERT_OK(Flush(1)); // This should trigger LogAndApply.
412 uint64_t manifest_after_flush
= dbfull()->TEST_Current_Manifest_FileNo();
413 ASSERT_GT(manifest_after_flush
, manifest_before_flush
);
414 ReopenWithColumnFamilies({"default", "pikachu"}, options
);
415 ASSERT_GT(dbfull()->TEST_Current_Manifest_FileNo(), manifest_after_flush
);
416 // check if a new manifest file got inserted or not.
417 ASSERT_EQ(std::string(1000, '1'), Get(1, "manifest_key1"));
418 ASSERT_EQ(std::string(1000, '2'), Get(1, "manifest_key2"));
419 ASSERT_EQ(std::string(1000, '3'), Get(1, "manifest_key3"));
421 } while (ChangeCompactOptions());
424 TEST_F(DBBasicTest
, IdentityAcrossRestarts
) {
427 ASSERT_OK(db_
->GetDbIdentity(id1
));
429 Options options
= CurrentOptions();
432 ASSERT_OK(db_
->GetDbIdentity(id2
));
433 // id1 should match id2 because identity was not regenerated
434 ASSERT_EQ(id1
.compare(id2
), 0);
436 std::string idfilename
= IdentityFileName(dbname_
);
437 ASSERT_OK(env_
->DeleteFile(idfilename
));
440 ASSERT_OK(db_
->GetDbIdentity(id3
));
441 // id1 should NOT match id3 because identity was regenerated
442 ASSERT_NE(id1
.compare(id3
), 0);
443 } while (ChangeCompactOptions());
447 TEST_F(DBBasicTest
, Snapshot
) {
448 anon::OptionsOverride options_override
;
449 options_override
.skip_policy
= kSkipNoSnapshot
;
451 CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override
));
452 Put(0, "foo", "0v1");
453 Put(1, "foo", "1v1");
455 const Snapshot
* s1
= db_
->GetSnapshot();
456 ASSERT_EQ(1U, GetNumSnapshots());
457 uint64_t time_snap1
= GetTimeOldestSnapshots();
458 ASSERT_GT(time_snap1
, 0U);
459 Put(0, "foo", "0v2");
460 Put(1, "foo", "1v2");
462 env_
->addon_time_
.fetch_add(1);
464 const Snapshot
* s2
= db_
->GetSnapshot();
465 ASSERT_EQ(2U, GetNumSnapshots());
466 ASSERT_EQ(time_snap1
, GetTimeOldestSnapshots());
467 Put(0, "foo", "0v3");
468 Put(1, "foo", "1v3");
471 ManagedSnapshot
s3(db_
);
472 ASSERT_EQ(3U, GetNumSnapshots());
473 ASSERT_EQ(time_snap1
, GetTimeOldestSnapshots());
475 Put(0, "foo", "0v4");
476 Put(1, "foo", "1v4");
477 ASSERT_EQ("0v1", Get(0, "foo", s1
));
478 ASSERT_EQ("1v1", Get(1, "foo", s1
));
479 ASSERT_EQ("0v2", Get(0, "foo", s2
));
480 ASSERT_EQ("1v2", Get(1, "foo", s2
));
481 ASSERT_EQ("0v3", Get(0, "foo", s3
.snapshot()));
482 ASSERT_EQ("1v3", Get(1, "foo", s3
.snapshot()));
483 ASSERT_EQ("0v4", Get(0, "foo"));
484 ASSERT_EQ("1v4", Get(1, "foo"));
487 ASSERT_EQ(2U, GetNumSnapshots());
488 ASSERT_EQ(time_snap1
, GetTimeOldestSnapshots());
489 ASSERT_EQ("0v1", Get(0, "foo", s1
));
490 ASSERT_EQ("1v1", Get(1, "foo", s1
));
491 ASSERT_EQ("0v2", Get(0, "foo", s2
));
492 ASSERT_EQ("1v2", Get(1, "foo", s2
));
493 ASSERT_EQ("0v4", Get(0, "foo"));
494 ASSERT_EQ("1v4", Get(1, "foo"));
496 db_
->ReleaseSnapshot(s1
);
497 ASSERT_EQ("0v2", Get(0, "foo", s2
));
498 ASSERT_EQ("1v2", Get(1, "foo", s2
));
499 ASSERT_EQ("0v4", Get(0, "foo"));
500 ASSERT_EQ("1v4", Get(1, "foo"));
501 ASSERT_EQ(1U, GetNumSnapshots());
502 ASSERT_LT(time_snap1
, GetTimeOldestSnapshots());
504 db_
->ReleaseSnapshot(s2
);
505 ASSERT_EQ(0U, GetNumSnapshots());
506 ASSERT_EQ("0v4", Get(0, "foo"));
507 ASSERT_EQ("1v4", Get(1, "foo"));
508 } while (ChangeOptions());
511 #endif // ROCKSDB_LITE
513 TEST_F(DBBasicTest
, CompactBetweenSnapshots
) {
514 anon::OptionsOverride options_override
;
515 options_override
.skip_policy
= kSkipNoSnapshot
;
517 Options options
= CurrentOptions(options_override
);
518 options
.disable_auto_compactions
= true;
519 CreateAndReopenWithCF({"pikachu"}, options
);
521 FillLevels("a", "z", 1);
523 Put(1, "foo", "first");
524 const Snapshot
* snapshot1
= db_
->GetSnapshot();
525 Put(1, "foo", "second");
526 Put(1, "foo", "third");
527 Put(1, "foo", "fourth");
528 const Snapshot
* snapshot2
= db_
->GetSnapshot();
529 Put(1, "foo", "fifth");
530 Put(1, "foo", "sixth");
532 // All entries (including duplicates) exist
533 // before any compaction or flush is triggered.
534 ASSERT_EQ(AllEntriesFor("foo", 1),
535 "[ sixth, fifth, fourth, third, second, first ]");
536 ASSERT_EQ("sixth", Get(1, "foo"));
537 ASSERT_EQ("fourth", Get(1, "foo", snapshot2
));
538 ASSERT_EQ("first", Get(1, "foo", snapshot1
));
540 // After a flush, "second", "third" and "fifth" should
543 ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth, first ]");
545 // after we release the snapshot1, only two values left
546 db_
->ReleaseSnapshot(snapshot1
);
547 FillLevels("a", "z", 1);
548 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
551 // We have only one valid snapshot snapshot2. Since snapshot1 is
552 // not valid anymore, "first" should be removed by a compaction.
553 ASSERT_EQ("sixth", Get(1, "foo"));
554 ASSERT_EQ("fourth", Get(1, "foo", snapshot2
));
555 ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth ]");
557 // after we release the snapshot2, only one value should be left
558 db_
->ReleaseSnapshot(snapshot2
);
559 FillLevels("a", "z", 1);
560 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
562 ASSERT_EQ("sixth", Get(1, "foo"));
563 ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth ]");
564 } while (ChangeOptions(kSkipFIFOCompaction
));
567 TEST_F(DBBasicTest
, DBOpen_Options
) {
568 Options options
= CurrentOptions();
572 // Does not exist, and create_if_missing == false: error
574 options
.create_if_missing
= false;
575 Status s
= DB::Open(options
, dbname_
, &db
);
576 ASSERT_TRUE(strstr(s
.ToString().c_str(), "does not exist") != nullptr);
577 ASSERT_TRUE(db
== nullptr);
579 // Does not exist, and create_if_missing == true: OK
580 options
.create_if_missing
= true;
581 s
= DB::Open(options
, dbname_
, &db
);
583 ASSERT_TRUE(db
!= nullptr);
588 // Does exist, and error_if_exists == true: error
589 options
.create_if_missing
= false;
590 options
.error_if_exists
= true;
591 s
= DB::Open(options
, dbname_
, &db
);
592 ASSERT_TRUE(strstr(s
.ToString().c_str(), "exists") != nullptr);
593 ASSERT_TRUE(db
== nullptr);
595 // Does exist, and error_if_exists == false: OK
596 options
.create_if_missing
= true;
597 options
.error_if_exists
= false;
598 s
= DB::Open(options
, dbname_
, &db
);
600 ASSERT_TRUE(db
!= nullptr);
606 TEST_F(DBBasicTest
, CompactOnFlush
) {
607 anon::OptionsOverride options_override
;
608 options_override
.skip_policy
= kSkipNoSnapshot
;
610 Options options
= CurrentOptions(options_override
);
611 options
.disable_auto_compactions
= true;
612 CreateAndReopenWithCF({"pikachu"}, options
);
616 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v1 ]");
618 // Write two new keys
619 Put(1, "a", "begin");
623 // Case1: Delete followed by a put
626 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v2, DEL, v1 ]");
628 // After the current memtable is flushed, the DEL should
631 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v2, v1 ]");
633 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
635 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v2 ]");
637 // Case 2: Delete followed by another delete
640 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL, DEL, v2 ]");
642 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL, v2 ]");
643 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
645 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
647 // Case 3: Put followed by a delete
650 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL, v3 ]");
652 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL ]");
653 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
655 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
657 // Case 4: Put followed by another Put
660 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v5, v4 ]");
662 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v5 ]");
663 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
665 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v5 ]");
669 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
671 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
673 // Case 5: Put followed by snapshot followed by another Put
674 // Both puts should remain.
676 const Snapshot
* snapshot
= db_
->GetSnapshot();
679 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v7, v6 ]");
680 db_
->ReleaseSnapshot(snapshot
);
684 dbfull()->CompactRange(CompactRangeOptions(), handles_
[1], nullptr,
686 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
688 // Case 5: snapshot followed by a put followed by another Put
689 // Only the last put should remain.
690 const Snapshot
* snapshot1
= db_
->GetSnapshot();
694 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v9 ]");
695 db_
->ReleaseSnapshot(snapshot1
);
696 } while (ChangeCompactOptions());
699 TEST_F(DBBasicTest
, FlushOneColumnFamily
) {
700 Options options
= CurrentOptions();
701 CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
702 "alyosha", "popovich"},
705 ASSERT_OK(Put(0, "Default", "Default"));
706 ASSERT_OK(Put(1, "pikachu", "pikachu"));
707 ASSERT_OK(Put(2, "ilya", "ilya"));
708 ASSERT_OK(Put(3, "muromec", "muromec"));
709 ASSERT_OK(Put(4, "dobrynia", "dobrynia"));
710 ASSERT_OK(Put(5, "nikitich", "nikitich"));
711 ASSERT_OK(Put(6, "alyosha", "alyosha"));
712 ASSERT_OK(Put(7, "popovich", "popovich"));
714 for (int i
= 0; i
< 8; ++i
) {
716 auto tables
= ListTableFiles(env_
, dbname_
);
717 ASSERT_EQ(tables
.size(), i
+ 1U);
721 TEST_F(DBBasicTest
, MultiGetSimple
) {
723 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
724 SetPerfLevel(kEnableCount
);
725 ASSERT_OK(Put(1, "k1", "v1"));
726 ASSERT_OK(Put(1, "k2", "v2"));
727 ASSERT_OK(Put(1, "k3", "v3"));
728 ASSERT_OK(Put(1, "k4", "v4"));
729 ASSERT_OK(Delete(1, "k4"));
730 ASSERT_OK(Put(1, "k5", "v5"));
731 ASSERT_OK(Delete(1, "no_key"));
733 std::vector
<Slice
> keys({"k1", "k2", "k3", "k4", "k5", "no_key"});
735 std::vector
<std::string
> values(20, "Temporary data to be overwritten");
736 std::vector
<ColumnFamilyHandle
*> cfs(keys
.size(), handles_
[1]);
738 get_perf_context()->Reset();
739 std::vector
<Status
> s
= db_
->MultiGet(ReadOptions(), cfs
, keys
, &values
);
740 ASSERT_EQ(values
.size(), keys
.size());
741 ASSERT_EQ(values
[0], "v1");
742 ASSERT_EQ(values
[1], "v2");
743 ASSERT_EQ(values
[2], "v3");
744 ASSERT_EQ(values
[4], "v5");
745 // four kv pairs * two bytes per value
746 ASSERT_EQ(8, (int)get_perf_context()->multiget_read_bytes
);
751 ASSERT_TRUE(s
[3].IsNotFound());
753 ASSERT_TRUE(s
[5].IsNotFound());
754 SetPerfLevel(kDisable
);
755 } while (ChangeCompactOptions());
758 TEST_F(DBBasicTest
, MultiGetEmpty
) {
760 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
762 std::vector
<Slice
> keys
;
763 std::vector
<std::string
> values
;
764 std::vector
<ColumnFamilyHandle
*> cfs
;
765 std::vector
<Status
> s
= db_
->MultiGet(ReadOptions(), cfs
, keys
, &values
);
766 ASSERT_EQ(s
.size(), 0U);
768 // Empty Database, Empty Key Set
769 Options options
= CurrentOptions();
770 options
.create_if_missing
= true;
771 DestroyAndReopen(options
);
772 CreateAndReopenWithCF({"pikachu"}, options
);
773 s
= db_
->MultiGet(ReadOptions(), cfs
, keys
, &values
);
774 ASSERT_EQ(s
.size(), 0U);
776 // Empty Database, Search for Keys
780 cfs
.push_back(handles_
[0]);
781 cfs
.push_back(handles_
[1]);
782 s
= db_
->MultiGet(ReadOptions(), cfs
, keys
, &values
);
783 ASSERT_EQ(static_cast<int>(s
.size()), 2);
784 ASSERT_TRUE(s
[0].IsNotFound() && s
[1].IsNotFound());
785 } while (ChangeCompactOptions());
788 TEST_F(DBBasicTest
, ChecksumTest
) {
789 BlockBasedTableOptions table_options
;
790 Options options
= CurrentOptions();
791 // change when new checksum type added
792 int max_checksum
= static_cast<int>(kxxHash64
);
793 const int kNumPerFile
= 2;
795 // generate one table with each type of checksum
796 for (int i
= 0; i
<= max_checksum
; ++i
) {
797 table_options
.checksum
= static_cast<ChecksumType
>(i
);
798 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
800 for (int j
= 0; j
< kNumPerFile
; ++j
) {
801 ASSERT_OK(Put(Key(i
* kNumPerFile
+ j
), Key(i
* kNumPerFile
+ j
)));
806 // verify data with each type of checksum
807 for (int i
= 0; i
<= kxxHash64
; ++i
) {
808 table_options
.checksum
= static_cast<ChecksumType
>(i
);
809 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
811 for (int j
= 0; j
< (max_checksum
+ 1) * kNumPerFile
; ++j
) {
812 ASSERT_EQ(Key(j
), Get(Key(j
)));
817 // On Windows you can have either memory mapped file or a file
818 // with unbuffered access. So this asserts and does not make
821 TEST_F(DBBasicTest
, MmapAndBufferOptions
) {
822 if (!IsMemoryMappedAccessSupported()) {
825 Options options
= CurrentOptions();
827 options
.use_direct_reads
= true;
828 options
.allow_mmap_reads
= true;
829 ASSERT_NOK(TryReopen(options
));
831 // All other combinations are acceptable
832 options
.use_direct_reads
= false;
833 ASSERT_OK(TryReopen(options
));
835 if (IsDirectIOSupported()) {
836 options
.use_direct_reads
= true;
837 options
.allow_mmap_reads
= false;
838 ASSERT_OK(TryReopen(options
));
841 options
.use_direct_reads
= false;
842 ASSERT_OK(TryReopen(options
));
846 class TestEnv
: public EnvWrapper
{
848 explicit TestEnv() : EnvWrapper(Env::Default()),
851 class TestLogger
: public Logger
{
854 TestLogger(TestEnv
*env_ptr
) : Logger() { env
= env_ptr
; }
855 ~TestLogger() override
{
860 void Logv(const char* /*format*/, va_list /*ap*/) override
{};
863 Status
CloseImpl() override
{ return CloseHelper(); }
866 Status
CloseHelper() {
867 env
->CloseCountInc();;
868 return Status::IOError();
873 void CloseCountInc() { close_count
++; }
875 int GetCloseCount() { return close_count
; }
877 Status
NewLogger(const std::string
& /*fname*/,
878 std::shared_ptr
<Logger
>* result
) override
{
879 result
->reset(new TestLogger(this));
887 TEST_F(DBBasicTest
, DBClose
) {
888 Options options
= GetDefaultOptions();
889 std::string dbname
= test::PerThreadDBPath("db_close_test");
890 ASSERT_OK(DestroyDB(dbname
, options
));
893 TestEnv
* env
= new TestEnv();
894 options
.create_if_missing
= true;
896 Status s
= DB::Open(options
, dbname
, &db
);
898 ASSERT_TRUE(db
!= nullptr);
901 ASSERT_EQ(env
->GetCloseCount(), 1);
902 ASSERT_EQ(s
, Status::IOError());
905 ASSERT_EQ(env
->GetCloseCount(), 1);
907 // Do not call DB::Close() and ensure our logger Close() still gets called
908 s
= DB::Open(options
, dbname
, &db
);
910 ASSERT_TRUE(db
!= nullptr);
912 ASSERT_EQ(env
->GetCloseCount(), 2);
914 // Provide our own logger and ensure DB::Close() does not close it
915 options
.info_log
.reset(new TestEnv::TestLogger(env
));
916 options
.create_if_missing
= false;
917 s
= DB::Open(options
, dbname
, &db
);
919 ASSERT_TRUE(db
!= nullptr);
922 ASSERT_EQ(s
, Status::OK());
924 ASSERT_EQ(env
->GetCloseCount(), 2);
925 options
.info_log
.reset();
926 ASSERT_EQ(env
->GetCloseCount(), 3);
931 TEST_F(DBBasicTest
, DBCloseFlushError
) {
932 std::unique_ptr
<FaultInjectionTestEnv
> fault_injection_env(
933 new FaultInjectionTestEnv(Env::Default()));
934 Options options
= GetDefaultOptions();
935 options
.create_if_missing
= true;
936 options
.manual_wal_flush
= true;
937 options
.write_buffer_size
=100;
938 options
.env
= fault_injection_env
.get();
941 ASSERT_OK(Put("key1", "value1"));
942 ASSERT_OK(Put("key2", "value2"));
943 ASSERT_OK(dbfull()->TEST_SwitchMemtable());
944 ASSERT_OK(Put("key3", "value3"));
945 fault_injection_env
->SetFilesystemActive(false);
946 Status s
= dbfull()->Close();
947 fault_injection_env
->SetFilesystemActive(true);
948 ASSERT_NE(s
, Status::OK());
953 TEST_F(DBBasicTest
, MultiGetMultiCF
) {
954 Options options
= CurrentOptions();
955 CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
956 "alyosha", "popovich"},
959 for (int i
= 0; i
< 8; ++i
) {
960 ASSERT_OK(Put(i
, "cf" + std::to_string(i
) + "_key",
961 "cf" + std::to_string(i
) + "_val"));
964 int get_sv_count
= 0;
965 rocksdb::DBImpl
* db
= reinterpret_cast<DBImpl
*>(db_
);
966 rocksdb::SyncPoint::GetInstance()->SetCallBack(
967 "DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
968 if (++get_sv_count
== 2) {
969 // After MultiGet refs a couple of CFs, flush all CFs so MultiGet
970 // is forced to repeat the process
971 for (int i
= 0; i
< 8; ++i
) {
973 ASSERT_OK(Put(i
, "cf" + std::to_string(i
) + "_key",
974 "cf" + std::to_string(i
) + "_val2"));
977 if (get_sv_count
== 11) {
978 for (int i
= 0; i
< 8; ++i
) {
979 auto* cfd
= reinterpret_cast<ColumnFamilyHandleImpl
*>(
980 db
->GetColumnFamilyHandle(i
))
982 ASSERT_EQ(cfd
->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse
);
986 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
988 std::vector
<int> cfs
;
989 std::vector
<std::string
> keys
;
990 std::vector
<std::string
> values
;
992 for (int i
= 0; i
< 8; ++i
) {
994 keys
.push_back("cf" + std::to_string(i
) + "_key");
997 values
= MultiGet(cfs
, keys
);
998 ASSERT_EQ(values
.size(), 8);
999 for (unsigned int j
= 0; j
< values
.size(); ++j
) {
1000 ASSERT_EQ(values
[j
], "cf" + std::to_string(j
) + "_val2");
1002 for (int i
= 0; i
< 8; ++i
) {
1003 auto* cfd
= reinterpret_cast<ColumnFamilyHandleImpl
*>(
1004 reinterpret_cast<DBImpl
*>(db_
)->GetColumnFamilyHandle(i
))
1006 ASSERT_NE(cfd
->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse
);
1007 ASSERT_NE(cfd
->TEST_GetLocalSV()->Get(), SuperVersion::kSVObsolete
);
1011 TEST_F(DBBasicTest
, MultiGetMultiCFMutex
) {
1012 Options options
= CurrentOptions();
1013 CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
1014 "alyosha", "popovich"},
1017 for (int i
= 0; i
< 8; ++i
) {
1018 ASSERT_OK(Put(i
, "cf" + std::to_string(i
) + "_key",
1019 "cf" + std::to_string(i
) + "_val"));
1022 int get_sv_count
= 0;
1024 bool last_try
= false;
1025 rocksdb::SyncPoint::GetInstance()->SetCallBack(
1026 "DBImpl::MultiGet::LastTry", [&](void* /*arg*/) {
1028 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
1030 rocksdb::SyncPoint::GetInstance()->SetCallBack(
1031 "DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
1035 if (++get_sv_count
== 2) {
1038 for (int i
= 0; i
< 8; ++i
) {
1039 ASSERT_OK(Flush(i
));
1041 i
, "cf" + std::to_string(i
) + "_key",
1042 "cf" + std::to_string(i
) + "_val" + std::to_string(retries
)));
1046 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
1048 std::vector
<int> cfs
;
1049 std::vector
<std::string
> keys
;
1050 std::vector
<std::string
> values
;
1052 for (int i
= 0; i
< 8; ++i
) {
1054 keys
.push_back("cf" + std::to_string(i
) + "_key");
1057 values
= MultiGet(cfs
, keys
);
1058 ASSERT_TRUE(last_try
);
1059 ASSERT_EQ(values
.size(), 8);
1060 for (unsigned int j
= 0; j
< values
.size(); ++j
) {
1061 ASSERT_EQ(values
[j
],
1062 "cf" + std::to_string(j
) + "_val" + std::to_string(retries
));
1064 for (int i
= 0; i
< 8; ++i
) {
1065 auto* cfd
= reinterpret_cast<ColumnFamilyHandleImpl
*>(
1066 reinterpret_cast<DBImpl
*>(db_
)->GetColumnFamilyHandle(i
))
1068 ASSERT_NE(cfd
->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse
);
1072 TEST_F(DBBasicTest
, MultiGetMultiCFSnapshot
) {
1073 Options options
= CurrentOptions();
1074 CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
1075 "alyosha", "popovich"},
1078 for (int i
= 0; i
< 8; ++i
) {
1079 ASSERT_OK(Put(i
, "cf" + std::to_string(i
) + "_key",
1080 "cf" + std::to_string(i
) + "_val"));
1083 int get_sv_count
= 0;
1084 rocksdb::DBImpl
* db
= reinterpret_cast<DBImpl
*>(db_
);
1085 rocksdb::SyncPoint::GetInstance()->SetCallBack(
1086 "DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
1087 if (++get_sv_count
== 2) {
1088 for (int i
= 0; i
< 8; ++i
) {
1089 ASSERT_OK(Flush(i
));
1090 ASSERT_OK(Put(i
, "cf" + std::to_string(i
) + "_key",
1091 "cf" + std::to_string(i
) + "_val2"));
1094 if (get_sv_count
== 8) {
1095 for (int i
= 0; i
< 8; ++i
) {
1096 auto* cfd
= reinterpret_cast<ColumnFamilyHandleImpl
*>(
1097 db
->GetColumnFamilyHandle(i
))
1100 (cfd
->TEST_GetLocalSV()->Get() == SuperVersion::kSVInUse
) ||
1101 (cfd
->TEST_GetLocalSV()->Get() == SuperVersion::kSVObsolete
));
1105 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
1107 std::vector
<int> cfs
;
1108 std::vector
<std::string
> keys
;
1109 std::vector
<std::string
> values
;
1111 for (int i
= 0; i
< 8; ++i
) {
1113 keys
.push_back("cf" + std::to_string(i
) + "_key");
1116 const Snapshot
* snapshot
= db_
->GetSnapshot();
1117 values
= MultiGet(cfs
, keys
, snapshot
);
1118 db_
->ReleaseSnapshot(snapshot
);
1119 ASSERT_EQ(values
.size(), 8);
1120 for (unsigned int j
= 0; j
< values
.size(); ++j
) {
1121 ASSERT_EQ(values
[j
], "cf" + std::to_string(j
) + "_val");
1123 for (int i
= 0; i
< 8; ++i
) {
1124 auto* cfd
= reinterpret_cast<ColumnFamilyHandleImpl
*>(
1125 reinterpret_cast<DBImpl
*>(db_
)->GetColumnFamilyHandle(i
))
1127 ASSERT_NE(cfd
->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse
);
1131 } // namespace rocksdb
1133 int main(int argc
, char** argv
) {
1134 rocksdb::port::InstallStackTraceHandler();
1135 ::testing::InitGoogleTest(&argc
, argv
);
1136 return RUN_ALL_TESTS();