]>
Commit | Line | Data |
---|---|---|
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 | ||
17 | namespace rocksdb { | |
18 | ||
19 | class DBBasicTest : public DBTestBase { | |
20 | public: | |
21 | DBBasicTest() : DBTestBase("/db_basic_test") {} | |
22 | }; | |
23 | ||
24 | TEST_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 | |
38 | TEST_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 | ||
70 | TEST_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 | ||
171 | TEST_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 | ||
196 | TEST_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 | ||
208 | TEST_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 | ||
224 | TEST_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 | ||
246 | TEST_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 | |
257 | TEST_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 | ||
284 | TEST_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 | ||
295 | TEST_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 | ||
314 | TEST_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 | ||
358 | TEST_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 | ||
404 | TEST_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 | ||
428 | TEST_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 | |
451 | TEST_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 | ||
517 | TEST_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 | ||
572 | TEST_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 | ||
611 | TEST_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 | ||
704 | TEST_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 | ||
726 | TEST_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 | ||
763 | TEST_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 | ||
793 | TEST_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 | |
826 | TEST_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 |
851 | class 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 | ||
893 | TEST_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 | ||
937 | TEST_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 | ||
961 | int main(int argc, char** argv) { | |
962 | rocksdb::port::InstallStackTraceHandler(); | |
963 | ::testing::InitGoogleTest(&argc, argv); | |
964 | return RUN_ALL_TESTS(); | |
965 | } |