Files
mongo/jstests/core/txns/read_concern.js
Zac 591928c619 SERVER-108478 JS formatted by prettier and remove clang-format (#39656)
GitOrigin-RevId: 6c8f6aded47f260aa4f7c231b17dae3302cb1e04
2025-08-21 17:27:09 +00:00

155 lines
5.9 KiB
JavaScript

// Test that no matter the readConcern specified on a multi-statement transaction, it has snapshot
// isolation. Also test that readConcern linearizable and available are not allowed with a
// transaction.
//
// @tags: [
// # The test runs commands that are not allowed with security token: endSession.
// not_allowed_with_signed_security_token,
// uses_transactions,
// requires_majority_read_concern
// ]
import {withRetryOnTransientTxnError} from "jstests/libs/auto_retry_transaction_in_sharding.js";
const dbName = "test";
const collName = "read_concern";
const testDB = db.getSiblingDB(dbName);
const testColl = testDB[collName];
testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});
assert.commandWorked(testDB.createCollection(testColl.getName(), {writeConcern: {w: "majority"}}));
const sessionOptions = {
causalConsistency: false,
};
const session = db.getMongo().startSession(sessionOptions);
const sessionDb = session.getDatabase(dbName);
function testReadConcernMaintainsSnapshotIsolationInTransaction(readConcern) {
jsTest.log(
"Test that the following multi-document transaction has snapshot isolation with readConcern: " +
tojson(readConcern),
);
withRetryOnTransientTxnError(
() => {
assert.commandWorked(testColl.remove({}, {writeConcern: {w: "majority"}}));
// Start a new transaction with the given readConcern.
session.startTransaction();
let command = {find: collName};
if (readConcern) {
Object.extend(command, {readConcern: readConcern});
}
assert.commandWorked(sessionDb.runCommand(command));
// Insert a document outside of the transaction.
assert.commandWorked(testColl.insert({_id: 0}, {writeConcern: {w: "majority"}}));
// Test that the transaction does not see the new document (it has snapshot isolation).
let res = assert.commandWorked(sessionDb.runCommand({find: collName}));
assert.eq(res.cursor.firstBatch.length, 0, tojson(res));
// Commit the transaction.
assert.commandWorked(session.commitTransaction_forTesting());
},
() => {
session.abortTransaction_forTesting();
},
);
}
testReadConcernMaintainsSnapshotIsolationInTransaction(null);
testReadConcernMaintainsSnapshotIsolationInTransaction({});
testReadConcernMaintainsSnapshotIsolationInTransaction({level: "local"});
testReadConcernMaintainsSnapshotIsolationInTransaction({level: "majority"});
testReadConcernMaintainsSnapshotIsolationInTransaction({level: "snapshot"});
function testReadConcernNotSupportedInTransaction(readConcern) {
jsTest.log("Test that the following readConcern is not supported in a multi-document transaction: " + readConcern);
withRetryOnTransientTxnError(
() => {
// Start a new transaction with the given readConcern.
session.startTransaction();
assert.commandFailedWithCode(
sessionDb.runCommand({find: collName, readConcern: readConcern}),
ErrorCodes.InvalidOptions,
);
// No more operations are allowed in the transaction.
assert.commandFailedWithCode(sessionDb.runCommand({find: collName}), ErrorCodes.NoSuchTransaction);
assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);
},
() => {
session.abortTransaction_forTesting();
},
);
}
testReadConcernNotSupportedInTransaction({level: "available"});
testReadConcernNotSupportedInTransaction({level: "linearizable"});
jsTest.log("Test starting a transaction with an invalid readConcern");
withRetryOnTransientTxnError(
() => {
// Start a new transaction with the given readConcern.
session.startTransaction();
assert.commandFailedWithCode(sessionDb.runCommand({find: collName, readConcern: {level: "bad"}}), [
ErrorCodes.FailedToParse,
ErrorCodes.BadValue,
]);
// No more operations are allowed in the transaction.
assert.commandFailedWithCode(sessionDb.runCommand({find: collName}), ErrorCodes.NoSuchTransaction);
assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);
},
() => {
session.abortTransaction_forTesting();
},
);
jsTest.log("Test specifying readConcern on the second statement in a transaction");
withRetryOnTransientTxnError(
() => {
// Start a new transaction with snapshot readConcern.
session.startTransaction();
assert.commandWorked(sessionDb.runCommand({find: collName, readConcern: {level: "snapshot"}}));
// The second statement cannot specify a readConcern.
assert.commandFailedWithCode(
sessionDb.runCommand({find: collName, readConcern: {level: "snapshot"}}),
ErrorCodes.InvalidOptions,
);
// The transaction is still active and can be committed.
assert.commandWorked(session.commitTransaction_forTesting());
},
() => {
session.abortTransaction_forTesting();
},
);
jsTest.log("Test specifying non-snapshot readConcern on the second statement in a transaction");
withRetryOnTransientTxnError(
() => {
// Start a new transaction with majority readConcern.
session.startTransaction();
assert.commandWorked(sessionDb.runCommand({find: collName, readConcern: {level: "majority"}}));
// The second statement cannot specify a readConcern.
assert.commandFailedWithCode(
sessionDb.runCommand({find: collName, readConcern: {level: "majority"}}),
ErrorCodes.InvalidOptions,
);
// The transaction is still active and can be committed.
assert.commandWorked(session.commitTransaction_forTesting());
session.endSession();
},
() => {
session.abortTransaction_forTesting();
},
);