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
main
feilongliu 6 years ago committed by Facebook Github Bot
parent 24f73436fb
commit 0b0cb6f1a2
  1. 17
      utilities/ttl/db_ttl_impl.cc
  2. 6
      utilities/ttl/db_ttl_impl.h
  3. 39
      utilities/ttl/ttl_test.cc

@ -34,12 +34,25 @@ void DBWithTTLImpl::SanitizeOptions(int32_t ttl, ColumnFamilyOptions* options,
}
// 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() {
if (!closed_) {
Close();
}
}
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);
delete GetOptions().compaction_filter;
ret = db_->Close();
delete default_options.compaction_filter;
closed_ = true;
}
return ret;
}
Status UtilityDB::OpenTtlDB(const Options& options, const std::string& dbname,

@ -35,6 +35,8 @@ class DBWithTTLImpl : public DBWithTTL {
virtual ~DBWithTTLImpl();
virtual Status Close() override;
Status CreateColumnFamilyWithTtl(const ColumnFamilyOptions& options,
const std::string& column_family_name,
ColumnFamilyHandle** handle,
@ -99,6 +101,10 @@ class DBWithTTLImpl : public DBWithTTL {
void SetTtl(int32_t ttl) override { SetTtl(DefaultColumnFamily(), ttl); }
void SetTtl(ColumnFamilyHandle *h, int32_t ttl) override;
private:
// remember whether the Close completes or not
bool closed_;
};
class TtlIterator : public Iterator {

@ -86,10 +86,25 @@ class TtlTest : public testing::Test {
ASSERT_OK(DBWithTTL::Open(options_, dbname_, &db_ttl_, ttl, true));
}
// Call db_ttl_->Close() before delete db_ttl_
void CloseTtl() {
CloseTtlHelper(true);
}
// 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
void MakeKVMap(int64_t num_entries) {
@ -401,6 +416,30 @@ TEST_F(TtlTest, NoEffect) {
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
TEST_F(TtlTest, PresentDuringTTL) {
MakeKVMap(kSampleSize_);

Loading…
Cancel
Save