summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2020-08-22 10:17:48 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-09-10 00:34:29 +0000
commit74c76f296cfe3e6c2943d248bec49c84c0ea5b96 (patch)
treea18a1f707c65a8b20c31a0882e7f72be64d17655
parent2b5dc35f019a3606c2dfa845cdfb320ffbac8014 (diff)
downloadmongo-74c76f296cfe3e6c2943d248bec49c84c0ea5b96.tar.gz
SERVER-50375 Ensure mongos forwards API params to shards
-rw-r--r--jstests/sharding/mongos_forwards_api_parameters_to_shards.js213
-rw-r--r--src/mongo/client/dbclient_base.cpp2
-rw-r--r--src/mongo/db/SConscript27
-rw-r--r--src/mongo/db/api_parameters.cpp79
-rw-r--r--src/mongo/db/api_parameters.h122
-rw-r--r--src/mongo/db/api_parameters.idl (renamed from src/mongo/db/initialize_api_parameters.idl)0
-rw-r--r--src/mongo/db/clientcursor.h2
-rw-r--r--src/mongo/db/command_generic_argument.cpp6
-rw-r--r--src/mongo/db/commands.cpp8
-rw-r--r--src/mongo/db/commands.h8
-rw-r--r--src/mongo/db/commands/test_api_version_2_commands.cpp2
-rw-r--r--src/mongo/db/commands/test_deprecation_command.cpp2
-rw-r--r--src/mongo/db/dbdirectclient.cpp1
-rw-r--r--src/mongo/db/initialize_api_parameters.cpp49
-rw-r--r--src/mongo/db/initialize_api_parameters.h66
-rw-r--r--src/mongo/db/query/SConscript1
-rw-r--r--src/mongo/db/s/config/configsvr_drop_database_command.cpp2
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp5
-rw-r--r--src/mongo/db/stats/api_version_metrics.h4
-rw-r--r--src/mongo/db/transaction_participant.h2
-rw-r--r--src/mongo/executor/SConscript1
-rw-r--r--src/mongo/executor/remote_command_request.cpp7
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/query/async_results_merger.cpp3
-rw-r--r--src/mongo/s/query/cluster_client_cursor.h2
-rw-r--r--src/mongo/s/query/cluster_client_cursor_params.h2
-rw-r--r--src/mongo/s/query/cluster_find.cpp1
-rw-r--r--src/mongo/s/transaction_router.cpp6
-rw-r--r--src/mongo/s/transaction_router.h2
-rw-r--r--src/mongo/s/transaction_router_test.cpp120
30 files changed, 539 insertions, 207 deletions
diff --git a/jstests/sharding/mongos_forwards_api_parameters_to_shards.js b/jstests/sharding/mongos_forwards_api_parameters_to_shards.js
new file mode 100644
index 00000000000..e611f716992
--- /dev/null
+++ b/jstests/sharding/mongos_forwards_api_parameters_to_shards.js
@@ -0,0 +1,213 @@
+/**
+ * When a client calls a mongos command with API parameters, mongos must forward them to shards.
+ *
+ * @tags: [multiversion_incompatible]
+ */
+
+(function() {
+'use strict';
+
+load('jstests/sharding/libs/sharded_transactions_helpers.js');
+
+let st = new ShardingTest({
+ mongos: 1,
+ shards: 2,
+ rs: {nodes: 1, setParameter: {logComponentVerbosity: tojson({command: {verbosity: 2}})}}
+});
+
+class APIParameterTest {
+ constructor(
+ command,
+ {dbName = "db", inAPIVersion1 = true, permittedInTxn = true, shardCommandName} = {}) {
+ this.command = command;
+ this.dbName = dbName;
+ this.inAPIVersion1 = inAPIVersion1;
+ this.permittedInTxn = permittedInTxn;
+ if (shardCommandName === undefined) {
+ this.commandName = Object.keys(command)[0];
+ } else {
+ // mongos executes a different command on the shards, e.g. mapReduce becomes aggregate.
+ this.commandName = shardCommandName;
+ }
+ }
+}
+
+const tests = [
+ // Write commands. Note, these rely on _id 1 residing on shard 0.
+ new APIParameterTest({insert: "collection", documents: [{_id: 1}]}),
+ new APIParameterTest({update: "collection", updates: [{q: {_id: 1}, u: {$set: {x: 1}}}]}),
+ new APIParameterTest({delete: "collection", deletes: [{q: {_id: 1}, limit: 1}]}),
+
+ // Read commands.
+ new APIParameterTest({aggregate: "collection", pipeline: [], cursor: {}}),
+ new APIParameterTest({aggregate: "collection", pipeline: [], cursor: {}, explain: true},
+ {shardCommandName: "explain", permittedInTxn: false}),
+ new APIParameterTest({find: "collection"}),
+ new APIParameterTest({count: "collection"}, {permittedInTxn: false}),
+ new APIParameterTest({count: "collection", query: {_id: {$lt: 0}}},
+ {inAPIVersion1: false, permittedInTxn: false}),
+ new APIParameterTest({distinct: "collection", key: "_id"},
+ {inAPIVersion1: false, permittedInTxn: false}),
+ new APIParameterTest(
+ {
+ mapReduce: "collection",
+ map: function() {
+ emit(1, 1);
+ },
+ reduce: function(key, values) {
+ return {count: values.length};
+ },
+ out: {inline: 1}
+ },
+ {inAPIVersion1: false, permittedInTxn: false, shardCommandName: "aggregate"}),
+
+ // FindAndModify.
+ new APIParameterTest({findAndModify: "collection", query: {_id: 1}, remove: true}),
+
+ // DDL. Order matters: we must create, modify, then drop an index on collection2.
+ new APIParameterTest({createIndexes: "collection2", indexes: [{key: {x: 1}, name: "x_1"}]}),
+ new APIParameterTest({collMod: "collection2", index: {keyPattern: {x: 1}, hidden: true}},
+ {permittedInTxn: false}),
+ new APIParameterTest({dropIndexes: "collection2", index: "x_1"}, {permittedInTxn: false}),
+ // We can create indexes on a non-existent collection in a sharded transaction.
+ new APIParameterTest({create: "newCollection"}),
+ new APIParameterTest({renameCollection: "db.newCollection", to: "db.newerCollection"},
+ {inAPIVersion1: false, permittedInTxn: false, dbName: "admin"}),
+ new APIParameterTest({drop: "collection"}, {permittedInTxn: false}),
+ new APIParameterTest({dropDatabase: 1}, {permittedInTxn: false}),
+];
+
+function checkPrimaryLog(conn, commandName, apiVersion, apiStrict, apiDeprecationErrors, message) {
+ const logs = checkLog.getGlobalLog(conn);
+ let lastCommandInvocation;
+
+ for (let logMsg of logs) {
+ const obj = JSON.parse(logMsg);
+ // Search for "About to run the command" logs.
+ if (obj.id !== 21965) {
+ continue;
+ }
+
+ const args = obj.attr.commandArgs;
+ if (commandName !== Object.keys(args)[0]) {
+ continue;
+ }
+
+ lastCommandInvocation = args;
+ if (args.apiVersion !== apiVersion || args.apiStrict !== apiStrict ||
+ args.apiDeprecationErrors !== apiDeprecationErrors) {
+ continue;
+ }
+
+ // Found a match.
+ return;
+ }
+
+ if (lastCommandInvocation === undefined) {
+ doassert(`Primary didn't log ${commandName}`);
+ return;
+ }
+
+ doassert(`Primary didn't log ${message}, last invocation of ${commandName} was` +
+ ` ${tojson(lastCommandInvocation)}`);
+}
+
+for (const sharded of [false, true]) {
+ 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 (let inTransaction of [false, true]) {
+ if (sharded) {
+ jsTestLog("Sharded setup");
+ assert.commandWorked(st.s.getDB("db")["collection"].insert(
+ {_id: 0}, {writeConcern: {w: "majority"}}));
+ assert.commandWorked(st.s.getDB("db")["collection"].insert(
+ {_id: 20}, {writeConcern: {w: "majority"}}));
+
+ assert.commandWorked(st.s.adminCommand({enableSharding: "db"}));
+ st.ensurePrimaryShard("db", st.shard0.shardName);
+ assert.commandWorked(
+ st.s.adminCommand({shardCollection: "db.collection", key: {_id: 1}}));
+
+ // The chunk with _id 1 is on shard 0.
+ assert.commandWorked(
+ st.s.adminCommand({split: "db.collection", middle: {_id: 10}}));
+ assert.commandWorked(st.s.adminCommand(
+ {moveChunk: "db.collection", find: {_id: 20}, to: st.shard1.shardName}));
+ } else {
+ jsTestLog("Unsharded setup");
+ assert.commandWorked(st.s.getDB("db")["collection"].insert(
+ {_id: 0}, {writeConcern: {w: "majority"}}));
+ st.ensurePrimaryShard("db", st.shard0.shardName);
+ }
+
+ // Shard 0's primary.
+ const primary = st.rs0.getPrimary();
+
+ for (const test of tests) {
+ if (inTransaction && !test.permittedInTxn) {
+ continue;
+ }
+
+ if (apiStrict && !test.inAPIVersion1) {
+ continue;
+ }
+
+ // Make a copy of the test's command body, and set its API parameters.
+ const commandWithAPIParams = Object.assign({}, test.command);
+ if (apiVersion !== undefined) {
+ commandWithAPIParams.apiVersion = apiVersion;
+ }
+
+ if (apiStrict !== undefined) {
+ commandWithAPIParams.apiStrict = apiStrict;
+ }
+
+ if (apiDeprecationErrors !== undefined) {
+ commandWithAPIParams.apiDeprecationErrors = apiDeprecationErrors;
+ }
+
+ assert.commandWorked(primary.adminCommand({clearLog: "global"}));
+ const message = `command ${tojson(commandWithAPIParams)}` +
+ ` ${sharded ? "sharded" : "unsharded"},` +
+ ` ${inTransaction ? "in" : "outside"} transaction`;
+
+ flushRoutersAndRefreshShardMetadata(st, {ns: "db.collection"});
+
+ jsTestLog(`Running ${message}`);
+
+ if (inTransaction) {
+ const session = st.s0.startSession();
+ const sessionDb = session.getDatabase(test.dbName);
+ session.startTransaction();
+ assert.commandWorked(sessionDb.runCommand(commandWithAPIParams));
+ assert.commandWorked(session.commitTransaction_forTesting());
+ } else {
+ const db = st.s0.getDB(test.dbName);
+ assert.commandWorked(db.runCommand(commandWithAPIParams));
+ }
+
+ checkPrimaryLog(primary,
+ test.commandName,
+ apiVersion,
+ apiStrict,
+ apiDeprecationErrors,
+ message);
+ }
+
+ jsTestLog("JS test cleanup: Drop database 'db'");
+ st.s0.getDB("db").runCommand({dropDatabase: 1});
+ }
+ }
+}
+
+st.stop();
+})();
diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp
index 04748a525af..9a7c87eed66 100644
--- a/src/mongo/client/dbclient_base.cpp
+++ b/src/mongo/client/dbclient_base.cpp
@@ -49,8 +49,8 @@
#include "mongo/client/constants.h"
#include "mongo/client/dbclient_cursor.h"
#include "mongo/config.h"
+#include "mongo/db/api_parameters_gen.h"
#include "mongo/db/commands.h"
-#include "mongo/db/initialize_api_parameters_gen.h"
#include "mongo/db/json.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/kill_cursors_gen.h"
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 2d90253f008..a0c8de84714 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -869,6 +869,7 @@ env.Library(
'$BUILD_DIR/mongo/db/storage/storage_engine_lock_file',
'$BUILD_DIR/mongo/db/storage/storage_engine_metadata',
'commands/server_status_core',
+ 'initialize_api_parameters',
'introspect',
'lasterror',
'query_exec',
@@ -1460,17 +1461,39 @@ env.Library(
env.Library(
target='shared_request_handling',
source=[
- 'initialize_api_parameters.cpp',
'transaction_validation.cpp',
- env.Idlc('initialize_api_parameters.idl')[0],
],
LIBDEPS=[
+ 'api_parameters',
'error_labels',
'logical_session_cache_impl',
],
)
env.Library(
+ target='api_parameters',
+ source=[
+ 'api_parameters.cpp',
+ env.Idlc('api_parameters.idl')[0],
+ ],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/idl/idl_parser',
+ '$BUILD_DIR/mongo/idl/server_parameter',
+ ],
+)
+
+env.Library(
+ target='initialize_api_parameters',
+ source=[
+ 'initialize_api_parameters.cpp',
+ ],
+ LIBDEPS_PRIVATE=[
+ 'api_parameters',
+ 'commands',
+ ],
+)
+
+env.Library(
target='logical_time',
source=[
'logical_time.cpp',
diff --git a/src/mongo/db/api_parameters.cpp b/src/mongo/db/api_parameters.cpp
new file mode 100644
index 00000000000..05ffe9c49cb
--- /dev/null
+++ b/src/mongo/db/api_parameters.cpp
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/api_parameters.h"
+
+namespace mongo {
+
+const OperationContext::Decoration<APIParameters> APIParameters::get =
+ OperationContext::declareDecoration<APIParameters>();
+
+APIParameters APIParameters::fromClient(const APIParametersFromClient& apiParamsFromClient) {
+ APIParameters apiParameters = APIParameters();
+ auto apiVersion = apiParamsFromClient.getApiVersion();
+ auto apiStrict = apiParamsFromClient.getApiStrict();
+ auto apiDeprecationErrors = apiParamsFromClient.getApiDeprecationErrors();
+
+ if (apiVersion) {
+ apiParameters.setAPIVersion(apiVersion.value());
+ }
+
+ if (apiStrict) {
+ apiParameters.setAPIStrict(apiStrict.value());
+ }
+
+ if (apiDeprecationErrors) {
+ apiParameters.setAPIDeprecationErrors(apiDeprecationErrors.value());
+ }
+
+ return apiParameters;
+}
+
+APIParameters APIParameters::fromBSON(const BSONObj& cmdObj) {
+ return APIParameters::fromClient(
+ APIParametersFromClient::parse("APIParametersFromClient"_sd, cmdObj));
+}
+
+void APIParameters::appendInfo(BSONObjBuilder* builder) const {
+ if (_apiVersion) {
+ builder->append(kAPIVersionFieldName, *_apiVersion);
+ }
+ if (_apiStrict) {
+ builder->append(kAPIStrictFieldName, *_apiStrict);
+ }
+ if (_apiDeprecationErrors) {
+ builder->append(kAPIDeprecationErrorsFieldName, *_apiDeprecationErrors);
+ }
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/api_parameters.h b/src/mongo/db/api_parameters.h
new file mode 100644
index 00000000000..7539dcb345e
--- /dev/null
+++ b/src/mongo/db/api_parameters.h
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/api_parameters_gen.h"
+#include "mongo/db/operation_context.h"
+
+namespace mongo {
+
+/**
+ * Decorates operation context with methods to retrieve apiVersion, apiStrict, and
+ * apiDeprecationErrors.
+ */
+class APIParameters {
+
+public:
+ static constexpr StringData kAPIVersionFieldName = "apiVersion"_sd;
+ static constexpr StringData kAPIStrictFieldName = "apiStrict"_sd;
+ static constexpr StringData kAPIDeprecationErrorsFieldName = "apiDeprecationErrors"_sd;
+
+ static const OperationContext::Decoration<APIParameters> get;
+ static APIParameters fromClient(const APIParametersFromClient& apiParamsFromClient);
+ static APIParameters fromBSON(const BSONObj& cmdObj);
+
+ void appendInfo(BSONObjBuilder* builder) const;
+
+ const boost::optional<std::string>& getAPIVersion() const {
+ return _apiVersion;
+ }
+
+ void setAPIVersion(StringData apiVersion) {
+ _apiVersion = apiVersion.toString();
+ }
+
+ const boost::optional<bool>& getAPIStrict() const {
+ return _apiStrict;
+ }
+
+ void setAPIStrict(bool apiStrict) {
+ _apiStrict = apiStrict;
+ }
+
+ const boost::optional<bool>& getAPIDeprecationErrors() const {
+ return _apiDeprecationErrors;
+ }
+
+ void setAPIDeprecationErrors(bool apiDeprecationErrors) {
+ _apiDeprecationErrors = apiDeprecationErrors;
+ }
+
+ const bool getParamsPassed() const {
+ return _apiVersion || _apiStrict || _apiDeprecationErrors;
+ }
+
+private:
+ boost::optional<std::string> _apiVersion;
+ boost::optional<bool> _apiStrict;
+ boost::optional<bool> _apiDeprecationErrors;
+};
+
+
+/**
+ * Temporarily remove the user's API parameters from an OperationContext.
+ */
+class IgnoreAPIParametersBlock {
+public:
+ IgnoreAPIParametersBlock() = delete;
+ IgnoreAPIParametersBlock(const IgnoreAPIParametersBlock&) = delete;
+ IgnoreAPIParametersBlock& operator=(const IgnoreAPIParametersBlock&) = delete;
+
+ explicit IgnoreAPIParametersBlock(OperationContext* opCtx) : _opCtx(opCtx) {
+ _apiParams = APIParameters::get(_opCtx);
+ APIParameters::get(_opCtx) = APIParameters();
+ }
+
+ void release() {
+ if (_released) {
+ return;
+ }
+
+ APIParameters::get(_opCtx) = _apiParams;
+ _released = true;
+ }
+
+ ~IgnoreAPIParametersBlock() {
+ release();
+ }
+
+private:
+ OperationContext* _opCtx;
+ APIParameters _apiParams;
+ bool _released = false;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/initialize_api_parameters.idl b/src/mongo/db/api_parameters.idl
index cc3a3d13e6c..cc3a3d13e6c 100644
--- a/src/mongo/db/initialize_api_parameters.idl
+++ b/src/mongo/db/api_parameters.idl
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index ee2040764b6..f4d7960a759 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -32,10 +32,10 @@
#include <boost/optional.hpp>
#include <functional>
+#include "mongo/db/api_parameters.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/cursor_id.h"
-#include "mongo/db/initialize_api_parameters.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/db/query/plan_executor.h"
diff --git a/src/mongo/db/command_generic_argument.cpp b/src/mongo/db/command_generic_argument.cpp
index 8434b65a3c3..e15c2498a97 100644
--- a/src/mongo/db/command_generic_argument.cpp
+++ b/src/mongo/db/command_generic_argument.cpp
@@ -56,9 +56,9 @@ static constexpr std::array<SpecialArgRecord, 34> specials{{
// /-isGeneric
// | /-stripFromRequest
// | | /-stripFromReply
- {"apiVersion"_sd, 1, 0, 0},
- {"apiStrict"_sd, 1, 0, 0},
- {"apiDeprecationErrors"_sd, 1, 0, 0},
+ {"apiVersion"_sd, 1, 1, 0},
+ {"apiStrict"_sd, 1, 1, 0},
+ {"apiDeprecationErrors"_sd, 1, 1, 0},
{"$audit"_sd, 1, 1, 0},
{"$client"_sd, 1, 1, 0},
{"$configServerState"_sd, 1, 1, 1},
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index f9ef6f72574..8d9a8de296b 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -867,6 +867,14 @@ Command::Command(StringData name, std::vector<StringData> aliases)
globalCommandRegistry()->registerCommand(this, _name, _aliases);
}
+const std::set<std::string>& Command::apiVersions() const {
+ return kNoApiVersions;
+}
+
+const std::set<std::string>& Command::deprecatedApiVersions() const {
+ return kNoApiVersions;
+}
+
bool Command::hasAlias(const StringData& alias) const {
return globalCommandRegistry()->findCommand(alias) == this;
}
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 1877556f356..57b3ce272ca 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -358,16 +358,12 @@ public:
/*
* Returns the list of API versions that include this command.
*/
- virtual const std::set<std::string>& apiVersions() const {
- return kNoApiVersions;
- }
+ virtual const std::set<std::string>& apiVersions() const;
/*
* Returns the list of API versions in which this command is deprecated.
*/
- virtual const std::set<std::string>& deprecatedApiVersions() const {
- return kNoApiVersions;
- }
+ virtual const std::set<std::string>& deprecatedApiVersions() const;
/**
* Like adminOnly, but even stricter: we must either be authenticated for admin db,
diff --git a/src/mongo/db/commands/test_api_version_2_commands.cpp b/src/mongo/db/commands/test_api_version_2_commands.cpp
index b2c79a7ef70..738e13b1366 100644
--- a/src/mongo/db/commands/test_api_version_2_commands.cpp
+++ b/src/mongo/db/commands/test_api_version_2_commands.cpp
@@ -27,8 +27,8 @@
* it in the license file.
*/
+#include "mongo/db/api_parameters.h"
#include "mongo/db/commands.h"
-#include "mongo/db/initialize_api_parameters.h"
namespace mongo {
diff --git a/src/mongo/db/commands/test_deprecation_command.cpp b/src/mongo/db/commands/test_deprecation_command.cpp
index 44e61edb4a0..74d93942ddd 100644
--- a/src/mongo/db/commands/test_deprecation_command.cpp
+++ b/src/mongo/db/commands/test_deprecation_command.cpp
@@ -27,8 +27,8 @@
* it in the license file.
*/
+#include "mongo/db/api_parameters.h"
#include "mongo/db/commands.h"
-#include "mongo/db/initialize_api_parameters.h"
namespace mongo {
diff --git a/src/mongo/db/dbdirectclient.cpp b/src/mongo/db/dbdirectclient.cpp
index 5386bf567d2..bb1f5553906 100644
--- a/src/mongo/db/dbdirectclient.cpp
+++ b/src/mongo/db/dbdirectclient.cpp
@@ -143,6 +143,7 @@ DbResponse loopbackBuildResponse(OperationContext* const opCtx,
toSend.header().setId(nextMessageId());
toSend.header().setResponseToMsgId(0);
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(opCtx);
return opCtx->getServiceContext()->getServiceEntryPoint()->handleRequest(opCtx, toSend).get();
}
} // namespace
diff --git a/src/mongo/db/initialize_api_parameters.cpp b/src/mongo/db/initialize_api_parameters.cpp
index 11a5b68ae10..9ad9fc4d026 100644
--- a/src/mongo/db/initialize_api_parameters.cpp
+++ b/src/mongo/db/initialize_api_parameters.cpp
@@ -27,8 +27,17 @@
* it in the license file.
*/
+#include "mongo/platform/basic.h"
+
#include "mongo/db/initialize_api_parameters.h"
+#include <string>
+
+#include "mongo/db/commands.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/str.h"
+
namespace mongo {
const APIParametersFromClient initializeAPIParameters(OperationContext* opCtx,
@@ -88,44 +97,4 @@ const APIParametersFromClient initializeAPIParameters(OperationContext* opCtx,
return apiParamsFromClient;
}
-const OperationContext::Decoration<APIParameters> handle =
- OperationContext::declareDecoration<APIParameters>();
-
-APIParameters& APIParameters::get(OperationContext* opCtx) {
- return handle(opCtx);
-}
-
-APIParameters APIParameters::fromClient(const APIParametersFromClient& apiParamsFromClient) {
- APIParameters apiParameters = APIParameters();
- auto apiVersion = apiParamsFromClient.getApiVersion();
- auto apiStrict = apiParamsFromClient.getApiStrict();
- auto apiDeprecationErrors = apiParamsFromClient.getApiDeprecationErrors();
-
- if (apiVersion) {
- apiParameters.setAPIVersion(apiVersion.value());
- }
-
- if (apiStrict) {
- apiParameters.setAPIStrict(apiStrict.value());
- }
-
- if (apiDeprecationErrors) {
- apiParameters.setAPIDeprecationErrors(apiDeprecationErrors.value());
- }
-
- return apiParameters;
-}
-
-void APIParameters::appendInfo(BSONObjBuilder* builder) const {
- if (_apiVersion) {
- builder->append(kAPIVersionFieldName, *_apiVersion);
- }
- if (_apiStrict) {
- builder->append(kAPIStrictFieldName, *_apiStrict);
- }
- if (_apiDeprecationErrors) {
- builder->append(kAPIDeprecationErrorsFieldName, *_apiDeprecationErrors);
- }
-}
-
} // namespace mongo
diff --git a/src/mongo/db/initialize_api_parameters.h b/src/mongo/db/initialize_api_parameters.h
index 73215f607c8..e62d0defecc 100644
--- a/src/mongo/db/initialize_api_parameters.h
+++ b/src/mongo/db/initialize_api_parameters.h
@@ -29,73 +29,19 @@
#pragma once
-#include "mongo/db/commands.h"
-#include "mongo/db/initialize_api_parameters_gen.h"
-#include "mongo/db/operation_context.h"
+#include "api_parameters.h"
namespace mongo {
+class BSONObj;
+class Command;
+class OperationContext;
+
/**
- * See VERSIONED_API_README.md for an overview of the Versioned API.
- *
- * This function parses a command's API Version parameters from a request and stores the apiVersion,
+ * Parse a command's API Version parameters from a request and store the apiVersion,
* apiStrict, and apiDeprecationErrors fields.
*/
const APIParametersFromClient initializeAPIParameters(OperationContext* opCtx,
const BSONObj& requestBody,
Command* command);
-
-/**
- * Decorates operation context with methods to retrieve apiVersion, apiStrict, and
- * apiDeprecationErrors.
- */
-class APIParameters {
-
-public:
- static constexpr StringData kAPIVersionFieldName = "apiVersion"_sd;
- static constexpr StringData kAPIStrictFieldName = "apiStrict"_sd;
- static constexpr StringData kAPIDeprecationErrorsFieldName = "apiDeprecationErrors"_sd;
-
- APIParameters() = default;
- static APIParameters& get(OperationContext* opCtx);
- static APIParameters fromClient(const APIParametersFromClient& apiParamsFromClient);
-
- void appendInfo(BSONObjBuilder* builder) const;
-
- const boost::optional<std::string>& getAPIVersion() const {
- return _apiVersion;
- }
-
- void setAPIVersion(StringData apiVersion) {
- _apiVersion = apiVersion.toString();
- }
-
- const boost::optional<bool>& getAPIStrict() const {
- return _apiStrict;
- }
-
- void setAPIStrict(bool apiStrict) {
- _apiStrict = apiStrict;
- }
-
- const boost::optional<bool>& getAPIDeprecationErrors() const {
- return _apiDeprecationErrors;
- }
-
- void setAPIDeprecationErrors(bool apiDeprecationErrors) {
- _apiDeprecationErrors = apiDeprecationErrors;
- }
-
- bool getParamsPassed() const {
- return _apiVersion || _apiStrict || _apiDeprecationErrors;
- }
-
- BSONObj toBSON() const;
-
-private:
- boost::optional<std::string> _apiVersion;
- boost::optional<bool> _apiStrict;
- boost::optional<bool> _apiDeprecationErrors;
-};
-
} // namespace mongo
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index eada898eafb..b57c45a30e4 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -179,6 +179,7 @@ env.Library(
],
LIBDEPS=[
"$BUILD_DIR/mongo/base",
+ "$BUILD_DIR/mongo/db/api_parameters",
"$BUILD_DIR/mongo/db/catalog/collection_catalog",
# TODO: This dependency edge can be removed when the 'allowDiskUse' option no longer depends
# on enabling test commands.
diff --git a/src/mongo/db/s/config/configsvr_drop_database_command.cpp b/src/mongo/db/s/config/configsvr_drop_database_command.cpp
index eb3ef547e70..91513e5d2e4 100644
--- a/src/mongo/db/s/config/configsvr_drop_database_command.cpp
+++ b/src/mongo/db/s/config/configsvr_drop_database_command.cpp
@@ -29,6 +29,7 @@
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding
+#include "mongo/db/api_parameters.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
@@ -177,6 +178,7 @@ public:
status, str::stream() << "Could not remove database '" << dbname << "' from metadata");
// Send _flushDatabaseCacheUpdates to all shards
+ IgnoreAPIParametersBlock ignoreApiParametersBlock{opCtx};
for (const ShardId& shardId : allShardIds) {
const auto shard =
uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getShard(opCtx, shardId));
diff --git a/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp
index d6544e922d2..381a5e62029 100644
--- a/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp
+++ b/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp
@@ -42,6 +42,7 @@
#include "mongo/client/read_preference.h"
#include "mongo/client/remote_command_targeter.h"
#include "mongo/client/replica_set_monitor.h"
+#include "mongo/db/api_parameters.h"
#include "mongo/db/auth/authorization_session_impl.h"
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/client.h"
@@ -398,6 +399,7 @@ void sendSSVToAllShards(OperationContext* opCtx, const NamespaceString& nss) {
auto* const shardRegistry = Grid::get(opCtx)->shardRegistry();
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(opCtx);
for (const auto& shardEntry : allShards) {
const auto& shard = uassertStatusOK(shardRegistry->getShard(opCtx, shardEntry.getName()));
@@ -417,6 +419,7 @@ void sendSSVToAllShards(OperationContext* opCtx, const NamespaceString& nss) {
}
void removeChunksAndTagsForDroppedCollection(OperationContext* opCtx, const NamespaceString& nss) {
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(opCtx);
const auto catalogClient = Grid::get(opCtx)->catalogClient();
// Remove chunk data
@@ -502,6 +505,8 @@ void ShardingCatalogManager::ensureDropCollectionCompleted(OperationContext* opC
"Ensuring config entries from previous dropCollection are cleared",
"namespace"_attr = nss.ns());
sendDropCollectionToAllShards(opCtx, nss);
+
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(opCtx);
removeChunksAndTagsForDroppedCollection(opCtx, nss);
sendSSVToAllShards(opCtx, nss);
}
diff --git a/src/mongo/db/stats/api_version_metrics.h b/src/mongo/db/stats/api_version_metrics.h
index fc1de1d9766..354312a3992 100644
--- a/src/mongo/db/stats/api_version_metrics.h
+++ b/src/mongo/db/stats/api_version_metrics.h
@@ -29,7 +29,7 @@
#pragma once
-#include "mongo/db/initialize_api_parameters.h"
+#include "mongo/db/api_parameters.h"
#include "mongo/db/service_context.h"
#include "mongo/platform/mutex.h"
#include "mongo/rpc/metadata/client_metadata.h"
@@ -70,4 +70,4 @@ private:
APIVersionMetricsMap _apiVersionMetrics;
};
-} // namespace mongo \ No newline at end of file
+} // namespace mongo
diff --git a/src/mongo/db/transaction_participant.h b/src/mongo/db/transaction_participant.h
index f898b21c112..37b71ce8589 100644
--- a/src/mongo/db/transaction_participant.h
+++ b/src/mongo/db/transaction_participant.h
@@ -33,11 +33,11 @@
#include <iostream>
#include <map>
+#include "mongo/db/api_parameters.h"
#include "mongo/db/catalog/uncommitted_collections.h"
#include "mongo/db/commands/txn_cmds_gen.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/concurrency/locker.h"
-#include "mongo/db/initialize_api_parameters.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/db/multi_key_path_tracker.h"
#include "mongo/db/ops/update_request.h"
diff --git a/src/mongo/executor/SConscript b/src/mongo/executor/SConscript
index 76956c8818e..b10cf01369a 100644
--- a/src/mongo/executor/SConscript
+++ b/src/mongo/executor/SConscript
@@ -31,6 +31,7 @@ env.Library(
'remote_command_response.cpp',
],
LIBDEPS=[
+ '$BUILD_DIR/mongo/db/api_parameters',
'$BUILD_DIR/mongo/rpc/metadata',
'$BUILD_DIR/mongo/util/net/network',
]
diff --git a/src/mongo/executor/remote_command_request.cpp b/src/mongo/executor/remote_command_request.cpp
index 875da25ef9f..4c35525e6a9 100644
--- a/src/mongo/executor/remote_command_request.cpp
+++ b/src/mongo/executor/remote_command_request.cpp
@@ -34,6 +34,7 @@
#include <fmt/format.h>
#include "mongo/bson/simple_bsonobj_comparator.h"
+#include "mongo/db/api_parameters.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/query/query_request.h"
#include "mongo/platform/atomic_word.h"
@@ -86,6 +87,12 @@ RemoteCommandRequestBase::RemoteCommandRequestBase(RequestId requestId,
cmdObj = cmdObj.addField(BSON("clientOperationKey" << operationKey.get()).firstElement());
}
+ if (opCtx && APIParameters::get(opCtx).getParamsPassed()) {
+ BSONObjBuilder bob(std::move(cmdObj));
+ APIParameters::get(opCtx).appendInfo(&bob);
+ cmdObj = bob.obj();
+ }
+
_updateTimeoutFromOpCtxDeadline(opCtx);
}
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index 780d4d4bc9a..8fc761a2e0b 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -124,6 +124,7 @@ env.Library(
'$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/commands/write_commands_common',
'$BUILD_DIR/mongo/db/ftdc/ftdc_server',
+ '$BUILD_DIR/mongo/db/initialize_api_parameters',
'$BUILD_DIR/mongo/db/logical_session_cache_impl',
'$BUILD_DIR/mongo/db/pipeline/aggregation',
'$BUILD_DIR/mongo/db/query/command_request_response',
diff --git a/src/mongo/s/query/async_results_merger.cpp b/src/mongo/s/query/async_results_merger.cpp
index 2ad05010afb..98aec3332ec 100644
--- a/src/mongo/s/query/async_results_merger.cpp
+++ b/src/mongo/s/query/async_results_merger.cpp
@@ -462,8 +462,11 @@ Status AsyncResultsMerger::_askForNextBatch(WithLock, size_t remoteIndex) {
cmdObj = newCmdBob.obj();
}
+ // Never pass API parameters with getMore.
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(_opCtx);
executor::RemoteCommandRequest request(
remote.getTargetHost(), remote.cursorNss.db().toString(), cmdObj, _opCtx);
+ ignoreApiParametersBlock.release();
auto callbackStatus =
_executor->scheduleRemoteCommand(request, [this, remoteIndex](auto const& cbData) {
diff --git a/src/mongo/s/query/cluster_client_cursor.h b/src/mongo/s/query/cluster_client_cursor.h
index 44aae05e34d..87e3271e692 100644
--- a/src/mongo/s/query/cluster_client_cursor.h
+++ b/src/mongo/s/query/cluster_client_cursor.h
@@ -32,8 +32,8 @@
#include <boost/optional.hpp>
#include "mongo/client/read_preference.h"
+#include "mongo/db/api_parameters.h"
#include "mongo/db/auth/user_name.h"
-#include "mongo/db/initialize_api_parameters.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/s/query/cluster_client_cursor_params.h"
diff --git a/src/mongo/s/query/cluster_client_cursor_params.h b/src/mongo/s/query/cluster_client_cursor_params.h
index d8bb0ae8da0..b0fae249884 100644
--- a/src/mongo/s/query/cluster_client_cursor_params.h
+++ b/src/mongo/s/query/cluster_client_cursor_params.h
@@ -36,10 +36,10 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/client/read_preference.h"
+#include "mongo/db/api_parameters.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/cursor_id.h"
-#include "mongo/db/initialize_api_parameters.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/pipeline/pipeline.h"
#include "mongo/db/query/cursor_response.h"
diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp
index 12073508642..57925b873ed 100644
--- a/src/mongo/s/query/cluster_find.cpp
+++ b/src/mongo/s/query/cluster_find.cpp
@@ -772,6 +772,7 @@ StatusWith<CursorResponse> ClusterFind::runGetMore(OperationContext* opCtx,
StatusWith<ClusterQueryResult> next =
Status{ErrorCodes::InternalError, "uninitialized cluster query result"};
try {
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(opCtx);
next = pinnedCursor.getValue()->next(context);
} catch (const ExceptionFor<ErrorCodes::CloseChangeStream>&) {
// This exception is thrown when a $changeStream stage encounters an event
diff --git a/src/mongo/s/transaction_router.cpp b/src/mongo/s/transaction_router.cpp
index b7b26698e78..c269d734365 100644
--- a/src/mongo/s/transaction_router.cpp
+++ b/src/mongo/s/transaction_router.cpp
@@ -125,7 +125,6 @@ BSONObj appendReadConcernForTxn(BSONObj cmd,
}
BSONObjBuilder appendFieldsForStartTransaction(BSONObj cmd,
- APIParameters apiParameters,
repl::ReadConcernArgs readConcernArgs,
boost::optional<LogicalTime> atClusterTime,
bool doAppendStartTransaction) {
@@ -134,8 +133,6 @@ BSONObjBuilder appendFieldsForStartTransaction(BSONObj cmd,
appendReadConcernForTxn(std::move(cmd), readConcernArgs, atClusterTime);
BSONObjBuilder bob(std::move(cmdWithReadConcern));
-
- apiParameters.appendInfo(&bob);
if (doAppendStartTransaction) {
bob.append(OperationSessionInfoFromClient::kStartTransactionFieldName, true);
}
@@ -433,7 +430,6 @@ BSONObj TransactionRouter::Participant::attachTxnFieldsIfNeeded(
BSONObjBuilder newCmd = mustStartTransaction
? appendFieldsForStartTransaction(std::move(cmd),
- sharedOptions.apiParameters,
sharedOptions.readConcernArgs,
sharedOptions.atClusterTime,
!hasStartTxn)
@@ -1203,6 +1199,8 @@ BSONObj TransactionRouter::Router::abortTransaction(OperationContext* opCtx) {
"txnNumber"_attr = o().txnNumber,
"numParticipantShards"_attr = o().participants.size());
+ // Omit API parameters from abortTransaction.
+ IgnoreAPIParametersBlock ignoreApiParametersBlock(opCtx);
const auto responses = gatherResponses(opCtx,
NamespaceString::kAdminDb,
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
diff --git a/src/mongo/s/transaction_router.h b/src/mongo/s/transaction_router.h
index 25ce17831fe..3d6be675077 100644
--- a/src/mongo/s/transaction_router.h
+++ b/src/mongo/s/transaction_router.h
@@ -31,8 +31,8 @@
#include <boost/optional.hpp>
+#include "mongo/db/api_parameters.h"
#include "mongo/db/commands/txn_cmds_gen.h"
-#include "mongo/db/initialize_api_parameters.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/repl/read_concern_args.h"
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index eb827201e84..a507d3e4f3f 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -316,16 +316,9 @@ TEST_F(TransactionRouterTestWithDefaultSession, CannotContiueTxnWithoutStarting)
ErrorCodes::NoSuchTransaction);
}
-TEST_F(TransactionRouterTestWithDefaultSession,
- NewParticipantMustAttachTxnAndReadConcernAndAPIParams) {
+TEST_F(TransactionRouterTestWithDefaultSession, NewParticipantMustAttachTxnAndReadConcern) {
TxnNumber txnNum{3};
- APIParameters apiParameters = APIParameters();
- apiParameters.setAPIVersion("1");
- apiParameters.setAPIStrict(false);
- apiParameters.setAPIDeprecationErrors(false);
- APIParameters::get(operationContext()) = apiParameters;
-
auto txnRouter = TransactionRouter::get(operationContext());
txnRouter.beginOrContinueTxn(
operationContext(), txnNum, TransactionRouter::TransactionActions::kStart);
@@ -337,9 +330,6 @@ TEST_F(TransactionRouterTestWithDefaultSession,
<< BSON("level"
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
- << "apiVersion"
- << "1"
- << "apiStrict" << false << "apiDeprecationErrors" << false
<< "startTransaction" << true << "coordinator" << true
<< "autocommit" << false << "txnNumber" << txnNum);
@@ -369,9 +359,6 @@ TEST_F(TransactionRouterTestWithDefaultSession,
<< BSON("level"
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
- << "apiVersion"
- << "1"
- << "apiStrict" << false << "apiDeprecationErrors" << false
<< "startTransaction" << true << "autocommit" << false << "txnNumber"
<< txnNum);
@@ -735,40 +722,6 @@ TEST_F(TransactionRouterTestWithDefaultSession, AttachTxnValidatesReadConcernIfA
}
}
-TEST_F(TransactionRouterTestWithDefaultSession, AttachTxnAttachesAPIParameters) {
- APIParameters apiParams = APIParameters();
- apiParams.setAPIVersion("2");
- apiParams.setAPIStrict(true);
- apiParams.setAPIDeprecationErrors(true);
-
- APIParameters::get(operationContext()) = apiParams;
-
- TxnNumber txnNum{3};
- auto txnRouter = TransactionRouter::get(operationContext());
- txnRouter.beginOrContinueTxn(
- operationContext(), txnNum, TransactionRouter::TransactionActions::kStart);
- txnRouter.setDefaultAtClusterTime(operationContext());
-
- {
- auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
- shard1,
- BSON("insert"
- << "test"));
- ASSERT_BSONOBJ_EQ(BSON("insert"
- << "test"
- << "readConcern"
- << BSON("level"
- << "snapshot"
- << "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
- << "apiVersion"
- << "2"
- << "apiStrict" << true << "apiDeprecationErrors" << true
- << "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum),
- newCmd);
- }
-}
-
TEST_F(TransactionRouterTestWithDefaultSession, CannotSpecifyAPIParametersAfterFirstStatement) {
APIParameters apiParameters = APIParameters();
apiParameters.setAPIVersion("1");
@@ -787,40 +740,6 @@ TEST_F(TransactionRouterTestWithDefaultSession, CannotSpecifyAPIParametersAfterF
4937701);
}
-TEST_F(TransactionRouterTestWithDefaultSession, PassesThroughAPIParametersToParticipants) {
- APIParameters apiParams = APIParameters();
- apiParams.setAPIVersion("2");
- apiParams.setAPIStrict(true);
- apiParams.setAPIDeprecationErrors(true);
-
- APIParameters::get(operationContext()) = apiParams;
-
- TxnNumber txnNum{3};
-
- auto txnRouter = TransactionRouter::get(operationContext());
- txnRouter.beginOrContinueTxn(
- operationContext(), txnNum, TransactionRouter::TransactionActions::kStart);
- txnRouter.setDefaultAtClusterTime(operationContext());
-
- BSONObj expectedNewObj = BSON("insert"
- << "test"
- << "readConcern"
- << BSON("level"
- << "snapshot"
- << "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
- << "apiVersion"
- << "2"
- << "apiStrict" << true << "apiDeprecationErrors" << true
- << "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum);
-
- auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
- shard1,
- BSON("insert"
- << "test"));
- ASSERT_BSONOBJ_EQ(expectedNewObj, newCmd);
-}
-
TEST_F(TransactionRouterTestWithDefaultSession, CannotSpecifyReadConcernAfterFirstStatement) {
TxnNumber txnNum{3};
@@ -3294,6 +3213,43 @@ TEST_F(TransactionRouterMetricsTest, LogsTransactionsOverSlowMSThreshold) {
assertPrintedExactlyOneSlowLogLine();
}
+TEST_F(TransactionRouterMetricsTest, LogsTransactionsWithAPIParameters) {
+ const auto originalSlowMS = serverGlobalParams.slowMS;
+ const auto originalSampleRate = serverGlobalParams.sampleRate;
+
+ serverGlobalParams.slowMS = 100;
+ serverGlobalParams.sampleRate = 1;
+
+ // Reset the global parameters to their original values after this test exits.
+ ON_BLOCK_EXIT([originalSlowMS, originalSampleRate] {
+ serverGlobalParams.slowMS = originalSlowMS;
+ serverGlobalParams.sampleRate = originalSampleRate;
+ });
+
+ APIParameters::get(operationContext()).setAPIVersion("1");
+ APIParameters::get(operationContext()).setAPIStrict(true);
+ APIParameters::get(operationContext()).setAPIDeprecationErrors(false);
+ beginTxnWithDefaultTxnNumber();
+ tickSource()->advance(Milliseconds(101));
+ runCommit(kDummyOkRes);
+ assertPrintedExactlyOneSlowLogLine();
+
+ int nFound = 0;
+ for (auto&& bson : getCapturedBSONFormatLogMessages()) {
+ if (bson["id"].Int() != 51805) {
+ continue;
+ }
+
+ auto parameters = bson["attr"]["parameters"];
+ ASSERT_EQUALS(parameters["apiVersion"].String(), "1");
+ ASSERT_EQUALS(parameters["apiStrict"].Bool(), true);
+ ASSERT_EQUALS(parameters["apiDeprecationErrors"].Bool(), false);
+ ++nFound;
+ }
+
+ ASSERT_EQUALS(nFound, 1);
+}
+
TEST_F(TransactionRouterMetricsTest, DoesNotLogTransactionsWithSampleRateZero) {
const auto originalSlowMS = serverGlobalParams.slowMS;
const auto originalSampleRate = serverGlobalParams.sampleRate;