mirror of https://github.com/mongodb/mongo
191 lines
6.9 KiB
JavaScript
191 lines
6.9 KiB
JavaScript
/**
|
|
* Tests that mongod and mongos
|
|
* - Do not throw an error if a client specifies readConcern for an insert, update, delete, and
|
|
* findAndModify command running as the first command in a transaction.
|
|
* - Throw InvalidOptions if a client specifies readConcern in all other cases.
|
|
* @tags: [requires_fcv_51, uses_transactions, uses_multi_shard_transaction]
|
|
*/
|
|
import {withRetryOnTransientTxnErrorIncrementTxnNum} from "jstests/libs/auto_retry_transaction_in_sharding.js";
|
|
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
|
|
|
const st = new ShardingTest({
|
|
mongos: 1,
|
|
shards: 1,
|
|
});
|
|
const shard0Primary = st.rs0.getPrimary();
|
|
|
|
const kDbName = "testDb";
|
|
assert.commandWorked(st.s.adminCommand({enableSharding: kDbName}));
|
|
|
|
function testWriteCommandOutsideSessionAndTransaction(conn, cmdObj, readConcern, shouldBypassCheck) {
|
|
const cmdObjWithReadConcern = Object.assign({}, cmdObj, {
|
|
readConcern: readConcern,
|
|
});
|
|
const res = conn.getDB(kDbName).runCommand(cmdObjWithReadConcern);
|
|
if (shouldBypassCheck) {
|
|
assert.commandWorked(res);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.InvalidOptions);
|
|
}
|
|
}
|
|
|
|
function testWriteCommandOutsideTransaction(conn, cmdObj, readConcern, shouldBypassCheck) {
|
|
const lsid = {id: UUID()};
|
|
const cmdObjWithReadConcern = Object.assign({}, cmdObj, {readConcern: readConcern, lsid: lsid});
|
|
const res = conn.getDB(kDbName).runCommand(cmdObjWithReadConcern);
|
|
if (shouldBypassCheck) {
|
|
assert.commandWorked(res);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.InvalidOptions);
|
|
}
|
|
}
|
|
|
|
function testWriteCommandInsideTransactionFirstCommand(conn, cmdObj, readConcern) {
|
|
const lsid = {id: UUID()};
|
|
const txnNumber = NumberLong(1);
|
|
|
|
withRetryOnTransientTxnErrorIncrementTxnNum(txnNumber, (txnNum) => {
|
|
const cmdObjWithReadConcern = Object.assign({}, cmdObj, {
|
|
readConcern: readConcern,
|
|
lsid: lsid,
|
|
txnNumber: NumberLong(txnNum),
|
|
startTransaction: true,
|
|
autocommit: false,
|
|
});
|
|
|
|
assert.commandWorked(conn.getDB(kDbName).runCommand(cmdObjWithReadConcern));
|
|
assert.commandWorked(
|
|
conn.getDB(kDbName).adminCommand({
|
|
commitTransaction: 1,
|
|
lsid: lsid,
|
|
txnNumber: NumberLong(txnNum),
|
|
autocommit: false,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
);
|
|
});
|
|
}
|
|
|
|
function testWriteCommandInsideTransactionNotFirstCommand(conn, cmdObj, readConcern, kCollName) {
|
|
const lsid = {id: UUID()};
|
|
const txnNumber = NumberLong(1);
|
|
|
|
withRetryOnTransientTxnErrorIncrementTxnNum(txnNumber, (txnNum) => {
|
|
assert.commandWorked(
|
|
conn.getDB(kDbName).runCommand({
|
|
findAndModify: kCollName,
|
|
query: {x: -10},
|
|
update: {$set: {z: -10}},
|
|
lsid: lsid,
|
|
txnNumber: NumberLong(txnNum),
|
|
startTransaction: true,
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
const cmdObjWithReadConcern = Object.assign({}, cmdObj, {
|
|
readConcern: readConcern,
|
|
lsid: lsid,
|
|
txnNumber: NumberLong(txnNum),
|
|
autocommit: false,
|
|
});
|
|
const res = conn.getDB(kDbName).runCommand(cmdObjWithReadConcern);
|
|
assert.commandFailedWithCode(res, ErrorCodes.InvalidOptions);
|
|
assert.commandWorked(
|
|
conn.getDB(kDbName).adminCommand({
|
|
abortTransaction: 1,
|
|
lsid: lsid,
|
|
txnNumber: NumberLong(txnNum),
|
|
autocommit: false,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
);
|
|
});
|
|
}
|
|
|
|
function runTest(conn, cmdObj, mongosConn, kCollName) {
|
|
const defaultReadConcerns = [{}, {"level": "local"}, {"level": "majority"}, {"level": "available"}];
|
|
|
|
defaultReadConcerns.forEach((defaultReadConcern) => {
|
|
if ("level" in defaultReadConcern) {
|
|
assert.commandWorked(
|
|
mongosConn.adminCommand({
|
|
setDefaultRWConcern: 1,
|
|
defaultReadConcern: defaultReadConcern,
|
|
}),
|
|
);
|
|
jsTest.log("Testing defaultReadConcern " + tojson(defaultReadConcern));
|
|
} else {
|
|
jsTest.log("Testing without specifying defaultReadConcern");
|
|
}
|
|
|
|
const testCases = [
|
|
{supportedInTransaction: true, readConcern: {level: "snapshot"}},
|
|
{supportedInTransaction: true, readConcern: {level: "majority"}},
|
|
{supportedInTransaction: false, readConcern: {level: "linearizable"}},
|
|
{supportedInTransaction: false, readConcern: {level: "available"}},
|
|
// "local" is the default readConcern so it is exempt from the check.
|
|
{supportedInTransaction: true, readConcern: {level: "local"}, shouldBypassCheck: true},
|
|
];
|
|
testCases.forEach((testCase) => {
|
|
jsTest.log("Testing readConcern " + tojson(testCase.readConcern));
|
|
testWriteCommandOutsideSessionAndTransaction(
|
|
conn,
|
|
cmdObj,
|
|
testCase.readConcern,
|
|
testCase.shouldBypassCheck,
|
|
);
|
|
testWriteCommandOutsideTransaction(conn, cmdObj, testCase.readConcern, testCase.shouldBypassCheck);
|
|
if (testCase.supportedInTransaction) {
|
|
testWriteCommandInsideTransactionFirstCommand(conn, cmdObj, testCase.readConcern);
|
|
testWriteCommandInsideTransactionNotFirstCommand(conn, cmdObj, testCase.readConcern, kCollName);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function runTests(conn, mongosConn, kCollName) {
|
|
const kNs = kDbName + "." + kCollName;
|
|
|
|
// Do an insert to force a refresh so the first transaction doesn't fail due to StaleConfig.
|
|
assert.commandWorked(st.s.getCollection(kNs).insert({x: 0}));
|
|
|
|
jsTest.log("Running insert");
|
|
const insertCmdObj = {
|
|
insert: kCollName,
|
|
documents: [{x: -10}],
|
|
};
|
|
runTest(conn, insertCmdObj, mongosConn, kCollName);
|
|
|
|
jsTest.log("Running update");
|
|
const updateCmdObj = {
|
|
update: kCollName,
|
|
updates: [{q: {x: -10}, u: {$set: {y: -10}}}],
|
|
};
|
|
runTest(conn, updateCmdObj, mongosConn, kCollName);
|
|
|
|
jsTest.log("Running delete");
|
|
const deleteCmdObj = {
|
|
delete: kCollName,
|
|
deletes: [{q: {x: -10}, limit: 1}],
|
|
};
|
|
runTest(conn, deleteCmdObj, mongosConn, kCollName);
|
|
|
|
jsTest.log("Running findAndModify");
|
|
const findAndModifyCmdObj = {
|
|
findAndModify: kCollName,
|
|
query: {x: -10},
|
|
update: {$set: {z: -10}},
|
|
};
|
|
runTest(conn, findAndModifyCmdObj, mongosConn, kCollName);
|
|
}
|
|
|
|
jsTest.log("Running tests against mongod");
|
|
const kCollName0 = "testColl0";
|
|
runTests(shard0Primary, st.s, kCollName0);
|
|
|
|
jsTest.log("Running tests against mongos");
|
|
const kCollName1 = "testColl1";
|
|
runTests(st.s, st.s, kCollName1);
|
|
|
|
st.stop();
|