diff options
author | Sophia Tan <sophia_tll@hotmail.com> | 2022-09-28 17:10:38 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-09-28 17:42:54 +0000 |
commit | 1c23efb07b0958cb8e43211982326587db1edefe (patch) | |
tree | c848e9512fc950bee0ebf418dcba9c23f652035a | |
parent | cd5bb08d63a1a72d68d26b8535c76c947569c48e (diff) | |
download | mongo-1c23efb07b0958cb8e43211982326587db1edefe.tar.gz |
SERVER-66737 performing reads for retryable writes should not be recorded in Operation Metrics
-rw-r--r-- | jstests/noPassthrough/retryable_writes_operation_metrics.js | 146 | ||||
-rw-r--r-- | src/mongo/db/transaction/transaction_participant.cpp | 14 |
2 files changed, 158 insertions, 2 deletions
diff --git a/jstests/noPassthrough/retryable_writes_operation_metrics.js b/jstests/noPassthrough/retryable_writes_operation_metrics.js new file mode 100644 index 00000000000..bbc2fda6a78 --- /dev/null +++ b/jstests/noPassthrough/retryable_writes_operation_metrics.js @@ -0,0 +1,146 @@ +/** + * Tests resource consumption metrics for retryable writes. + * Retryable writes persist transaction documents in "config.transaction" and all reads and writes + * to "config.transaction" should not trigger any operation metrics. + * + * @tags: [ + * requires_replication, + * ] + */ +(function() { +"use strict"; + +load("jstests/libs/retryable_writes_util.js"); + +function setupReplicaSet() { + var rst = new ReplSetTest({ + nodes: 2, + nodeOptions: {setParameter: {"aggregateOperationResourceConsumptionMetrics": true}} + }); + + rst.startSet(); + rst.initiate(); + return rst; +} + +function clearMetrics(conn) { + conn.getDB('admin').aggregate([{$operationMetrics: {clearMetrics: true}}]); +} + +function getMetrics(conn) { + const cursor = conn.getDB('admin').aggregate([{$operationMetrics: {}}]); + + let allMetrics = {}; + while (cursor.hasNext()) { + let doc = cursor.next(); + allMetrics[doc.db] = doc; + } + return allMetrics; +} + +function assertMetricsZeroRead(metrics, dbName) { + assert(metrics[dbName]); + assert.eq(metrics[dbName].primaryMetrics.docBytesRead, 0); + assert.eq(metrics[dbName].primaryMetrics.docUnitsRead, 0); + assert.eq(metrics[dbName].primaryMetrics.idxEntryBytesRead, 0); + assert.eq(metrics[dbName].primaryMetrics.idxEntryUnitsRead, 0); +} + +function assertMetricsWritten(metrics, dbName, expectedWritten) { + assert(metrics[dbName]); + assert.eq(metrics[dbName].docBytesWritten, expectedWritten.docBytesWritten); + assert.eq(metrics[dbName].docUnitsWritten, expectedWritten.docUnitsWritten); + assert.eq(metrics[dbName].idxEntryBytesWritten, expectedWritten.idxEntryBytesWritten); + assert.eq(metrics[dbName].idxEntryUnitsWritten, expectedWritten.idxEntryUnitsWritten); + assert.eq(metrics[dbName].totalUnitsWritten, expectedWritten.totalUnitsWritten); +} + +function runRetryableTransaction(sessionDb, txnNumber, cmdObj) { + let cmd = Object.assign( + {}, cmdObj, {txnNumber: NumberLong(txnNumber), startTransaction: true, autocommit: false}); + + assert.commandWorked(sessionDb.runCommand(cmd)); + assert.commandWorked(sessionDb.adminCommand({ + commitTransaction: 1, + txnNumber: NumberLong(txnNumber), + autocommit: false, + writeConcern: {w: "majority"} + })); +} + +function runRetryableWriteCmd(sessionDb, txnNumber, cmdObj) { + let cmd = Object.assign({}, cmdObj, {txnNumber: NumberLong(txnNumber)}); + jsTest.log("run retryable write with command :" + tojson(cmd)); + assert.commandWorked(sessionDb.runCommand(cmd)); +} + +const rst = setupReplicaSet(); +const primary = rst.getPrimary(); +const kDbName = "testDb"; +const kCollName = "testColl"; +const testDb = primary.getDB(kDbName); + +assert.commandWorked(testDb.createCollection(kCollName)); + +const session = testDb.getMongo().startSession(); +let sessionDB = session.getDatabase(kDbName); +let txnNumber = 0; + +let makeInsertCmdObj = (docs) => { + return {insert: kCollName, documents: docs, ordered: false}; +}; + +let makeDocs = (fromId, toId) => { + let docs = []; + for (let i = fromId; i <= toId; i++) { + docs.push({_id: i, number: i}); + } + return docs; +}; + +let expectedWritten; + +jsTest.log("Tests non-retryable insert comamnd which has no transaction number."); +{ + clearMetrics(primary); + let cmdObj = makeInsertCmdObj(makeDocs(1, 3)); + assert.commandWorked(testDb.runCommand(cmdObj)); + let metrics = getMetrics(primary); + + assertMetricsZeroRead(metrics, kDbName); + + // Init the expected doc, index and total data written. The following retryable writes + // should have the same output as the size of inserted user data is same. + expectedWritten = { + docBytesWritten: metrics[kDbName].docBytesWritten, + docUnitsWritten: metrics[kDbName].docUnitsWritten, + idxEntryBytesWritten: metrics[kDbName].idxEntryBytesWritten, + idxEntryUnitsWritten: metrics[kDbName].idxEntryUnitsWritten, + totalUnitsWritten: metrics[kDbName].totalUnitsWritten + }; +} + +jsTest.log("Tests retryable commitTransaction command which inserts documents."); +{ + clearMetrics(primary); + let cmdObj = makeInsertCmdObj(makeDocs(4, 6)); + runRetryableTransaction(sessionDB, txnNumber, cmdObj); + let metrics = getMetrics(primary); + assertMetricsZeroRead(metrics, kDbName); + assertMetricsWritten(metrics, kDbName, expectedWritten); +} + +txnNumber++; +jsTest.log("Tests retryable insert command."); +{ + clearMetrics(primary); + let cmdObj = makeInsertCmdObj(makeDocs(7, 9)); + runRetryableWriteCmd(sessionDB, txnNumber, cmdObj); + let metrics = getMetrics(primary); + assertMetricsZeroRead(metrics, kDbName); + assertMetricsWritten(metrics, kDbName, expectedWritten); +} + +session.endSession(); +rst.stopSet(); +}()); diff --git a/src/mongo/db/transaction/transaction_participant.cpp b/src/mongo/db/transaction/transaction_participant.cpp index 64b6550d877..691a7da6365 100644 --- a/src/mongo/db/transaction/transaction_participant.cpp +++ b/src/mongo/db/transaction/transaction_participant.cpp @@ -3057,7 +3057,12 @@ void TransactionParticipant::Participant::onWriteOpCompletedOnPrimary( repl::UnreplicatedWritesBlock doNotReplicateWrites(opCtx); - updateSessionEntry(opCtx, updateRequest, _sessionId(), sessionTxnRecord.getTxnNum()); + { + // Do not increase consumption metrics during updating session entry, as this + // will cause a tenant to be billed for reading or writing on session transactions table. + ResourceConsumption::PauseMetricsCollectorBlock pauseMetricsCollection(opCtx); + updateSessionEntry(opCtx, updateRequest, _sessionId(), sessionTxnRecord.getTxnNum()); + } _registerUpdateCacheOnCommit( opCtx, std::move(stmtIdsWritten), sessionTxnRecord.getLastWriteOpTime()); } @@ -3074,7 +3079,12 @@ void TransactionParticipant::Participant::onRetryableWriteCloningCompleted( repl::UnreplicatedWritesBlock doNotReplicateWrites(opCtx); - updateSessionEntry(opCtx, updateRequest, _sessionId(), sessionTxnRecord.getTxnNum()); + { + // Do not increase consumption metrics during updating session entry, as this + // will cause a tenant to be billed for reading or writing on session transactions table. + ResourceConsumption::PauseMetricsCollectorBlock pauseMetricsCollection(opCtx); + updateSessionEntry(opCtx, updateRequest, _sessionId(), sessionTxnRecord.getTxnNum()); + } _registerUpdateCacheOnCommit( opCtx, std::move(stmtIdsWritten), sessionTxnRecord.getLastWriteOpTime()); } |