do not call DeleteFile for not-created sst files (#9920)
Summary: When a memtable is flushed and the flush would lead to a 0 byte .sst file being created, RocksDB does not write out the empty .sst file to disk. However it still calls Env::DeleteFile() on the file as part of some cleanup procedure at the end of BuildTable(). Because the to-be-deleted file does not exist, this requires implementors of the DeleteFile() API to check if the file exists on their own code, or otherwise risk running into PathNotFound errors when DeleteFile is invoked on non-existing files. This PR fixes the situation so that when no .sst file is created, Deletefile will not be called either. TableFileCreationStarted() will still be called as before. Pull Request resolved: https://github.com/facebook/rocksdb/pull/9920 Reviewed By: ajkr Differential Revision: D36107102 Pulled By: riversand963 fbshipit-source-id: 15881ba3fa3192dd448f906280a1cfc7a68a114a
This commit is contained in:
parent
de537dcaf1
commit
95663ff763
@ -115,6 +115,7 @@ Status BuildTable(
|
|||||||
assert(fs);
|
assert(fs);
|
||||||
|
|
||||||
TableProperties tp;
|
TableProperties tp;
|
||||||
|
bool table_file_created = false;
|
||||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||||
std::unique_ptr<CompactionFilter> compaction_filter;
|
std::unique_ptr<CompactionFilter> compaction_filter;
|
||||||
if (ioptions.compaction_filter_factory != nullptr &&
|
if (ioptions.compaction_filter_factory != nullptr &&
|
||||||
@ -158,6 +159,8 @@ Status BuildTable(
|
|||||||
file_checksum_func_name);
|
file_checksum_func_name);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table_file_created = true;
|
||||||
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
||||||
file->SetIOPriority(io_priority);
|
file->SetIOPriority(io_priority);
|
||||||
file->SetWriteLifeTimeHint(write_hint);
|
file->SetWriteLifeTimeHint(write_hint);
|
||||||
@ -371,14 +374,16 @@ Status BuildTable(
|
|||||||
|
|
||||||
constexpr IODebugContext* dbg = nullptr;
|
constexpr IODebugContext* dbg = nullptr;
|
||||||
|
|
||||||
|
if (table_file_created) {
|
||||||
Status ignored = fs->DeleteFile(fname, IOOptions(), dbg);
|
Status ignored = fs->DeleteFile(fname, IOOptions(), dbg);
|
||||||
ignored.PermitUncheckedError();
|
ignored.PermitUncheckedError();
|
||||||
|
}
|
||||||
|
|
||||||
assert(blob_file_additions || blob_file_paths.empty());
|
assert(blob_file_additions || blob_file_paths.empty());
|
||||||
|
|
||||||
if (blob_file_additions) {
|
if (blob_file_additions) {
|
||||||
for (const std::string& blob_file_path : blob_file_paths) {
|
for (const std::string& blob_file_path : blob_file_paths) {
|
||||||
ignored = DeleteDBFile(&db_options, blob_file_path, dbname,
|
Status ignored = DeleteDBFile(&db_options, blob_file_path, dbname,
|
||||||
/*force_bg=*/false, /*force_fg=*/false);
|
/*force_bg=*/false, /*force_fg=*/false);
|
||||||
ignored.PermitUncheckedError();
|
ignored.PermitUncheckedError();
|
||||||
TEST_SYNC_POINT("BuildTable::AfterDeleteFile");
|
TEST_SYNC_POINT("BuildTable::AfterDeleteFile");
|
||||||
|
@ -280,6 +280,58 @@ TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
|
|||||||
listener->VerifyMatchedCount(1);
|
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) {
|
TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||||
|
Loading…
Reference in New Issue
Block a user