]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/db_basic_test.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / rocksdb / db / db_basic_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
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).
7c673cae
FG
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 "db/db_test_util.h"
10#include "port/stack_trace.h"
11#include "rocksdb/perf_context.h"
11fdf7f2 12#include "util/fault_injection_test_env.h"
7c673cae
FG
13#if !defined(ROCKSDB_LITE)
14#include "util/sync_point.h"
15#endif
16
17namespace rocksdb {
18
19class DBBasicTest : public DBTestBase {
20 public:
21 DBBasicTest() : DBTestBase("/db_basic_test") {}
22};
23
24TEST_F(DBBasicTest, OpenWhenOpen) {
25 Options options = CurrentOptions();
26 options.env = env_;
27 rocksdb::DB* db2 = nullptr;
28 rocksdb::Status s = DB::Open(options, dbname_, &db2);
29
30 ASSERT_EQ(Status::Code::kIOError, s.code());
31 ASSERT_EQ(Status::SubCode::kNone, s.subcode());
32 ASSERT_TRUE(strstr(s.getState(), "lock ") != nullptr);
33
34 delete db2;
35}
36
37#ifndef ROCKSDB_LITE
38TEST_F(DBBasicTest, ReadOnlyDB) {
39 ASSERT_OK(Put("foo", "v1"));
40 ASSERT_OK(Put("bar", "v2"));
41 ASSERT_OK(Put("foo", "v3"));
42 Close();
43
44 auto options = CurrentOptions();
11fdf7f2 45 assert(options.env == env_);
7c673cae
FG
46 ASSERT_OK(ReadOnlyReopen(options));
47 ASSERT_EQ("v3", Get("foo"));
48 ASSERT_EQ("v2", Get("bar"));
49 Iterator* iter = db_->NewIterator(ReadOptions());
50 int count = 0;
51 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
52 ASSERT_OK(iter->status());
53 ++count;
54 }
55 ASSERT_EQ(count, 2);
56 delete iter;
57 Close();
58
59 // Reopen and flush memtable.
60 Reopen(options);
61 Flush();
62 Close();
63 // Now check keys in read only mode.
64 ASSERT_OK(ReadOnlyReopen(options));
65 ASSERT_EQ("v3", Get("foo"));
66 ASSERT_EQ("v2", Get("bar"));
67 ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
68}
69
70TEST_F(DBBasicTest, CompactedDB) {
71 const uint64_t kFileSize = 1 << 20;
72 Options options = CurrentOptions();
73 options.disable_auto_compactions = true;
74 options.write_buffer_size = kFileSize;
75 options.target_file_size_base = kFileSize;
76 options.max_bytes_for_level_base = 1 << 30;
77 options.compression = kNoCompression;
78 Reopen(options);
79 // 1 L0 file, use CompactedDB if max_open_files = -1
80 ASSERT_OK(Put("aaa", DummyString(kFileSize / 2, '1')));
81 Flush();
82 Close();
83 ASSERT_OK(ReadOnlyReopen(options));
84 Status s = Put("new", "value");
85 ASSERT_EQ(s.ToString(),
86 "Not implemented: Not supported operation in read only mode.");
87 ASSERT_EQ(DummyString(kFileSize / 2, '1'), Get("aaa"));
88 Close();
89 options.max_open_files = -1;
90 ASSERT_OK(ReadOnlyReopen(options));
91 s = Put("new", "value");
92 ASSERT_EQ(s.ToString(),
93 "Not implemented: Not supported in compacted db mode.");
94 ASSERT_EQ(DummyString(kFileSize / 2, '1'), Get("aaa"));
95 Close();
96 Reopen(options);
97 // Add more L0 files
98 ASSERT_OK(Put("bbb", DummyString(kFileSize / 2, '2')));
99 Flush();
100 ASSERT_OK(Put("aaa", DummyString(kFileSize / 2, 'a')));
101 Flush();
102 ASSERT_OK(Put("bbb", DummyString(kFileSize / 2, 'b')));
103 ASSERT_OK(Put("eee", DummyString(kFileSize / 2, 'e')));
104 Flush();
105 Close();
106
107 ASSERT_OK(ReadOnlyReopen(options));
108 // Fallback to read-only DB
109 s = Put("new", "value");
110 ASSERT_EQ(s.ToString(),
111 "Not implemented: Not supported operation in read only mode.");
112 Close();
113
114 // Full compaction
115 Reopen(options);
116 // Add more keys
117 ASSERT_OK(Put("fff", DummyString(kFileSize / 2, 'f')));
118 ASSERT_OK(Put("hhh", DummyString(kFileSize / 2, 'h')));
119 ASSERT_OK(Put("iii", DummyString(kFileSize / 2, 'i')));
120 ASSERT_OK(Put("jjj", DummyString(kFileSize / 2, 'j')));
121 db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
122 ASSERT_EQ(3, NumTableFilesAtLevel(1));
123 Close();
124
125 // CompactedDB
126 ASSERT_OK(ReadOnlyReopen(options));
127 s = Put("new", "value");
128 ASSERT_EQ(s.ToString(),
129 "Not implemented: Not supported in compacted db mode.");
130 ASSERT_EQ("NOT_FOUND", Get("abc"));
131 ASSERT_EQ(DummyString(kFileSize / 2, 'a'), Get("aaa"));
132 ASSERT_EQ(DummyString(kFileSize / 2, 'b'), Get("bbb"));
133 ASSERT_EQ("NOT_FOUND", Get("ccc"));
134 ASSERT_EQ(DummyString(kFileSize / 2, 'e'), Get("eee"));
135 ASSERT_EQ(DummyString(kFileSize / 2, 'f'), Get("fff"));
136 ASSERT_EQ("NOT_FOUND", Get("ggg"));
137 ASSERT_EQ(DummyString(kFileSize / 2, 'h'), Get("hhh"));
138 ASSERT_EQ(DummyString(kFileSize / 2, 'i'), Get("iii"));
139 ASSERT_EQ(DummyString(kFileSize / 2, 'j'), Get("jjj"));
140 ASSERT_EQ("NOT_FOUND", Get("kkk"));
141
142 // MultiGet
143 std::vector<std::string> values;
144 std::vector<Status> status_list = dbfull()->MultiGet(
145 ReadOptions(),
146 std::vector<Slice>({Slice("aaa"), Slice("ccc"), Slice("eee"),
147 Slice("ggg"), Slice("iii"), Slice("kkk")}),
148 &values);
149 ASSERT_EQ(status_list.size(), static_cast<uint64_t>(6));
150 ASSERT_EQ(values.size(), static_cast<uint64_t>(6));
151 ASSERT_OK(status_list[0]);
152 ASSERT_EQ(DummyString(kFileSize / 2, 'a'), values[0]);
153 ASSERT_TRUE(status_list[1].IsNotFound());
154 ASSERT_OK(status_list[2]);
155 ASSERT_EQ(DummyString(kFileSize / 2, 'e'), values[2]);
156 ASSERT_TRUE(status_list[3].IsNotFound());
157 ASSERT_OK(status_list[4]);
158 ASSERT_EQ(DummyString(kFileSize / 2, 'i'), values[4]);
159 ASSERT_TRUE(status_list[5].IsNotFound());
160
161 Reopen(options);
162 // Add a key
163 ASSERT_OK(Put("fff", DummyString(kFileSize / 2, 'f')));
164 Close();
165 ASSERT_OK(ReadOnlyReopen(options));
166 s = Put("new", "value");
167 ASSERT_EQ(s.ToString(),
168 "Not implemented: Not supported operation in read only mode.");
169}
170
171TEST_F(DBBasicTest, LevelLimitReopen) {
172 Options options = CurrentOptions();
173 CreateAndReopenWithCF({"pikachu"}, options);
174
175 const std::string value(1024 * 1024, ' ');
176 int i = 0;
177 while (NumTableFilesAtLevel(2, 1) == 0) {
178 ASSERT_OK(Put(1, Key(i++), value));
11fdf7f2
TL
179 dbfull()->TEST_WaitForFlushMemTable();
180 dbfull()->TEST_WaitForCompact();
7c673cae
FG
181 }
182
183 options.num_levels = 1;
184 options.max_bytes_for_level_multiplier_additional.resize(1, 1);
185 Status s = TryReopenWithColumnFamilies({"default", "pikachu"}, options);
186 ASSERT_EQ(s.IsInvalidArgument(), true);
187 ASSERT_EQ(s.ToString(),
188 "Invalid argument: db has more levels than options.num_levels");
189
190 options.num_levels = 10;
191 options.max_bytes_for_level_multiplier_additional.resize(10, 1);
192 ASSERT_OK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
193}
194#endif // ROCKSDB_LITE
195
196TEST_F(DBBasicTest, PutDeleteGet) {
197 do {
198 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
199 ASSERT_OK(Put(1, "foo", "v1"));
200 ASSERT_EQ("v1", Get(1, "foo"));
201 ASSERT_OK(Put(1, "foo", "v2"));
202 ASSERT_EQ("v2", Get(1, "foo"));
203 ASSERT_OK(Delete(1, "foo"));
204 ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
205 } while (ChangeOptions());
206}
207
208TEST_F(DBBasicTest, PutSingleDeleteGet) {
209 do {
210 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
211 ASSERT_OK(Put(1, "foo", "v1"));
212 ASSERT_EQ("v1", Get(1, "foo"));
213 ASSERT_OK(Put(1, "foo2", "v2"));
214 ASSERT_EQ("v2", Get(1, "foo2"));
215 ASSERT_OK(SingleDelete(1, "foo"));
216 ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
217 // Skip HashCuckooRep as it does not support single delete. FIFO and
218 // universal compaction do not apply to the test case. Skip MergePut
219 // because single delete does not get removed when it encounters a merge.
220 } while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
221 kSkipUniversalCompaction | kSkipMergePut));
222}
223
224TEST_F(DBBasicTest, EmptyFlush) {
225 // It is possible to produce empty flushes when using single deletes. Tests
226 // whether empty flushes cause issues.
227 do {
228 Random rnd(301);
229
230 Options options = CurrentOptions();
231 options.disable_auto_compactions = true;
232 CreateAndReopenWithCF({"pikachu"}, options);
233
234 Put(1, "a", Slice());
235 SingleDelete(1, "a");
236 ASSERT_OK(Flush(1));
237
238 ASSERT_EQ("[ ]", AllEntriesFor("a", 1));
239 // Skip HashCuckooRep as it does not support single delete. FIFO and
240 // universal compaction do not apply to the test case. Skip MergePut
241 // because merges cannot be combined with single deletions.
242 } while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
243 kSkipUniversalCompaction | kSkipMergePut));
244}
245
246TEST_F(DBBasicTest, GetFromVersions) {
247 do {
248 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
249 ASSERT_OK(Put(1, "foo", "v1"));
250 ASSERT_OK(Flush(1));
251 ASSERT_EQ("v1", Get(1, "foo"));
252 ASSERT_EQ("NOT_FOUND", Get(0, "foo"));
253 } while (ChangeOptions());
254}
255
256#ifndef ROCKSDB_LITE
257TEST_F(DBBasicTest, GetSnapshot) {
258 anon::OptionsOverride options_override;
259 options_override.skip_policy = kSkipNoSnapshot;
260 do {
261 CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override));
262 // Try with both a short key and a long key
263 for (int i = 0; i < 2; i++) {
264 std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
265 ASSERT_OK(Put(1, key, "v1"));
266 const Snapshot* s1 = db_->GetSnapshot();
267 if (option_config_ == kHashCuckoo) {
268 // Unsupported case.
269 ASSERT_TRUE(s1 == nullptr);
270 break;
271 }
272 ASSERT_OK(Put(1, key, "v2"));
273 ASSERT_EQ("v2", Get(1, key));
274 ASSERT_EQ("v1", Get(1, key, s1));
275 ASSERT_OK(Flush(1));
276 ASSERT_EQ("v2", Get(1, key));
277 ASSERT_EQ("v1", Get(1, key, s1));
278 db_->ReleaseSnapshot(s1);
279 }
280 } while (ChangeOptions());
281}
282#endif // ROCKSDB_LITE
283
284TEST_F(DBBasicTest, CheckLock) {
285 do {
286 DB* localdb;
287 Options options = CurrentOptions();
288 ASSERT_OK(TryReopen(options));
289
290 // second open should fail
291 ASSERT_TRUE(!(DB::Open(options, dbname_, &localdb)).ok());
292 } while (ChangeCompactOptions());
293}
294
295TEST_F(DBBasicTest, FlushMultipleMemtable) {
296 do {
297 Options options = CurrentOptions();
298 WriteOptions writeOpt = WriteOptions();
299 writeOpt.disableWAL = true;
300 options.max_write_buffer_number = 4;
301 options.min_write_buffer_number_to_merge = 3;
302 options.max_write_buffer_number_to_maintain = -1;
303 CreateAndReopenWithCF({"pikachu"}, options);
304 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v1"));
305 ASSERT_OK(Flush(1));
306 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v1"));
307
308 ASSERT_EQ("v1", Get(1, "foo"));
309 ASSERT_EQ("v1", Get(1, "bar"));
310 ASSERT_OK(Flush(1));
311 } while (ChangeCompactOptions());
312}
313
314TEST_F(DBBasicTest, FlushEmptyColumnFamily) {
315 // Block flush thread and disable compaction thread
316 env_->SetBackgroundThreads(1, Env::HIGH);
317 env_->SetBackgroundThreads(1, Env::LOW);
318 test::SleepingBackgroundTask sleeping_task_low;
319 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
320 Env::Priority::LOW);
321 test::SleepingBackgroundTask sleeping_task_high;
322 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
323 &sleeping_task_high, Env::Priority::HIGH);
324
325 Options options = CurrentOptions();
326 // disable compaction
327 options.disable_auto_compactions = true;
328 WriteOptions writeOpt = WriteOptions();
329 writeOpt.disableWAL = true;
330 options.max_write_buffer_number = 2;
331 options.min_write_buffer_number_to_merge = 1;
332 options.max_write_buffer_number_to_maintain = 1;
333 CreateAndReopenWithCF({"pikachu"}, options);
334
335 // Compaction can still go through even if no thread can flush the
336 // mem table.
337 ASSERT_OK(Flush(0));
338 ASSERT_OK(Flush(1));
339
340 // Insert can go through
341 ASSERT_OK(dbfull()->Put(writeOpt, handles_[0], "foo", "v1"));
342 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v1"));
343
344 ASSERT_EQ("v1", Get(0, "foo"));
345 ASSERT_EQ("v1", Get(1, "bar"));
346
347 sleeping_task_high.WakeUp();
348 sleeping_task_high.WaitUntilDone();
349
350 // Flush can still go through.
351 ASSERT_OK(Flush(0));
352 ASSERT_OK(Flush(1));
353
354 sleeping_task_low.WakeUp();
355 sleeping_task_low.WaitUntilDone();
356}
357
358TEST_F(DBBasicTest, FLUSH) {
359 do {
360 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
361 WriteOptions writeOpt = WriteOptions();
362 writeOpt.disableWAL = true;
363 SetPerfLevel(kEnableTime);
7c673cae
FG
364 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v1"));
365 // this will now also flush the last 2 writes
366 ASSERT_OK(Flush(1));
367 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v1"));
368
11fdf7f2 369 get_perf_context()->Reset();
7c673cae 370 Get(1, "foo");
11fdf7f2
TL
371 ASSERT_TRUE((int)get_perf_context()->get_from_output_files_time > 0);
372 ASSERT_EQ(2, (int)get_perf_context()->get_read_bytes);
7c673cae
FG
373
374 ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
375 ASSERT_EQ("v1", Get(1, "foo"));
376 ASSERT_EQ("v1", Get(1, "bar"));
377
378 writeOpt.disableWAL = true;
379 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v2"));
380 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v2"));
381 ASSERT_OK(Flush(1));
382
383 ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
384 ASSERT_EQ("v2", Get(1, "bar"));
11fdf7f2 385 get_perf_context()->Reset();
7c673cae 386 ASSERT_EQ("v2", Get(1, "foo"));
11fdf7f2 387 ASSERT_TRUE((int)get_perf_context()->get_from_output_files_time > 0);
7c673cae
FG
388
389 writeOpt.disableWAL = false;
390 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v3"));
391 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v3"));
392 ASSERT_OK(Flush(1));
393
394 ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
395 // 'foo' should be there because its put
396 // has WAL enabled.
397 ASSERT_EQ("v3", Get(1, "foo"));
398 ASSERT_EQ("v3", Get(1, "bar"));
399
400 SetPerfLevel(kDisable);
401 } while (ChangeCompactOptions());
402}
403
404TEST_F(DBBasicTest, ManifestRollOver) {
405 do {
406 Options options;
407 options.max_manifest_file_size = 10; // 10 bytes
408 options = CurrentOptions(options);
409 CreateAndReopenWithCF({"pikachu"}, options);
410 {
411 ASSERT_OK(Put(1, "manifest_key1", std::string(1000, '1')));
412 ASSERT_OK(Put(1, "manifest_key2", std::string(1000, '2')));
413 ASSERT_OK(Put(1, "manifest_key3", std::string(1000, '3')));
414 uint64_t manifest_before_flush = dbfull()->TEST_Current_Manifest_FileNo();
415 ASSERT_OK(Flush(1)); // This should trigger LogAndApply.
416 uint64_t manifest_after_flush = dbfull()->TEST_Current_Manifest_FileNo();
417 ASSERT_GT(manifest_after_flush, manifest_before_flush);
418 ReopenWithColumnFamilies({"default", "pikachu"}, options);
419 ASSERT_GT(dbfull()->TEST_Current_Manifest_FileNo(), manifest_after_flush);
420 // check if a new manifest file got inserted or not.
421 ASSERT_EQ(std::string(1000, '1'), Get(1, "manifest_key1"));
422 ASSERT_EQ(std::string(1000, '2'), Get(1, "manifest_key2"));
423 ASSERT_EQ(std::string(1000, '3'), Get(1, "manifest_key3"));
424 }
425 } while (ChangeCompactOptions());
426}
427
428TEST_F(DBBasicTest, IdentityAcrossRestarts) {
429 do {
430 std::string id1;
431 ASSERT_OK(db_->GetDbIdentity(id1));
432
433 Options options = CurrentOptions();
434 Reopen(options);
435 std::string id2;
436 ASSERT_OK(db_->GetDbIdentity(id2));
437 // id1 should match id2 because identity was not regenerated
438 ASSERT_EQ(id1.compare(id2), 0);
439
440 std::string idfilename = IdentityFileName(dbname_);
441 ASSERT_OK(env_->DeleteFile(idfilename));
442 Reopen(options);
443 std::string id3;
444 ASSERT_OK(db_->GetDbIdentity(id3));
445 // id1 should NOT match id3 because identity was regenerated
446 ASSERT_NE(id1.compare(id3), 0);
447 } while (ChangeCompactOptions());
448}
449
450#ifndef ROCKSDB_LITE
451TEST_F(DBBasicTest, Snapshot) {
452 anon::OptionsOverride options_override;
453 options_override.skip_policy = kSkipNoSnapshot;
454 do {
455 CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override));
456 Put(0, "foo", "0v1");
457 Put(1, "foo", "1v1");
458
459 const Snapshot* s1 = db_->GetSnapshot();
460 ASSERT_EQ(1U, GetNumSnapshots());
461 uint64_t time_snap1 = GetTimeOldestSnapshots();
462 ASSERT_GT(time_snap1, 0U);
463 Put(0, "foo", "0v2");
464 Put(1, "foo", "1v2");
465
466 env_->addon_time_.fetch_add(1);
467
468 const Snapshot* s2 = db_->GetSnapshot();
469 ASSERT_EQ(2U, GetNumSnapshots());
470 ASSERT_EQ(time_snap1, GetTimeOldestSnapshots());
471 Put(0, "foo", "0v3");
472 Put(1, "foo", "1v3");
473
474 {
475 ManagedSnapshot s3(db_);
476 ASSERT_EQ(3U, GetNumSnapshots());
477 ASSERT_EQ(time_snap1, GetTimeOldestSnapshots());
478
479 Put(0, "foo", "0v4");
480 Put(1, "foo", "1v4");
481 ASSERT_EQ("0v1", Get(0, "foo", s1));
482 ASSERT_EQ("1v1", Get(1, "foo", s1));
483 ASSERT_EQ("0v2", Get(0, "foo", s2));
484 ASSERT_EQ("1v2", Get(1, "foo", s2));
485 ASSERT_EQ("0v3", Get(0, "foo", s3.snapshot()));
486 ASSERT_EQ("1v3", Get(1, "foo", s3.snapshot()));
487 ASSERT_EQ("0v4", Get(0, "foo"));
488 ASSERT_EQ("1v4", Get(1, "foo"));
489 }
490
491 ASSERT_EQ(2U, GetNumSnapshots());
492 ASSERT_EQ(time_snap1, GetTimeOldestSnapshots());
493 ASSERT_EQ("0v1", Get(0, "foo", s1));
494 ASSERT_EQ("1v1", Get(1, "foo", s1));
495 ASSERT_EQ("0v2", Get(0, "foo", s2));
496 ASSERT_EQ("1v2", Get(1, "foo", s2));
497 ASSERT_EQ("0v4", Get(0, "foo"));
498 ASSERT_EQ("1v4", Get(1, "foo"));
499
500 db_->ReleaseSnapshot(s1);
501 ASSERT_EQ("0v2", Get(0, "foo", s2));
502 ASSERT_EQ("1v2", Get(1, "foo", s2));
503 ASSERT_EQ("0v4", Get(0, "foo"));
504 ASSERT_EQ("1v4", Get(1, "foo"));
505 ASSERT_EQ(1U, GetNumSnapshots());
506 ASSERT_LT(time_snap1, GetTimeOldestSnapshots());
507
508 db_->ReleaseSnapshot(s2);
509 ASSERT_EQ(0U, GetNumSnapshots());
510 ASSERT_EQ("0v4", Get(0, "foo"));
511 ASSERT_EQ("1v4", Get(1, "foo"));
512 } while (ChangeOptions(kSkipHashCuckoo));
513}
514
515#endif // ROCKSDB_LITE
516
517TEST_F(DBBasicTest, CompactBetweenSnapshots) {
518 anon::OptionsOverride options_override;
519 options_override.skip_policy = kSkipNoSnapshot;
520 do {
521 Options options = CurrentOptions(options_override);
522 options.disable_auto_compactions = true;
523 CreateAndReopenWithCF({"pikachu"}, options);
524 Random rnd(301);
525 FillLevels("a", "z", 1);
526
527 Put(1, "foo", "first");
528 const Snapshot* snapshot1 = db_->GetSnapshot();
529 Put(1, "foo", "second");
530 Put(1, "foo", "third");
531 Put(1, "foo", "fourth");
532 const Snapshot* snapshot2 = db_->GetSnapshot();
533 Put(1, "foo", "fifth");
534 Put(1, "foo", "sixth");
535
536 // All entries (including duplicates) exist
537 // before any compaction or flush is triggered.
538 ASSERT_EQ(AllEntriesFor("foo", 1),
539 "[ sixth, fifth, fourth, third, second, first ]");
540 ASSERT_EQ("sixth", Get(1, "foo"));
541 ASSERT_EQ("fourth", Get(1, "foo", snapshot2));
542 ASSERT_EQ("first", Get(1, "foo", snapshot1));
543
544 // After a flush, "second", "third" and "fifth" should
545 // be removed
546 ASSERT_OK(Flush(1));
547 ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth, first ]");
548
549 // after we release the snapshot1, only two values left
550 db_->ReleaseSnapshot(snapshot1);
551 FillLevels("a", "z", 1);
552 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
553 nullptr);
554
555 // We have only one valid snapshot snapshot2. Since snapshot1 is
556 // not valid anymore, "first" should be removed by a compaction.
557 ASSERT_EQ("sixth", Get(1, "foo"));
558 ASSERT_EQ("fourth", Get(1, "foo", snapshot2));
559 ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth ]");
560
561 // after we release the snapshot2, only one value should be left
562 db_->ReleaseSnapshot(snapshot2);
563 FillLevels("a", "z", 1);
564 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
565 nullptr);
566 ASSERT_EQ("sixth", Get(1, "foo"));
567 ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth ]");
568 // skip HashCuckooRep as it does not support snapshot
569 } while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction));
570}
571
572TEST_F(DBBasicTest, DBOpen_Options) {
573 Options options = CurrentOptions();
11fdf7f2
TL
574 Close();
575 Destroy(options);
7c673cae
FG
576
577 // Does not exist, and create_if_missing == false: error
578 DB* db = nullptr;
579 options.create_if_missing = false;
11fdf7f2 580 Status s = DB::Open(options, dbname_, &db);
7c673cae
FG
581 ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != nullptr);
582 ASSERT_TRUE(db == nullptr);
583
584 // Does not exist, and create_if_missing == true: OK
585 options.create_if_missing = true;
11fdf7f2 586 s = DB::Open(options, dbname_, &db);
7c673cae
FG
587 ASSERT_OK(s);
588 ASSERT_TRUE(db != nullptr);
589
590 delete db;
591 db = nullptr;
592
593 // Does exist, and error_if_exists == true: error
594 options.create_if_missing = false;
595 options.error_if_exists = true;
11fdf7f2 596 s = DB::Open(options, dbname_, &db);
7c673cae
FG
597 ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != nullptr);
598 ASSERT_TRUE(db == nullptr);
599
600 // Does exist, and error_if_exists == false: OK
601 options.create_if_missing = true;
602 options.error_if_exists = false;
11fdf7f2 603 s = DB::Open(options, dbname_, &db);
7c673cae
FG
604 ASSERT_OK(s);
605 ASSERT_TRUE(db != nullptr);
606
607 delete db;
608 db = nullptr;
609}
610
611TEST_F(DBBasicTest, CompactOnFlush) {
612 anon::OptionsOverride options_override;
613 options_override.skip_policy = kSkipNoSnapshot;
614 do {
615 Options options = CurrentOptions(options_override);
616 options.disable_auto_compactions = true;
617 CreateAndReopenWithCF({"pikachu"}, options);
618
619 Put(1, "foo", "v1");
620 ASSERT_OK(Flush(1));
621 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v1 ]");
622
623 // Write two new keys
624 Put(1, "a", "begin");
625 Put(1, "z", "end");
626 Flush(1);
627
628 // Case1: Delete followed by a put
629 Delete(1, "foo");
630 Put(1, "foo", "v2");
631 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v2, DEL, v1 ]");
632
633 // After the current memtable is flushed, the DEL should
634 // have been removed
635 ASSERT_OK(Flush(1));
636 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v2, v1 ]");
637
638 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
639 nullptr);
640 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v2 ]");
641
642 // Case 2: Delete followed by another delete
643 Delete(1, "foo");
644 Delete(1, "foo");
645 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL, DEL, v2 ]");
646 ASSERT_OK(Flush(1));
647 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL, v2 ]");
648 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
649 nullptr);
650 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
651
652 // Case 3: Put followed by a delete
653 Put(1, "foo", "v3");
654 Delete(1, "foo");
655 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL, v3 ]");
656 ASSERT_OK(Flush(1));
657 ASSERT_EQ(AllEntriesFor("foo", 1), "[ DEL ]");
658 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
659 nullptr);
660 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
661
662 // Case 4: Put followed by another Put
663 Put(1, "foo", "v4");
664 Put(1, "foo", "v5");
665 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v5, v4 ]");
666 ASSERT_OK(Flush(1));
667 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v5 ]");
668 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
669 nullptr);
670 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v5 ]");
671
672 // clear database
673 Delete(1, "foo");
674 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
675 nullptr);
676 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
677
678 // Case 5: Put followed by snapshot followed by another Put
679 // Both puts should remain.
680 Put(1, "foo", "v6");
681 const Snapshot* snapshot = db_->GetSnapshot();
682 Put(1, "foo", "v7");
683 ASSERT_OK(Flush(1));
684 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v7, v6 ]");
685 db_->ReleaseSnapshot(snapshot);
686
687 // clear database
688 Delete(1, "foo");
689 dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
690 nullptr);
691 ASSERT_EQ(AllEntriesFor("foo", 1), "[ ]");
692
693 // Case 5: snapshot followed by a put followed by another Put
694 // Only the last put should remain.
695 const Snapshot* snapshot1 = db_->GetSnapshot();
696 Put(1, "foo", "v8");
697 Put(1, "foo", "v9");
698 ASSERT_OK(Flush(1));
699 ASSERT_EQ(AllEntriesFor("foo", 1), "[ v9 ]");
700 db_->ReleaseSnapshot(snapshot1);
701 } while (ChangeCompactOptions());
702}
703
704TEST_F(DBBasicTest, FlushOneColumnFamily) {
705 Options options = CurrentOptions();
706 CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
707 "alyosha", "popovich"},
708 options);
709
710 ASSERT_OK(Put(0, "Default", "Default"));
711 ASSERT_OK(Put(1, "pikachu", "pikachu"));
712 ASSERT_OK(Put(2, "ilya", "ilya"));
713 ASSERT_OK(Put(3, "muromec", "muromec"));
714 ASSERT_OK(Put(4, "dobrynia", "dobrynia"));
715 ASSERT_OK(Put(5, "nikitich", "nikitich"));
716 ASSERT_OK(Put(6, "alyosha", "alyosha"));
717 ASSERT_OK(Put(7, "popovich", "popovich"));
718
719 for (int i = 0; i < 8; ++i) {
720 Flush(i);
721 auto tables = ListTableFiles(env_, dbname_);
722 ASSERT_EQ(tables.size(), i + 1U);
723 }
724}
725
726TEST_F(DBBasicTest, MultiGetSimple) {
727 do {
728 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
11fdf7f2 729 SetPerfLevel(kEnableCount);
7c673cae
FG
730 ASSERT_OK(Put(1, "k1", "v1"));
731 ASSERT_OK(Put(1, "k2", "v2"));
732 ASSERT_OK(Put(1, "k3", "v3"));
733 ASSERT_OK(Put(1, "k4", "v4"));
734 ASSERT_OK(Delete(1, "k4"));
735 ASSERT_OK(Put(1, "k5", "v5"));
736 ASSERT_OK(Delete(1, "no_key"));
737
738 std::vector<Slice> keys({"k1", "k2", "k3", "k4", "k5", "no_key"});
739
740 std::vector<std::string> values(20, "Temporary data to be overwritten");
741 std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
742
11fdf7f2 743 get_perf_context()->Reset();
7c673cae
FG
744 std::vector<Status> s = db_->MultiGet(ReadOptions(), cfs, keys, &values);
745 ASSERT_EQ(values.size(), keys.size());
746 ASSERT_EQ(values[0], "v1");
747 ASSERT_EQ(values[1], "v2");
748 ASSERT_EQ(values[2], "v3");
749 ASSERT_EQ(values[4], "v5");
11fdf7f2
TL
750 // four kv pairs * two bytes per value
751 ASSERT_EQ(8, (int)get_perf_context()->multiget_read_bytes);
7c673cae
FG
752
753 ASSERT_OK(s[0]);
754 ASSERT_OK(s[1]);
755 ASSERT_OK(s[2]);
756 ASSERT_TRUE(s[3].IsNotFound());
757 ASSERT_OK(s[4]);
758 ASSERT_TRUE(s[5].IsNotFound());
11fdf7f2 759 SetPerfLevel(kDisable);
7c673cae
FG
760 } while (ChangeCompactOptions());
761}
762
763TEST_F(DBBasicTest, MultiGetEmpty) {
764 do {
765 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
766 // Empty Key Set
767 std::vector<Slice> keys;
768 std::vector<std::string> values;
769 std::vector<ColumnFamilyHandle*> cfs;
770 std::vector<Status> s = db_->MultiGet(ReadOptions(), cfs, keys, &values);
771 ASSERT_EQ(s.size(), 0U);
772
773 // Empty Database, Empty Key Set
774 Options options = CurrentOptions();
775 options.create_if_missing = true;
776 DestroyAndReopen(options);
777 CreateAndReopenWithCF({"pikachu"}, options);
778 s = db_->MultiGet(ReadOptions(), cfs, keys, &values);
779 ASSERT_EQ(s.size(), 0U);
780
781 // Empty Database, Search for Keys
782 keys.resize(2);
783 keys[0] = "a";
784 keys[1] = "b";
785 cfs.push_back(handles_[0]);
786 cfs.push_back(handles_[1]);
787 s = db_->MultiGet(ReadOptions(), cfs, keys, &values);
788 ASSERT_EQ(static_cast<int>(s.size()), 2);
789 ASSERT_TRUE(s[0].IsNotFound() && s[1].IsNotFound());
790 } while (ChangeCompactOptions());
791}
792
793TEST_F(DBBasicTest, ChecksumTest) {
794 BlockBasedTableOptions table_options;
795 Options options = CurrentOptions();
11fdf7f2
TL
796 // change when new checksum type added
797 int max_checksum = static_cast<int>(kxxHash);
798 const int kNumPerFile = 2;
799
800 // generate one table with each type of checksum
801 for (int i = 0; i <= max_checksum; ++i) {
802 table_options.checksum = static_cast<ChecksumType>(i);
803 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
804 Reopen(options);
805 for (int j = 0; j < kNumPerFile; ++j) {
806 ASSERT_OK(Put(Key(i * kNumPerFile + j), Key(i * kNumPerFile + j)));
807 }
808 ASSERT_OK(Flush());
809 }
7c673cae 810
11fdf7f2
TL
811 // verify data with each type of checksum
812 for (int i = 0; i <= kxxHash; ++i) {
813 table_options.checksum = static_cast<ChecksumType>(i);
814 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
815 Reopen(options);
816 for (int j = 0; j < (max_checksum + 1) * kNumPerFile; ++j) {
817 ASSERT_EQ(Key(j), Get(Key(j)));
818 }
819 }
7c673cae
FG
820}
821
822// On Windows you can have either memory mapped file or a file
823// with unbuffered access. So this asserts and does not make
824// sense to run
825#ifndef OS_WIN
826TEST_F(DBBasicTest, MmapAndBufferOptions) {
11fdf7f2
TL
827 if (!IsMemoryMappedAccessSupported()) {
828 return;
829 }
7c673cae
FG
830 Options options = CurrentOptions();
831
832 options.use_direct_reads = true;
833 options.allow_mmap_reads = true;
834 ASSERT_NOK(TryReopen(options));
835
836 // All other combinations are acceptable
837 options.use_direct_reads = false;
838 ASSERT_OK(TryReopen(options));
839
840 if (IsDirectIOSupported()) {
841 options.use_direct_reads = true;
842 options.allow_mmap_reads = false;
843 ASSERT_OK(TryReopen(options));
844 }
845
846 options.use_direct_reads = false;
847 ASSERT_OK(TryReopen(options));
848}
849#endif
850
11fdf7f2
TL
851class TestEnv : public EnvWrapper {
852 public:
853 explicit TestEnv() : EnvWrapper(Env::Default()),
854 close_count(0) { }
855
856 class TestLogger : public Logger {
857 public:
858 using Logger::Logv;
859 TestLogger(TestEnv *env_ptr) : Logger() { env = env_ptr; }
860 ~TestLogger() {
861 if (!closed_) {
862 CloseHelper();
863 }
864 }
865 virtual void Logv(const char* /*format*/, va_list /*ap*/) override{};
866
867 protected:
868 virtual Status CloseImpl() override {
869 return CloseHelper();
870 }
871 private:
872 Status CloseHelper() {
873 env->CloseCountInc();;
874 return Status::IOError();
875 }
876 TestEnv *env;
877 };
878
879 void CloseCountInc() { close_count++; }
880
881 int GetCloseCount() { return close_count; }
882
883 virtual Status NewLogger(const std::string& /*fname*/,
884 shared_ptr<Logger>* result) {
885 result->reset(new TestLogger(this));
886 return Status::OK();
887 }
888
889 private:
890 int close_count;
891};
892
893TEST_F(DBBasicTest, DBClose) {
894 Options options = GetDefaultOptions();
895 std::string dbname = test::PerThreadDBPath("db_close_test");
896 ASSERT_OK(DestroyDB(dbname, options));
897
898 DB* db = nullptr;
899 TestEnv* env = new TestEnv();
900 options.create_if_missing = true;
901 options.env = env;
902 Status s = DB::Open(options, dbname, &db);
903 ASSERT_OK(s);
904 ASSERT_TRUE(db != nullptr);
905
906 s = db->Close();
907 ASSERT_EQ(env->GetCloseCount(), 1);
908 ASSERT_EQ(s, Status::IOError());
909
910 delete db;
911 ASSERT_EQ(env->GetCloseCount(), 1);
912
913 // Do not call DB::Close() and ensure our logger Close() still gets called
914 s = DB::Open(options, dbname, &db);
915 ASSERT_OK(s);
916 ASSERT_TRUE(db != nullptr);
917 delete db;
918 ASSERT_EQ(env->GetCloseCount(), 2);
919
920 // Provide our own logger and ensure DB::Close() does not close it
921 options.info_log.reset(new TestEnv::TestLogger(env));
922 options.create_if_missing = false;
923 s = DB::Open(options, dbname, &db);
924 ASSERT_OK(s);
925 ASSERT_TRUE(db != nullptr);
926
927 s = db->Close();
928 ASSERT_EQ(s, Status::OK());
929 delete db;
930 ASSERT_EQ(env->GetCloseCount(), 2);
931 options.info_log.reset();
932 ASSERT_EQ(env->GetCloseCount(), 3);
933
934 delete options.env;
935}
936
937TEST_F(DBBasicTest, DBCloseFlushError) {
938 std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
939 new FaultInjectionTestEnv(Env::Default()));
940 Options options = GetDefaultOptions();
941 options.create_if_missing = true;
942 options.manual_wal_flush = true;
943 options.write_buffer_size=100;
944 options.env = fault_injection_env.get();
945
946 Reopen(options);
947 ASSERT_OK(Put("key1", "value1"));
948 ASSERT_OK(Put("key2", "value2"));
949 ASSERT_OK(dbfull()->TEST_SwitchMemtable());
950 ASSERT_OK(Put("key3", "value3"));
951 fault_injection_env->SetFilesystemActive(false);
952 Status s = dbfull()->Close();
953 fault_injection_env->SetFilesystemActive(true);
954 ASSERT_NE(s, Status::OK());
955
956 Destroy(options);
957}
958
7c673cae
FG
959} // namespace rocksdb
960
961int main(int argc, char** argv) {
962 rocksdb::port::InstallStackTraceHandler();
963 ::testing::InitGoogleTest(&argc, argv);
964 return RUN_ALL_TESTS();
965}