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