summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Russotto <matthew.russotto@10gen.com>2019-05-22 14:36:19 -0400
committerMatthew Russotto <matthew.russotto@10gen.com>2019-05-23 10:48:33 -0400
commite35e202e414df3f917ff26937f5e8325cce56b1e (patch)
treeabb394e16886c6918014fe5b7ef370509f16b267
parent358c0af2fe875d6a768cf87d7ddfaeb3181f804a (diff)
downloadmongo-e35e202e414df3f917ff26937f5e8325cce56b1e.tar.gz
SERVER-39811 Add a threshold of the total size of buffered oplog entries in a transaction and make it tunable, defaulting to unlimited
-rw-r--r--src/mongo/db/transaction_participant.cpp7
-rw-r--r--src/mongo/db/transaction_participant.idl9
-rw-r--r--src/mongo/db/transaction_participant_test.cpp27
3 files changed, 43 insertions, 0 deletions
diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp
index 34f83ba6888..3b963c9d7b8 100644
--- a/src/mongo/db/transaction_participant.cpp
+++ b/src/mongo/db/transaction_participant.cpp
@@ -1128,6 +1128,13 @@ void TransactionParticipant::Participant::addTransactionOperation(
p().transactionOperations.push_back(operation);
p().transactionOperationBytes += repl::OplogEntry::getDurableReplOperationSize(operation);
+ auto transactionSizeLimitBytes = gTransactionSizeLimitBytes.load();
+ uassert(ErrorCodes::TransactionTooLarge,
+ str::stream() << "Total size of all transaction operations must be less than "
+ << "server parameter 'transactionSizeLimitBytes' = "
+ << transactionSizeLimitBytes,
+ p().transactionOperationBytes <= static_cast<size_t>(transactionSizeLimitBytes));
+
// Creating transactions larger than 16MB requires a new oplog format only available in FCV 4.2.
const auto isFCV42 = serverGlobalParams.featureCompatibility.getVersion() ==
ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42;
diff --git a/src/mongo/db/transaction_participant.idl b/src/mongo/db/transaction_participant.idl
index 55a408ac30e..7e0a0194613 100644
--- a/src/mongo/db/transaction_participant.idl
+++ b/src/mongo/db/transaction_participant.idl
@@ -74,3 +74,12 @@ server_parameters:
cpp_varname: gMaxNumberOfTransactionOperationsInSingleOplogEntry
default: 2147483647 # INT_MAX
validator: { gte: 1 }
+
+ transactionSizeLimitBytes:
+ description: >-
+ Maximum total size of operations in a multi-document transaction.
+ set_at: [ startup, runtime ]
+ cpp_vartype: AtomicWord<long long>
+ cpp_varname: gTransactionSizeLimitBytes
+ default:
+ expr: std::numeric_limits<long long>::max()
diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp
index ea04f5b7829..8dbdd4aa6f5 100644
--- a/src/mongo/db/transaction_participant_test.cpp
+++ b/src/mongo/db/transaction_participant_test.cpp
@@ -1097,6 +1097,33 @@ TEST_F(TxnParticipantTest, TransactionTooLargeWhileBuilding) {
ErrorCodes::TransactionTooLarge);
}
+// Tests that a transaction aborts if it becomes too large based on the server parameter
+// 'transactionLimitBytes'.
+TEST_F(TxnParticipantTest, TransactionExceedsSizeParameter) {
+ auto sessionCheckout = checkOutSession();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
+ auto oldLimit = gTransactionSizeLimitBytes.load();
+ ON_BLOCK_EXIT([oldLimit] { gTransactionSizeLimitBytes.store(oldLimit); });
+
+ // Set a limit of 2.5 MB
+ gTransactionSizeLimitBytes.store(2 * 1024 * 1024 + 512 * 1024);
+
+ // Two 1MB operations should succeed; three 1MB operations should fail.
+ constexpr size_t kBigDataSize = 1 * 1024 * 1024;
+ std::unique_ptr<uint8_t[]> bigData(new uint8_t[kBigDataSize]());
+ auto operation = repl::OplogEntry::makeInsertOperation(
+ kNss,
+ _uuid,
+ BSON("_id" << 0 << "data" << BSONBinData(bigData.get(), kBigDataSize, BinDataGeneral)));
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ ASSERT_THROWS_CODE(txnParticipant.addTransactionOperation(opCtx(), operation),
+ AssertionException,
+ ErrorCodes::TransactionTooLarge);
+}
+
TEST_F(TxnParticipantTest, StashInNestedSessionIsANoop) {
auto outerScopedSession = checkOutSession();
Locker* originalLocker = opCtx()->lockState();