adds missing PopSavePoint method to Transaction (#4256)

Summary:
Transaction has had methods to deal with SavePoints already, but
was missing the PopSavePoint method provided by WriteBatch and
WriteBatchWithIndex.
This PR adds PopSavePoint to Transaction as well. Having the method
on Transaction-level too is useful for applications that repeatedly
execute a sequence of operations that normally succeed, but infrequently
need to get rolled back. Using SavePoints here is sensible, but as
operations normally succeed the application may pile up a lot of
useless SavePoints inside a Transaction, leading to slightly increased
memory usage for managing the unneeded SavePoints.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/4256

Differential Revision: D9326932

Pulled By: yiwu-arbug

fbshipit-source-id: 53a0af18a6c7e87feff8a56f1f3eab9df7f371d6
This commit is contained in:
jsteemann 2018-08-17 11:53:33 -07:00 committed by Facebook Github Bot
parent c7cf981a85
commit 90f744941d
4 changed files with 94 additions and 0 deletions

View File

@ -152,6 +152,12 @@ class Transaction {
// If there is no previous call to SetSavePoint(), returns Status::NotFound() // If there is no previous call to SetSavePoint(), returns Status::NotFound()
virtual Status RollbackToSavePoint() = 0; virtual Status RollbackToSavePoint() = 0;
// Pop the most recent save point.
// If there is no previous call to SetSavePoint(), Status::NotFound()
// will be returned.
// Otherwise returns Status::OK().
virtual Status PopSavePoint() = 0;
// This function is similar to DB::Get() except it will also read pending // This function is similar to DB::Get() except it will also read pending
// changes in this transaction. Currently, this function will return // changes in this transaction. Currently, this function will return
// Status::MergeInProgress if the most recent write to the queried key in // Status::MergeInProgress if the most recent write to the queried key in

View File

@ -179,6 +179,19 @@ Status TransactionBaseImpl::RollbackToSavePoint() {
} }
} }
Status TransactionBaseImpl::PopSavePoint() {
if (save_points_ == nullptr ||
save_points_->empty()) {
// No SavePoint yet.
assert(write_batch_.PopSavePoint().IsNotFound());
return Status::NotFound();
}
assert(!save_points_->empty());
save_points_->pop();
return write_batch_.PopSavePoint();
}
Status TransactionBaseImpl::Get(const ReadOptions& read_options, Status TransactionBaseImpl::Get(const ReadOptions& read_options,
ColumnFamilyHandle* column_family, ColumnFamilyHandle* column_family,
const Slice& key, std::string* value) { const Slice& key, std::string* value) {

View File

@ -46,6 +46,8 @@ class TransactionBaseImpl : public Transaction {
Status RollbackToSavePoint() override; Status RollbackToSavePoint() override;
Status PopSavePoint() override;
using Transaction::Get; using Transaction::Get;
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family, Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
const Slice& key, std::string* value) override; const Slice& key, std::string* value) override;

View File

@ -3771,6 +3771,79 @@ TEST_P(TransactionTest, SavepointTest2) {
delete txn2; delete txn2;
} }
TEST_P(TransactionTest, SavepointTest3) {
WriteOptions write_options;
ReadOptions read_options;
TransactionOptions txn_options;
Status s;
txn_options.lock_timeout = 1; // 1 ms
Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
ASSERT_TRUE(txn1);
s = txn1->PopSavePoint(); // No SavePoint present
ASSERT_TRUE(s.IsNotFound());
s = txn1->Put("A", "");
ASSERT_OK(s);
s = txn1->PopSavePoint(); // Still no SavePoint present
ASSERT_TRUE(s.IsNotFound());
txn1->SetSavePoint(); // 1
s = txn1->Put("A", "a");
ASSERT_OK(s);
s = txn1->PopSavePoint(); // Remove 1
ASSERT_TRUE(txn1->RollbackToSavePoint().IsNotFound());
// Verify that "A" is still locked
Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
ASSERT_TRUE(txn2);
s = txn2->Put("A", "a2");
ASSERT_TRUE(s.IsTimedOut());
delete txn2;
txn1->SetSavePoint(); // 2
s = txn1->Put("B", "b");
ASSERT_OK(s);
txn1->SetSavePoint(); // 3
s = txn1->Put("B", "b2");
ASSERT_OK(s);
ASSERT_OK(txn1->RollbackToSavePoint()); // Roll back to 2
s = txn1->PopSavePoint();
ASSERT_OK(s);
s = txn1->PopSavePoint();
ASSERT_TRUE(s.IsNotFound());
s = txn1->Commit();
ASSERT_OK(s);
delete txn1;
std::string value;
// tnx1 should have modified "A" to "a"
s = db->Get(read_options, "A", &value);
ASSERT_OK(s);
ASSERT_EQ("a", value);
// tnx1 should have set "B" to just "b"
s = db->Get(read_options, "B", &value);
ASSERT_OK(s);
ASSERT_EQ("b", value);
s = db->Get(read_options, "C", &value);
ASSERT_TRUE(s.IsNotFound());
}
TEST_P(TransactionTest, UndoGetForUpdateTest) { TEST_P(TransactionTest, UndoGetForUpdateTest) {
WriteOptions write_options; WriteOptions write_options;
ReadOptions read_options; ReadOptions read_options;