| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file. See the AUTHORS file for names of contributors.
- #include "db/db_test_util.h"
- #include "env/mock_env.h"
- #include "file/sst_file_manager_impl.h"
- #include "port/port.h"
- #include "port/stack_trace.h"
- #include "rocksdb/cache.h"
- #include "rocksdb/sst_file_manager.h"
- #include "rocksdb/table.h"
- #include "util/random.h"
- namespace ROCKSDB_NAMESPACE {
- class DBSSTTest : public DBTestBase {
- public:
- DBSSTTest(const std::string& test_name = "db_sst_test")
- : DBTestBase(test_name, /*env_do_fsync=*/true) {}
- };
- // A class which remembers the name of each flushed file.
- class FlushedFileCollector : public EventListener {
- public:
- FlushedFileCollector() = default;
- ~FlushedFileCollector() override = default;
- void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
- std::lock_guard<std::mutex> lock(mutex_);
- flushed_files_.push_back(info.file_path);
- }
- std::vector<std::string> GetFlushedFiles() {
- std::lock_guard<std::mutex> lock(mutex_);
- std::vector<std::string> result;
- for (const auto& fname : flushed_files_) {
- result.push_back(fname);
- }
- return result;
- }
- void ClearFlushedFiles() {
- std::lock_guard<std::mutex> lock(mutex_);
- flushed_files_.clear();
- }
- private:
- std::vector<std::string> flushed_files_;
- std::mutex mutex_;
- };
- TEST_F(DBSSTTest, DontDeletePendingOutputs) {
- Options options;
- options.env = env_;
- options.create_if_missing = true;
- DestroyAndReopen(options);
- // Every time we write to a table file, call FOF/POF with full DB scan. This
- // will make sure our pending_outputs_ protection work correctly
- std::function<void()> purge_obsolete_files_function = [&]() {
- JobContext job_context(0);
- dbfull()->TEST_LockMutex();
- dbfull()->FindObsoleteFiles(&job_context, true /*force*/);
- dbfull()->TEST_UnlockMutex();
- dbfull()->PurgeObsoleteFiles(job_context);
- job_context.Clean();
- };
- env_->table_write_callback_ = &purge_obsolete_files_function;
- for (int i = 0; i < 2; ++i) {
- ASSERT_OK(Put("a", "begin"));
- ASSERT_OK(Put("z", "end"));
- ASSERT_OK(Flush());
- }
- // If pending output guard does not work correctly, PurgeObsoleteFiles() will
- // delete the file that Compaction is trying to create, causing this: error
- // db/db_test.cc:975: IO error:
- // /tmp/rocksdbtest-1552237650/db_test/000009.sst: No such file or directory
- Compact("a", "b");
- }
- // 1 Create some SST files by inserting K-V pairs into DB
- // 2 Close DB and change suffix from ".sst" to ".ldb" for every other SST file
- // 3 Open DB and check if all key can be read
- TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
- Options options = CurrentOptions();
- options.write_buffer_size = 110 << 10; // 110KB
- options.num_levels = 4;
- DestroyAndReopen(options);
- Random rnd(301);
- int key_id = 0;
- for (int i = 0; i < 10; ++i) {
- GenerateNewFile(&rnd, &key_id, false);
- }
- ASSERT_OK(Flush());
- Close();
- int const num_files = GetSstFileCount(dbname_);
- ASSERT_GT(num_files, 0);
- Reopen(options);
- std::vector<std::string> values;
- values.reserve(key_id);
- for (int k = 0; k < key_id; ++k) {
- values.push_back(Get(Key(k)));
- }
- Close();
- std::vector<std::string> filenames;
- GetSstFiles(env_, dbname_, &filenames);
- int num_ldb_files = 0;
- for (size_t i = 0; i < filenames.size(); ++i) {
- if (i & 1) {
- continue;
- }
- std::string const rdb_name = dbname_ + "/" + filenames[i];
- std::string const ldb_name = Rocks2LevelTableFileName(rdb_name);
- ASSERT_TRUE(env_->RenameFile(rdb_name, ldb_name).ok());
- ++num_ldb_files;
- }
- ASSERT_GT(num_ldb_files, 0);
- ASSERT_EQ(num_files, GetSstFileCount(dbname_));
- Reopen(options);
- for (int k = 0; k < key_id; ++k) {
- ASSERT_EQ(values[k], Get(Key(k)));
- }
- Destroy(options);
- }
- TEST_F(DBSSTTest, DontDeleteMovedFile) {
- // This test triggers move compaction and verifies that the file is not
- // deleted when it's part of move compaction
- Options options = CurrentOptions();
- options.env = env_;
- options.create_if_missing = true;
- options.max_bytes_for_level_base = 1024 * 1024; // 1 MB
- options.level0_file_num_compaction_trigger =
- 2; // trigger compaction when we have 2 files
- DestroyAndReopen(options);
- Random rnd(301);
- // Create two 1MB sst files
- for (int i = 0; i < 2; ++i) {
- // Create 1MB sst file
- for (int j = 0; j < 100; ++j) {
- ASSERT_OK(Put(Key(i * 50 + j), rnd.RandomString(10 * 1024)));
- }
- ASSERT_OK(Flush());
- }
- // this should execute both L0->L1 and L1->(move)->L2 compactions
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ("0,0,1", FilesPerLevel(0));
- // If the moved file is actually deleted (the move-safeguard in
- // ~Version::Version() is not there), we get this failure:
- // Corruption: Can't access /000009.sst
- Reopen(options);
- }
- // This reproduces a bug where we don't delete a file because when it was
- // supposed to be deleted, it was blocked by pending_outputs
- // Consider:
- // 1. current file_number is 13
- // 2. compaction (1) starts, blocks deletion of all files starting with 13
- // (pending outputs)
- // 3. file 13 is created by compaction (2)
- // 4. file 13 is consumed by compaction (3) and file 15 was created. Since file
- // 13 has no references, it is put into VersionSet::obsolete_files_
- // 5. FindObsoleteFiles() gets file 13 from VersionSet::obsolete_files_. File 13
- // is deleted from obsolete_files_ set.
- // 6. PurgeObsoleteFiles() tries to delete file 13, but this file is blocked by
- // pending outputs since compaction (1) is still running. It is not deleted and
- // it is not present in obsolete_files_ anymore. Therefore, we never delete it.
- TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
- Options options = CurrentOptions();
- options.env = env_;
- options.write_buffer_size = 2 * 1024 * 1024; // 2 MB
- options.max_bytes_for_level_base = 1024 * 1024; // 1 MB
- options.level0_file_num_compaction_trigger =
- 2; // trigger compaction when we have 2 files
- options.max_background_flushes = 2;
- options.max_background_compactions = 2;
- OnFileDeletionListener* listener = new OnFileDeletionListener();
- options.listeners.emplace_back(listener);
- Reopen(options);
- Random rnd(301);
- // Create two 1MB sst files
- for (int i = 0; i < 2; ++i) {
- // Create 1MB sst file
- for (int j = 0; j < 100; ++j) {
- ASSERT_OK(Put(Key(i * 50 + j), rnd.RandomString(10 * 1024)));
- }
- ASSERT_OK(Flush());
- }
- // this should execute both L0->L1 and L1->(move)->L2 compactions
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ("0,0,1", FilesPerLevel(0));
- test::SleepingBackgroundTask blocking_thread;
- port::Mutex mutex_;
- bool already_blocked(false);
- // block the flush
- std::function<void()> block_first_time = [&]() {
- bool blocking = false;
- {
- MutexLock l(&mutex_);
- if (!already_blocked) {
- blocking = true;
- already_blocked = true;
- }
- }
- if (blocking) {
- blocking_thread.DoSleep();
- }
- };
- env_->table_write_callback_ = &block_first_time;
- // Insert 2.5MB data, which should trigger a flush because we exceed
- // write_buffer_size. The flush will be blocked with block_first_time
- // pending_file is protecting all the files created after
- for (int j = 0; j < 256; ++j) {
- ASSERT_OK(Put(Key(j), rnd.RandomString(10 * 1024)));
- }
- blocking_thread.WaitUntilSleeping();
- ASSERT_OK(dbfull()->TEST_CompactRange(2, nullptr, nullptr));
- ASSERT_EQ("0,0,0,1", FilesPerLevel(0));
- std::vector<LiveFileMetaData> metadata;
- db_->GetLiveFilesMetaData(&metadata);
- ASSERT_EQ(metadata.size(), 1U);
- auto file_on_L2 = metadata[0].name;
- listener->SetExpectedFileName(dbname_ + file_on_L2);
- ASSERT_OK(dbfull()->TEST_CompactRange(3, nullptr, nullptr, nullptr,
- true /* disallow trivial move */));
- ASSERT_EQ("0,0,0,0,1", FilesPerLevel(0));
- // finish the flush!
- blocking_thread.WakeUp();
- blocking_thread.WaitUntilDone();
- ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
- // File just flushed is too big for L0 and L1 so gets moved to L2.
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ("0,0,1,0,1", FilesPerLevel(0));
- metadata.clear();
- db_->GetLiveFilesMetaData(&metadata);
- ASSERT_EQ(metadata.size(), 2U);
- // This file should have been deleted during last compaction
- ASSERT_EQ(Status::NotFound(), env_->FileExists(dbname_ + file_on_L2));
- listener->VerifyMatchedCount(1);
- }
- // Test that producing an empty .sst file does not write it out to
- // disk, and that the DeleteFile() env method is not called for
- // removing the non-existing file later.
- TEST_F(DBSSTTest, DeleteFileNotCalledForNotCreatedSSTFile) {
- Options options = CurrentOptions();
- options.env = env_;
- OnFileDeletionListener* listener = new OnFileDeletionListener();
- options.listeners.emplace_back(listener);
- Reopen(options);
- // Flush the empty database.
- ASSERT_OK(Flush());
- ASSERT_EQ("", FilesPerLevel(0));
- // We expect no .sst files.
- std::vector<LiveFileMetaData> metadata;
- db_->GetLiveFilesMetaData(&metadata);
- ASSERT_EQ(metadata.size(), 0U);
- // We expect no file deletions.
- listener->VerifyMatchedCount(0);
- }
- // Test that producing a non-empty .sst file does write it out to
- // disk, and that the DeleteFile() env method is not called for removing
- // the file later.
- TEST_F(DBSSTTest, DeleteFileNotCalledForCreatedSSTFile) {
- Options options = CurrentOptions();
- options.env = env_;
- OnFileDeletionListener* listener = new OnFileDeletionListener();
- options.listeners.emplace_back(listener);
- Reopen(options);
- ASSERT_OK(Put("pika", "choo"));
- // Flush the non-empty database.
- ASSERT_OK(Flush());
- ASSERT_EQ("1", FilesPerLevel(0));
- // We expect 1 .sst files.
- std::vector<LiveFileMetaData> metadata;
- db_->GetLiveFilesMetaData(&metadata);
- ASSERT_EQ(metadata.size(), 1U);
- // We expect no file deletions.
- listener->VerifyMatchedCount(0);
- }
- TEST_F(DBSSTTest, DBWithSstFileManager) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- int files_added = 0;
- int files_deleted = 0;
- int files_moved = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnAddFile", [&](void* /*arg*/) { files_added++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnDeleteFile",
- [&](void* /*arg*/) { files_deleted++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- DestroyAndReopen(options);
- Random rnd(301);
- for (int i = 0; i < 25; i++) {
- GenerateNewRandomFile(&rnd);
- ASSERT_OK(Flush());
- ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- // Verify that we are tracking all sst files in dbname_
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- }
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
- // Verify that we are tracking all sst files in dbname_
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- // Verify the total files size
- uint64_t total_files_size = 0;
- for (auto& file_to_size : files_in_db) {
- total_files_size += file_to_size.second;
- }
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- // We flushed at least 25 files
- ASSERT_GE(files_added, 25);
- // Compaction must have deleted some files
- ASSERT_GT(files_deleted, 0);
- // No files were moved
- ASSERT_EQ(files_moved, 0);
- Close();
- ASSERT_EQ(sfm->GetTrackedFiles().size(), 0) << "sfm should be empty";
- ASSERT_EQ(sfm->GetTotalSize(), 0) << "sfm should be empty";
- Reopen(options);
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- // Verify that we track all the files again after the DB is closed and opened
- Close();
- ASSERT_EQ(sfm->GetTrackedFiles().size(), 0) << "sfm should be empty";
- ASSERT_EQ(sfm->GetTotalSize(), 0) << "sfm should be empty";
- sst_file_manager.reset(NewSstFileManager(env_));
- options.sst_file_manager = sst_file_manager;
- sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Reopen(options);
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFiles) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- int files_added = 0;
- int files_deleted = 0;
- int files_moved = 0;
- int files_scheduled_to_delete = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnAddFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (file_path->find(".blob") != std::string::npos) {
- files_added++;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnDeleteFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (file_path->find(".blob") != std::string::npos) {
- files_deleted++;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::ScheduleFileDeletion", [&](void* arg) {
- assert(arg);
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (file_path->find(".blob") != std::string::npos) {
- ++files_scheduled_to_delete;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
- int64_t untracked_files = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnUntrackFile",
- [&](void* /*arg*/) { ++untracked_files; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.enable_blob_files = true;
- options.blob_file_size = 32; // create one blob per file
- DestroyAndReopen(options);
- Random rnd(301);
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Put("Key_" + std::to_string(i), "Value_" + std::to_string(i)));
- ASSERT_OK(Flush());
- ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- // Verify that we are tracking all sst and blob files in dbname_
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
- ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- }
- std::vector<uint64_t> blob_files = GetBlobFileNumbers();
- ASSERT_EQ(files_added, blob_files.size());
- // No blob file is obsoleted.
- ASSERT_EQ(files_deleted, 0);
- ASSERT_EQ(files_scheduled_to_delete, 0);
- // No files were moved.
- ASSERT_EQ(files_moved, 0);
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
- ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
- // Verify that we are tracking all sst and blob files in dbname_
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- // Verify the total files size
- uint64_t total_files_size = 0;
- for (auto& file_to_size : files_in_db) {
- total_files_size += file_to_size.second;
- }
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- Close();
- ASSERT_EQ(untracked_files, files_in_db.size());
- untracked_files = 0;
- ASSERT_EQ(sfm->GetTrackedFiles().size(), 0) << "sfm should be empty";
- ASSERT_EQ(sfm->GetTotalSize(), 0) << "sfm should be empty";
- Reopen(options);
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- // Verify that we track all the files again after the DB is closed and opened.
- Close();
- ASSERT_EQ(untracked_files, files_in_db.size());
- untracked_files = 0;
- ASSERT_EQ(sfm->GetTrackedFiles().size(), 0) << "sfm should be empty";
- ASSERT_EQ(sfm->GetTotalSize(), 0) << "sfm should be empty";
- sst_file_manager.reset(NewSstFileManager(env_));
- options.sst_file_manager = sst_file_manager;
- sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Reopen(options);
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- // Destroy DB and it will remove all the blob files from sst file manager and
- // blob files deletion will go through ScheduleFileDeletion.
- ASSERT_EQ(files_deleted, 0);
- ASSERT_EQ(files_scheduled_to_delete, 0);
- Close();
- ASSERT_EQ(untracked_files, files_in_db.size());
- untracked_files = 0;
- ASSERT_EQ(sfm->GetTrackedFiles().size(), 0) << "sfm should be empty";
- ASSERT_EQ(sfm->GetTotalSize(), 0) << "sfm should be empty";
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::ScheduleUnaccountedFileDeletion", [&](void* arg) {
- assert(arg);
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- ++files_scheduled_to_delete;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::OnDeleteFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- files_deleted++;
- }
- });
- ASSERT_OK(DestroyDB(dbname_, options));
- ASSERT_EQ(files_deleted, blob_files.size());
- ASSERT_EQ(files_scheduled_to_delete, blob_files.size());
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- }
- TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFilesWithGC) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.enable_blob_files = true;
- options.blob_file_size = 32; // create one blob per file
- options.disable_auto_compactions = true;
- options.enable_blob_garbage_collection = true;
- options.blob_garbage_collection_age_cutoff = 0.5;
- int files_added = 0;
- int files_deleted = 0;
- int files_moved = 0;
- int files_scheduled_to_delete = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnAddFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (file_path->find(".blob") != std::string::npos) {
- files_added++;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnDeleteFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (file_path->find(".blob") != std::string::npos) {
- files_deleted++;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::ScheduleFileDeletion", [&](void* arg) {
- assert(arg);
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (file_path->find(".blob") != std::string::npos) {
- ++files_scheduled_to_delete;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- DestroyAndReopen(options);
- Random rnd(301);
- constexpr char first_key[] = "first_key";
- constexpr char first_value[] = "first_value";
- constexpr char second_key[] = "second_key";
- constexpr char second_value[] = "second_value";
- ASSERT_OK(Put(first_key, first_value));
- ASSERT_OK(Put(second_key, second_value));
- ASSERT_OK(Flush());
- constexpr char third_key[] = "third_key";
- constexpr char third_value[] = "third_value";
- constexpr char fourth_key[] = "fourth_key";
- constexpr char fourth_value[] = "fourth_value";
- constexpr char fifth_key[] = "fifth_key";
- constexpr char fifth_value[] = "fifth_value";
- ASSERT_OK(Put(third_key, third_value));
- ASSERT_OK(Put(fourth_key, fourth_value));
- ASSERT_OK(Put(fifth_key, fifth_value));
- ASSERT_OK(Flush());
- const std::vector<uint64_t> original_blob_files = GetBlobFileNumbers();
- ASSERT_EQ(original_blob_files.size(), 5);
- ASSERT_EQ(files_added, 5);
- ASSERT_EQ(files_deleted, 0);
- ASSERT_EQ(files_scheduled_to_delete, 0);
- ASSERT_EQ(files_moved, 0);
- {
- // Verify that we are tracking all sst and blob files in dbname_
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
- ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- }
- const size_t cutoff_index = static_cast<size_t>(
- options.blob_garbage_collection_age_cutoff * original_blob_files.size());
- size_t expected_number_of_files = original_blob_files.size();
- // Note: turning off enable_blob_files before the compaction results in
- // garbage collected values getting inlined.
- ASSERT_OK(db_->SetOptions({{"enable_blob_files", "false"}}));
- expected_number_of_files -= cutoff_index;
- files_added = 0;
- constexpr Slice* begin = nullptr;
- constexpr Slice* end = nullptr;
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(Get(first_key), first_value);
- ASSERT_EQ(Get(second_key), second_value);
- ASSERT_EQ(Get(third_key), third_value);
- ASSERT_EQ(Get(fourth_key), fourth_value);
- ASSERT_EQ(Get(fifth_key), fifth_value);
- const std::vector<uint64_t> new_blob_files = GetBlobFileNumbers();
- ASSERT_EQ(new_blob_files.size(), expected_number_of_files);
- // No new file is added.
- ASSERT_EQ(files_added, 0);
- ASSERT_EQ(files_deleted, cutoff_index);
- ASSERT_EQ(files_scheduled_to_delete, cutoff_index);
- ASSERT_EQ(files_moved, 0);
- // Original blob files below the cutoff should be gone, original blob files at
- // or above the cutoff should be still there
- for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
- ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
- }
- {
- // Verify that we are tracking all sst and blob files in dbname_
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
- ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
- ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
- }
- Close();
- ASSERT_EQ(sfm->GetTrackedFiles().size(), 0) << "sfm should be empty";
- ASSERT_EQ(sfm->GetTotalSize(), 0) << "sfm should be empty";
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::ScheduleUnaccountedFileDeletion", [&](void* arg) {
- assert(arg);
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- ++files_scheduled_to_delete;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::OnDeleteFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- files_deleted++;
- }
- });
- ASSERT_OK(DestroyDB(dbname_, options));
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(files_deleted, 5);
- ASSERT_EQ(files_scheduled_to_delete, 5);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- }
- class DBSSTTestRateLimit : public DBSSTTest,
- public ::testing::WithParamInterface<bool> {
- public:
- DBSSTTestRateLimit() : DBSSTTest() {}
- ~DBSSTTestRateLimit() override = default;
- };
- TEST_P(DBSSTTestRateLimit, RateLimitedDelete) {
- Destroy(last_options_);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
- {"DBSSTTest::RateLimitedDelete:1",
- "DeleteScheduler::BackgroundEmptyTrash"},
- });
- std::vector<uint64_t> penalties;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::BackgroundEmptyTrash:Wait",
- [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
- // Turn timed wait into a simulated sleep
- uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
- uint64_t cur_time = env_->NowMicros();
- if (*abs_time_us > cur_time) {
- env_->MockSleepForMicroseconds(*abs_time_us - cur_time);
- }
- // Plus an additional short, random amount
- env_->MockSleepForMicroseconds(Random::GetTLSInstance()->Uniform(10));
- // Set wait until time to before (actual) current time to force not
- // to sleep
- *abs_time_us = Env::Default()->NowMicros();
- });
- // Disable PeriodicTaskScheduler as it also has TimedWait, which could update
- // the simulated sleep time
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::StartPeriodicTaskScheduler:DisableScheduler", [&](void* arg) {
- bool* disable_scheduler = static_cast<bool*>(arg);
- *disable_scheduler = true;
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- bool different_wal_dir = GetParam();
- Options options = CurrentOptions();
- SetTimeElapseOnlySleepOnReopen(&options);
- options.disable_auto_compactions = true;
- options.env = env_;
- options.statistics = CreateDBStatistics();
- if (different_wal_dir) {
- options.wal_dir = alternative_wal_dir_;
- }
- int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
- Status s;
- options.sst_file_manager.reset(
- NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
- ASSERT_OK(s);
- options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
- auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
- sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1);
- WriteOptions wo;
- if (!different_wal_dir) {
- wo.disableWAL = true;
- }
- Reopen(options);
- // Create 4 files in L0
- for (char v = 'a'; v <= 'd'; v++) {
- ASSERT_OK(Put("Key2", DummyString(1024, v), wo));
- ASSERT_OK(Put("Key3", DummyString(1024, v), wo));
- ASSERT_OK(Put("Key4", DummyString(1024, v), wo));
- ASSERT_OK(Put("Key1", DummyString(1024, v), wo));
- ASSERT_OK(Put("Key4", DummyString(1024, v), wo));
- ASSERT_OK(Flush());
- }
- // We created 4 sst files in L0
- ASSERT_EQ("4", FilesPerLevel(0));
- std::vector<LiveFileMetaData> metadata;
- db_->GetLiveFilesMetaData(&metadata);
- // Compaction will move the 4 files in L0 to trash and create 1 L1 file
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ("0,1", FilesPerLevel(0));
- uint64_t delete_start_time = env_->NowMicros();
- // Hold BackgroundEmptyTrash
- TEST_SYNC_POINT("DBSSTTest::RateLimitedDelete:1");
- sfm->WaitForEmptyTrash();
- uint64_t time_spent_deleting = env_->NowMicros() - delete_start_time;
- uint64_t total_files_size = 0;
- uint64_t expected_penlty = 0;
- ASSERT_EQ(penalties.size(), metadata.size());
- for (size_t i = 0; i < metadata.size(); i++) {
- total_files_size += metadata[i].size;
- expected_penlty = ((total_files_size * 1000000) / rate_bytes_per_sec);
- ASSERT_EQ(expected_penlty, penalties[i]);
- }
- ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
- ASSERT_LT(time_spent_deleting, expected_penlty * 1.1);
- ASSERT_EQ(4, options.statistics->getAndResetTickerCount(FILES_MARKED_TRASH));
- ASSERT_EQ(
- 0, options.statistics->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- INSTANTIATE_TEST_CASE_P(RateLimitedDelete, DBSSTTestRateLimit,
- ::testing::Bool());
- TEST_F(DBSSTTest, RateLimitedWALDelete) {
- Destroy(last_options_);
- std::vector<uint64_t> penalties;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::BackgroundEmptyTrash:Wait",
- [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
- Options options = CurrentOptions();
- options.disable_auto_compactions = true;
- options.compression = kNoCompression;
- options.env = env_;
- int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
- Status s;
- options.sst_file_manager.reset(
- NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
- ASSERT_OK(s);
- options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
- auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
- sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
- SetTimeElapseOnlySleepOnReopen(&options);
- ASSERT_OK(TryReopen(options));
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- // Create 4 files in L0
- for (char v = 'a'; v <= 'd'; v++) {
- ASSERT_OK(Put("Key2", DummyString(1024, v)));
- ASSERT_OK(Put("Key3", DummyString(1024, v)));
- ASSERT_OK(Put("Key4", DummyString(1024, v)));
- ASSERT_OK(Put("Key1", DummyString(1024, v)));
- ASSERT_OK(Put("Key4", DummyString(1024, v)));
- ASSERT_OK(Flush());
- }
- // We created 4 sst files in L0
- ASSERT_EQ("4", FilesPerLevel(0));
- // Compaction will move the 4 files in L0 to trash and create 1 L1 file.
- // Use kForceOptimized to not rewrite the new L1 file.
- CompactRangeOptions cro;
- cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
- ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ("0,1", FilesPerLevel(0));
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(penalties.size(), 8);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- class DBWALTestWithParam
- : public DBTestBase,
- public testing::WithParamInterface<std::tuple<std::string, bool>> {
- public:
- explicit DBWALTestWithParam()
- : DBTestBase("db_wal_test_with_params", /*env_do_fsync=*/true) {
- wal_dir_ = std::get<0>(GetParam());
- wal_dir_same_as_dbname_ = std::get<1>(GetParam());
- }
- std::string wal_dir_;
- bool wal_dir_same_as_dbname_;
- };
- TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
- class MyEnv : public EnvWrapper {
- public:
- MyEnv(Env* t) : EnvWrapper(t), fake_log_delete(false) {}
- const char* Name() const override { return "MyEnv"; }
- Status DeleteFile(const std::string& fname) override {
- if (fname.find(".log.trash") != std::string::npos && fake_log_delete) {
- return Status::OK();
- }
- return target()->DeleteFile(fname);
- }
- void set_fake_log_delete(bool fake) { fake_log_delete = fake; }
- private:
- bool fake_log_delete;
- };
- std::unique_ptr<MyEnv> env(new MyEnv(env_));
- Destroy(last_options_);
- env->set_fake_log_delete(true);
- Options options = CurrentOptions();
- options.disable_auto_compactions = true;
- options.compression = kNoCompression;
- options.env = env.get();
- options.wal_dir = dbname_ + wal_dir_;
- int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
- Status s;
- options.sst_file_manager.reset(
- NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
- ASSERT_OK(s);
- options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
- auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
- sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
- Reopen(options);
- // Create 4 files in L0
- for (char v = 'a'; v <= 'd'; v++) {
- if (v == 'c') {
- // Maximize the chance that the last log file will be preserved in trash
- // before restarting the DB. (Enable slow deletion but at a very slow
- // deletion rate)
- // We have to set this on the 2nd to last file for it to delay deletion
- // on the last file. (Quirk of DeleteScheduler::BackgroundEmptyTrash())
- options.sst_file_manager->SetDeleteRateBytesPerSecond(1);
- }
- ASSERT_OK(Put("Key2", DummyString(1024, v)));
- ASSERT_OK(Put("Key3", DummyString(1024, v)));
- ASSERT_OK(Put("Key4", DummyString(1024, v)));
- ASSERT_OK(Put("Key1", DummyString(1024, v)));
- ASSERT_OK(Put("Key4", DummyString(1024, v)));
- ASSERT_OK(Flush());
- }
- // We created 4 sst files in L0
- ASSERT_EQ("4", FilesPerLevel(0));
- Close();
- options.sst_file_manager.reset();
- std::vector<std::string> filenames;
- int trash_log_count = 0;
- if (!wal_dir_same_as_dbname_) {
- // Forcibly create some trash log files
- std::unique_ptr<WritableFile> result;
- ASSERT_OK(env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result,
- EnvOptions()));
- result.reset();
- }
- ASSERT_OK(env->GetChildren(options.wal_dir, &filenames));
- for (const std::string& fname : filenames) {
- if (fname.find(".log.trash") != std::string::npos) {
- trash_log_count++;
- }
- }
- ASSERT_GE(trash_log_count, 1);
- env->set_fake_log_delete(false);
- Reopen(options);
- filenames.clear();
- trash_log_count = 0;
- ASSERT_OK(env->GetChildren(options.wal_dir, &filenames));
- for (const std::string& fname : filenames) {
- if (fname.find(".log.trash") != std::string::npos) {
- trash_log_count++;
- }
- }
- ASSERT_EQ(trash_log_count, 0);
- Close();
- }
- INSTANTIATE_TEST_CASE_P(DBWALTestWithParam, DBWALTestWithParam,
- ::testing::Values(std::make_tuple("", true),
- std::make_tuple("_wal_dir", false)));
- // Test param: max_trash_db_ratio for DeleteScheduler
- class DBObsoleteFileDeletionOnOpenTest
- : public DBSSTTest,
- public ::testing::WithParamInterface<double> {
- public:
- explicit DBObsoleteFileDeletionOnOpenTest()
- : DBSSTTest("db_sst_deletion_on_open_test") {}
- };
- TEST_P(DBObsoleteFileDeletionOnOpenTest, Basic) {
- Options options = CurrentOptions();
- options.sst_file_manager.reset(
- NewSstFileManager(env_, nullptr, "", 1024 * 1024 /* 1 MB/sec */));
- auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
- sfm->SetDeleteRateBytesPerSecond(1024 * 1024);
- double max_trash_db_ratio = GetParam();
- sfm->delete_scheduler()->SetMaxTrashDBRatio(max_trash_db_ratio);
- int bg_delete_file = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::DeleteTrashFile:DeleteFile",
- [&](void* /*arg*/) { bg_delete_file++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Destroy(last_options_);
- // Add some trash files to the db directory so the DB can clean them up
- ASSERT_OK(env_->CreateDirIfMissing(dbname_));
- ASSERT_OK(
- WriteStringToFile(env_, "abc", dbname_ + "/" + "001.sst.trash", false));
- ASSERT_OK(
- WriteStringToFile(env_, "abc", dbname_ + "/" + "002.sst.trash", false));
- ASSERT_OK(
- WriteStringToFile(env_, "abc", dbname_ + "/" + "003.sst.trash", false));
- // Manually add an obsolete sst file. Obsolete SST files are discovered and
- // deleted upon recovery.
- uint64_t sst_file_number = 100;
- const std::string kObsoleteSstFileOne =
- MakeTableFileName(dbname_, sst_file_number);
- ASSERT_OK(WriteStringToFile(env_, "abc", kObsoleteSstFileOne, false));
- // The slow deletion on recovery had a bug before where a file's size is not
- // first tracked in `total_size_` in SstFileManager before passed to
- // DeleteScheduler. The first obsolete file is still slow deleted because
- // 0 (total_trash_size_) > 0 (total_size_) * 1000 (max_trash_db_ratio)
- // is always false.
- // Here we explicitly create a second obsolete file to verify this bug's fix
- const std::string kObsoleteSstFileTwo =
- MakeTableFileName(dbname_, sst_file_number - 1);
- ASSERT_OK(WriteStringToFile(env_, "abc", kObsoleteSstFileTwo, false));
- // Reopen the DB and verify that it deletes existing trash files and obsolete
- // SST files with rate limiting.
- Reopen(options);
- sfm->WaitForEmptyTrash();
- ASSERT_NOK(env_->FileExists(dbname_ + "/" + "001.sst.trash"));
- ASSERT_NOK(env_->FileExists(dbname_ + "/" + "002.sst.trash"));
- ASSERT_NOK(env_->FileExists(dbname_ + "/" + "003.sst.trash"));
- ASSERT_NOK(env_->FileExists(kObsoleteSstFileOne));
- ASSERT_NOK(env_->FileExists(kObsoleteSstFileTwo));
- // The files in the DB's directory are all either trash or obsolete sst files.
- // So the trash/db ratio is 1. A ratio equal to or higher than 1 should
- // schedule all files' deletion in background. A ratio lower than 1 may
- // send some files to be deleted immediately.
- if (max_trash_db_ratio < 1) {
- ASSERT_LE(bg_delete_file, 5);
- } else {
- ASSERT_EQ(bg_delete_file, 5);
- }
- ASSERT_EQ(sfm->GetTotalSize(), 0);
- ASSERT_EQ(sfm->delete_scheduler()->GetTotalTrashSize(), 0);
- }
- INSTANTIATE_TEST_CASE_P(DBObsoleteFileDeletionOnOpenTest,
- DBObsoleteFileDeletionOnOpenTest,
- ::testing::Values(0, 0.5, 1, 1.2));
- // Create a DB with 2 db_paths, and generate multiple files in the 2
- // db_paths using CompactRangeOptions, make sure that files that were
- // deleted from first db_path were deleted using DeleteScheduler and
- // files in the second path were not.
- TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
- std::atomic<int> bg_delete_file(0);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::DeleteTrashFile:DeleteFile",
- [&](void* /*arg*/) { bg_delete_file++; });
- // The deletion scheduler sometimes skips marking file as trash according to
- // a heuristic. In that case the deletion will go through the below SyncPoint.
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::DeleteFile", [&](void* /*arg*/) { bg_delete_file++; });
- Options options = CurrentOptions();
- options.disable_auto_compactions = true;
- options.db_paths.emplace_back(dbname_, 1024 * 100);
- options.db_paths.emplace_back(dbname_ + "_2", 1024 * 100);
- options.env = env_;
- int64_t rate_bytes_per_sec = 1024 * 1024; // 1 Mb / Sec
- Status s;
- options.sst_file_manager.reset(
- NewSstFileManager(env_, nullptr, "", rate_bytes_per_sec, false, &s,
- /* max_trash_db_ratio= */ 1.1));
- ASSERT_OK(s);
- auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
- DestroyAndReopen(options);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- WriteOptions wo;
- wo.disableWAL = true;
- // Create 4 files in L0
- for (int i = 0; i < 4; i++) {
- ASSERT_OK(Put("Key" + std::to_string(i), DummyString(1024, 'A'), wo));
- ASSERT_OK(Flush());
- }
- // We created 4 sst files in L0
- ASSERT_EQ("4", FilesPerLevel(0));
- // Compaction will delete files from L0 in first db path and generate a new
- // file in L1 in second db path
- CompactRangeOptions compact_options;
- compact_options.target_path_id = 1;
- Slice begin("Key0");
- Slice end("Key3");
- ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
- ASSERT_EQ("0,1", FilesPerLevel(0));
- // Create 4 files in L0
- for (int i = 4; i < 8; i++) {
- ASSERT_OK(Put("Key" + std::to_string(i), DummyString(1024, 'B'), wo));
- ASSERT_OK(Flush());
- }
- ASSERT_EQ("4,1", FilesPerLevel(0));
- // Compaction will delete files from L0 in first db path and generate a new
- // file in L1 in second db path
- begin = "Key4";
- end = "Key7";
- ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
- ASSERT_EQ("0,2", FilesPerLevel(0));
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(bg_delete_file, 8);
- // Compaction will delete both files and regenerate a file in L1 in second
- // db path. The deleted files should still be cleaned up via delete scheduler.
- compact_options.bottommost_level_compaction =
- BottommostLevelCompaction::kForceOptimized;
- ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
- ASSERT_EQ("0,1", FilesPerLevel(0));
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(bg_delete_file, 10);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
- int bg_delete_file = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::DeleteTrashFile:DeleteFile",
- [&](void* /*arg*/) { bg_delete_file++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Status s;
- Options options = CurrentOptions();
- options.disable_auto_compactions = true;
- options.env = env_;
- options.sst_file_manager.reset(
- NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
- ASSERT_OK(s);
- DestroyAndReopen(options);
- // Create 4 files in L0
- for (int i = 0; i < 4; i++) {
- ASSERT_OK(Put("Key" + std::to_string(i), DummyString(1024, 'A')));
- ASSERT_OK(Flush());
- }
- // We created 4 sst files in L0
- ASSERT_EQ("4", FilesPerLevel(0));
- // Close DB and destroy it using DeleteScheduler
- Close();
- int num_sst_files = 0;
- int num_wal_files = 0;
- std::vector<std::string> db_files;
- ASSERT_OK(env_->GetChildren(dbname_, &db_files));
- for (const std::string& f : db_files) {
- if (f.substr(f.find_last_of('.') + 1) == "sst") {
- num_sst_files++;
- } else if (f.substr(f.find_last_of('.') + 1) == "log") {
- num_wal_files++;
- }
- }
- ASSERT_GT(num_sst_files, 0);
- ASSERT_GT(num_wal_files, 0);
- auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
- sfm->SetDeleteRateBytesPerSecond(1024 * 1024);
- // Set an extra high trash ratio to prevent immediate/non-rate limited
- // deletions
- sfm->delete_scheduler()->SetMaxTrashDBRatio(1000.0);
- ASSERT_OK(DestroyDB(dbname_, options));
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(bg_delete_file, num_sst_files + num_wal_files);
- }
- TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.disable_auto_compactions = true;
- DestroyAndReopen(options);
- Random rnd(301);
- // Generate a file containing 100 keys.
- for (int i = 0; i < 100; i++) {
- ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
- }
- ASSERT_OK(Flush());
- uint64_t first_file_size = 0;
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &first_file_size));
- ASSERT_EQ(sfm->GetTotalSize(), first_file_size);
- // Set the maximum allowed space usage to the current total size
- sfm->SetMaxAllowedSpaceUsage(first_file_size + 1);
- ASSERT_OK(Put("key1", "val1"));
- // This flush will cause bg_error_ and will fail
- ASSERT_NOK(Flush());
- }
- TEST_F(DBSSTTest, DBWithMaxSpaceAllowedWithBlobFiles) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.disable_auto_compactions = true;
- options.enable_blob_files = true;
- DestroyAndReopen(options);
- Random rnd(301);
- // Generate a file containing keys.
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
- }
- ASSERT_OK(Flush());
- uint64_t files_size = 0;
- uint64_t total_files_size = 0;
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db, &files_size));
- // Make sure blob files are considered by SSTFileManage in size limits.
- ASSERT_GT(files_size, 0);
- total_files_size = files_size;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &files_size));
- total_files_size += files_size;
- ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
- // Set the maximum allowed space usage to the current total size.
- sfm->SetMaxAllowedSpaceUsage(total_files_size + 1);
- bool max_allowed_space_reached = false;
- bool delete_blob_file = false;
- // Sync point called after blob file is closed and max allowed space is
- // checked.
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "BlobFileCompletionCallback::CallBack::MaxAllowedSpaceReached",
- [&](void* /*arg*/) { max_allowed_space_reached = true; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "BuildTable::AfterDeleteFile",
- [&](void* /*arg*/) { delete_blob_file = true; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
- {
- "BuildTable::AfterDeleteFile",
- "DBSSTTest::DBWithMaxSpaceAllowedWithBlobFiles:1",
- },
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_OK(Put("key1", "val1"));
- // This flush will fail
- ASSERT_NOK(Flush());
- ASSERT_TRUE(max_allowed_space_reached);
- TEST_SYNC_POINT("DBSSTTest::DBWithMaxSpaceAllowedWithBlobFiles:1");
- ASSERT_TRUE(delete_blob_file);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- TEST_F(DBSSTTest, CancellingCompactionsWorks) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.level0_file_num_compaction_trigger = 2;
- options.statistics = CreateDBStatistics();
- DestroyAndReopen(options);
- int completed_compactions = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* /*arg*/) {
- sfm->SetMaxAllowedSpaceUsage(0);
- ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::BackgroundCompaction:NonTrivial:AfterRun",
- [&](void* /*arg*/) { completed_compactions++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Random rnd(301);
- // Generate a file containing 10 keys.
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
- }
- ASSERT_OK(Flush());
- uint64_t total_file_size = 0;
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &total_file_size));
- // Set the maximum allowed space usage to the current total size
- sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
- // Generate another file to trigger compaction.
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
- }
- ASSERT_OK(Flush());
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- // Because we set a callback in CancelledCompaction, we actually
- // let the compaction run
- ASSERT_GT(completed_compactions, 0);
- ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
- // Make sure the stat is bumped
- ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
- COMPACTION_CANCELLED),
- 0);
- ASSERT_EQ(0,
- dbfull()->immutable_db_options().statistics.get()->getTickerCount(
- FILES_MARKED_TRASH));
- ASSERT_EQ(4,
- dbfull()->immutable_db_options().statistics.get()->getTickerCount(
- FILES_DELETED_IMMEDIATELY));
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.statistics = CreateDBStatistics();
- FlushedFileCollector* collector = new FlushedFileCollector();
- options.listeners.emplace_back(collector);
- DestroyAndReopen(options);
- Random rnd(301);
- // Generate a file containing 10 keys.
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
- }
- ASSERT_OK(Flush());
- uint64_t total_file_size = 0;
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &total_file_size));
- // Set the maximum allowed space usage to the current total size
- sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
- // Generate another file to trigger compaction.
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
- }
- ASSERT_OK(Flush());
- // OK, now trigger a manual compaction
- ASSERT_TRUE(dbfull()
- ->CompactRange(CompactRangeOptions(), nullptr, nullptr)
- .IsCompactionTooLarge());
- // Wait for manual compaction to get scheduled and finish
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
- // Make sure the stat is bumped
- ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
- COMPACTION_CANCELLED),
- 1);
- // Now make sure CompactFiles also gets cancelled
- auto l0_files = collector->GetFlushedFiles();
- ASSERT_TRUE(
- dbfull()
- ->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0)
- .IsCompactionTooLarge());
- // Wait for manual compaction to get scheduled and finish
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
- COMPACTION_CANCELLED),
- 2);
- ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
- // Now let the flush through and make sure GetCompactionsReservedSize
- // returns to normal
- sfm->SetMaxAllowedSpaceUsage(0);
- int completed_compactions = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "CompactFilesImpl:End", [&](void* /*arg*/) { completed_compactions++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_OK(dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(),
- l0_files, 0));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
- ASSERT_GT(completed_compactions, 0);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
- // This test will set a maximum allowed space for the DB, then it will
- // keep filling the DB until the limit is reached and bg_error_ is set.
- // When bg_error_ is set we will verify that the DB size is greater
- // than the limit.
- std::vector<int> max_space_limits_mbs = {1, 10};
- std::atomic<bool> bg_error_set(false);
- std::atomic<int> reached_max_space_on_flush(0);
- std::atomic<int> reached_max_space_on_compaction(0);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached",
- [&](void* arg) {
- Status* bg_error = static_cast<Status*>(arg);
- bg_error_set = true;
- reached_max_space_on_flush++;
- // clear error to ensure compaction callback is called
- *bg_error = Status::OK();
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* arg) {
- bool* enough_room = static_cast<bool*>(arg);
- *enough_room = true;
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "CompactionJob::FinishCompactionOutputFile:MaxAllowedSpaceReached",
- [&](void* /*arg*/) {
- bg_error_set = true;
- reached_max_space_on_compaction++;
- });
- for (auto limit_mb : max_space_limits_mbs) {
- bg_error_set = false;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.write_buffer_size = 1024 * 512; // 512 Kb
- DestroyAndReopen(options);
- Random rnd(301);
- sfm->SetMaxAllowedSpaceUsage(limit_mb * 1024 * 1024);
- // It is easy to detect if the test is stuck in a loop. No need for
- // complex termination logic.
- while (true) {
- auto s = Put(rnd.RandomString(10), rnd.RandomString(50));
- if (!s.ok()) {
- break;
- }
- }
- ASSERT_TRUE(bg_error_set);
- uint64_t total_sst_files_size = 0;
- std::unordered_map<std::string, uint64_t> files_in_db;
- ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &total_sst_files_size));
- ASSERT_GE(total_sst_files_size, limit_mb * 1024 * 1024);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- ASSERT_GT(reached_max_space_on_flush, 0);
- ASSERT_GT(reached_max_space_on_compaction, 0);
- }
- TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
- // Open DB with infinite max open files
- // - First iteration use 1 thread to open files
- // - Second iteration use 5 threads to open files
- for (int iter = 0; iter < 2; iter++) {
- Options options;
- options.create_if_missing = true;
- options.write_buffer_size = 100000;
- options.disable_auto_compactions = true;
- options.max_open_files = -1;
- if (iter == 0) {
- options.max_file_opening_threads = 1;
- } else {
- options.max_file_opening_threads = 5;
- }
- options = CurrentOptions(options);
- DestroyAndReopen(options);
- // Create 12 Files in L0 (then move then to L2)
- for (int i = 0; i < 12; i++) {
- std::string k = "L2_" + Key(i);
- ASSERT_OK(Put(k, k + std::string(1000, 'a')));
- ASSERT_OK(Flush());
- }
- CompactRangeOptions compact_options;
- compact_options.change_level = true;
- compact_options.target_level = 2;
- ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
- // Create 12 Files in L0
- for (int i = 0; i < 12; i++) {
- std::string k = "L0_" + Key(i);
- ASSERT_OK(Put(k, k + std::string(1000, 'a')));
- ASSERT_OK(Flush());
- }
- Close();
- // Reopening the DB will load all existing files
- Reopen(options);
- ASSERT_EQ("12,0,12", FilesPerLevel(0));
- std::vector<std::vector<FileMetaData>> files;
- dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
- for (const auto& level : files) {
- for (const auto& file : level) {
- ASSERT_TRUE(file.table_reader_handle != nullptr);
- }
- }
- for (int i = 0; i < 12; i++) {
- ASSERT_EQ(Get("L0_" + Key(i)), "L0_" + Key(i) + std::string(1000, 'a'));
- ASSERT_EQ(Get("L2_" + Key(i)), "L2_" + Key(i) + std::string(1000, 'a'));
- }
- }
- }
- TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFilesSubjectToMemoryLimit) {
- for (CacheEntryRoleOptions::Decision charge_table_reader :
- {CacheEntryRoleOptions::Decision::kEnabled,
- CacheEntryRoleOptions::Decision::kDisabled}) {
- // Open DB with infinite max open files
- // - First iteration use 1 thread to open files
- // - Second iteration use 5 threads to open files
- for (int iter = 0; iter < 2; iter++) {
- Options options;
- options.create_if_missing = true;
- options.write_buffer_size = 100000;
- options.disable_auto_compactions = true;
- options.max_open_files = -1;
- BlockBasedTableOptions table_options;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- if (iter == 0) {
- options.max_file_opening_threads = 1;
- } else {
- options.max_file_opening_threads = 5;
- }
- DestroyAndReopen(options);
- // Create 5 Files in L0 (then move then to L2)
- for (int i = 0; i < 5; i++) {
- std::string k = "L2_" + Key(i);
- ASSERT_OK(Put(k, k + std::string(1000, 'a')));
- ASSERT_OK(Flush()) << i;
- }
- CompactRangeOptions compact_options;
- compact_options.change_level = true;
- compact_options.target_level = 2;
- ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
- // Create 5 Files in L0
- for (int i = 0; i < 5; i++) {
- std::string k = "L0_" + Key(i);
- ASSERT_OK(Put(k, k + std::string(1000, 'a')));
- ASSERT_OK(Flush());
- }
- Close();
- table_options.cache_usage_options.options_overrides.insert(
- {CacheEntryRole::kBlockBasedTableReader,
- {/*.charged = */ charge_table_reader}});
- table_options.block_cache =
- NewLRUCache(1024 /* capacity */, 0 /* num_shard_bits */,
- true /* strict_capacity_limit */);
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- // Reopening the DB will try to load all existing files, conditionally
- // subject to memory limit
- Status s = TryReopen(options);
- if (charge_table_reader == CacheEntryRoleOptions::Decision::kEnabled) {
- EXPECT_TRUE(s.IsMemoryLimit());
- EXPECT_TRUE(s.ToString().find(
- kCacheEntryRoleToCamelString[static_cast<std::uint32_t>(
- CacheEntryRole::kBlockBasedTableReader)]) !=
- std::string::npos);
- EXPECT_TRUE(s.ToString().find("memory limit based on cache capacity") !=
- std::string::npos);
- } else {
- EXPECT_TRUE(s.ok());
- ASSERT_EQ("5,0,5", FilesPerLevel(0));
- }
- }
- }
- }
- TEST_F(DBSSTTest, GetTotalSstFilesSize) {
- // FIXME: L0 file and L1+ file also differ in size of `oldest_key_time`.
- // L0 file has non-zero `oldest_key_time` while L1+ files have 0.
- // The test passes since L1+ file uses current time instead of 0
- // as oldest_ancestor_time.
- //
- // We don't propagate oldest-key-time table property on compaction and
- // just write 0 as default value. This affect the exact table size, since
- // we encode table properties as varint64. Force time to be 0 to work around
- // it. Should remove the workaround after we propagate the property on
- // compaction.
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "FlushJob::WriteLevel0Table:oldest_ancester_time", [&](void* arg) {
- uint64_t* current_time = static_cast<uint64_t*>(arg);
- *current_time = 0;
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Options options = CurrentOptions();
- options.disable_auto_compactions = true;
- options.compression = kNoCompression;
- DestroyAndReopen(options);
- // Generate 5 files in L0
- for (int i = 0; i < 5; i++) {
- for (int j = 0; j < 10; j++) {
- std::string val = "val_file_" + std::to_string(i);
- ASSERT_OK(Put(Key(j), val));
- }
- ASSERT_OK(Flush());
- }
- ASSERT_EQ("5", FilesPerLevel(0));
- std::vector<LiveFileMetaData> live_files_meta;
- dbfull()->GetLiveFilesMetaData(&live_files_meta);
- ASSERT_EQ(live_files_meta.size(), 5);
- uint64_t single_file_size = live_files_meta[0].size;
- uint64_t live_sst_files_size = 0;
- uint64_t total_sst_files_size = 0;
- for (const auto& file_meta : live_files_meta) {
- live_sst_files_size += file_meta.size;
- }
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 5
- // Total SST files = 5
- ASSERT_EQ(live_sst_files_size, 5 * single_file_size);
- ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
- // hold current version
- std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
- ASSERT_OK(iter1->status());
- // Compact 5 files into 1 file in L0
- ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_EQ("0,1", FilesPerLevel(0));
- live_files_meta.clear();
- dbfull()->GetLiveFilesMetaData(&live_files_meta);
- ASSERT_EQ(live_files_meta.size(), 1);
- live_sst_files_size = 0;
- total_sst_files_size = 0;
- for (const auto& file_meta : live_files_meta) {
- live_sst_files_size += file_meta.size;
- }
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 1 (compacted file)
- // Total SST files = 6 (5 original files + compacted file)
- ASSERT_EQ(live_sst_files_size, 1 * single_file_size);
- ASSERT_EQ(total_sst_files_size, 6 * single_file_size);
- // hold current version
- std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
- ASSERT_OK(iter2->status());
- // Delete all keys and compact, this will delete all live files
- for (int i = 0; i < 10; i++) {
- ASSERT_OK(Delete(Key(i)));
- }
- ASSERT_OK(Flush());
- ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_EQ("", FilesPerLevel(0));
- live_files_meta.clear();
- dbfull()->GetLiveFilesMetaData(&live_files_meta);
- ASSERT_EQ(live_files_meta.size(), 0);
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 0
- // Total SST files = 6 (5 original files + compacted file)
- ASSERT_EQ(total_sst_files_size, 6 * single_file_size);
- ASSERT_OK(iter1->status());
- iter1.reset();
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 0
- // Total SST files = 1 (compacted file)
- ASSERT_EQ(total_sst_files_size, 1 * single_file_size);
- ASSERT_OK(iter2->status());
- iter2.reset();
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 0
- // Total SST files = 0
- ASSERT_EQ(total_sst_files_size, 0);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- }
- TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
- Options options = CurrentOptions();
- options.disable_auto_compactions = true;
- options.compression = kNoCompression;
- DestroyAndReopen(options);
- // Generate 5 files in L0
- for (int i = 0; i < 5; i++) {
- ASSERT_OK(Put(Key(i), "val"));
- ASSERT_OK(Flush());
- }
- ASSERT_EQ("5", FilesPerLevel(0));
- std::vector<LiveFileMetaData> live_files_meta;
- dbfull()->GetLiveFilesMetaData(&live_files_meta);
- ASSERT_EQ(live_files_meta.size(), 5);
- uint64_t single_file_size = live_files_meta[0].size;
- uint64_t live_sst_files_size = 0;
- uint64_t total_sst_files_size = 0;
- for (const auto& file_meta : live_files_meta) {
- live_sst_files_size += file_meta.size;
- }
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 5
- // Total SST files = 5
- ASSERT_EQ(live_sst_files_size, 5 * single_file_size);
- ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
- // hold current version
- std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
- ASSERT_OK(iter1->status());
- // Compaction will do trivial move from L0 to L1
- ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_EQ("0,5", FilesPerLevel(0));
- live_files_meta.clear();
- dbfull()->GetLiveFilesMetaData(&live_files_meta);
- ASSERT_EQ(live_files_meta.size(), 5);
- live_sst_files_size = 0;
- total_sst_files_size = 0;
- for (const auto& file_meta : live_files_meta) {
- live_sst_files_size += file_meta.size;
- }
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 5
- // Total SST files = 5 (used in 2 version)
- ASSERT_EQ(live_sst_files_size, 5 * single_file_size);
- ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
- // hold current version
- std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
- ASSERT_OK(iter2->status());
- // Delete all keys and compact, this will delete all live files
- for (int i = 0; i < 5; i++) {
- ASSERT_OK(Delete(Key(i)));
- }
- ASSERT_OK(Flush());
- ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- ASSERT_EQ("", FilesPerLevel(0));
- live_files_meta.clear();
- dbfull()->GetLiveFilesMetaData(&live_files_meta);
- ASSERT_EQ(live_files_meta.size(), 0);
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 0
- // Total SST files = 5 (used in 2 version)
- ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
- ASSERT_OK(iter1->status());
- iter1.reset();
- ASSERT_OK(iter2->status());
- iter2.reset();
- ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
- &total_sst_files_size));
- // Live SST files = 0
- // Total SST files = 0
- ASSERT_EQ(total_sst_files_size, 0);
- }
- // This test if blob files are recorded by SST File Manager when Compaction job
- // creates/delete them and in case of AtomicFlush.
- TEST_F(DBSSTTest, DBWithSFMForBlobFilesAtomicFlush) {
- std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
- auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
- Options options = CurrentOptions();
- options.sst_file_manager = sst_file_manager;
- options.enable_blob_files = true;
- options.min_blob_size = 0;
- options.disable_auto_compactions = true;
- options.enable_blob_garbage_collection = true;
- options.blob_garbage_collection_age_cutoff = 0.5;
- options.atomic_flush = true;
- int files_added = 0;
- int files_deleted = 0;
- int files_scheduled_to_delete = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnAddFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- files_added++;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::OnDeleteFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- files_deleted++;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::ScheduleFileDeletion", [&](void* arg) {
- assert(arg);
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- ++files_scheduled_to_delete;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- DestroyAndReopen(options);
- Random rnd(301);
- ASSERT_OK(Put("key_1", "value_1"));
- ASSERT_OK(Put("key_2", "value_2"));
- ASSERT_OK(Put("key_3", "value_3"));
- ASSERT_OK(Put("key_4", "value_4"));
- ASSERT_OK(Flush());
- // Overwrite will create the garbage data.
- ASSERT_OK(Put("key_3", "new_value_3"));
- ASSERT_OK(Put("key_4", "new_value_4"));
- ASSERT_OK(Flush());
- ASSERT_OK(Put("Key5", "blob_value5"));
- ASSERT_OK(Put("Key6", "blob_value6"));
- ASSERT_OK(Flush());
- ASSERT_EQ(files_added, 3);
- ASSERT_EQ(files_deleted, 0);
- ASSERT_EQ(files_scheduled_to_delete, 0);
- files_added = 0;
- constexpr Slice* begin = nullptr;
- constexpr Slice* end = nullptr;
- // Compaction job will create a new file and delete the older files.
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
- ASSERT_EQ(files_added, 1);
- ASSERT_EQ(files_scheduled_to_delete, 1);
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(files_deleted, 1);
- Close();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "SstFileManagerImpl::ScheduleUnaccountedFileDeletion", [&](void* arg) {
- assert(arg);
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- ++files_scheduled_to_delete;
- }
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DeleteScheduler::OnDeleteFile", [&](void* arg) {
- const std::string* const file_path =
- static_cast<const std::string*>(arg);
- if (EndsWith(*file_path, ".blob")) {
- files_deleted++;
- }
- });
- ASSERT_OK(DestroyDB(dbname_, options));
- ASSERT_EQ(files_scheduled_to_delete, 4);
- sfm->WaitForEmptyTrash();
- ASSERT_EQ(files_deleted, 4);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- }
- TEST_F(DBSSTTest, SstGetFileSizeFails) {
- // Build an SST file
- ASSERT_OK(Put("x", "zaphod"));
- ASSERT_OK(Flush());
- std::vector<LiveFileMetaData> metadata;
- db_->GetLiveFilesMetaData(&metadata);
- ASSERT_EQ(1U, metadata.size());
- std::string filename = dbname_ + metadata[0].name;
- // Prepare for fault injection
- std::shared_ptr<FaultInjectionTestFS> fault_fs =
- std::make_shared<FaultInjectionTestFS>(
- CurrentOptions().env->GetFileSystem());
- std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
- Options options = CurrentOptions();
- options.env = fault_fs_env.get();
- options.paranoid_checks = false; // don't check file sizes on open
- for (int i = 0; i < 4; i++) {
- SCOPED_TRACE("Iteration = " + std::to_string(i));
- fault_fs->SetFailRandomAccessGetFileSizeSst(false);
- fault_fs->SetFailFilesystemGetFileSizeSst(false);
- Close();
- if (i == 1) {
- // Just FSRandomAccessFile::GetFileSize fails, which should be worked
- // around
- fault_fs->SetFailRandomAccessGetFileSizeSst(true);
- } else if (i == 2) {
- // FileSystem::GetFileSize fails, which should be worked around if
- // FSRandomAccessFile::GetFileSize is supported
- fault_fs->SetFailFilesystemGetFileSizeSst(true);
- } else if (i == 3) {
- // Both GetFileSize APIs fail with an IOError
- fault_fs->SetFailRandomAccessGetFileSizeSst(true);
- fault_fs->SetFailFilesystemGetFileSizeSst(true);
- }
- ASSERT_OK(TryReopen(options));
- std::string value;
- Status get_status = db_->Get({}, "x", &value);
- if (i < 2) {
- ASSERT_OK(get_status);
- } else if (i == 2) {
- if (encrypted_env_) {
- // Can't recover because RandomAccessFile::GetFileSize is not supported
- // on EncryptedEnv
- // Fail with propagated IOError. (Not Corruption nor NotSupported!)
- ASSERT_EQ(get_status.code(), Status::Code::kIOError);
- ASSERT_STREQ(get_status.getState(), "FileSystem::GetFileSize failed");
- } else {
- // Never sees the FileSystem::GetFileSize failure
- ASSERT_OK(get_status);
- }
- } else {
- ASSERT_EQ(i, 3);
- // Fail with propagated IOError. (Not Corruption nor NotSupported!)
- ASSERT_EQ(get_status.code(), Status::Code::kIOError);
- ASSERT_STREQ(get_status.getState(), "FileSystem::GetFileSize failed");
- }
- }
- Close();
- }
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- RegisterCustomObjects(argc, argv);
- return RUN_ALL_TESTS();
- }
|