DB::Close() to fail when there are unreleased snapshots (#5272)

Summary:
Sometimes, users might make mistake of not releasing snapshots before closing the DB. This is undocumented use of RocksDB and the behavior is unknown. We return DB::Close() to provide a way to check it for the users. Aborted() will be returned to users when they call DB::Close().
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5272

Differential Revision: D15159713

Pulled By: siying

fbshipit-source-id: 39369def612398d9f239d83d396b5a28e5af65cd
main
Siying Dong 6 years ago committed by Facebook Github Bot
parent 521d234bda
commit 4e0f2aadb0
  1. 2
      HISTORY.md
  2. 14
      db/db_impl.cc
  3. 15
      db/db_test2.cc
  4. 10
      include/rocksdb/db.h

@ -1,5 +1,7 @@
# Rocksdb Change Log # Rocksdb Change Log
## Unreleased ## Unreleased
### Public API Change
* Now DB::Close() will return Aborted() error when there is unreleased snapshot. Users can retry after all snapshots are released.
## 6.2.0 (4/30/2019) ## 6.2.0 (4/30/2019)
### New Features ### New Features

@ -582,6 +582,12 @@ Status DBImpl::CloseHelper() {
ret = s; ret = s;
} }
} }
if (ret.IsAborted()) {
// Reserve IsAborted() error for those where users didn't release
// certain resource and they can release them and come back and
// retry. In this case, we wrap this exception to something else.
return Status::Incomplete(ret.ToString());
}
return ret; return ret;
} }
@ -3036,6 +3042,14 @@ DB::~DB() {}
Status DBImpl::Close() { Status DBImpl::Close() {
if (!closed_) { if (!closed_) {
{
InstrumentedMutexLock l(&mutex_);
// If there is unreleased snapshot, fail the close call
if (!snapshots_.empty()) {
return Status::Aborted("Cannot close DB with unreleased snapshot.");
}
}
closed_ = true; closed_ = true;
return CloseImpl(); return CloseImpl();
} }

@ -3738,6 +3738,21 @@ TEST_F(DBTest2, OldStatsInterface) {
ASSERT_GT(dos->num_rt, 0); ASSERT_GT(dos->num_rt, 0);
ASSERT_GT(dos->num_mt, 0); ASSERT_GT(dos->num_mt, 0);
} }
TEST_F(DBTest2, CloseWithUnreleasedSnapshot) {
const Snapshot* ss = db_->GetSnapshot();
for (auto h : handles_) {
db_->DestroyColumnFamilyHandle(h);
}
handles_.clear();
ASSERT_NOK(db_->Close());
db_->ReleaseSnapshot(ss);
ASSERT_OK(db_->Close());
delete db_;
db_ = nullptr;
}
} // namespace rocksdb } // namespace rocksdb
int main(int argc, char** argv) { int main(int argc, char** argv) {

@ -232,9 +232,13 @@ class DB {
// status in case there are any errors. This will not fsync the WAL files. // status in case there are any errors. This will not fsync the WAL files.
// If syncing is required, the caller must first call SyncWAL(), or Write() // If syncing is required, the caller must first call SyncWAL(), or Write()
// using an empty write batch with WriteOptions.sync=true. // using an empty write batch with WriteOptions.sync=true.
// Regardless of the return status, the DB must be freed. If the return // Regardless of the return status, the DB must be freed.
// status is NotSupported(), then the DB implementation does cleanup in the // If the return status is Aborted(), closing fails because there is
// destructor // unreleased snapshot in the system. In this case, users can release
// the unreleased snapshots and try again and expect it to succeed. For
// other status, recalling Close() will be no-op.
// If the return status is NotSupported(), then the DB implementation does
// cleanup in the destructor
virtual Status Close() { return Status::NotSupported(); } virtual Status Close() { return Status::NotSupported(); }
// ListColumnFamilies will open the DB specified by argument name // ListColumnFamilies will open the DB specified by argument name

Loading…
Cancel
Save