This is an automated email from the ASF dual-hosted git repository.
awong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new ea16958 KUDU-2612: persist commit MVCC op timestamp in txn metadata
ea16958 is described below
commit ea1695885067dc5d39ad1f794a91a9d9e0540b1a
Author: Andrew Wong <[email protected]>
AuthorDate: Thu Oct 8 17:01:00 2020 -0700
KUDU-2612: persist commit MVCC op timestamp in txn metadata
We previously didn't persist the MVCC op timestamp used for the
BEGIN_COMMIT op. This was problematic, as this timestamp is necessary to
determine relevancy upon iteration.
This adds the timestamp to the TxnMetadata, and ensures that we anchor
the BEGIN_COMMIT op until we flush metadata, as we do with other
persistent participant op state.
Change-Id: Ib2400fbb7e96ddba78544a9549965fc095e32ca3
Reviewed-on: http://gerrit.cloudera.org:8080/16569
Tested-by: Kudu Jenkins
Reviewed-by: Hao Hao <[email protected]>
---
src/kudu/tablet/metadata.proto | 16 ++++-
src/kudu/tablet/ops/participant_op.cc | 2 +-
src/kudu/tablet/tablet.cc | 8 +++
src/kudu/tablet/tablet.h | 6 ++
src/kudu/tablet/tablet_metadata-test.cc | 10 +++
src/kudu/tablet/tablet_metadata.cc | 21 ++++++
src/kudu/tablet/tablet_metadata.h | 7 ++
src/kudu/tablet/txn_metadata.h | 7 +-
src/kudu/tablet/txn_participant-test.cc | 112 +++++++++++++++++++++++++++++++-
9 files changed, 180 insertions(+), 9 deletions(-)
diff --git a/src/kudu/tablet/metadata.proto b/src/kudu/tablet/metadata.proto
index e59ee80..5cbfa14 100644
--- a/src/kudu/tablet/metadata.proto
+++ b/src/kudu/tablet/metadata.proto
@@ -87,7 +87,21 @@ message TxnMetadataPB {
// set.
optional int64 commit_timestamp = 2;
- // TODO(awong): persist the op timestamp for the commit's MVCC op.
+ // The timestamp used by the MVCC op that tracks the commit of this
+ // transaction. This is sent to the transaction status manager in response to
+ // a BEGIN_COMMIT request to be used to assign a commit timestamp that is
+ // higher than all participants' commit MVCC op timestamps.
+ //
+ // When iterating through mutations at a specific clean snapshot (as in a
+ // READ_AT_SNAPSHOT or diff scan), both this MVCC op timestamp and the commit
+ // timestamp must be applied for the mutation to be considered committed in
+ // that snapshot.
+ //
+ // When iterating through mutations at the latest snapshot (as in READ_LATEST
+ // or during compactions), this MVCC op timestamp must be applied and there
+ // must be a commit timestamp for the mutation to be considered committed --
+ // this avoids reading dirty, uncommitted rows.
+ optional int64 commit_mvcc_op_timestamp = 3;
// TODO(awong): add an owner field to this for uncommitted transactions.
}
diff --git a/src/kudu/tablet/ops/participant_op.cc
b/src/kudu/tablet/ops/participant_op.cc
index 2241ad3..fb5295b 100644
--- a/src/kudu/tablet/ops/participant_op.cc
+++ b/src/kudu/tablet/ops/participant_op.cc
@@ -174,7 +174,7 @@ Status ParticipantOpState::PerformOp(const consensus::OpId&
op_id, Tablet* table
break;
}
case ParticipantOpPB::BEGIN_COMMIT: {
- txn_->BeginCommit(op_id);
+ tablet->BeginCommit(txn_.get(), begin_commit_mvcc_op_->timestamp(),
op_id);
ReleaseMvccOpToTxn();
break;
}
diff --git a/src/kudu/tablet/tablet.cc b/src/kudu/tablet/tablet.cc
index badd25b..4badfb0 100644
--- a/src/kudu/tablet/tablet.cc
+++ b/src/kudu/tablet/tablet.cc
@@ -1041,6 +1041,14 @@ void Tablet::BeginTransaction(Txn* txn, const OpId&
op_id) {
txn->BeginTransaction();
}
+void Tablet::BeginCommit(Txn* txn, Timestamp mvcc_op_ts, const OpId& op_id) {
+ unique_ptr<MinLogIndexAnchorer> anchor(new
MinLogIndexAnchorer(log_anchor_registry_.get(),
+ Substitute("BEGIN_COMMIT-$0-$1", txn->txn_id(), txn)));
+ anchor->AnchorIfMinimum(op_id.index());
+ metadata_->BeginCommitTransaction(txn->txn_id(), mvcc_op_ts,
std::move(anchor));
+ txn->BeginCommit(op_id);
+}
+
void Tablet::CommitTransaction(Txn* txn, Timestamp commit_ts, const OpId&
op_id) {
unique_ptr<MinLogIndexAnchorer> anchor(new
MinLogIndexAnchorer(log_anchor_registry_.get(),
Substitute("FINALIZE_COMMIT-$0-$1", txn->txn_id(), txn)));
diff --git a/src/kudu/tablet/tablet.h b/src/kudu/tablet/tablet.h
index 24219bd..ce57bfb 100644
--- a/src/kudu/tablet/tablet.h
+++ b/src/kudu/tablet/tablet.h
@@ -183,6 +183,12 @@ class Tablet {
// using 'txn' as the anchor owner.
void BeginTransaction(Txn* txn, const consensus::OpId& op_id);
+ // Indicates that the transaction has started to commit, recording the
+ // timestamp used by the MVCC op to demarcate the end of the transaction in
+ // the tablet metadata. Upon calling this, 'op_id' will be anchored until
+ // the metadata is flushed, using 'txn' as the anchor owner.
+ void BeginCommit(Txn* txn, Timestamp mvcc_op_ts, const consensus::OpId&
op_id);
+
// Commits the transaction, recording its commit timestamp in the tablet
metadata.
// Upon calling this, 'op_id' will be anchored until the metadata is flushed,
// using 'txn' as the anchor owner.
diff --git a/src/kudu/tablet/tablet_metadata-test.cc
b/src/kudu/tablet/tablet_metadata-test.cc
index 254b4cb..de6d3e5 100644
--- a/src/kudu/tablet/tablet_metadata-test.cc
+++ b/src/kudu/tablet/tablet_metadata-test.cc
@@ -241,14 +241,18 @@ TEST_F(TestTabletMetadata, TestTxnMetadata) {
int64_t kAbortedTxnId = 2;
int64_t kInFlightTxnId = 3;
meta->AddTxnMetadata(kCommittedTxnId, make_anchor());
+ meta->BeginCommitTransaction(kCommittedTxnId, kDummyTimestamp,
make_anchor());
meta->AddCommitTimestamp(kCommittedTxnId, kDummyTimestamp, make_anchor());
ASSERT_EQ(1, meta->GetTxnMetadata().size());
meta->AddTxnMetadata(kAbortedTxnId, make_anchor());
+ meta->BeginCommitTransaction(kAbortedTxnId, kDummyTimestamp, make_anchor());
meta->AbortTransaction(kAbortedTxnId, make_anchor());
ASSERT_EQ(2, meta->GetTxnMetadata().size());
meta->AddTxnMetadata(kInFlightTxnId, make_anchor());
+ meta->BeginCommitTransaction(kInFlightTxnId, kDummyTimestamp, make_anchor());
+ ASSERT_EQ(3, meta->GetTxnMetadata().size());
// Validate the transactions' fields.
const auto validate_txn_metas = [&] (TabletMetadata* meta) {
@@ -260,16 +264,22 @@ TEST_F(TestTabletMetadata, TestTxnMetadata) {
const auto& committed_txn = FindOrDie(txn_metas, kCommittedTxnId);
ASSERT_FALSE(committed_txn->aborted());
+ ASSERT_NE(boost::none, committed_txn->commit_mvcc_op_timestamp());
+ ASSERT_EQ(kDummyTimestamp, *committed_txn->commit_mvcc_op_timestamp());
ASSERT_NE(boost::none, committed_txn->commit_timestamp());
ASSERT_EQ(kDummyTimestamp, *committed_txn->commit_timestamp());
const auto& aborted_txn = FindOrDie(txn_metas, kAbortedTxnId);
ASSERT_TRUE(aborted_txn->aborted());
ASSERT_EQ(boost::none, aborted_txn->commit_timestamp());
+ ASSERT_NE(boost::none, aborted_txn->commit_mvcc_op_timestamp());
+ ASSERT_EQ(kDummyTimestamp, *aborted_txn->commit_mvcc_op_timestamp());
const auto& in_flight_txn = FindOrDie(txn_metas, kInFlightTxnId);
ASSERT_FALSE(in_flight_txn->aborted());
ASSERT_EQ(boost::none, in_flight_txn->commit_timestamp());
+ ASSERT_NE(boost::none, in_flight_txn->commit_mvcc_op_timestamp());
+ ASSERT_EQ(kDummyTimestamp, *in_flight_txn->commit_mvcc_op_timestamp());
unordered_set<int64_t> in_flight_txn_ids;
unordered_set<int64_t> terminal_txn_ids;
diff --git a/src/kudu/tablet/tablet_metadata.cc
b/src/kudu/tablet/tablet_metadata.cc
index 699d98b..07be998 100644
--- a/src/kudu/tablet/tablet_metadata.cc
+++ b/src/kudu/tablet/tablet_metadata.cc
@@ -505,6 +505,9 @@ Status TabletMetadata::LoadFromSuperBlock(const
TabletSuperBlockPB& superblock)
EmplaceOrDie(&txn_metas, txn_id_and_metadata.first,
new TxnMetadata(
txn_meta.has_aborted() && txn_meta.aborted(),
+ txn_meta.has_commit_mvcc_op_timestamp() ?
+
boost::make_optional(Timestamp(txn_meta.commit_mvcc_op_timestamp())) :
+ boost::none,
txn_meta.has_commit_timestamp() ?
boost::make_optional(Timestamp(txn_meta.commit_timestamp()))
:
boost::none
@@ -742,6 +745,10 @@ Status
TabletMetadata::ToSuperBlockUnlocked(TabletSuperBlockPB* super_block,
if (commit_ts) {
meta_pb.set_commit_timestamp(commit_ts->value());
}
+ const auto& commit_mvcc_op_ts = txn_meta->commit_mvcc_op_timestamp();
+ if (commit_mvcc_op_ts) {
+ meta_pb.set_commit_mvcc_op_timestamp(commit_mvcc_op_ts->value());
+ }
if (txn_meta->aborted()) {
meta_pb.set_aborted(true);
}
@@ -802,6 +809,20 @@ void TabletMetadata::AddTxnMetadata(int64_t txn_id,
unique_ptr<MinLogIndexAnchor
anchors_needing_flush_.emplace_back(std::move(log_anchor));
}
+void TabletMetadata::BeginCommitTransaction(int64_t txn_id, Timestamp
mvcc_op_timestamp,
+ unique_ptr<MinLogIndexAnchorer>
log_anchor) {
+ std::lock_guard<LockType> l(data_lock_);
+ auto txn_metadata = FindPtrOrNull(txn_metadata_by_txn_id_, txn_id);
+ CHECK(txn_metadata);
+ // NOTE: we may already have an MVCC op timestamp if we are bootstrapping and
+ // the timestamp was persisted already, in which case, we don't need to
+ // anchor the WAL to ensure the timestamp's persistence in metadata.
+ if (!txn_metadata->commit_mvcc_op_timestamp()) {
+ txn_metadata->set_commit_mvcc_op_timestamp(mvcc_op_timestamp);
+ anchors_needing_flush_.emplace_back(std::move(log_anchor));
+ }
+}
+
void TabletMetadata::AddCommitTimestamp(int64_t txn_id, Timestamp
commit_timestamp,
unique_ptr<MinLogIndexAnchorer>
log_anchor) {
std::lock_guard<LockType> l(data_lock_);
diff --git a/src/kudu/tablet/tablet_metadata.h
b/src/kudu/tablet/tablet_metadata.h
index e8fbb3f..6bba726 100644
--- a/src/kudu/tablet/tablet_metadata.h
+++ b/src/kudu/tablet/tablet_metadata.h
@@ -242,6 +242,13 @@ class TabletMetadata : public
RefCountedThreadSafe<TabletMetadata> {
// have metadata associated with it.
void AddTxnMetadata(int64_t txn_id,
std::unique_ptr<log::MinLogIndexAnchorer> log_anchor);
+ // Records the fact that the given transaction has started committing, with
+ // the given timestamp as the end of its open window, adopting the anchor
+ // until the metadata is flushed. The transaction must already have metadata
+ // associated with it.
+ void BeginCommitTransaction(int64_t txn_id, Timestamp mvcc_op_timestamp,
+ std::unique_ptr<log::MinLogIndexAnchorer>
log_anchor);
+
// Records the fact that the transaction was committed at the given
// timestamp, adopting the anchor until the metadata is flushed. The
// transaction must already have metadata associated with it.
diff --git a/src/kudu/tablet/txn_metadata.h b/src/kudu/tablet/txn_metadata.h
index ea2708b..177495e 100644
--- a/src/kudu/tablet/txn_metadata.h
+++ b/src/kudu/tablet/txn_metadata.h
@@ -30,12 +30,11 @@ namespace tablet {
// Encapsulates the persistent state associated with a transaction.
class TxnMetadata : public RefCountedThreadSafe<TxnMetadata> {
public:
- // TODO(awong): add commit_mvcc_op_timestamp to the contructor when reading
- // from TxnMetadataPB.
explicit TxnMetadata(bool aborted = false,
+ boost::optional<Timestamp> commit_mvcc_op_timestamp =
boost::none,
boost::optional<Timestamp> commit_ts = boost::none)
: aborted_(aborted),
- commit_mvcc_op_timestamp_(boost::none),
+ commit_mvcc_op_timestamp_(std::move(commit_mvcc_op_timestamp)),
commit_timestamp_(std::move(commit_ts)) {}
void set_aborted() {
std::lock_guard<simple_spinlock> l(lock_);
@@ -45,13 +44,13 @@ class TxnMetadata : public
RefCountedThreadSafe<TxnMetadata> {
void set_commit_timestamp(Timestamp commit_ts) {
std::lock_guard<simple_spinlock> l(lock_);
CHECK(boost::none == commit_timestamp_);
+ CHECK(boost::none != commit_mvcc_op_timestamp_);
CHECK(!aborted_);
commit_timestamp_ = commit_ts;
}
void set_commit_mvcc_op_timestamp(Timestamp op_ts) {
std::lock_guard<simple_spinlock> l(lock_);
CHECK(boost::none == commit_timestamp_);
- CHECK(boost::none == commit_mvcc_op_timestamp_);
commit_mvcc_op_timestamp_ = op_ts;
}
diff --git a/src/kudu/tablet/txn_participant-test.cc
b/src/kudu/tablet/txn_participant-test.cc
index 02c9287..b1229fb 100644
--- a/src/kudu/tablet/txn_participant-test.cc
+++ b/src/kudu/tablet/txn_participant-test.cc
@@ -24,9 +24,11 @@
#include <ostream>
#include <string>
#include <thread>
+#include <unordered_map>
#include <utility>
#include <vector>
+#include <boost/optional/optional.hpp>
#include <gflags/gflags_declare.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
@@ -52,6 +54,7 @@
#include "kudu/tablet/tablet_metadata.h"
#include "kudu/tablet/tablet_replica-test-base.h"
#include "kudu/tablet/tablet_replica.h"
+#include "kudu/tablet/txn_metadata.h"
#include "kudu/tablet/txn_participant-test-util.h"
#include "kudu/tserver/tserver.pb.h"
#include "kudu/tserver/tserver_admin.pb.h"
@@ -464,9 +467,13 @@ TEST_F(TxnParticipantTest, TestTxnMetadataSurvivesRestart)
{
ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::BEGIN_COMMIT,
kDummyCommitTimestamp, &resp));
ASSERT_FALSE(resp.has_error());
- ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
ASSERT_OK(tablet_replica_->log()->WaitUntilAllFlushed());
ASSERT_OK(tablet_replica_->log()->AllocateSegmentAndRollOverForTests());
+ // There should be two anchors for this op: one that is in place until the
+ // FINALIZE_COMMIT op, another until we flush.
+ ASSERT_EQ(2,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ ASSERT_EQ(1,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
ASSERT_OK(tablet_replica_->GetGCableDataSize(&gcable_size));
ASSERT_GT(gcable_size, 0);
ASSERT_OK(tablet_replica_->RunLogGC());
@@ -513,6 +520,104 @@ TEST_F(TxnParticipantTest,
TestTxnMetadataSurvivesRestart) {
}), txn_participant()->GetTxnsForTests());
}
+// Test that we can replay BEGIN_COMMIT ops, given it anchors WALs until
+// metadata flush _and_ until the transaction is finalized or aborted.
+TEST_F(TxnParticipantTest, TestBeginCommitAnchorsOnFlush) {
+ const int64_t kTxnId = 1;
+ ParticipantResponsePB resp;
+ // Start a transaction and begin committing.
+ ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::BEGIN_TXN,
+ kDummyCommitTimestamp, &resp));
+ ASSERT_FALSE(resp.has_error());
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ auto txn_meta =
FindOrDie(tablet_replica_->tablet_metadata()->GetTxnMetadata(), kTxnId);
+ ASSERT_EQ(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::BEGIN_COMMIT,
+ kDummyCommitTimestamp, &resp));
+ ASSERT_FALSE(resp.has_error());
+ // We should have two anchors: one that lasts until we flush, another that
+ // lasts until we finalize the commit.
+ ASSERT_EQ(2,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+
+ // We should have an MVCC op timestamp in the metadata, even after
+ // restarting.
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ const auto orig_mvcc_op_timestamp = *txn_meta->commit_mvcc_op_timestamp();
+ txn_meta.reset();
+ RestartReplica(/*reset_tablet*/true);
+ txn_meta = FindOrDie(tablet_replica_->tablet_metadata()->GetTxnMetadata(),
kTxnId);
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ ASSERT_EQ(orig_mvcc_op_timestamp, *txn_meta->commit_mvcc_op_timestamp());
+
+ // Once we flush, we should drop down to one anchor.
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ ASSERT_EQ(1,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+ ASSERT_OK(RestartReplica(/*reset_tablet*/true));
+ ASSERT_EQ(1,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+ ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::FINALIZE_COMMIT,
+ kDummyCommitTimestamp, &resp));
+ ASSERT_FALSE(resp.has_error());
+
+ // The anchor from the BEGIN_COMMIT op should be gone, but we should have
+ // another anchor for the FINALIZE_COMMIT op until we flush the metadata.
+ ASSERT_EQ(1,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ ASSERT_EQ(0,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+
+ // As another sanity check, we should still have metadata for the MVCC op
+ // after restarting.
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ txn_meta.reset();
+ RestartReplica(/*reset_tablet*/true);
+ txn_meta = FindOrDie(tablet_replica_->tablet_metadata()->GetTxnMetadata(),
kTxnId);
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ ASSERT_EQ(orig_mvcc_op_timestamp, *txn_meta->commit_mvcc_op_timestamp());
+}
+
+// Like the above test but finalizing the commit before flushing the metadata.
+TEST_F(TxnParticipantTest, TestBeginCommitAnchorsOnFinalize) {
+ const int64_t kTxnId = 1;
+ ParticipantResponsePB resp;
+ ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::BEGIN_TXN,
+ kDummyCommitTimestamp, &resp));
+ ASSERT_FALSE(resp.has_error());
+ auto txn_meta =
FindOrDie(tablet_replica_->tablet_metadata()->GetTxnMetadata(), kTxnId);
+ ASSERT_EQ(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::BEGIN_COMMIT,
+ kDummyCommitTimestamp, &resp));
+ ASSERT_FALSE(resp.has_error());
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ const auto orig_mvcc_op_timestamp = *txn_meta->commit_mvcc_op_timestamp();
+
+ // Restarting shouldn't affect our metadata.
+ txn_meta.reset();
+ RestartReplica(/*reset_tablet*/true);
+ txn_meta = FindOrDie(tablet_replica_->tablet_metadata()->GetTxnMetadata(),
kTxnId);
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ ASSERT_EQ(orig_mvcc_op_timestamp, *txn_meta->commit_mvcc_op_timestamp());
+
+ // We should have two anchors, one that lasts until we flush, another that
+ // lasts until we finalize.
+ ASSERT_EQ(2,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+ ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
+ ParticipantOpPB::FINALIZE_COMMIT,
kDummyCommitTimestamp, &resp));
+ ASSERT_FALSE(resp.has_error());
+
+ // Finalizing the commit shouldn't affect our metadata.
+ txn_meta.reset();
+ RestartReplica(/*reset_tablet*/true);
+ txn_meta = FindOrDie(tablet_replica_->tablet_metadata()->GetTxnMetadata(),
kTxnId);
+ ASSERT_NE(boost::none, txn_meta->commit_mvcc_op_timestamp());
+ ASSERT_EQ(orig_mvcc_op_timestamp, *txn_meta->commit_mvcc_op_timestamp());
+
+ // One anchor should be gone and another should be registered in its place
+ // that lasts until we flush the finalized metadata.
+ ASSERT_EQ(2,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ ASSERT_EQ(0,
tablet_replica_->log_anchor_registry()->GetAnchorCountForTests());
+}
+
class MetadataFlushTxnParticipantTest : public TxnParticipantTest,
public
::testing::WithParamInterface<bool> {};
@@ -535,8 +640,9 @@ TEST_P(MetadataFlushTxnParticipantTest,
TestRebuildTxnMetadata) {
ASSERT_OK(CallParticipantOp(tablet_replica_.get(), kTxnId,
ParticipantOpPB::BEGIN_COMMIT,
kDummyCommitTimestamp, &resp));
ASSERT_FALSE(resp.has_error());
- // NOTE: BEGIN_COMMIT ops don't anchor on metadata flush, so don't bother
- // flushing.
+ if (should_flush) {
+ ASSERT_OK(tablet_replica_->tablet_metadata()->Flush());
+ }
ASSERT_OK(RestartReplica(/*reset_tablet*/true));
ASSERT_EQ(vector<TxnParticipant::TxnEntry>({