Skip to content

Commit ebfc0dc

Browse files
committed
SERVER-41470 Remove FCV checks for multiple-entry transaction format
1 parent d2b1639 commit ebfc0dc

10 files changed

+20
-282
lines changed

buildscripts/resmokeconfig/suites/replica_sets_kill_primary_jscore_passthrough.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ selector:
7979
# Inserts enough data that recovery takes more than 8 seconds, so we never get a working primary.
8080
- jstests/core/geo_s2ordering.js
8181

82-
# Inserts enough data that the secondaries get too far behind to catch up.
83-
- jstests/core/txns/large_transactions_require_fcv42.js
84-
8582
exclude_with_any_tags:
8683
##
8784
# The next four tags correspond to the special errors thrown by the auto_retry_on_network_error.js

buildscripts/resmokeconfig/suites/replica_sets_kill_secondaries_jscore_passthrough.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ selector:
1414
# true. This causes the test to hang because the secondary is running with the "rsSyncStopApply"
1515
# failpoint enabled.
1616
- jstests/core/geo_update_btree.js
17-
# Inserts enough data that the secondaries get too far behind to catch up.
18-
- jstests/core/txns/large_transactions_require_fcv42.js
1917
# The following tests create large oplog entries, which can cause the secondary to fall off the
2018
# primary's oplog when run as a part of burn_in_tests.
2119
- jstests/core/max_doc_size.js

buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ selector:
1313

1414
# No featureCompatibilityVersion parameter on mongos.
1515
- jstests/core/txns/abort_unprepared_transactions_on_FCV_downgrade.js
16-
- jstests/core/txns/downgrade_fcv_while_large_partial_txn_in_progress.js
17-
- jstests/core/txns/large_transactions_require_fcv42.js
1816

1917
# Mongos doesn't upconvert from local or majority level readConcern to snapshot.
2018
- jstests/core/txns/upconvert_read_concern.js

buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ selector:
99

1010
# No featureCompatibilityVersion parameter on mongos.
1111
- jstests/core/txns/abort_unprepared_transactions_on_FCV_downgrade.js
12-
- jstests/core/txns/downgrade_fcv_while_large_partial_txn_in_progress.js
13-
- jstests/core/txns/large_transactions_require_fcv42.js
1412

1513
# Mongos doesn't upconvert from local or majority level readConcern to snapshot.
1614
- jstests/core/txns/upconvert_read_concern.js

buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ selector:
1313

1414
# No featureCompatibilityVersion parameter on mongos.
1515
- jstests/core/txns/abort_unprepared_transactions_on_FCV_downgrade.js
16-
- jstests/core/txns/downgrade_fcv_while_large_partial_txn_in_progress.js
17-
- jstests/core/txns/large_transactions_require_fcv42.js
1816

1917
# Mongos doesn't upconvert from local or majority level readConcern to snapshot.
2018
- jstests/core/txns/upconvert_read_concern.js

buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ selector:
99

1010
# No featureCompatibilityVersion parameter on mongos.
1111
- jstests/core/txns/abort_unprepared_transactions_on_FCV_downgrade.js
12-
- jstests/core/txns/downgrade_fcv_while_large_partial_txn_in_progress.js
13-
- jstests/core/txns/large_transactions_require_fcv42.js
1412

1513
# Mongos doesn't upconvert from local or majority level readConcern to snapshot.
1614
- jstests/core/txns/upconvert_read_concern.js

jstests/core/txns/downgrade_fcv_while_large_partial_txn_in_progress.js

Lines changed: 0 additions & 72 deletions
This file was deleted.

jstests/core/txns/large_transactions_require_fcv42.js

Lines changed: 0 additions & 76 deletions
This file was deleted.

src/mongo/db/op_observer_impl.cpp

Lines changed: 15 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -745,15 +745,11 @@ namespace {
745745
// field. Appends as many operations as possible until either the constructed object exceeds the
746746
// 16MB limit or the maximum number of transaction statements allowed in one entry.
747747
//
748-
// If 'limitSize' is false, then it attempts to include all given operations, regardless of whether
749-
// or not they fit. If the ops don't fit, TransactionTooLarge will be thrown in that case.
750-
//
751748
// Returns an iterator to the first statement that wasn't packed into the applyOps object.
752749
std::vector<repl::ReplOperation>::const_iterator packTransactionStatementsForApplyOps(
753750
BSONObjBuilder* applyOpsBuilder,
754751
std::vector<repl::ReplOperation>::const_iterator stmtBegin,
755-
std::vector<repl::ReplOperation>::const_iterator stmtEnd,
756-
bool limitSize) {
752+
std::vector<repl::ReplOperation>::const_iterator stmtEnd) {
757753

758754
std::vector<repl::ReplOperation>::const_iterator stmtIter;
759755
BSONArrayBuilder opsArray(applyOpsBuilder->subarrayStart("applyOps"_sd));
@@ -765,11 +761,9 @@ std::vector<repl::ReplOperation>::const_iterator packTransactionStatementsForApp
765761
// BSON overhead and the other applyOps fields. But if the array with a single operation
766762
// exceeds BSONObjMaxUserSize, we still log it, as a single max-length operation
767763
// should be able to be applied.
768-
if (limitSize &&
769-
(opsArray.arrSize() == gMaxNumberOfTransactionOperationsInSingleOplogEntry ||
770-
(opsArray.arrSize() > 0 &&
771-
(opsArray.len() + OplogEntry::getDurableReplOperationSize(stmt) >
772-
BSONObjMaxUserSize))))
764+
if (opsArray.arrSize() == gMaxNumberOfTransactionOperationsInSingleOplogEntry ||
765+
(opsArray.arrSize() > 0 &&
766+
(opsArray.len() + OplogEntry::getDurableReplOperationSize(stmt) > BSONObjMaxUserSize)))
773767
break;
774768
opsArray.append(stmt.toBSON());
775769
}
@@ -880,8 +874,8 @@ int logOplogEntriesForTransaction(OperationContext* opCtx,
880874
while (stmtsIter != stmts.end()) {
881875

882876
BSONObjBuilder applyOpsBuilder;
883-
auto nextStmt = packTransactionStatementsForApplyOps(
884-
&applyOpsBuilder, stmtsIter, stmts.end(), true /* limitSize */);
877+
auto nextStmt =
878+
packTransactionStatementsForApplyOps(&applyOpsBuilder, stmtsIter, stmts.end());
885879

886880
// If we packed the last op, then the next oplog entry we log should be the implicit
887881
// commit or implicit prepare, i.e. we omit the 'partialTxn' field.
@@ -999,41 +993,14 @@ void OpObserverImpl::onUnpreparedTransactionCommit(
999993
return;
1000994

1001995
repl::OpTime commitOpTime;
1002-
// As FCV downgrade/upgrade is racey, we want to avoid performing a FCV check multiple times in
1003-
// a single call into the OpObserver. Therefore, we store the result here and pass it as an
1004-
// argument.
1005-
const auto fcv = serverGlobalParams.featureCompatibility.getVersion();
1006-
if (fcv < ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42) {
1007-
auto txnParticipant = TransactionParticipant::get(opCtx);
1008-
const auto lastWriteOpTime = txnParticipant.getLastWriteOpTime();
1009-
invariant(lastWriteOpTime.isNull());
1010-
MutableOplogEntry oplogEntry;
1011-
oplogEntry.setPrevWriteOpTimeInTransaction(lastWriteOpTime);
1012-
oplogEntry.setStatementId(StmtId(0));
1013-
1014-
BSONObjBuilder applyOpsBuilder;
1015-
// TODO(SERVER-41470): Remove limitSize==false once old transaction format is no longer
1016-
// needed.
1017-
packTransactionStatementsForApplyOps(
1018-
&applyOpsBuilder, statements.begin(), statements.end(), false /* limitSize */);
1019-
oplogEntry.setObject(applyOpsBuilder.done());
1020-
1021-
auto txnState = boost::make_optional(
1022-
fcv >= ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo42,
1023-
DurableTxnStateEnum::kCommitted);
1024-
commitOpTime = logApplyOpsForTransaction(
1025-
opCtx, &oplogEntry, txnState, boost::none, true /* updateTxnTable */)
1026-
.writeOpTime;
1027-
} else {
1028-
// Reserve all the optimes in advance, so we only need to get the optime mutex once. We
1029-
// reserve enough entries for all statements in the transaction.
1030-
auto oplogSlots = repl::getNextOpTimes(opCtx, statements.size());
1031-
invariant(oplogSlots.size() == statements.size());
1032-
1033-
// Log in-progress entries for the transaction along with the implicit commit.
1034-
int numOplogEntries = logOplogEntriesForTransaction(opCtx, statements, oplogSlots, false);
1035-
commitOpTime = oplogSlots[numOplogEntries - 1];
1036-
}
996+
// Reserve all the optimes in advance, so we only need to get the optime mutex once. We
997+
// reserve enough entries for all statements in the transaction.
998+
auto oplogSlots = repl::getNextOpTimes(opCtx, statements.size());
999+
invariant(oplogSlots.size() == statements.size());
1000+
1001+
// Log in-progress entries for the transaction along with the implicit commit.
1002+
int numOplogEntries = logOplogEntriesForTransaction(opCtx, statements, oplogSlots, false);
1003+
commitOpTime = oplogSlots[numOplogEntries - 1];
10371004
invariant(!commitOpTime.isNull());
10381005
shardObserveTransactionPrepareOrUnpreparedCommit(opCtx, statements, commitOpTime);
10391006
}
@@ -1074,48 +1041,7 @@ void OpObserverImpl::onTransactionPrepare(OperationContext* opCtx,
10741041
return;
10751042
}
10761043

1077-
// As FCV downgrade/upgrade is racey, we want to avoid performing a FCV check multiple times in
1078-
// a single call into the OpObserver. Therefore, we store the result here and pass it as an
1079-
// argument.
1080-
const auto fcv = serverGlobalParams.featureCompatibility.getVersion();
1081-
if (fcv < ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42) {
1082-
// We write the oplog entry in a side transaction so that we do not commit the now-prepared
1083-
// transaction.
1084-
// We write an empty 'applyOps' entry if there were no writes to choose a prepare timestamp
1085-
// and allow this transaction to be continued on failover.
1086-
TransactionParticipant::SideTransactionBlock sideTxn(opCtx);
1087-
1088-
writeConflictRetry(
1089-
opCtx, "onTransactionPrepare", NamespaceString::kRsOplogNamespace.ns(), [&] {
1090-
// Writes to the oplog only require a Global intent lock. Guaranteed by
1091-
// OplogSlotReserver.
1092-
invariant(opCtx->lockState()->isWriteLocked());
1093-
1094-
WriteUnitOfWork wuow(opCtx);
1095-
auto txnParticipant = TransactionParticipant::get(opCtx);
1096-
const auto lastWriteOpTime = txnParticipant.getLastWriteOpTime();
1097-
invariant(lastWriteOpTime.isNull());
1098-
1099-
MutableOplogEntry oplogEntry;
1100-
oplogEntry.setOpTime(prepareOpTime);
1101-
oplogEntry.setPrevWriteOpTimeInTransaction(lastWriteOpTime);
1102-
1103-
BSONObjBuilder applyOpsBuilder;
1104-
// TODO(SERVER-41470): Remove limitSize==false once old transaction format is no
1105-
// longer needed.
1106-
packTransactionStatementsForApplyOps(
1107-
&applyOpsBuilder, statements.begin(), statements.end(), false /* limitSize */);
1108-
applyOpsBuilder.append("prepare", true);
1109-
oplogEntry.setObject(applyOpsBuilder.done());
1110-
1111-
auto txnState = boost::make_optional(
1112-
fcv >= ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo42,
1113-
DurableTxnStateEnum::kPrepared);
1114-
logApplyOpsForTransaction(
1115-
opCtx, &oplogEntry, txnState, prepareOpTime, true /* updateTxnTable */);
1116-
wuow.commit();
1117-
});
1118-
} else {
1044+
{
11191045
// We should have reserved enough slots.
11201046
invariant(reservedSlots.size() >= statements.size());
11211047
TransactionParticipant::SideTransactionBlock sideTxn(opCtx);

src/mongo/db/transaction_participant.cpp

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,24 +1091,11 @@ Timestamp TransactionParticipant::Participant::prepareTransaction(
10911091
o(lk).prepareOpTime = *prepareOptime;
10921092
reservedSlots.push_back(prepareOplogSlot);
10931093
} else {
1094-
// On primary, we reserve an optime, prepare the transaction and write the oplog entry.
1095-
//
1096-
// Reserve an optime for the 'prepareTimestamp'. This will create a hole in the oplog and
1097-
// cause 'snapshot' and 'afterClusterTime' readers to block until this transaction is done
1098-
// being prepared. When the OplogSlotReserver goes out of scope and is destroyed, the
1099-
// storage-transaction it uses to keep the hole open will abort and the slot (and
1100-
// corresponding oplog hole) will vanish.
1101-
// TODO(SERVER-41470): Remove the if-clause here.
1102-
if (serverGlobalParams.featureCompatibility.getVersion() <
1103-
ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42) {
1104-
oplogSlotReserver.emplace(opCtx);
1105-
} else {
1106-
// Even if the prepared transaction contained no statements, we always reserve at least
1107-
// 1 oplog slot for the prepare oplog entry.
1108-
const auto numSlotsToReserve = retrieveCompletedTransactionOperations(opCtx).size();
1109-
oplogSlotReserver.emplace(opCtx, std::max(1, static_cast<int>(numSlotsToReserve)));
1110-
invariant(oplogSlotReserver->getSlots().size() >= 1);
1111-
}
1094+
// Even if the prepared transaction contained no statements, we always reserve at least
1095+
// 1 oplog slot for the prepare oplog entry.
1096+
const auto numSlotsToReserve = retrieveCompletedTransactionOperations(opCtx).size();
1097+
oplogSlotReserver.emplace(opCtx, std::max(1, static_cast<int>(numSlotsToReserve)));
1098+
invariant(oplogSlotReserver->getSlots().size() >= 1);
11121099
prepareOplogSlot = oplogSlotReserver->getLastSlot();
11131100
reservedSlots = oplogSlotReserver->getSlots();
11141101
invariant(o().prepareOpTime.isNull(),
@@ -1190,20 +1177,6 @@ void TransactionParticipant::Participant::addTransactionOperation(
11901177
<< "server parameter 'transactionSizeLimitBytes' = "
11911178
<< transactionSizeLimitBytes,
11921179
p().transactionOperationBytes <= static_cast<size_t>(transactionSizeLimitBytes));
1193-
1194-
// Creating transactions larger than 16MB requires a new oplog format only available in FCV 4.2.
1195-
const auto isFCV42 = serverGlobalParams.featureCompatibility.getVersion() ==
1196-
ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42;
1197-
// _transactionOperationBytes is based on the in-memory size of the operation. With overhead,
1198-
// we expect the BSON size of the operation to be larger, so it's possible to make a transaction
1199-
// just a bit too large and have it fail only in the commit. It's still useful to fail early
1200-
// when possible (e.g. to avoid exhausting server memory).
1201-
uassert(ErrorCodes::TransactionTooLarge,
1202-
str::stream() << "Total size of all transaction operations must be less than "
1203-
<< BSONObjMaxInternalSize
1204-
<< " when using featureCompatibilityVersion < 4.2. Actual size is "
1205-
<< p().transactionOperationBytes,
1206-
isFCV42 || p().transactionOperationBytes <= BSONObjMaxInternalSize);
12071180
}
12081181

12091182
std::vector<repl::ReplOperation>&

0 commit comments

Comments
 (0)