Skip to content

Commit

Permalink
WritePrepared: improve IsInSnapshotEmptyMapTest (#4853)
Browse files Browse the repository at this point in the history
Summary:
IsInSnapshotEmptyMapTest tests that IsInSnapshot returns correct value for existing data after a recovery, where max is not zero and yet commit cache is empty. The existing test was preliminary which is improved in this patch. It also increases the db sequence after recovery so that there the snapshot immediately taken after recovery would have a sequence number different than that of max_evicted_seq. This simplifies the logic in IsInSnapshot by not having to consider the special case that an old snapshot might be equal to max_evicted_seq and yet not present in old_commit_map.
Pull Request resolved: #4853

Differential Revision: D13595223

Pulled By: maysamyabandeh

fbshipit-source-id: 77c12ca8a3f61a47479a93bef2038ff502dc3322
  • Loading branch information
Maysam Yabandeh authored and facebook-github-bot committed Jan 8, 2019
1 parent e686caf commit cd227d7
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 13 deletions.
72 changes: 59 additions & 13 deletions utilities/transactions/write_prepared_transaction_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1267,20 +1267,20 @@ TEST_P(SeqAdvanceConcurrentTest, SeqAdvanceConcurrentTest) {
assert(db != nullptr);
db_impl = reinterpret_cast<DBImpl*>(db->GetRootDB());
seq = db_impl->TEST_GetLastVisibleSequence();
ASSERT_EQ(exp_seq, seq);
ASSERT_LE(exp_seq, seq);

// Check if flush preserves the last sequence number
db_impl->Flush(fopt);
seq = db_impl->GetLatestSequenceNumber();
ASSERT_EQ(exp_seq, seq);
ASSERT_LE(exp_seq, seq);

// Check if recovery after flush preserves the last sequence number
db_impl->FlushWAL(true);
ReOpenNoDelete();
assert(db != nullptr);
db_impl = reinterpret_cast<DBImpl*>(db->GetRootDB());
seq = db_impl->GetLatestSequenceNumber();
ASSERT_EQ(exp_seq, seq);
ASSERT_LE(exp_seq, seq);
}
}

Expand Down Expand Up @@ -1425,17 +1425,63 @@ TEST_P(WritePreparedTransactionTest, BasicRecoveryTest) {
}

// After recovery the commit map is empty while the max is set. The code would
// go through a different path which requires a separate test.
// go through a different path which requires a separate test. Test that the
// committed data before the restart is visible to all snapshots.
TEST_P(WritePreparedTransactionTest, IsInSnapshotEmptyMapTest) {
WritePreparedTxnDB* wp_db = dynamic_cast<WritePreparedTxnDB*>(db);
wp_db->max_evicted_seq_ = 100;
ASSERT_FALSE(wp_db->IsInSnapshot(50, 40));
ASSERT_TRUE(wp_db->IsInSnapshot(50, 50));
ASSERT_TRUE(wp_db->IsInSnapshot(50, 100));
ASSERT_TRUE(wp_db->IsInSnapshot(50, 150));
ASSERT_FALSE(wp_db->IsInSnapshot(100, 80));
ASSERT_TRUE(wp_db->IsInSnapshot(100, 100));
ASSERT_TRUE(wp_db->IsInSnapshot(100, 150));
for (bool end_with_prepare : {false, true}) {
ReOpen();
WriteOptions woptions;
ASSERT_OK(db->Put(woptions, "key", "value"));
ASSERT_OK(db->Put(woptions, "key", "value"));
ASSERT_OK(db->Put(woptions, "key", "value"));
SequenceNumber prepare_seq = kMaxSequenceNumber;
if (end_with_prepare) {
TransactionOptions txn_options;
Transaction* txn = db->BeginTransaction(woptions, txn_options);
ASSERT_OK(txn->SetName("xid0"));
ASSERT_OK(txn->Prepare());
prepare_seq = txn->GetId();
delete txn;
}
dynamic_cast<WritePreparedTxnDB*>(db)->TEST_Crash();
auto db_impl = reinterpret_cast<DBImpl*>(db->GetRootDB());
db_impl->FlushWAL(true);
ReOpenNoDelete();
WritePreparedTxnDB* wp_db = dynamic_cast<WritePreparedTxnDB*>(db);
ASSERT_GT(wp_db->max_evicted_seq_, 0); // max after recovery
// Take a snapshot right after recovery
const Snapshot* snap = db->GetSnapshot();
auto snap_seq = snap->GetSequenceNumber();
ASSERT_GT(snap_seq, 0);

for (SequenceNumber seq = 0;
seq <= wp_db->max_evicted_seq_ && seq != prepare_seq; seq++) {
ASSERT_TRUE(wp_db->IsInSnapshot(seq, snap_seq));
}
if (end_with_prepare) {
ASSERT_FALSE(wp_db->IsInSnapshot(prepare_seq, snap_seq));
}
// trivial check
ASSERT_FALSE(wp_db->IsInSnapshot(snap_seq + 1, snap_seq));

db->ReleaseSnapshot(snap);

ASSERT_OK(db->Put(woptions, "key", "value"));
// Take a snapshot after some writes
snap = db->GetSnapshot();
snap_seq = snap->GetSequenceNumber();
for (SequenceNumber seq = 0;
seq <= wp_db->max_evicted_seq_ && seq != prepare_seq; seq++) {
ASSERT_TRUE(wp_db->IsInSnapshot(seq, snap_seq));
}
if (end_with_prepare) {
ASSERT_FALSE(wp_db->IsInSnapshot(prepare_seq, snap_seq));
}
// trivial check
ASSERT_FALSE(wp_db->IsInSnapshot(snap_seq + 1, snap_seq));

db->ReleaseSnapshot(snap);
}
}

// Test WritePreparedTxnDB's IsInSnapshot against different ordering of
Expand Down
8 changes: 8 additions & 0 deletions utilities/transactions/write_prepared_txn_db.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ Status WritePreparedTxnDB::Initialize(
SequenceNumber prev_max = max_evicted_seq_;
SequenceNumber last_seq = db_impl_->GetLatestSequenceNumber();
AdvanceMaxEvictedSeq(prev_max, last_seq);
// Create a gap between max and the next snapshot. This simplifies the logic
// in IsInSnapshot by not having to consider the special case of max ==
// snapshot after recovery. This is tested in IsInSnapshotEmptyMapTest.
if (last_seq) {
db_impl_->versions_->SetLastAllocatedSequence(last_seq + 1);
db_impl_->versions_->SetLastSequence(last_seq + 1);
db_impl_->versions_->SetLastPublishedSequence(last_seq + 1);
}

db_impl_->SetSnapshotChecker(new WritePreparedSnapshotChecker(this));
// A callback to commit a single sub-batch
Expand Down

0 comments on commit cd227d7

Please sign in to comment.