0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-24 00:17:37 +01:00
mongodb/jstests/sharding/txn_with_several_routers.js
Matt Broadstone 771dabd098 SERVER-81339 Convert ReplSetTest and ShardingTest to modules (#26332)
GitOrigin-RevId: 744aa110a53786b23c62ff53f87a1418b5991e8d
2024-08-20 22:00:49 +00:00

201 lines
7.1 KiB
JavaScript

/**
* Sanity checks that illegally attempting to use more than one router for the
* same transaction does not leave the server in an invalid state.
*
* @tags: [uses_transactions, uses_prepare_transaction, uses_multi_shard_transaction]
*/
import {ShardingTest} from "jstests/libs/shardingtest.js";
import {
flushRoutersAndRefreshShardMetadata
} from "jstests/sharding/libs/sharded_transactions_helpers.js";
const dbName = "test";
const collName = "foo";
const ns = dbName + "." + collName;
function removeAllDocumentsFromTestCollection() {
assert.commandWorked(router0.getDB(dbName).foo.deleteMany({}));
}
function runTest(testFn) {
testFn();
removeAllDocumentsFromTestCollection();
}
let st = new ShardingTest({shards: 3, mongos: 2, causallyConsistent: true});
let router0 = st.s0;
let router1 = st.s1;
// Create a sharded collection with a chunk on each shard:
// shard0: [-inf, 0)
// shard1: [0, 10)
// shard2: [10, +inf)
assert.commandWorked(
st.s.adminCommand({enableSharding: dbName, primaryShard: st.shard0.shardName}));
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 0}}));
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 10}}));
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: st.shard1.shardName}));
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 10}, to: st.shard2.shardName}));
flushRoutersAndRefreshShardMetadata(st, {ns});
// Test that trying to run start txn from two routers with the same transaction number fails
// through the second router if they target overlapping shards.
runTest(() => {
let lsid = {id: UUID()};
let txnNumber = 0;
// Start a new transaction on router 0 by inserting a document onto each shard.
assert.commandWorked(router0.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: -5}, {_id: 5}, {_id: 15}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(0),
startTransaction: true,
autocommit: false,
}));
// Try to start a new transaction with the same transaction number on router 1 by inserting
// a document onto each shard.
assert.commandFailedWithCode(router1.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: -50}, {_id: 4}, {_id: 150}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(0),
startTransaction: true,
autocommit: false,
// Because ordered writes are done serially for different shard targets and abort early
// on first error, this can leave the transaction on the other shards open.
// To ensure this router implicitly aborts the transaction on all participants (so
// that the next test case does not encounter an open transaction), make this
// router do an *unordered* write that touches all the same participants as the
// first router touched.
ordered: false,
}),
50911);
// The automatic abort-on-error path will occur when the above
// statement fails, so commit will fail.
assert.commandFailedWithCode(router0.getDB(dbName).adminCommand({
commitTransaction: 1,
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(0),
autocommit: false,
}),
ErrorCodes.NoSuchTransaction);
});
// Test that trying to run start txn from one router and running an operation for that same
// transaction from another router fails through the second router.
runTest(() => {
let lsid = {id: UUID()};
let txnNumber = 0;
let stmtId = 0;
// Start a new transaction on router 0 by inserting a document onto each shard.
assert.commandWorked(router0.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: -5}, {_id: 5}, {_id: 15}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
startTransaction: true,
autocommit: false,
}));
++stmtId;
// Try to continue the same transaction but through router 1. Should fail because no txn
// with this number exists on that router.
assert.commandFailed(router1.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: -50}, {_id: 4}, {_id: 150}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
autocommit: false,
}));
// Commit should succeed since the command from router 2 should never reach the shard.
assert.commandWorked(router0.getDB(dbName).adminCommand({
commitTransaction: 1,
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(0),
autocommit: false,
}));
});
// Test that trying to run start txn from one router, start txn on the second router with the
// same transaction number, and running operations on overlapping shards will lead to failure.
runTest(() => {
let lsid = {id: UUID()};
let txnNumber = 0;
let stmtId = 0;
// Start a new transaction on router 0 by inserting a document onto the first shard
assert.commandWorked(router0.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: -5}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
startTransaction: true,
autocommit: false,
}));
// Start a new transaction on router 1 with the same transaction number, targeting the last
// shard.
assert.commandWorked(router1.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: 15}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
startTransaction: true,
autocommit: false,
}));
++stmtId;
// Try to do an operation on the last shard through router 0. Fails because it sends
// startTxn: true to the new participant, which has already seen an operation from router 1.
// Implicitly aborts the transaction when the error is thrown.
assert.commandFailedWithCode(router0.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: 50}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
autocommit: false,
}),
50911);
// Commit through router 0 should fail.
assert.commandFailedWithCode(router0.getDB(dbName).adminCommand({
commitTransaction: 1,
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
autocommit: false,
}),
ErrorCodes.NoSuchTransaction);
// Commit through router 1 should fail.
assert.commandFailedWithCode(router1.getDB(dbName).adminCommand({
commitTransaction: 1,
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(stmtId),
autocommit: false,
}),
ErrorCodes.NoSuchTransaction);
});
st.stop();