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