summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2021-05-04 19:29:17 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-13 17:48:10 +0000
commit500a405a5ce235507f56fb47e8d5d4b368d3458d (patch)
treeeecb1020c3049a816d28ffd5461b8d3ecab8686a
parent2070fc76b3604f8c04997862159b0fc721eeb465 (diff)
downloadmongo-500a405a5ce235507f56fb47e8d5d4b368d3458d.tar.gz
SERVER-56550 Require consistent API params in getMore and txns
Transaction-continuing commands must use the same API parameters as the transaction's first command (it is no longer optional), and similarly getMore must use the same as the cursor-creating command.
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml3
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/tenant_migration_multi_stmt_txn_jscore_passthrough.yml4
-rw-r--r--jstests/core/api_params_getmore.js3
-rw-r--r--jstests/core/api_version_unstable_indexes.js4
-rw-r--r--jstests/core/txns/api_params_transaction.js20
-rw-r--r--jstests/noPassthrough/require_api_version.js62
-rw-r--r--jstests/sharding/libs/mongos_api_params_util.js92
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp17
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp6
-rw-r--r--src/mongo/db/initialize_api_parameters.cpp4
-rw-r--r--src/mongo/db/s/transaction_coordinator.cpp10
-rw-r--r--src/mongo/db/s/transaction_coordinator_test.cpp24
-rw-r--r--src/mongo/db/s/transaction_coordinator_util.cpp9
-rw-r--r--src/mongo/db/s/transaction_coordinator_util.h1
-rw-r--r--src/mongo/db/s/txn_two_phase_commit_cmds.cpp4
-rw-r--r--src/mongo/db/service_entry_point_common.cpp22
-rw-r--r--src/mongo/s/commands/strategy.cpp5
-rw-r--r--src/mongo/s/query/cluster_find.cpp15
-rw-r--r--src/mongo/s/transaction_router.cpp18
-rw-r--r--src/mongo/s/transaction_router_test.cpp50
29 files changed, 204 insertions, 205 deletions
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml
index 06c09009e68..4a5f3d1d24a 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml
@@ -273,8 +273,8 @@ selector:
# it, since we won't know whether the cursor was advanced or not.
- requires_getmore
- does_not_support_transactions
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml
index 6316711660c..3c5c0651e7d 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml
@@ -291,8 +291,8 @@ selector:
# Retrying a query can change whether a plan cache entry is active.
- inspects_whether_plan_cache_entry_is_active
- does_not_support_transactions
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml
index 53261e8c935..3f05c322ffd 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml
@@ -341,6 +341,9 @@ selector:
# Retrying a query can change whether a plan cache entry is active.
- inspects_whether_plan_cache_entry_is_active
- does_not_support_transactions
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
+ - uses_api_parameters
executor:
archive:
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml
index 5d39cf739f8..379ec1c5036 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml
@@ -341,7 +341,9 @@ selector:
# Retrying a query can change whether a plan cache entry is active.
- inspects_whether_plan_cache_entry_is_active
- does_not_support_transactions
-
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
+ - uses_api_parameters
executor:
archive:
hooks:
diff --git a/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml b/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml
index 24dfae74d2e..bd280facfe3 100644
--- a/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml
+++ b/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml
@@ -301,8 +301,8 @@ selector:
# Cannot retry a getMore command if a transient transaction or network error occurs during
# it, since we won't know whether the cursor was advanced or not.
- requires_getmore
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
index 9046aab17d3..13b15a3d081 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
@@ -233,8 +233,8 @@ selector:
# Retrying a query can change whether a plan cache entry is active.
- inspects_whether_plan_cache_entry_is_active
- does_not_support_transactions
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml
index 65d73da462c..40650b6e04b 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml
@@ -301,8 +301,8 @@ selector:
# operation and cluster times aren't shared between shells.
# "Cowardly refusing to run test with network retries enabled when it uses startParallelShell()"
- uses_parallel_shell
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml
index 4d22a4b2009..330171262db 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml
@@ -282,8 +282,8 @@ selector:
# operation and cluster times aren't shared between shells.
# "Cowardly refusing to run test with network retries enabled when it uses startParallelShell()"
- uses_parallel_shell
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml
index 696f3642ca4..6acd3672558 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml
@@ -290,8 +290,8 @@ selector:
# operation and cluster times aren't shared between shells.
# "Cowardly refusing to run test with network retries enabled when it uses startParallelShell()"
- uses_parallel_shell
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
diff --git a/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
index 64bcbde630d..9689d16ec5d 100644
--- a/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
@@ -257,8 +257,8 @@ selector:
# Retrying a query can change whether a plan cache entry is active.
- inspects_whether_plan_cache_entry_is_active
- does_not_support_transactions
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
executor:
diff --git a/buildscripts/resmokeconfig/suites/tenant_migration_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/tenant_migration_multi_stmt_txn_jscore_passthrough.yml
index fe45a3fd6b6..8b480876583 100644
--- a/buildscripts/resmokeconfig/suites/tenant_migration_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/tenant_migration_multi_stmt_txn_jscore_passthrough.yml
@@ -347,8 +347,8 @@ selector:
- inspects_whether_plan_cache_entry_is_active
# $out is not supported in transactions
- uses_$out
- # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
- # cannot be run with transactions.
+ # Transaction-continuing commands must use the same API parameters as the first command, so tests
+ # that use API parameters cannot be run with transactions.
- uses_api_parameters
- does_not_support_transactions
diff --git a/jstests/core/api_params_getmore.js b/jstests/core/api_params_getmore.js
index 0d4a0df3e58..40b4454bab1 100644
--- a/jstests/core/api_params_getmore.js
+++ b/jstests/core/api_params_getmore.js
@@ -38,8 +38,7 @@ for (const initParams of apiParamCombos) {
for (const continueParams of apiParamCombos) {
const findCmd = addApiParams({find: testColl.getName(), batchSize: 1}, initParams);
const cursorId = assert.commandWorked(testDB.runCommand(findCmd)).cursor.id;
- // TODO (SERVER-56550): Remove "!continueParams.apiVersion".
- const compatibleParams = !continueParams.apiVersion || continueParams === initParams;
+ const compatibleParams = (continueParams === initParams);
const getMoreCmd =
addApiParams({getMore: cursorId, collection: testColl.getName()}, continueParams);
diff --git a/jstests/core/api_version_unstable_indexes.js b/jstests/core/api_version_unstable_indexes.js
index a4e3b1f3d20..8c23b56acab 100644
--- a/jstests/core/api_version_unstable_indexes.js
+++ b/jstests/core/api_version_unstable_indexes.js
@@ -5,7 +5,7 @@
*
* @tags: [
* requires_fcv_50,
- * uses_api_parameters,
+ * uses_api_parameters
* ]
*/
@@ -44,7 +44,7 @@ assert.commandFailedWithCode(db.runCommand({
}),
ErrorCodes.NoQueryExecutionPlans);
-// Can not hint a sparse index which is exclued from API version 1 with 'apiStrict: true'.
+// Can not hint a sparse index which is excluded from API version 1 with 'apiStrict: true'.
assert.commandFailedWithCode(db.runCommand({
"find": collName,
"filter": {views: 50},
diff --git a/jstests/core/txns/api_params_transaction.js b/jstests/core/txns/api_params_transaction.js
index 929f5087a5e..6b141b280ff 100644
--- a/jstests/core/txns/api_params_transaction.js
+++ b/jstests/core/txns/api_params_transaction.js
@@ -38,9 +38,7 @@ function addApiParams(obj, params) {
for (const txnInitiatingParams of apiParamCombos) {
for (const txnContinuingParams of apiParamCombos) {
for (const txnEndingCmdName of ["commitTransaction", "abortTransaction"]) {
- // TODO (SERVER-56550): Remove "!txnContinuingParams.apiVersion".
- const compatibleParams =
- !txnContinuingParams.apiVersion || txnContinuingParams === txnInitiatingParams;
+ const compatibleParams = (txnContinuingParams === txnInitiatingParams);
const session = db.getMongo().startSession();
const sessionDb = session.getDatabase(dbName);
@@ -80,8 +78,20 @@ for (const txnInitiatingParams of apiParamCombos) {
checkCommand(session.getDatabase("admin"), txnEndingCmd);
- // Clean up.
- session.abortTransaction();
+ if (!compatibleParams) {
+ jsTestLog('Cleaning up');
+ // Clean up by calling abortTransaction with the right API parameters.
+ const abortCmd = {
+ abortTransaction: 1,
+ txnNumber: session.getTxnNumber_forTesting(),
+ autocommit: false
+ };
+ const cleanupReply = session.getDatabase("admin").runCommand(
+ addApiParams(abortCmd, txnInitiatingParams));
+ jsTestLog(`Cleanup reply ${tojson(cleanupReply)}`);
+ }
+
+ session.endSession();
}
}
}
diff --git a/jstests/noPassthrough/require_api_version.js b/jstests/noPassthrough/require_api_version.js
index 640a149adab..aafe763f3a4 100644
--- a/jstests/noPassthrough/require_api_version.js
+++ b/jstests/noPassthrough/require_api_version.js
@@ -14,7 +14,7 @@
(function() {
"use strict";
-function runTest(db, supportsTransctions, isMongos, writeConcern = {}, secondaries = []) {
+function runTest(db, supportsTransctions, writeConcern = {}, secondaries = []) {
assert.commandWorked(db.runCommand({setParameter: 1, requireApiVersion: true}));
for (const secondary of secondaries) {
assert.commandWorked(secondary.adminCommand({setParameter: 1, requireApiVersion: true}));
@@ -38,65 +38,82 @@ function runTest(db, supportsTransctions, isMongos, writeConcern = {}, secondari
/*
* "getMore" accepts apiVersion.
- *
- * TODO (SERVER-56550): Test that getMore fails *without* API params.
*/
assert.commandWorked(db.runCommand(
{insert: "collection", documents: [{}, {}, {}], apiVersion: "1", writeConcern}));
let reply = db.runCommand({find: "collection", batchSize: 1, apiVersion: "1"});
assert.commandWorked(reply);
- assert.commandWorked(
- db.runCommand({getMore: reply.cursor.id, collection: "collection", batchSize: 1}));
+ assert.commandFailedWithCode(
+ db.runCommand({getMore: reply.cursor.id, collection: "collection"}), 498870);
assert.commandWorked(
db.runCommand({getMore: reply.cursor.id, collection: "collection", apiVersion: "1"}));
if (supportsTransctions) {
/*
- * Transaction-starting commands must have apiVersion, transaction-continuing commands may.
- *
- * TODO (SERVER-56550): Test that transaction-continuing commands fail *without* API params.
+ * Commands in transactions require API version.
*/
const session = db.getMongo().startSession({causalConsistency: false});
const sessionDb = session.getDatabase(db.getName());
+ assert.commandFailedWithCode(sessionDb.runCommand({
+ find: "collection",
+ batchSize: 1,
+ txnNumber: NumberLong(0),
+ stmtId: NumberInt(2),
+ startTransaction: true,
+ autocommit: false
+ }),
+ 498870);
reply = sessionDb.runCommand({
find: "collection",
batchSize: 1,
apiVersion: "1",
- txnNumber: NumberLong(0),
+ txnNumber: NumberLong(1),
stmtId: NumberInt(0),
startTransaction: true,
autocommit: false
});
assert.commandWorked(reply);
- assert.commandWorked(sessionDb.runCommand({
+ assert.commandFailedWithCode(sessionDb.runCommand({
getMore: reply.cursor.id,
collection: "collection",
- txnNumber: NumberLong(0),
+ txnNumber: NumberLong(1),
stmtId: NumberInt(1),
autocommit: false
- }));
+ }),
+ 498870);
assert.commandWorked(sessionDb.runCommand({
- find: "collection",
- batchSize: 1,
- txnNumber: NumberLong(0),
- stmtId: NumberInt(2),
- autocommit: false
+ getMore: reply.cursor.id,
+ collection: "collection",
+ txnNumber: NumberLong(1),
+ stmtId: NumberInt(1),
+ autocommit: false,
+ apiVersion: "1"
}));
+
+ assert.commandFailedWithCode(
+ sessionDb.runCommand(
+ {commitTransaction: 1, txnNumber: NumberLong(1), autocommit: false}),
+ 498870);
+
assert.commandWorked(sessionDb.runCommand(
- {commitTransaction: 1, apiVersion: "1", txnNumber: NumberLong(0), autocommit: false}));
+ {commitTransaction: 1, apiVersion: "1", txnNumber: NumberLong(1), autocommit: false}));
// Start a new txn so we can test abortTransaction.
reply = sessionDb.runCommand({
find: "collection",
apiVersion: "1",
- txnNumber: NumberLong(1),
+ txnNumber: NumberLong(2),
stmtId: NumberInt(0),
startTransaction: true,
autocommit: false
});
assert.commandWorked(reply);
+ assert.commandFailedWithCode(
+ sessionDb.runCommand(
+ {abortTransaction: 1, txnNumber: NumberLong(2), autocommit: false}),
+ 498870);
assert.commandWorked(sessionDb.runCommand(
- {abortTransaction: 1, apiVersion: "1", txnNumber: NumberLong(1), autocommit: false}));
+ {abortTransaction: 1, apiVersion: "1", txnNumber: NumberLong(2), autocommit: false}));
}
assert.commandWorked(
@@ -139,7 +156,7 @@ function requireApiVersionOnShardOrConfigServerTest() {
requireApiVersionOnShardOrConfigServerTest();
const mongod = MongoRunner.runMongod();
-runTest(mongod.getDB("admin"), false /* supportsTransactions */, false /* isMongos */);
+runTest(mongod.getDB("admin"), false /* supportsTransactions */);
MongoRunner.stopMongod(mongod);
const rst = new ReplSetTest({nodes: 3});
@@ -148,12 +165,11 @@ rst.initiateWithHighElectionTimeout();
runTest(rst.getPrimary().getDB("admin"),
true /* supportsTransactions */,
- false /* isMongos */,
{w: "majority"} /* writeConcern */,
rst.getSecondaries());
rst.stopSet();
const st = new ShardingTest({});
-runTest(st.s0.getDB("admin"), true /* supportsTransactions */, true /* isMongos */);
+runTest(st.s0.getDB("admin"), true /* supportsTransactions */);
st.stop();
}());
diff --git a/jstests/sharding/libs/mongos_api_params_util.js b/jstests/sharding/libs/mongos_api_params_util.js
index 45999197cf0..97c5b57f099 100644
--- a/jstests/sharding/libs/mongos_api_params_util.js
+++ b/jstests/sharding/libs/mongos_api_params_util.js
@@ -1394,7 +1394,7 @@ let MongosAPIParametersUtil = (function() {
}
})();
- function checkPrimaryLog(conn, commandName, apiVersion, apiStrict, apiDeprecationErrors) {
+ function checkPrimaryLog(conn, commandName, apiParameters) {
let msg;
assert.soon(
() => {
@@ -1412,8 +1412,9 @@ let MongosAPIParametersUtil = (function() {
continue;
lastCommandInvocation = args;
- if (args.apiVersion !== apiVersion || args.apiStrict !== apiStrict ||
- args.apiDeprecationErrors !== apiDeprecationErrors)
+ if (args.apiVersion !== apiParameters.apiVersion ||
+ args.apiStrict !== apiParameters.apiStrict ||
+ args.apiDeprecationErrors !== apiParameters.apiDeprecationErrors)
continue;
// Found a match.
@@ -1421,16 +1422,13 @@ let MongosAPIParametersUtil = (function() {
}
if (lastCommandInvocation === undefined) {
- msg = `Primary didn't log ${commandName} with apiVersion ${apiVersion},` +
- ` apiStrict ${apiStrict},` +
- ` apiDeprecationErrors ${apiDeprecationErrors}.`;
+ msg = `Primary didn't log ${commandName} with API parameters ` +
+ `${tojson(apiParameters)}.`;
return false;
}
- msg = `Primary didn't log ${commandName} with apiVersion ${apiVersion},` +
- ` apiStrict ${apiStrict},` +
- ` apiDeprecationErrors ${apiDeprecationErrors}.` +
- ` Last invocation of ${commandName} was` +
+ msg = `Primary didn't log ${commandName} with API parameters ` +
+ `${tojson(apiParameters)}. Last invocation of ${commandName} was` +
` ${tojson(lastCommandInvocation)}`;
return false;
},
@@ -1446,17 +1444,17 @@ let MongosAPIParametersUtil = (function() {
// progress.
let testInstances = [];
- for (const [apiVersion, apiStrict, apiDeprecationErrors] of [
- [undefined, undefined, undefined],
- ["1", undefined, undefined],
- ["1", undefined, false],
- ["1", undefined, true],
- ["1", false, undefined],
- ["1", false, false],
- ["1", false, true],
- ["1", true, undefined],
- ["1", true, false],
- ["1", true, true],
+ for (const apiParameters
+ of [{},
+ {apiVersion: "1"},
+ {apiVersion: "1", apiDeprecationErrors: false},
+ {apiVersion: "1", apiDeprecationErrors: true},
+ {apiVersion: "1", apiStrict: false},
+ {apiVersion: "1", apiStrict: false, apiDeprecationErrors: false},
+ {apiVersion: "1", apiStrict: false, apiDeprecationErrors: true},
+ {apiVersion: "1", apiStrict: true},
+ {apiVersion: "1", apiStrict: true, apiDeprecationErrors: false},
+ {apiVersion: "1", apiStrict: true, apiDeprecationErrors: true},
]) {
for (const testCase of testCases) {
if (testCase.skip)
@@ -1478,13 +1476,11 @@ let MongosAPIParametersUtil = (function() {
if (!supportsCommittedReads && runOrExplain.requiresCommittedReads)
continue;
- if (apiStrict && !runOrExplain.inAPIVersion1)
+ if (apiParameters.apiStrict && !runOrExplain.inAPIVersion1)
continue;
testInstances.push({
- apiVersion: apiVersion,
- apiStrict: apiStrict,
- apiDeprecationErrors: apiDeprecationErrors,
+ apiParameters: apiParameters,
commandName: testCase.commandName,
runOrExplain: runOrExplain
});
@@ -1493,8 +1489,7 @@ let MongosAPIParametersUtil = (function() {
}
for (let i = 0; i < testInstances.length; ++i) {
- const {apiVersion, apiStrict, apiDeprecationErrors, commandName, runOrExplain} =
- testInstances[i];
+ const {apiParameters, commandName, runOrExplain} = testInstances[i];
if (shardedCollection) {
jsTestLog("Sharded setup");
@@ -1526,13 +1521,7 @@ let MongosAPIParametersUtil = (function() {
const configPrimary = st.configRS.getPrimary();
const shardZeroPrimary = st.rs0.getPrimary();
- const context = {
- apiParameters: {
- apiVersion: apiVersion,
- apiStrict: apiStrict,
- apiDeprecationErrors: apiDeprecationErrors
- }
- };
+ const context = {apiParameters: apiParameters};
const commandDbName = runOrExplain.runsAgainstAdminDb ? "admin" : "db";
if (inTransaction) {
@@ -1551,18 +1540,8 @@ let MongosAPIParametersUtil = (function() {
// Make a copy of the test's command body, and set its API parameters.
const commandBody = runOrExplain.command(context);
- const commandWithAPIParams = Object.assign({}, commandBody);
- if (apiVersion !== undefined) {
- commandWithAPIParams.apiVersion = apiVersion;
- }
-
- if (apiStrict !== undefined) {
- commandWithAPIParams.apiStrict = apiStrict;
- }
-
- if (apiDeprecationErrors !== undefined) {
- commandWithAPIParams.apiDeprecationErrors = apiDeprecationErrors;
- }
+ const commandWithAPIParams =
+ Object.assign(Object.assign({}, commandBody), apiParameters);
assert.commandWorked(configPrimary.adminCommand({clearLog: "global"}));
assert.commandWorked(shardZeroPrimary.adminCommand({clearLog: "global"}));
@@ -1583,7 +1562,14 @@ let MongosAPIParametersUtil = (function() {
assert.commandWorked(res);
if (inTransaction) {
- assert.commandWorked(context.session.commitTransaction_forTesting());
+ const commitCmd = {
+ commitTransaction: 1,
+ txnNumber: context.session.getTxnNumber_forTesting(),
+ autocommit: false
+ };
+
+ assert.commandWorked(context.session.getDatabase("admin").runCommand(
+ Object.assign(commitCmd, apiParameters)));
}
const configServerCommandName = runOrExplain.configServerCommandName;
@@ -1591,20 +1577,12 @@ let MongosAPIParametersUtil = (function() {
if (configServerCommandName) {
jsTestLog(`Check for ${configServerCommandName} in config server's log`);
- checkPrimaryLog(configPrimary,
- configServerCommandName,
- apiVersion,
- apiStrict,
- apiDeprecationErrors);
+ checkPrimaryLog(configPrimary, configServerCommandName, apiParameters);
}
if (shardCommandName) {
jsTestLog(`Check for ${shardCommandName} in shard server's log`);
- checkPrimaryLog(shardZeroPrimary,
- shardCommandName,
- apiVersion,
- apiStrict,
- apiDeprecationErrors);
+ checkPrimaryLog(shardZeroPrimary, shardCommandName, apiParameters);
}
setLogVerbosity([configPrimary, shardZeroPrimary, st.rs1.getPrimary()],
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 9624761a5e9..1772d5b3a25 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -225,18 +225,11 @@ void setUpOperationContextStateForGetMore(OperationContext* opCtx,
opCtx->setWriteConcern(cursor.getWriteConcernOptions());
auto apiParamsFromClient = APIParameters::get(opCtx);
- // TODO (SERVER-56550): Do this check even if !apiParamsFromClient.getParamsPassed().
- if (apiParamsFromClient.getParamsPassed()) {
- uassert(
- ErrorCodes::APIMismatchError,
- "API param conflict: getMore used params {}, the cursor-creating command used {}"_format(
- apiParamsFromClient.toBSON().toString(),
- cursor.getAPIParameters().toBSON().toString()),
- apiParamsFromClient == cursor.getAPIParameters());
- }
-
- // TODO (SERVER-56550): Remove.
- APIParameters::get(opCtx) = cursor.getAPIParameters();
+ uassert(
+ ErrorCodes::APIMismatchError,
+ "API parameter mismatch: getMore used params {}, the cursor-creating command used {}"_format(
+ apiParamsFromClient.toBSON().toString(), cursor.getAPIParameters().toBSON().toString()),
+ apiParamsFromClient == cursor.getAPIParameters());
setUpOperationDeadline(opCtx, cursor, cmd, disableAwaitDataFailpointActive);
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index d8c7582ad16..9c42dec4997 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -885,10 +885,8 @@ private:
_sessionInfo.serialize(cmdBuilder);
}
- if (_state == TransactionState::kInit || !_isReplSet) {
- // Set a default apiVersion for all UMC commands
- cmdBuilder->append("apiVersion", kOne);
- }
+ // Set a default apiVersion for all UMC commands
+ cmdBuilder->append("apiVersion", kOne);
auto svcCtx = _client->getServiceContext();
auto sep = svcCtx->getServiceEntryPoint();
diff --git a/src/mongo/db/initialize_api_parameters.cpp b/src/mongo/db/initialize_api_parameters.cpp
index 3962494fae6..812a452ea31 100644
--- a/src/mongo/db/initialize_api_parameters.cpp
+++ b/src/mongo/db/initialize_api_parameters.cpp
@@ -107,9 +107,7 @@ void enforceRequireAPIVersion(OperationContext* opCtx, Command* command) {
auto isInternalClient =
!client->session() || (client->session()->getTags() & transport::Session::kInternalClient);
- // TODO (SERVER-56550): Don't excuse getMore or transaction-continuing commands.
- if (gRequireApiVersion.load() && !opCtx->getClient()->isInDirectClient() && !isInternalClient &&
- command->getName() != "getMore" && !opCtx->isContinuingMultiDocumentTransaction()) {
+ if (gRequireApiVersion.load() && !opCtx->getClient()->isInDirectClient() && !isInternalClient) {
uassert(
498870,
"The apiVersion parameter is required, please configure your MongoClient's API version",
diff --git a/src/mongo/db/s/transaction_coordinator.cpp b/src/mongo/db/s/transaction_coordinator.cpp
index a551447964a..35d7f5f00f8 100644
--- a/src/mongo/db/s/transaction_coordinator.cpp
+++ b/src/mongo/db/s/transaction_coordinator.cpp
@@ -177,7 +177,7 @@ TransactionCoordinator::TransactionCoordinator(OperationContext* operationContex
std::move(opTime));
})
.thenRunOn(Grid::get(_serviceContext)->getExecutorPool()->getFixedExecutor())
- .then([this] {
+ .then([this, apiParams] {
{
stdx::lock_guard<Latch> lg(_mutex);
_participantsDurable = true;
@@ -204,8 +204,12 @@ TransactionCoordinator::TransactionCoordinator(OperationContext* operationContex
return Future<void>::makeReady();
}
- return txn::sendPrepare(
- _serviceContext, *_sendPrepareScheduler, _lsid, _txnNumber, *_participants)
+ return txn::sendPrepare(_serviceContext,
+ *_sendPrepareScheduler,
+ _lsid,
+ _txnNumber,
+ apiParams,
+ *_participants)
.then([this](PrepareVoteConsensus consensus) mutable {
{
stdx::lock_guard<Latch> lg(_mutex);
diff --git a/src/mongo/db/s/transaction_coordinator_test.cpp b/src/mongo/db/s/transaction_coordinator_test.cpp
index 42a4da83b4c..5b060c8d968 100644
--- a/src/mongo/db/s/transaction_coordinator_test.cpp
+++ b/src/mongo/db/s/transaction_coordinator_test.cpp
@@ -409,7 +409,8 @@ TEST_F(TransactionCoordinatorDriverTest,
TEST_F(TransactionCoordinatorDriverTest,
SendPrepareReturnsAbortDecisionWhenFirstParticipantVotesAbortAndSecondVotesCommit) {
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
onCommands({[&](const executor::RemoteCommandRequest& request) { return kNoSuchTransaction; },
[&](const executor::RemoteCommandRequest& request) { return kPrepareOk; }});
@@ -424,7 +425,8 @@ TEST_F(TransactionCoordinatorDriverTest,
TEST_F(TransactionCoordinatorDriverTest,
SendPrepareReturnsAbortDecisionWhenFirstParticipantVotesCommitAndSecondVotesAbort) {
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
assertPrepareSentAndRespondWithSuccess();
assertPrepareSentAndRespondWithNoSuchTransaction();
@@ -438,7 +440,8 @@ TEST_F(TransactionCoordinatorDriverTest,
TEST_F(TransactionCoordinatorDriverTest,
SendPrepareReturnsAbortDecisionWhenBothParticipantsVoteAbort) {
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
onCommands({[&](const executor::RemoteCommandRequest& request) { return kNoSuchTransaction; },
[&](const executor::RemoteCommandRequest& request) { return kNoSuchTransaction; }});
@@ -455,7 +458,8 @@ TEST_F(TransactionCoordinatorDriverTest,
const auto maxPrepareTimestamp = Timestamp(2, 1);
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
assertPrepareSentAndRespondWithSuccess(firstPrepareTimestamp);
assertPrepareSentAndRespondWithSuccess(maxPrepareTimestamp);
@@ -472,7 +476,8 @@ TEST_F(TransactionCoordinatorDriverTest,
const auto maxPrepareTimestamp = Timestamp(2, 1);
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
assertPrepareSentAndRespondWithSuccess(maxPrepareTimestamp);
assertPrepareSentAndRespondWithSuccess(firstPrepareTimestamp);
@@ -489,7 +494,8 @@ TEST_F(TransactionCoordinatorDriverTest,
const auto maxPrepareTimestamp = Timestamp(2, 1);
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
assertPrepareSentAndRespondWithSuccess(firstPrepareTimestamp);
assertPrepareSentAndRespondWithSuccess(maxPrepareTimestamp);
@@ -505,7 +511,8 @@ TEST_F(TransactionCoordinatorDriverTest,
const auto timestamp = Timestamp(1, 1);
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
assertPrepareSentAndRespondWithSuccess(timestamp);
assertCommandSentAndRespondWith(
@@ -521,7 +528,8 @@ TEST_F(TransactionCoordinatorDriverTest,
TEST_F(TransactionCoordinatorDriverTest,
SendPrepareReturnsErrorWhenOneShardReturnsReadConcernMajorityNotEnabled) {
txn::AsyncWorkScheduler aws(getServiceContext());
- auto future = txn::sendPrepare(getServiceContext(), aws, _lsid, _txnNumber, kTwoShardIdList);
+ auto future = txn::sendPrepare(
+ getServiceContext(), aws, _lsid, _txnNumber, APIParameters(), kTwoShardIdList);
assertPrepareSentAndRespondWithSuccess(Timestamp(100, 1));
assertCommandSentAndRespondWith(
diff --git a/src/mongo/db/s/transaction_coordinator_util.cpp b/src/mongo/db/s/transaction_coordinator_util.cpp
index f8e7bbb42c9..0bc0d019c6c 100644
--- a/src/mongo/db/s/transaction_coordinator_util.cpp
+++ b/src/mongo/db/s/transaction_coordinator_util.cpp
@@ -233,12 +233,15 @@ Future<PrepareVoteConsensus> sendPrepare(ServiceContext* service,
txn::AsyncWorkScheduler& scheduler,
const LogicalSessionId& lsid,
TxnNumber txnNumber,
+ const APIParameters& apiParams,
const txn::ParticipantsList& participants) {
PrepareTransaction prepareTransaction;
prepareTransaction.setDbName(NamespaceString::kAdminDb);
- auto prepareObj = prepareTransaction.toBSON(
- BSON("lsid" << lsid.toBSON() << "txnNumber" << txnNumber << "autocommit" << false
- << WriteConcernOptions::kWriteConcernField << WriteConcernOptions::Majority));
+ BSONObjBuilder bob(BSON("lsid" << lsid.toBSON() << "txnNumber" << txnNumber << "autocommit"
+ << false << WriteConcernOptions::kWriteConcernField
+ << WriteConcernOptions::Majority));
+ apiParams.appendInfo(&bob);
+ auto prepareObj = prepareTransaction.toBSON(bob.obj());
std::vector<Future<PrepareResponse>> responses;
diff --git a/src/mongo/db/s/transaction_coordinator_util.h b/src/mongo/db/s/transaction_coordinator_util.h
index 5563e5a29e0..36a2b2fcf3d 100644
--- a/src/mongo/db/s/transaction_coordinator_util.h
+++ b/src/mongo/db/s/transaction_coordinator_util.h
@@ -98,6 +98,7 @@ Future<PrepareVoteConsensus> sendPrepare(ServiceContext* service,
txn::AsyncWorkScheduler& scheduler,
const LogicalSessionId& lsid,
TxnNumber txnNumber,
+ const APIParameters& apiParams,
const txn::ParticipantsList& participants);
/**
diff --git a/src/mongo/db/s/txn_two_phase_commit_cmds.cpp b/src/mongo/db/s/txn_two_phase_commit_cmds.cpp
index e8105484ca2..41d47497a77 100644
--- a/src/mongo/db/s/txn_two_phase_commit_cmds.cpp
+++ b/src/mongo/db/s/txn_two_phase_commit_cmds.cpp
@@ -51,6 +51,10 @@ MONGO_FAIL_POINT_DEFINE(participantReturnNetworkErrorForPrepareAfterExecutingPre
class PrepareTransactionCmd : public TypedCommand<PrepareTransactionCmd> {
public:
+ bool acceptsAnyApiVersionParameters() const override {
+ return true;
+ }
+
class PrepareTimestamp {
public:
PrepareTimestamp(Timestamp timestamp) : _timestamp(std::move(timestamp)) {}
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index e60c89a3b65..d539a17b0d3 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -901,18 +901,15 @@ void CheckoutSessionAndInvokeCommand::_checkOutSession() {
_sessionTxnState = std::make_unique<MongoDOperationContextSession>(opCtx);
_txnParticipant.emplace(TransactionParticipant::get(opCtx));
- // TODO (SERVER-56550): Do this check even if !apiParamsFromClient.getParamsPassed().
auto apiParamsFromClient = APIParameters::get(opCtx);
- if (apiParamsFromClient.getParamsPassed()) {
- auto apiParamsFromTxn = _txnParticipant->getAPIParameters(opCtx);
- uassert(
- ErrorCodes::APIMismatchError,
- "API param conflict: {} used params {}, the transaction's first command used {}"_format(
- invocation->definition()->getName(),
- apiParamsFromClient.toBSON().toString(),
- apiParamsFromTxn.toBSON().toString()),
- apiParamsFromTxn == apiParamsFromClient);
- }
+ auto apiParamsFromTxn = _txnParticipant->getAPIParameters(opCtx);
+ uassert(
+ ErrorCodes::APIMismatchError,
+ "API parameter mismatch: {} used params {}, the transaction's first command used {}"_format(
+ invocation->definition()->getName(),
+ apiParamsFromClient.toBSON().toString(),
+ apiParamsFromTxn.toBSON().toString()),
+ apiParamsFromTxn == apiParamsFromClient);
if (!opCtx->getClient()->isInDirectClient()) {
bool beganOrContinuedTxn{false};
@@ -1000,9 +997,6 @@ void CheckoutSessionAndInvokeCommand::_checkOutSession() {
}
}
}
-
- // Use the API parameters that were stored when the transaction was initiated.
- APIParameters::get(opCtx) = _txnParticipant->getAPIParameters(opCtx);
}
void CheckoutSessionAndInvokeCommand::_tapError(Status status) {
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index 4429987d9f3..e16e8c7340a 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -600,6 +600,8 @@ Status ParseAndRunCommand::RunInvocation::_setup() {
ClientMetadata::setFromMetadata(opCtx->getClient(), metaElem);
}
+ enforceRequireAPIVersion(opCtx, command);
+
auto& apiParams = APIParameters::get(opCtx);
auto& apiVersionMetrics = APIVersionMetrics::get(opCtx->getServiceContext());
if (auto clientMetadata = ClientMetadata::get(opCtx->getClient())) {
@@ -814,9 +816,6 @@ Status ParseAndRunCommand::RunInvocation::_setup() {
// the execution needs to adjust its behavior based on this.
opCtx->setIsStartingMultiDocumentTransaction(startTransaction);
- // Once API params and txn state are set on opCtx, enforce the "requireApiVersion" setting.
- enforceRequireAPIVersion(opCtx, command);
-
command->incrementCommandsExecuted();
if (command->shouldAffectCommandCounter()) {
diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp
index 4f9f6ddd446..9f2665d95ac 100644
--- a/src/mongo/s/query/cluster_find.cpp
+++ b/src/mongo/s/query/cluster_find.cpp
@@ -455,18 +455,11 @@ Status setUpOperationContextStateForGetMore(OperationContext* opCtx,
}
auto apiParamsFromClient = APIParameters::get(opCtx);
- // TODO (SERVER-56550): Do this check even if !apiParamsFromClient.getParamsPassed().
- if (apiParamsFromClient.getParamsPassed()) {
- uassert(
- ErrorCodes::APIMismatchError,
- "API param conflict: getMore used params {}, the cursor-creating command used {}"_format(
- apiParamsFromClient.toBSON().toString(),
- cursor->getAPIParameters().toBSON().toString()),
+ uassert(ErrorCodes::APIMismatchError,
+ "API parameter mismatch: getMore used params {}, the cursor-creating command "
+ "used {}"_format(apiParamsFromClient.toBSON().toString(),
+ cursor->getAPIParameters().toBSON().toString()),
apiParamsFromClient == cursor->getAPIParameters());
- }
-
- // TODO (SERVER-56550): Remove.
- APIParameters::get(opCtx) = cursor->getAPIParameters();
// If the originating command had a 'comment' field, we extract it and set it on opCtx. Note
// that if the 'getMore' command itself has a 'comment' field, we give precedence to it.
diff --git a/src/mongo/s/transaction_router.cpp b/src/mongo/s/transaction_router.cpp
index 57f5e636a13..160bce00191 100644
--- a/src/mongo/s/transaction_router.cpp
+++ b/src/mongo/s/transaction_router.cpp
@@ -31,6 +31,8 @@
#include "mongo/platform/basic.h"
+#include <fmt/format.h>
+
#include "mongo/s/transaction_router.h"
#include "mongo/client/read_preference.h"
@@ -60,6 +62,8 @@
namespace mongo {
namespace {
+using namespace fmt::literals;
+
// TODO SERVER-39704: Remove this fail point once the router can safely retry within a transaction
// on stale version and snapshot errors.
MONGO_FAIL_POINT_DEFINE(enableStaleVersionAndSnapshotRetriesWithinTransactions);
@@ -900,13 +904,13 @@ void TransactionRouter::Router::beginOrContinueTxn(OperationContext* opCtx,
} else if (txnNumber == o().txnNumber) {
// This is the same transaction as the one in progress.
auto apiParamsFromClient = APIParameters::get(opCtx);
- // TODO (SERVER-56550): Do this check even if !apiParamsFromClient.getParamsPassed().
- if (apiParamsFromClient.getParamsPassed() &&
- (action == TransactionActions::kContinue || action == TransactionActions::kCommit)) {
- uassert(ErrorCodes::APIMismatchError,
- "A transaction-continuing command must use the same API parameters as "
- "the first command in the transaction",
- apiParamsFromClient == o().apiParameters);
+ if (action == TransactionActions::kContinue || action == TransactionActions::kCommit) {
+ uassert(
+ ErrorCodes::APIMismatchError,
+ "API parameter mismatch: transaction-continuing command used {}, the transaction's"
+ " first command used {}"_format(apiParamsFromClient.toBSON().toString(),
+ o().apiParameters.toBSON().toString()),
+ apiParamsFromClient == o().apiParameters);
}
switch (action) {
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index b1edf1164d8..4e286306a4d 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -719,7 +719,6 @@ TEST_F(TransactionRouterTestWithDefaultSession, AttachTxnValidatesReadConcernIfA
}
}
-// TODO (SERVER-56550): Test that API parameters are required in txn-continuing commands.
TEST_F(TransactionRouterTestWithDefaultSession, SameAPIParametersAfterFirstStatement) {
APIParameters apiParameters = APIParameters();
apiParameters.setAPIVersion("1");
@@ -758,6 +757,27 @@ TEST_F(TransactionRouterTestWithDefaultSession, DifferentAPIParametersAfterFirst
ErrorCodes::APIMismatchError);
}
+TEST_F(TransactionRouterTestWithDefaultSession, NoAPIParametersAfterFirstStatement) {
+ APIParameters apiParameters = APIParameters();
+ apiParameters.setAPIVersion("1");
+ APIParameters::get(operationContext()) = apiParameters;
+ TxnNumber txnNum{3};
+
+ auto txnRouter = TransactionRouter::get(operationContext());
+ txnRouter.beginOrContinueTxn(
+ operationContext(), txnNum, TransactionRouter::TransactionActions::kStart);
+ txnRouter.setDefaultAtClusterTime(operationContext());
+
+ // Can't continue without params. (Must reset readConcern from "snapshot".)
+ APIParameters::get(operationContext()) = APIParameters();
+ repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
+ ASSERT_THROWS_CODE(
+ txnRouter.beginOrContinueTxn(
+ operationContext(), txnNum, TransactionRouter::TransactionActions::kContinue),
+ DBException,
+ ErrorCodes::APIMismatchError);
+}
+
TEST_F(TransactionRouterTestWithDefaultSession, CannotSpecifyReadConcernAfterFirstStatement) {
TxnNumber txnNum{3};
@@ -2300,34 +2320,6 @@ TEST_F(TransactionRouterTestWithDefaultSession, ContinueOnlyOnStaleVersionOnFirs
ASSERT_FALSE(txnRouter.canContinueOnStaleShardOrDbError("update", kStaleConfigStatus));
}
-TEST_F(TransactionRouterTestWithDefaultSession,
- ContinuingTransactionPlacesItsAPIParametersOnOpCtx) {
- APIParameters apiParams = APIParameters();
- apiParams.setAPIVersion("2");
- apiParams.setAPIStrict(true);
- apiParams.setAPIDeprecationErrors(true);
-
- APIParameters::get(operationContext()) = apiParams;
- repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
-
- TxnNumber txnNum{3};
-
- auto txnRouter = TransactionRouter::get(operationContext());
-
- txnRouter.beginOrContinueTxn(
- operationContext(), txnNum, TransactionRouter::TransactionActions::kStart);
- txnRouter.setDefaultAtClusterTime(operationContext());
-
- APIParameters::get(operationContext()) = APIParameters();
- txnRouter.beginOrContinueTxn(
- operationContext(), txnNum, TransactionRouter::TransactionActions::kContinue);
-
- auto storedAPIParams = APIParameters::get(operationContext());
- ASSERT_EQ("2", *storedAPIParams.getAPIVersion());
- ASSERT_TRUE(*storedAPIParams.getAPIStrict());
- ASSERT_TRUE(*storedAPIParams.getAPIDeprecationErrors());
-}
-
TEST_F(TransactionRouterTestWithDefaultSession, ContinuingTransactionPlacesItsReadConcernOnOpCtx) {
TxnNumber txnNum{3};