Fix segfalut in ~DBWithTTLImpl() when called after Close() (#5485)
Summary: ~DBWithTTLImpl() fails after calling Close() function (will invoke the Close() function of DBImpl), because the Close() function deletes default_cf_handle_ which is used in the GetOptions() function called in ~DBWithTTLImpl(), hence lead to segfault. Fix by creating a Close() function for the DBWithTTLImpl class and do the close and the work originally in ~DBWithTTLImpl(). If the Close() function is not called, it will be called in the ~DBWithTTLImpl() function. Pull Request resolved: https://github.com/facebook/rocksdb/pull/5485 Test Plan: make clean; USE_CLANG=1 make all check -j Differential Revision: D15924498 fbshipit-source-id: 567397fb972961059083a1ae0f9f99ff74872b78
This commit is contained in:
parent
24f73436fb
commit
0b0cb6f1a2
@ -34,12 +34,25 @@ void DBWithTTLImpl::SanitizeOptions(int32_t ttl, ColumnFamilyOptions* options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open the db inside DBWithTTLImpl because options needs pointer to its ttl
|
// Open the db inside DBWithTTLImpl because options needs pointer to its ttl
|
||||||
DBWithTTLImpl::DBWithTTLImpl(DB* db) : DBWithTTL(db) {}
|
DBWithTTLImpl::DBWithTTLImpl(DB* db) : DBWithTTL(db), closed_(false) {}
|
||||||
|
|
||||||
DBWithTTLImpl::~DBWithTTLImpl() {
|
DBWithTTLImpl::~DBWithTTLImpl() {
|
||||||
// Need to stop background compaction before getting rid of the filter
|
if (!closed_) {
|
||||||
CancelAllBackgroundWork(db_, /* wait = */ true);
|
Close();
|
||||||
delete GetOptions().compaction_filter;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DBWithTTLImpl::Close() {
|
||||||
|
Status ret = Status::OK();
|
||||||
|
if (!closed_) {
|
||||||
|
Options default_options = GetOptions();
|
||||||
|
// Need to stop background compaction before getting rid of the filter
|
||||||
|
CancelAllBackgroundWork(db_, /* wait = */ true);
|
||||||
|
ret = db_->Close();
|
||||||
|
delete default_options.compaction_filter;
|
||||||
|
closed_ = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status UtilityDB::OpenTtlDB(const Options& options, const std::string& dbname,
|
Status UtilityDB::OpenTtlDB(const Options& options, const std::string& dbname,
|
||||||
|
@ -35,6 +35,8 @@ class DBWithTTLImpl : public DBWithTTL {
|
|||||||
|
|
||||||
virtual ~DBWithTTLImpl();
|
virtual ~DBWithTTLImpl();
|
||||||
|
|
||||||
|
virtual Status Close() override;
|
||||||
|
|
||||||
Status CreateColumnFamilyWithTtl(const ColumnFamilyOptions& options,
|
Status CreateColumnFamilyWithTtl(const ColumnFamilyOptions& options,
|
||||||
const std::string& column_family_name,
|
const std::string& column_family_name,
|
||||||
ColumnFamilyHandle** handle,
|
ColumnFamilyHandle** handle,
|
||||||
@ -99,6 +101,10 @@ class DBWithTTLImpl : public DBWithTTL {
|
|||||||
void SetTtl(int32_t ttl) override { SetTtl(DefaultColumnFamily(), ttl); }
|
void SetTtl(int32_t ttl) override { SetTtl(DefaultColumnFamily(), ttl); }
|
||||||
|
|
||||||
void SetTtl(ColumnFamilyHandle *h, int32_t ttl) override;
|
void SetTtl(ColumnFamilyHandle *h, int32_t ttl) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// remember whether the Close completes or not
|
||||||
|
bool closed_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TtlIterator : public Iterator {
|
class TtlIterator : public Iterator {
|
||||||
|
@ -86,9 +86,24 @@ class TtlTest : public testing::Test {
|
|||||||
ASSERT_OK(DBWithTTL::Open(options_, dbname_, &db_ttl_, ttl, true));
|
ASSERT_OK(DBWithTTL::Open(options_, dbname_, &db_ttl_, ttl, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call db_ttl_->Close() before delete db_ttl_
|
||||||
void CloseTtl() {
|
void CloseTtl() {
|
||||||
delete db_ttl_;
|
CloseTtlHelper(true);
|
||||||
db_ttl_ = nullptr;
|
}
|
||||||
|
|
||||||
|
// No db_ttl_->Close() before delete db_ttl_
|
||||||
|
void CloseTtlNoDBClose() {
|
||||||
|
CloseTtlHelper(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseTtlHelper(bool close_db) {
|
||||||
|
if (db_ttl_ != nullptr) {
|
||||||
|
if (close_db) {
|
||||||
|
db_ttl_->Close();
|
||||||
|
}
|
||||||
|
delete db_ttl_;
|
||||||
|
db_ttl_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates and returns a kv-map
|
// Populates and returns a kv-map
|
||||||
@ -401,6 +416,30 @@ TEST_F(TtlTest, NoEffect) {
|
|||||||
CloseTtl();
|
CloseTtl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rerun the NoEffect test with a different version of CloseTtl
|
||||||
|
// function, where db is directly deleted without close.
|
||||||
|
TEST_F(TtlTest, DestructWithoutClose) {
|
||||||
|
MakeKVMap(kSampleSize_);
|
||||||
|
int64_t boundary1 = kSampleSize_ / 3;
|
||||||
|
int64_t boundary2 = 2 * boundary1;
|
||||||
|
|
||||||
|
OpenTtl();
|
||||||
|
PutValues(0, boundary1); //T=0: Set1 never deleted
|
||||||
|
SleepCompactCheck(1, 0, boundary1); //T=1: Set1 still there
|
||||||
|
CloseTtlNoDBClose();
|
||||||
|
|
||||||
|
OpenTtl(0);
|
||||||
|
PutValues(boundary1, boundary2 - boundary1); //T=1: Set2 never deleted
|
||||||
|
SleepCompactCheck(1, 0, boundary2); //T=2: Sets1 & 2 still there
|
||||||
|
CloseTtlNoDBClose();
|
||||||
|
|
||||||
|
OpenTtl(-1);
|
||||||
|
PutValues(boundary2, kSampleSize_ - boundary2); //T=3: Set3 never deleted
|
||||||
|
SleepCompactCheck(1, 0, kSampleSize_, true); //T=4: Sets 1,2,3 still there
|
||||||
|
CloseTtlNoDBClose();
|
||||||
|
}
|
||||||
|
|
||||||
// Puts a set of values and checks its presence using Get during ttl
|
// Puts a set of values and checks its presence using Get during ttl
|
||||||
TEST_F(TtlTest, PresentDuringTTL) {
|
TEST_F(TtlTest, PresentDuringTTL) {
|
||||||
MakeKVMap(kSampleSize_);
|
MakeKVMap(kSampleSize_);
|
||||||
|
Loading…
Reference in New Issue
Block a user