diff options
author | Matthew Russotto <matthew.russotto@10gen.com> | 2019-05-22 14:36:19 -0400 |
---|---|---|
committer | Matthew Russotto <matthew.russotto@10gen.com> | 2019-05-23 10:48:33 -0400 |
commit | e35e202e414df3f917ff26937f5e8325cce56b1e (patch) | |
tree | abb394e16886c6918014fe5b7ef370509f16b267 | |
parent | 358c0af2fe875d6a768cf87d7ddfaeb3181f804a (diff) | |
download | mongo-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.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.idl | 9 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant_test.cpp | 27 |
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(); |