1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10 #include "db/db_test_util.h"
11 #include "file/sst_file_manager_impl.h"
12 #include "port/port.h"
13 #include "port/stack_trace.h"
14 #include "rocksdb/sst_file_manager.h"
15
16 namespace ROCKSDB_NAMESPACE {
17
18 class DBSSTTest : public DBTestBase {
19 public:
DBSSTTest()20 DBSSTTest() : DBTestBase("/db_sst_test") {}
21 };
22
23 #ifndef ROCKSDB_LITE
24 // A class which remembers the name of each flushed file.
25 class FlushedFileCollector : public EventListener {
26 public:
FlushedFileCollector()27 FlushedFileCollector() {}
~FlushedFileCollector()28 ~FlushedFileCollector() override {}
29
OnFlushCompleted(DB *,const FlushJobInfo & info)30 void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
31 std::lock_guard<std::mutex> lock(mutex_);
32 flushed_files_.push_back(info.file_path);
33 }
34
GetFlushedFiles()35 std::vector<std::string> GetFlushedFiles() {
36 std::lock_guard<std::mutex> lock(mutex_);
37 std::vector<std::string> result;
38 for (auto fname : flushed_files_) {
39 result.push_back(fname);
40 }
41 return result;
42 }
ClearFlushedFiles()43 void ClearFlushedFiles() {
44 std::lock_guard<std::mutex> lock(mutex_);
45 flushed_files_.clear();
46 }
47
48 private:
49 std::vector<std::string> flushed_files_;
50 std::mutex mutex_;
51 };
52 #endif // ROCKSDB_LITE
53
TEST_F(DBSSTTest,DontDeletePendingOutputs)54 TEST_F(DBSSTTest, DontDeletePendingOutputs) {
55 Options options;
56 options.env = env_;
57 options.create_if_missing = true;
58 DestroyAndReopen(options);
59
60 // Every time we write to a table file, call FOF/POF with full DB scan. This
61 // will make sure our pending_outputs_ protection work correctly
62 std::function<void()> purge_obsolete_files_function = [&]() {
63 JobContext job_context(0);
64 dbfull()->TEST_LockMutex();
65 dbfull()->FindObsoleteFiles(&job_context, true /*force*/);
66 dbfull()->TEST_UnlockMutex();
67 dbfull()->PurgeObsoleteFiles(job_context);
68 job_context.Clean();
69 };
70
71 env_->table_write_callback_ = &purge_obsolete_files_function;
72
73 for (int i = 0; i < 2; ++i) {
74 ASSERT_OK(Put("a", "begin"));
75 ASSERT_OK(Put("z", "end"));
76 ASSERT_OK(Flush());
77 }
78
79 // If pending output guard does not work correctly, PurgeObsoleteFiles() will
80 // delete the file that Compaction is trying to create, causing this: error
81 // db/db_test.cc:975: IO error:
82 // /tmp/rocksdbtest-1552237650/db_test/000009.sst: No such file or directory
83 Compact("a", "b");
84 }
85
86 // 1 Create some SST files by inserting K-V pairs into DB
87 // 2 Close DB and change suffix from ".sst" to ".ldb" for every other SST file
88 // 3 Open DB and check if all key can be read
TEST_F(DBSSTTest,SSTsWithLdbSuffixHandling)89 TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
90 Options options = CurrentOptions();
91 options.write_buffer_size = 110 << 10; // 110KB
92 options.num_levels = 4;
93 DestroyAndReopen(options);
94
95 Random rnd(301);
96 int key_id = 0;
97 for (int i = 0; i < 10; ++i) {
98 GenerateNewFile(&rnd, &key_id, false);
99 }
100 Flush();
101 Close();
102 int const num_files = GetSstFileCount(dbname_);
103 ASSERT_GT(num_files, 0);
104
105 Reopen(options);
106 std::vector<std::string> values;
107 values.reserve(key_id);
108 for (int k = 0; k < key_id; ++k) {
109 values.push_back(Get(Key(k)));
110 }
111 Close();
112
113 std::vector<std::string> filenames;
114 GetSstFiles(env_, dbname_, &filenames);
115 int num_ldb_files = 0;
116 for (size_t i = 0; i < filenames.size(); ++i) {
117 if (i & 1) {
118 continue;
119 }
120 std::string const rdb_name = dbname_ + "/" + filenames[i];
121 std::string const ldb_name = Rocks2LevelTableFileName(rdb_name);
122 ASSERT_TRUE(env_->RenameFile(rdb_name, ldb_name).ok());
123 ++num_ldb_files;
124 }
125 ASSERT_GT(num_ldb_files, 0);
126 ASSERT_EQ(num_files, GetSstFileCount(dbname_));
127
128 Reopen(options);
129 for (int k = 0; k < key_id; ++k) {
130 ASSERT_EQ(values[k], Get(Key(k)));
131 }
132 Destroy(options);
133 }
134
135 // Check that we don't crash when opening DB with
136 // DBOptions::skip_checking_sst_file_sizes_on_db_open = true.
TEST_F(DBSSTTest,SkipCheckingSSTFileSizesOnDBOpen)137 TEST_F(DBSSTTest, SkipCheckingSSTFileSizesOnDBOpen) {
138 ASSERT_OK(Put("pika", "choo"));
139 ASSERT_OK(Flush());
140
141 // Just open the DB with the option set to true and check that we don't crash.
142 Options options;
143 options.skip_checking_sst_file_sizes_on_db_open = true;
144 Reopen(options);
145
146 ASSERT_EQ("choo", Get("pika"));
147 }
148
149 #ifndef ROCKSDB_LITE
TEST_F(DBSSTTest,DontDeleteMovedFile)150 TEST_F(DBSSTTest, DontDeleteMovedFile) {
151 // This test triggers move compaction and verifies that the file is not
152 // deleted when it's part of move compaction
153 Options options = CurrentOptions();
154 options.env = env_;
155 options.create_if_missing = true;
156 options.max_bytes_for_level_base = 1024 * 1024; // 1 MB
157 options.level0_file_num_compaction_trigger =
158 2; // trigger compaction when we have 2 files
159 DestroyAndReopen(options);
160
161 Random rnd(301);
162 // Create two 1MB sst files
163 for (int i = 0; i < 2; ++i) {
164 // Create 1MB sst file
165 for (int j = 0; j < 100; ++j) {
166 ASSERT_OK(Put(Key(i * 50 + j), RandomString(&rnd, 10 * 1024)));
167 }
168 ASSERT_OK(Flush());
169 }
170 // this should execute both L0->L1 and L1->(move)->L2 compactions
171 dbfull()->TEST_WaitForCompact();
172 ASSERT_EQ("0,0,1", FilesPerLevel(0));
173
174 // If the moved file is actually deleted (the move-safeguard in
175 // ~Version::Version() is not there), we get this failure:
176 // Corruption: Can't access /000009.sst
177 Reopen(options);
178 }
179
180 // This reproduces a bug where we don't delete a file because when it was
181 // supposed to be deleted, it was blocked by pending_outputs
182 // Consider:
183 // 1. current file_number is 13
184 // 2. compaction (1) starts, blocks deletion of all files starting with 13
185 // (pending outputs)
186 // 3. file 13 is created by compaction (2)
187 // 4. file 13 is consumed by compaction (3) and file 15 was created. Since file
188 // 13 has no references, it is put into VersionSet::obsolete_files_
189 // 5. FindObsoleteFiles() gets file 13 from VersionSet::obsolete_files_. File 13
190 // is deleted from obsolete_files_ set.
191 // 6. PurgeObsoleteFiles() tries to delete file 13, but this file is blocked by
192 // pending outputs since compaction (1) is still running. It is not deleted and
193 // it is not present in obsolete_files_ anymore. Therefore, we never delete it.
TEST_F(DBSSTTest,DeleteObsoleteFilesPendingOutputs)194 TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
195 Options options = CurrentOptions();
196 options.env = env_;
197 options.write_buffer_size = 2 * 1024 * 1024; // 2 MB
198 options.max_bytes_for_level_base = 1024 * 1024; // 1 MB
199 options.level0_file_num_compaction_trigger =
200 2; // trigger compaction when we have 2 files
201 options.max_background_flushes = 2;
202 options.max_background_compactions = 2;
203
204 OnFileDeletionListener* listener = new OnFileDeletionListener();
205 options.listeners.emplace_back(listener);
206
207 Reopen(options);
208
209 Random rnd(301);
210 // Create two 1MB sst files
211 for (int i = 0; i < 2; ++i) {
212 // Create 1MB sst file
213 for (int j = 0; j < 100; ++j) {
214 ASSERT_OK(Put(Key(i * 50 + j), RandomString(&rnd, 10 * 1024)));
215 }
216 ASSERT_OK(Flush());
217 }
218 // this should execute both L0->L1 and L1->(move)->L2 compactions
219 dbfull()->TEST_WaitForCompact();
220 ASSERT_EQ("0,0,1", FilesPerLevel(0));
221
222 test::SleepingBackgroundTask blocking_thread;
223 port::Mutex mutex_;
224 bool already_blocked(false);
225
226 // block the flush
227 std::function<void()> block_first_time = [&]() {
228 bool blocking = false;
229 {
230 MutexLock l(&mutex_);
231 if (!already_blocked) {
232 blocking = true;
233 already_blocked = true;
234 }
235 }
236 if (blocking) {
237 blocking_thread.DoSleep();
238 }
239 };
240 env_->table_write_callback_ = &block_first_time;
241 // Insert 2.5MB data, which should trigger a flush because we exceed
242 // write_buffer_size. The flush will be blocked with block_first_time
243 // pending_file is protecting all the files created after
244 for (int j = 0; j < 256; ++j) {
245 ASSERT_OK(Put(Key(j), RandomString(&rnd, 10 * 1024)));
246 }
247 blocking_thread.WaitUntilSleeping();
248
249 ASSERT_OK(dbfull()->TEST_CompactRange(2, nullptr, nullptr));
250
251 ASSERT_EQ("0,0,0,1", FilesPerLevel(0));
252 std::vector<LiveFileMetaData> metadata;
253 db_->GetLiveFilesMetaData(&metadata);
254 ASSERT_EQ(metadata.size(), 1U);
255 auto file_on_L2 = metadata[0].name;
256 listener->SetExpectedFileName(dbname_ + file_on_L2);
257
258 ASSERT_OK(dbfull()->TEST_CompactRange(3, nullptr, nullptr, nullptr,
259 true /* disallow trivial move */));
260 ASSERT_EQ("0,0,0,0,1", FilesPerLevel(0));
261
262 // finish the flush!
263 blocking_thread.WakeUp();
264 blocking_thread.WaitUntilDone();
265 dbfull()->TEST_WaitForFlushMemTable();
266 // File just flushed is too big for L0 and L1 so gets moved to L2.
267 dbfull()->TEST_WaitForCompact();
268 ASSERT_EQ("0,0,1,0,1", FilesPerLevel(0));
269
270 metadata.clear();
271 db_->GetLiveFilesMetaData(&metadata);
272 ASSERT_EQ(metadata.size(), 2U);
273
274 // This file should have been deleted during last compaction
275 ASSERT_EQ(Status::NotFound(), env_->FileExists(dbname_ + file_on_L2));
276 listener->VerifyMatchedCount(1);
277 }
278
TEST_F(DBSSTTest,DBWithSstFileManager)279 TEST_F(DBSSTTest, DBWithSstFileManager) {
280 std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
281 auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
282
283 int files_added = 0;
284 int files_deleted = 0;
285 int files_moved = 0;
286 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
287 "SstFileManagerImpl::OnAddFile", [&](void* /*arg*/) { files_added++; });
288 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
289 "SstFileManagerImpl::OnDeleteFile",
290 [&](void* /*arg*/) { files_deleted++; });
291 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
292 "SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
293 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
294
295 Options options = CurrentOptions();
296 options.sst_file_manager = sst_file_manager;
297 DestroyAndReopen(options);
298
299 Random rnd(301);
300 for (int i = 0; i < 25; i++) {
301 GenerateNewRandomFile(&rnd);
302 ASSERT_OK(Flush());
303 dbfull()->TEST_WaitForFlushMemTable();
304 dbfull()->TEST_WaitForCompact();
305 // Verify that we are tracking all sst files in dbname_
306 ASSERT_EQ(sfm->GetTrackedFiles(), GetAllSSTFiles());
307 }
308 ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
309
310 auto files_in_db = GetAllSSTFiles();
311 // Verify that we are tracking all sst files in dbname_
312 ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
313 // Verify the total files size
314 uint64_t total_files_size = 0;
315 for (auto& file_to_size : files_in_db) {
316 total_files_size += file_to_size.second;
317 }
318 ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
319 // We flushed at least 25 files
320 ASSERT_GE(files_added, 25);
321 // Compaction must have deleted some files
322 ASSERT_GT(files_deleted, 0);
323 // No files were moved
324 ASSERT_EQ(files_moved, 0);
325
326 Close();
327 Reopen(options);
328 ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
329 ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
330
331 // Verify that we track all the files again after the DB is closed and opened
332 Close();
333 sst_file_manager.reset(NewSstFileManager(env_));
334 options.sst_file_manager = sst_file_manager;
335 sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
336
337 Reopen(options);
338 ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
339 ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
340
341 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
342 }
343
TEST_F(DBSSTTest,RateLimitedDelete)344 TEST_F(DBSSTTest, RateLimitedDelete) {
345 Destroy(last_options_);
346 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
347 {"DBSSTTest::RateLimitedDelete:1",
348 "DeleteScheduler::BackgroundEmptyTrash"},
349 });
350
351 std::vector<uint64_t> penalties;
352 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
353 "DeleteScheduler::BackgroundEmptyTrash:Wait",
354 [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
355 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
356 "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
357 // Turn timed wait into a simulated sleep
358 uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
359 int64_t cur_time = 0;
360 env_->GetCurrentTime(&cur_time);
361 if (*abs_time_us > static_cast<uint64_t>(cur_time)) {
362 env_->addon_time_.fetch_add(*abs_time_us -
363 static_cast<uint64_t>(cur_time));
364 }
365
366 // Randomly sleep shortly
367 env_->addon_time_.fetch_add(
368 static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
369
370 // Set wait until time to before current to force not to sleep.
371 int64_t real_cur_time = 0;
372 Env::Default()->GetCurrentTime(&real_cur_time);
373 *abs_time_us = static_cast<uint64_t>(real_cur_time);
374 });
375
376 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
377
378 env_->no_slowdown_ = true;
379 env_->time_elapse_only_sleep_ = true;
380 Options options = CurrentOptions();
381 options.disable_auto_compactions = true;
382 // Need to disable stats dumping and persisting which also use
383 // RepeatableThread, one of whose member variables is of type
384 // InstrumentedCondVar. The callback for
385 // InstrumentedCondVar::TimedWaitInternal can be triggered by stats dumping
386 // and persisting threads and cause time_spent_deleting measurement to become
387 // incorrect.
388 options.stats_dump_period_sec = 0;
389 options.stats_persist_period_sec = 0;
390 options.env = env_;
391
392 int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
393 Status s;
394 options.sst_file_manager.reset(
395 NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
396 ASSERT_OK(s);
397 options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
398 auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
399 sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1);
400
401 WriteOptions wo;
402 wo.disableWAL = true;
403 ASSERT_OK(TryReopen(options));
404 // Create 4 files in L0
405 for (char v = 'a'; v <= 'd'; v++) {
406 ASSERT_OK(Put("Key2", DummyString(1024, v), wo));
407 ASSERT_OK(Put("Key3", DummyString(1024, v), wo));
408 ASSERT_OK(Put("Key4", DummyString(1024, v), wo));
409 ASSERT_OK(Put("Key1", DummyString(1024, v), wo));
410 ASSERT_OK(Put("Key4", DummyString(1024, v), wo));
411 ASSERT_OK(Flush());
412 }
413 // We created 4 sst files in L0
414 ASSERT_EQ("4", FilesPerLevel(0));
415
416 std::vector<LiveFileMetaData> metadata;
417 db_->GetLiveFilesMetaData(&metadata);
418
419 // Compaction will move the 4 files in L0 to trash and create 1 L1 file
420 ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
421 ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
422 ASSERT_EQ("0,1", FilesPerLevel(0));
423
424 uint64_t delete_start_time = env_->NowMicros();
425 // Hold BackgroundEmptyTrash
426 TEST_SYNC_POINT("DBSSTTest::RateLimitedDelete:1");
427 sfm->WaitForEmptyTrash();
428 uint64_t time_spent_deleting = env_->NowMicros() - delete_start_time;
429
430 uint64_t total_files_size = 0;
431 uint64_t expected_penlty = 0;
432 ASSERT_EQ(penalties.size(), metadata.size());
433 for (size_t i = 0; i < metadata.size(); i++) {
434 total_files_size += metadata[i].size;
435 expected_penlty = ((total_files_size * 1000000) / rate_bytes_per_sec);
436 ASSERT_EQ(expected_penlty, penalties[i]);
437 }
438 ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
439 ASSERT_LT(time_spent_deleting, expected_penlty * 1.1);
440
441 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
442 }
443
TEST_F(DBSSTTest,RateLimitedWALDelete)444 TEST_F(DBSSTTest, RateLimitedWALDelete) {
445 Destroy(last_options_);
446
447 std::vector<uint64_t> penalties;
448 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
449 "DeleteScheduler::BackgroundEmptyTrash:Wait",
450 [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
451
452 env_->no_slowdown_ = true;
453 env_->time_elapse_only_sleep_ = true;
454 Options options = CurrentOptions();
455 options.disable_auto_compactions = true;
456 options.compression = kNoCompression;
457 options.env = env_;
458
459 int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
460 Status s;
461 options.sst_file_manager.reset(
462 NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
463 ASSERT_OK(s);
464 options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
465 auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
466 sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
467
468 ASSERT_OK(TryReopen(options));
469 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
470
471 // Create 4 files in L0
472 for (char v = 'a'; v <= 'd'; v++) {
473 ASSERT_OK(Put("Key2", DummyString(1024, v)));
474 ASSERT_OK(Put("Key3", DummyString(1024, v)));
475 ASSERT_OK(Put("Key4", DummyString(1024, v)));
476 ASSERT_OK(Put("Key1", DummyString(1024, v)));
477 ASSERT_OK(Put("Key4", DummyString(1024, v)));
478 ASSERT_OK(Flush());
479 }
480 // We created 4 sst files in L0
481 ASSERT_EQ("4", FilesPerLevel(0));
482
483 // Compaction will move the 4 files in L0 to trash and create 1 L1 file
484 CompactRangeOptions cro;
485 cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
486 ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
487 ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
488 ASSERT_EQ("0,1", FilesPerLevel(0));
489
490 sfm->WaitForEmptyTrash();
491 ASSERT_EQ(penalties.size(), 8);
492
493 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
494 }
495
496 class DBWALTestWithParam
497 : public DBSSTTest,
498 public testing::WithParamInterface<std::tuple<std::string, bool>> {
499 public:
DBWALTestWithParam()500 DBWALTestWithParam() {
501 wal_dir_ = std::get<0>(GetParam());
502 wal_dir_same_as_dbname_ = std::get<1>(GetParam());
503 }
504
505 std::string wal_dir_;
506 bool wal_dir_same_as_dbname_;
507 };
508
TEST_P(DBWALTestWithParam,WALTrashCleanupOnOpen)509 TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
510 class MyEnv : public EnvWrapper {
511 public:
512 MyEnv(Env* t) : EnvWrapper(t), fake_log_delete(false) {}
513
514 Status DeleteFile(const std::string& fname) {
515 if (fname.find(".log.trash") != std::string::npos && fake_log_delete) {
516 return Status::OK();
517 }
518
519 return target()->DeleteFile(fname);
520 }
521
522 void set_fake_log_delete(bool fake) { fake_log_delete = fake; }
523
524 private:
525 bool fake_log_delete;
526 };
527
528 std::unique_ptr<MyEnv> env(new MyEnv(Env::Default()));
529 Destroy(last_options_);
530
531 env->set_fake_log_delete(true);
532
533 Options options = CurrentOptions();
534 options.disable_auto_compactions = true;
535 options.compression = kNoCompression;
536 options.env = env.get();
537 options.wal_dir = dbname_ + wal_dir_;
538
539 int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
540 Status s;
541 options.sst_file_manager.reset(
542 NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
543 ASSERT_OK(s);
544 options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
545 auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
546 sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
547
548 ASSERT_OK(TryReopen(options));
549
550 // Create 4 files in L0
551 for (char v = 'a'; v <= 'd'; v++) {
552 ASSERT_OK(Put("Key2", DummyString(1024, v)));
553 ASSERT_OK(Put("Key3", DummyString(1024, v)));
554 ASSERT_OK(Put("Key4", DummyString(1024, v)));
555 ASSERT_OK(Put("Key1", DummyString(1024, v)));
556 ASSERT_OK(Put("Key4", DummyString(1024, v)));
557 ASSERT_OK(Flush());
558 }
559 // We created 4 sst files in L0
560 ASSERT_EQ("4", FilesPerLevel(0));
561
562 Close();
563
564 options.sst_file_manager.reset();
565 std::vector<std::string> filenames;
566 int trash_log_count = 0;
567 if (!wal_dir_same_as_dbname_) {
568 // Forcibly create some trash log files
569 std::unique_ptr<WritableFile> result;
570 env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result,
571 EnvOptions());
572 result.reset();
573 }
574 env->GetChildren(options.wal_dir, &filenames);
575 for (const std::string& fname : filenames) {
576 if (fname.find(".log.trash") != std::string::npos) {
577 trash_log_count++;
578 }
579 }
580 ASSERT_GE(trash_log_count, 1);
581
582 env->set_fake_log_delete(false);
583 ASSERT_OK(TryReopen(options));
584
585 filenames.clear();
586 trash_log_count = 0;
587 env->GetChildren(options.wal_dir, &filenames);
588 for (const std::string& fname : filenames) {
589 if (fname.find(".log.trash") != std::string::npos) {
590 trash_log_count++;
591 }
592 }
593 ASSERT_EQ(trash_log_count, 0);
594 Close();
595 }
596
597 INSTANTIATE_TEST_CASE_P(DBWALTestWithParam, DBWALTestWithParam,
598 ::testing::Values(std::make_tuple("", true),
599 std::make_tuple("_wal_dir", false)));
600
TEST_F(DBSSTTest,OpenDBWithExistingTrash)601 TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
602 Options options = CurrentOptions();
603
604 options.sst_file_manager.reset(
605 NewSstFileManager(env_, nullptr, "", 1024 * 1024 /* 1 MB/sec */));
606 auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
607
608 Destroy(last_options_);
609
610 // Add some trash files to the db directory so the DB can clean them up
611 env_->CreateDirIfMissing(dbname_);
612 ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "001.sst.trash"));
613 ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "002.sst.trash"));
614 ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "003.sst.trash"));
615
616 // Reopen the DB and verify that it deletes existing trash files
617 ASSERT_OK(TryReopen(options));
618 sfm->WaitForEmptyTrash();
619 ASSERT_NOK(env_->FileExists(dbname_ + "/" + "001.sst.trash"));
620 ASSERT_NOK(env_->FileExists(dbname_ + "/" + "002.sst.trash"));
621 ASSERT_NOK(env_->FileExists(dbname_ + "/" + "003.sst.trash"));
622 }
623
624
625 // Create a DB with 2 db_paths, and generate multiple files in the 2
626 // db_paths using CompactRangeOptions, make sure that files that were
627 // deleted from first db_path were deleted using DeleteScheduler and
628 // files in the second path were not.
TEST_F(DBSSTTest,DeleteSchedulerMultipleDBPaths)629 TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
630 std::atomic<int> bg_delete_file(0);
631 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
632 "DeleteScheduler::DeleteTrashFile:DeleteFile",
633 [&](void* /*arg*/) { bg_delete_file++; });
634 // The deletion scheduler sometimes skips marking file as trash according to
635 // a heuristic. In that case the deletion will go through the below SyncPoint.
636 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
637 "DeleteScheduler::DeleteFile", [&](void* /*arg*/) { bg_delete_file++; });
638
639 Options options = CurrentOptions();
640 options.disable_auto_compactions = true;
641 options.db_paths.emplace_back(dbname_, 1024 * 100);
642 options.db_paths.emplace_back(dbname_ + "_2", 1024 * 100);
643 options.env = env_;
644
645 int64_t rate_bytes_per_sec = 1024 * 1024; // 1 Mb / Sec
646 Status s;
647 options.sst_file_manager.reset(
648 NewSstFileManager(env_, nullptr, "", rate_bytes_per_sec, false, &s,
649 /* max_trash_db_ratio= */ 1.1));
650
651 ASSERT_OK(s);
652 auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
653
654 DestroyAndReopen(options);
655 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
656
657 WriteOptions wo;
658 wo.disableWAL = true;
659
660 // Create 4 files in L0
661 for (int i = 0; i < 4; i++) {
662 ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'A'), wo));
663 ASSERT_OK(Flush());
664 }
665 // We created 4 sst files in L0
666 ASSERT_EQ("4", FilesPerLevel(0));
667 // Compaction will delete files from L0 in first db path and generate a new
668 // file in L1 in second db path
669 CompactRangeOptions compact_options;
670 compact_options.target_path_id = 1;
671 Slice begin("Key0");
672 Slice end("Key3");
673 ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
674 ASSERT_EQ("0,1", FilesPerLevel(0));
675
676 // Create 4 files in L0
677 for (int i = 4; i < 8; i++) {
678 ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'B'), wo));
679 ASSERT_OK(Flush());
680 }
681 ASSERT_EQ("4,1", FilesPerLevel(0));
682
683 // Compaction will delete files from L0 in first db path and generate a new
684 // file in L1 in second db path
685 begin = "Key4";
686 end = "Key7";
687 ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
688 ASSERT_EQ("0,2", FilesPerLevel(0));
689
690 sfm->WaitForEmptyTrash();
691 ASSERT_EQ(bg_delete_file, 8);
692
693 // Compaction will delete both files and regenerate a file in L1 in second
694 // db path. The deleted files should still be cleaned up via delete scheduler.
695 compact_options.bottommost_level_compaction =
696 BottommostLevelCompaction::kForceOptimized;
697 ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
698 ASSERT_EQ("0,1", FilesPerLevel(0));
699
700 sfm->WaitForEmptyTrash();
701 ASSERT_EQ(bg_delete_file, 10);
702
703 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
704 }
705
TEST_F(DBSSTTest,DestroyDBWithRateLimitedDelete)706 TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
707 int bg_delete_file = 0;
708 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
709 "DeleteScheduler::DeleteTrashFile:DeleteFile",
710 [&](void* /*arg*/) { bg_delete_file++; });
711 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
712
713 Status s;
714 Options options = CurrentOptions();
715 options.disable_auto_compactions = true;
716 options.env = env_;
717 options.sst_file_manager.reset(
718 NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
719 ASSERT_OK(s);
720 DestroyAndReopen(options);
721
722 // Create 4 files in L0
723 for (int i = 0; i < 4; i++) {
724 ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'A')));
725 ASSERT_OK(Flush());
726 }
727 // We created 4 sst files in L0
728 ASSERT_EQ("4", FilesPerLevel(0));
729
730 // Close DB and destroy it using DeleteScheduler
731 Close();
732
733 int num_sst_files = 0;
734 int num_wal_files = 0;
735 std::vector<std::string> db_files;
736 env_->GetChildren(dbname_, &db_files);
737 for (std::string f : db_files) {
738 if (f.substr(f.find_last_of(".") + 1) == "sst") {
739 num_sst_files++;
740 } else if (f.substr(f.find_last_of(".") + 1) == "log") {
741 num_wal_files++;
742 }
743 }
744 ASSERT_GT(num_sst_files, 0);
745 ASSERT_GT(num_wal_files, 0);
746
747 auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
748
749 sfm->SetDeleteRateBytesPerSecond(1024 * 1024);
750 sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1);
751 ASSERT_OK(DestroyDB(dbname_, options));
752 sfm->WaitForEmptyTrash();
753 ASSERT_EQ(bg_delete_file, num_sst_files + num_wal_files);
754 }
755
TEST_F(DBSSTTest,DBWithMaxSpaceAllowed)756 TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) {
757 std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
758 auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
759
760 Options options = CurrentOptions();
761 options.sst_file_manager = sst_file_manager;
762 options.disable_auto_compactions = true;
763 DestroyAndReopen(options);
764
765 Random rnd(301);
766
767 // Generate a file containing 100 keys.
768 for (int i = 0; i < 100; i++) {
769 ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
770 }
771 ASSERT_OK(Flush());
772
773 uint64_t first_file_size = 0;
774 auto files_in_db = GetAllSSTFiles(&first_file_size);
775 ASSERT_EQ(sfm->GetTotalSize(), first_file_size);
776
777 // Set the maximum allowed space usage to the current total size
778 sfm->SetMaxAllowedSpaceUsage(first_file_size + 1);
779
780 ASSERT_OK(Put("key1", "val1"));
781 // This flush will cause bg_error_ and will fail
782 ASSERT_NOK(Flush());
783 }
784
TEST_F(DBSSTTest,CancellingCompactionsWorks)785 TEST_F(DBSSTTest, CancellingCompactionsWorks) {
786 std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
787 auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
788
789 Options options = CurrentOptions();
790 options.sst_file_manager = sst_file_manager;
791 options.level0_file_num_compaction_trigger = 2;
792 options.statistics = CreateDBStatistics();
793 DestroyAndReopen(options);
794
795 int completed_compactions = 0;
796 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
797 "DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* /*arg*/) {
798 sfm->SetMaxAllowedSpaceUsage(0);
799 ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
800 });
801 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
802 "DBImpl::BackgroundCompaction:NonTrivial:AfterRun",
803 [&](void* /*arg*/) { completed_compactions++; });
804 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
805
806 Random rnd(301);
807
808 // Generate a file containing 10 keys.
809 for (int i = 0; i < 10; i++) {
810 ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
811 }
812 ASSERT_OK(Flush());
813 uint64_t total_file_size = 0;
814 auto files_in_db = GetAllSSTFiles(&total_file_size);
815 // Set the maximum allowed space usage to the current total size
816 sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
817
818 // Generate another file to trigger compaction.
819 for (int i = 0; i < 10; i++) {
820 ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
821 }
822 ASSERT_OK(Flush());
823 dbfull()->TEST_WaitForCompact(true);
824
825 // Because we set a callback in CancelledCompaction, we actually
826 // let the compaction run
827 ASSERT_GT(completed_compactions, 0);
828 ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
829 // Make sure the stat is bumped
830 ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(COMPACTION_CANCELLED), 0);
831 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
832 }
833
TEST_F(DBSSTTest,CancellingManualCompactionsWorks)834 TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
835 std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
836 auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
837
838 Options options = CurrentOptions();
839 options.sst_file_manager = sst_file_manager;
840 options.statistics = CreateDBStatistics();
841
842 FlushedFileCollector* collector = new FlushedFileCollector();
843 options.listeners.emplace_back(collector);
844
845 DestroyAndReopen(options);
846
847 Random rnd(301);
848
849 // Generate a file containing 10 keys.
850 for (int i = 0; i < 10; i++) {
851 ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
852 }
853 ASSERT_OK(Flush());
854 uint64_t total_file_size = 0;
855 auto files_in_db = GetAllSSTFiles(&total_file_size);
856 // Set the maximum allowed space usage to the current total size
857 sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
858
859 // Generate another file to trigger compaction.
860 for (int i = 0; i < 10; i++) {
861 ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
862 }
863 ASSERT_OK(Flush());
864
865 // OK, now trigger a manual compaction
866 dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
867
868 // Wait for manual compaction to get scheduled and finish
869 dbfull()->TEST_WaitForCompact(true);
870
871 ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
872 // Make sure the stat is bumped
873 ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
874 COMPACTION_CANCELLED),
875 1);
876
877 // Now make sure CompactFiles also gets cancelled
878 auto l0_files = collector->GetFlushedFiles();
879 dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0);
880
881 // Wait for manual compaction to get scheduled and finish
882 dbfull()->TEST_WaitForCompact(true);
883
884 ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
885 COMPACTION_CANCELLED),
886 2);
887 ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
888
889 // Now let the flush through and make sure GetCompactionsReservedSize
890 // returns to normal
891 sfm->SetMaxAllowedSpaceUsage(0);
892 int completed_compactions = 0;
893 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
894 "CompactFilesImpl:End", [&](void* /*arg*/) { completed_compactions++; });
895
896 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
897 dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0);
898 dbfull()->TEST_WaitForCompact(true);
899
900 ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
901 ASSERT_GT(completed_compactions, 0);
902
903 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
904 }
905
TEST_F(DBSSTTest,DBWithMaxSpaceAllowedRandomized)906 TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
907 // This test will set a maximum allowed space for the DB, then it will
908 // keep filling the DB until the limit is reached and bg_error_ is set.
909 // When bg_error_ is set we will verify that the DB size is greater
910 // than the limit.
911
912 std::vector<int> max_space_limits_mbs = {1, 10};
913 std::atomic<bool> bg_error_set(false);
914
915 std::atomic<int> reached_max_space_on_flush(0);
916 std::atomic<int> reached_max_space_on_compaction(0);
917 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
918 "DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached",
919 [&](void* arg) {
920 Status* bg_error = static_cast<Status*>(arg);
921 bg_error_set = true;
922 reached_max_space_on_flush++;
923 // clear error to ensure compaction callback is called
924 *bg_error = Status::OK();
925 });
926
927 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
928 "DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* arg) {
929 bool* enough_room = static_cast<bool*>(arg);
930 *enough_room = true;
931 });
932
933 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
934 "CompactionJob::FinishCompactionOutputFile:MaxAllowedSpaceReached",
935 [&](void* /*arg*/) {
936 bg_error_set = true;
937 reached_max_space_on_compaction++;
938 });
939
940 for (auto limit_mb : max_space_limits_mbs) {
941 bg_error_set = false;
942 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
943 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
944 std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
945 auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
946
947 Options options = CurrentOptions();
948 options.sst_file_manager = sst_file_manager;
949 options.write_buffer_size = 1024 * 512; // 512 Kb
950 DestroyAndReopen(options);
951 Random rnd(301);
952
953 sfm->SetMaxAllowedSpaceUsage(limit_mb * 1024 * 1024);
954
955 // It is easy to detect if the test is stuck in a loop. No need for
956 // complex termination logic.
957 while (true) {
958 auto s = Put(RandomString(&rnd, 10), RandomString(&rnd, 50));
959 if (!s.ok()) {
960 break;
961 }
962 }
963 ASSERT_TRUE(bg_error_set);
964 uint64_t total_sst_files_size = 0;
965 GetAllSSTFiles(&total_sst_files_size);
966 ASSERT_GE(total_sst_files_size, limit_mb * 1024 * 1024);
967 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
968 }
969
970 ASSERT_GT(reached_max_space_on_flush, 0);
971 ASSERT_GT(reached_max_space_on_compaction, 0);
972 }
973
TEST_F(DBSSTTest,OpenDBWithInfiniteMaxOpenFiles)974 TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
975 // Open DB with infinite max open files
976 // - First iteration use 1 thread to open files
977 // - Second iteration use 5 threads to open files
978 for (int iter = 0; iter < 2; iter++) {
979 Options options;
980 options.create_if_missing = true;
981 options.write_buffer_size = 100000;
982 options.disable_auto_compactions = true;
983 options.max_open_files = -1;
984 if (iter == 0) {
985 options.max_file_opening_threads = 1;
986 } else {
987 options.max_file_opening_threads = 5;
988 }
989 options = CurrentOptions(options);
990 DestroyAndReopen(options);
991
992 // Create 12 Files in L0 (then move then to L2)
993 for (int i = 0; i < 12; i++) {
994 std::string k = "L2_" + Key(i);
995 ASSERT_OK(Put(k, k + std::string(1000, 'a')));
996 ASSERT_OK(Flush());
997 }
998 CompactRangeOptions compact_options;
999 compact_options.change_level = true;
1000 compact_options.target_level = 2;
1001 db_->CompactRange(compact_options, nullptr, nullptr);
1002
1003 // Create 12 Files in L0
1004 for (int i = 0; i < 12; i++) {
1005 std::string k = "L0_" + Key(i);
1006 ASSERT_OK(Put(k, k + std::string(1000, 'a')));
1007 ASSERT_OK(Flush());
1008 }
1009 Close();
1010
1011 // Reopening the DB will load all existing files
1012 Reopen(options);
1013 ASSERT_EQ("12,0,12", FilesPerLevel(0));
1014 std::vector<std::vector<FileMetaData>> files;
1015 dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
1016
1017 for (const auto& level : files) {
1018 for (const auto& file : level) {
1019 ASSERT_TRUE(file.table_reader_handle != nullptr);
1020 }
1021 }
1022
1023 for (int i = 0; i < 12; i++) {
1024 ASSERT_EQ(Get("L0_" + Key(i)), "L0_" + Key(i) + std::string(1000, 'a'));
1025 ASSERT_EQ(Get("L2_" + Key(i)), "L2_" + Key(i) + std::string(1000, 'a'));
1026 }
1027 }
1028 }
1029
TEST_F(DBSSTTest,GetTotalSstFilesSize)1030 TEST_F(DBSSTTest, GetTotalSstFilesSize) {
1031 // We don't propagate oldest-key-time table property on compaction and
1032 // just write 0 as default value. This affect the exact table size, since
1033 // we encode table properties as varint64. Force time to be 0 to work around
1034 // it. Should remove the workaround after we propagate the property on
1035 // compaction.
1036 std::unique_ptr<MockTimeEnv> mock_env(new MockTimeEnv(Env::Default()));
1037 mock_env->set_current_time(0);
1038
1039 Options options = CurrentOptions();
1040 options.disable_auto_compactions = true;
1041 options.compression = kNoCompression;
1042 options.env = mock_env.get();
1043 DestroyAndReopen(options);
1044 // Generate 5 files in L0
1045 for (int i = 0; i < 5; i++) {
1046 for (int j = 0; j < 10; j++) {
1047 std::string val = "val_file_" + ToString(i);
1048 ASSERT_OK(Put(Key(j), val));
1049 }
1050 Flush();
1051 }
1052 ASSERT_EQ("5", FilesPerLevel(0));
1053
1054 std::vector<LiveFileMetaData> live_files_meta;
1055 dbfull()->GetLiveFilesMetaData(&live_files_meta);
1056 ASSERT_EQ(live_files_meta.size(), 5);
1057 uint64_t single_file_size = live_files_meta[0].size;
1058
1059 uint64_t live_sst_files_size = 0;
1060 uint64_t total_sst_files_size = 0;
1061 for (const auto& file_meta : live_files_meta) {
1062 live_sst_files_size += file_meta.size;
1063 }
1064
1065 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1066 &total_sst_files_size));
1067 // Live SST files = 5
1068 // Total SST files = 5
1069 ASSERT_EQ(live_sst_files_size, 5 * single_file_size);
1070 ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
1071
1072 // hold current version
1073 std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
1074
1075 // Compact 5 files into 1 file in L0
1076 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1077 ASSERT_EQ("0,1", FilesPerLevel(0));
1078
1079 live_files_meta.clear();
1080 dbfull()->GetLiveFilesMetaData(&live_files_meta);
1081 ASSERT_EQ(live_files_meta.size(), 1);
1082
1083 live_sst_files_size = 0;
1084 total_sst_files_size = 0;
1085 for (const auto& file_meta : live_files_meta) {
1086 live_sst_files_size += file_meta.size;
1087 }
1088 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1089 &total_sst_files_size));
1090 // Live SST files = 1 (compacted file)
1091 // Total SST files = 6 (5 original files + compacted file)
1092 ASSERT_EQ(live_sst_files_size, 1 * single_file_size);
1093 ASSERT_EQ(total_sst_files_size, 6 * single_file_size);
1094
1095 // hold current version
1096 std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
1097
1098 // Delete all keys and compact, this will delete all live files
1099 for (int i = 0; i < 10; i++) {
1100 ASSERT_OK(Delete(Key(i)));
1101 }
1102 Flush();
1103 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1104 ASSERT_EQ("", FilesPerLevel(0));
1105
1106 live_files_meta.clear();
1107 dbfull()->GetLiveFilesMetaData(&live_files_meta);
1108 ASSERT_EQ(live_files_meta.size(), 0);
1109
1110 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1111 &total_sst_files_size));
1112 // Live SST files = 0
1113 // Total SST files = 6 (5 original files + compacted file)
1114 ASSERT_EQ(total_sst_files_size, 6 * single_file_size);
1115
1116 iter1.reset();
1117 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1118 &total_sst_files_size));
1119 // Live SST files = 0
1120 // Total SST files = 1 (compacted file)
1121 ASSERT_EQ(total_sst_files_size, 1 * single_file_size);
1122
1123 iter2.reset();
1124 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1125 &total_sst_files_size));
1126 // Live SST files = 0
1127 // Total SST files = 0
1128 ASSERT_EQ(total_sst_files_size, 0);
1129
1130 // Close db before mock_env destruct.
1131 Close();
1132 }
1133
TEST_F(DBSSTTest,GetTotalSstFilesSizeVersionsFilesShared)1134 TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
1135 Options options = CurrentOptions();
1136 options.disable_auto_compactions = true;
1137 options.compression = kNoCompression;
1138 DestroyAndReopen(options);
1139 // Generate 5 files in L0
1140 for (int i = 0; i < 5; i++) {
1141 ASSERT_OK(Put(Key(i), "val"));
1142 Flush();
1143 }
1144 ASSERT_EQ("5", FilesPerLevel(0));
1145
1146 std::vector<LiveFileMetaData> live_files_meta;
1147 dbfull()->GetLiveFilesMetaData(&live_files_meta);
1148 ASSERT_EQ(live_files_meta.size(), 5);
1149 uint64_t single_file_size = live_files_meta[0].size;
1150
1151 uint64_t live_sst_files_size = 0;
1152 uint64_t total_sst_files_size = 0;
1153 for (const auto& file_meta : live_files_meta) {
1154 live_sst_files_size += file_meta.size;
1155 }
1156
1157 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1158 &total_sst_files_size));
1159
1160 // Live SST files = 5
1161 // Total SST files = 5
1162 ASSERT_EQ(live_sst_files_size, 5 * single_file_size);
1163 ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
1164
1165 // hold current version
1166 std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
1167
1168 // Compaction will do trivial move from L0 to L1
1169 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1170 ASSERT_EQ("0,5", FilesPerLevel(0));
1171
1172 live_files_meta.clear();
1173 dbfull()->GetLiveFilesMetaData(&live_files_meta);
1174 ASSERT_EQ(live_files_meta.size(), 5);
1175
1176 live_sst_files_size = 0;
1177 total_sst_files_size = 0;
1178 for (const auto& file_meta : live_files_meta) {
1179 live_sst_files_size += file_meta.size;
1180 }
1181 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1182 &total_sst_files_size));
1183 // Live SST files = 5
1184 // Total SST files = 5 (used in 2 version)
1185 ASSERT_EQ(live_sst_files_size, 5 * single_file_size);
1186 ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
1187
1188 // hold current version
1189 std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
1190
1191 // Delete all keys and compact, this will delete all live files
1192 for (int i = 0; i < 5; i++) {
1193 ASSERT_OK(Delete(Key(i)));
1194 }
1195 Flush();
1196 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1197 ASSERT_EQ("", FilesPerLevel(0));
1198
1199 live_files_meta.clear();
1200 dbfull()->GetLiveFilesMetaData(&live_files_meta);
1201 ASSERT_EQ(live_files_meta.size(), 0);
1202
1203 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1204 &total_sst_files_size));
1205 // Live SST files = 0
1206 // Total SST files = 5 (used in 2 version)
1207 ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
1208
1209 iter1.reset();
1210 iter2.reset();
1211
1212 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
1213 &total_sst_files_size));
1214 // Live SST files = 0
1215 // Total SST files = 0
1216 ASSERT_EQ(total_sst_files_size, 0);
1217 }
1218
1219 #endif // ROCKSDB_LITE
1220
1221 } // namespace ROCKSDB_NAMESPACE
1222
main(int argc,char ** argv)1223 int main(int argc, char** argv) {
1224 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
1225 ::testing::InitGoogleTest(&argc, argv);
1226 return RUN_ALL_TESTS();
1227 }
1228