diff options
author | XueruiFa <xuerui.fa@mongodb.com> | 2020-07-16 18:03:11 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-07-29 15:23:07 +0000 |
commit | 7b910aba3c500839692d142b4c64560b1fc2f29b (patch) | |
tree | f56189478f203aac17cbe9d10bf91b1969653155 /src | |
parent | 4b880132b11c16a4a0cba4e5c3ce77892dd01f7c (diff) | |
download | mongo-7b910aba3c500839692d142b4c64560b1fc2f29b.tar.gz |
SERVER-49376: Ensure cursors inherit API settings of the initiating command
Diffstat (limited to 'src')
25 files changed, 171 insertions, 14 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 2f5aad83e4c..decffa6e11c 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1192,6 +1192,7 @@ env.Library( 'query/query_planner', 'repl/repl_coordinator_interface', 's/sharding_api_d', + 'shared_request_handling', 'stats/serveronly_stats', 'storage/oplog_hack', 'storage/snapshot_helper', diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp index 845c31e48b9..33d41061774 100644 --- a/src/mongo/db/clientcursor.cpp +++ b/src/mongo/db/clientcursor.cpp @@ -83,6 +83,7 @@ ClientCursor::ClientCursor(ClientCursorParams params, _authenticatedUsers(std::move(params.authenticatedUsers)), _lsid(operationUsingCursor->getLogicalSessionId()), _txnNumber(operationUsingCursor->getTxnNumber()), + _apiParameters(std::move(params.apiParameters)), _writeConcernOptions(std::move(params.writeConcernOptions)), _readConcernArgs(std::move(params.readConcernArgs)), _originatingCommand(params.originatingCommandObj), diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h index 1536921d8e1..ee2040764b6 100644 --- a/src/mongo/db/clientcursor.h +++ b/src/mongo/db/clientcursor.h @@ -35,6 +35,7 @@ #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" @@ -58,12 +59,14 @@ struct ClientCursorParams { ClientCursorParams(std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> planExecutor, NamespaceString nss, UserNameIterator authenticatedUsersIter, + APIParameters apiParameters, WriteConcernOptions writeConcernOptions, repl::ReadConcernArgs readConcernArgs, BSONObj originatingCommandObj, PrivilegeVector originatingPrivileges) : exec(std::move(planExecutor)), nss(std::move(nss)), + apiParameters(std::move(apiParameters)), writeConcernOptions(std::move(writeConcernOptions)), readConcernArgs(std::move(readConcernArgs)), queryOptions(exec->getCanonicalQuery() @@ -93,6 +96,7 @@ struct ClientCursorParams { std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> exec; const NamespaceString nss; std::vector<UserName> authenticatedUsers; + const APIParameters apiParameters; const WriteConcernOptions writeConcernOptions; const repl::ReadConcernArgs readConcernArgs; int queryOptions = 0; @@ -141,14 +145,18 @@ public: return _txnNumber; } - repl::ReadConcernArgs getReadConcernArgs() const { - return _readConcernArgs; + APIParameters getAPIParameters() const { + return _apiParameters; } WriteConcernOptions getWriteConcernOptions() const { return _writeConcernOptions; } + repl::ReadConcernArgs getReadConcernArgs() const { + return _readConcernArgs; + } + /** * Returns a pointer to the underlying query plan executor. All cursors manage a PlanExecutor, * so this method never returns a null pointer. @@ -358,6 +366,7 @@ private: // A transaction number for this cursor, if it was provided in the originating command. const boost::optional<TxnNumber> _txnNumber; + const APIParameters _apiParameters; const WriteConcernOptions _writeConcernOptions; const repl::ReadConcernArgs _readConcernArgs; diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 6a07d0404be..590be041081 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -355,12 +355,16 @@ public: return false; } - // List of API versions that include this command. + /* + * Returns the list of API versions that include this command. + */ virtual const std::set<std::string>& apiVersions() const { return kNoApiVersions; } - // API versions in which this command is deprecated. + /* + * Returns the list of API versions in which this command is deprecated. + */ virtual const std::set<std::string>& deprecatedApiVersions() const { return kNoApiVersions; } diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp index 517fb24bd54..55233df9dc2 100644 --- a/src/mongo/db/commands/find_cmd.cpp +++ b/src/mongo/db/commands/find_cmd.cpp @@ -490,6 +490,7 @@ public: {std::move(exec), nss, AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(), + APIParameters::get(opCtx), opCtx->getWriteConcern(), repl::ReadConcernArgs::get(opCtx), _request.body, diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp index ba9947b3e7e..1b3f43bf16c 100644 --- a/src/mongo/db/commands/getmore_cmd.cpp +++ b/src/mongo/db/commands/getmore_cmd.cpp @@ -220,6 +220,7 @@ void setUpOperationContextStateForGetMore(OperationContext* opCtx, bool disableAwaitDataFailpointActive) { applyCursorReadConcern(opCtx, cursor.getReadConcernArgs()); opCtx->setWriteConcern(cursor.getWriteConcernOptions()); + APIParameters::get(opCtx) = cursor.getAPIParameters(); setUpOperationDeadline(opCtx, cursor, request, disableAwaitDataFailpointActive); // If the originating command had a 'comment' field, we extract it and set it on opCtx. Note diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp index e90b0eeed4d..f8d10652830 100644 --- a/src/mongo/db/commands/list_collections.cpp +++ b/src/mongo/db/commands/list_collections.cpp @@ -405,6 +405,7 @@ public: {std::move(exec), cursorNss, AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(), + APIParameters::get(opCtx), opCtx->getWriteConcern(), repl::ReadConcernArgs::get(opCtx), jsobj, diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp index 15e8bb52420..6b3a51ad12e 100644 --- a/src/mongo/db/commands/list_indexes.cpp +++ b/src/mongo/db/commands/list_indexes.cpp @@ -218,6 +218,7 @@ public: {std::move(exec), nss, AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(), + APIParameters::get(opCtx), opCtx->getWriteConcern(), repl::ReadConcernArgs::get(opCtx), cmdObj, diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp index acc5fa1cd4e..855556d154d 100644 --- a/src/mongo/db/commands/run_aggregate.cpp +++ b/src/mongo/db/commands/run_aggregate.cpp @@ -704,6 +704,7 @@ Status runAggregate(OperationContext* opCtx, std::move(exec), origNss, AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(), + APIParameters::get(opCtx), opCtx->getWriteConcern(), repl::ReadConcernArgs::get(opCtx), cmdObj, diff --git a/src/mongo/db/exec/sbe_cmd.cpp b/src/mongo/db/exec/sbe_cmd.cpp index d88039b3d22..d544e79599f 100644 --- a/src/mongo/db/exec/sbe_cmd.cpp +++ b/src/mongo/db/exec/sbe_cmd.cpp @@ -114,6 +114,7 @@ public: {std::move(exec), nss, AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(), + APIParameters::get(opCtx), opCtx->getWriteConcern(), repl::ReadConcernArgs::get(opCtx), cmdObj, diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp index 440c3c580d0..cc317eadf3a 100644 --- a/src/mongo/db/query/find.cpp +++ b/src/mongo/db/query/find.cpp @@ -744,6 +744,7 @@ bool runQuery(OperationContext* opCtx, {std::move(exec), nss, AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(), + APIParameters::get(opCtx), opCtx->getWriteConcern(), readConcernArgs, upconvertedQuery, diff --git a/src/mongo/db/query/getmore_request.cpp b/src/mongo/db/query/getmore_request.cpp index f39ca762f07..808f33d0e0b 100644 --- a/src/mongo/db/query/getmore_request.cpp +++ b/src/mongo/db/query/getmore_request.cpp @@ -51,6 +51,9 @@ const char kBatchSizeField[] = "batchSize"; const char kAwaitDataTimeoutField[] = "maxTimeMS"; const char kTermField[] = "term"; const char kLastKnownCommittedOpTimeField[] = "lastKnownCommittedOpTime"; +const char kApiVersion[] = "apiVersion"; +const char kApiStrict[] = "apiStrict"; +const char kApiDeprecationErrors[] = "apiDeprecationErrors"; } // namespace @@ -105,6 +108,13 @@ StatusWith<GetMoreRequest> GetMoreRequest::parseFromBSON(const std::string& dbna for (BSONElement el : cmdObj) { const auto fieldName = el.fieldNameStringData(); + + auto containsAPIParamField = fieldName == kApiVersion || fieldName == kApiStrict || + fieldName == kApiDeprecationErrors; + uassert(4937600, + str::stream() << "Cannot pass in API parameter field " << fieldName, + !containsAPIParamField); + if (fieldName == kGetMoreCommandName) { if (el.type() != BSONType::NumberLong) { return {ErrorCodes::TypeMismatch, diff --git a/src/mongo/dbtests/cursor_manager_test.cpp b/src/mongo/dbtests/cursor_manager_test.cpp index b8a841b56a3..31ad5c0c8a9 100644 --- a/src/mongo/dbtests/cursor_manager_test.cpp +++ b/src/mongo/dbtests/cursor_manager_test.cpp @@ -89,6 +89,7 @@ public: makeFakePlanExecutor(opCtx), kTestNss, {}, + APIParameters(), opCtx->getWriteConcern(), repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -140,6 +141,7 @@ TEST_F(CursorManagerTest, ShouldBeAbleToKillPinnedCursor) { {makeFakePlanExecutor(), kTestNss, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -166,6 +168,7 @@ TEST_F(CursorManagerTest, ShouldBeAbleToKillPinnedCursorMultiClient) { {makeFakePlanExecutor(), kTestNss, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -202,6 +205,7 @@ TEST_F(CursorManagerTest, InactiveCursorShouldTimeout) { {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -217,6 +221,7 @@ TEST_F(CursorManagerTest, InactiveCursorShouldTimeout) { {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -237,6 +242,7 @@ TEST_F(CursorManagerTest, InactivePinnedCursorShouldNotTimeout) { {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -261,6 +267,7 @@ TEST_F(CursorManagerTest, MarkedAsKilledCursorsShouldBeDeletedOnCursorPin) { {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -294,6 +301,7 @@ TEST_F(CursorManagerTest, InactiveKilledCursorsShouldTimeout) { {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -326,6 +334,7 @@ TEST_F(CursorManagerTest, UsingACursorShouldUpdateTimeOfLastUse) { {makeFakePlanExecutor(), kTestNss, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -339,6 +348,7 @@ TEST_F(CursorManagerTest, UsingACursorShouldUpdateTimeOfLastUse) { {makeFakePlanExecutor(), kTestNss, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -376,6 +386,7 @@ TEST_F(CursorManagerTest, CursorShouldNotTimeOutUntilIdleForLongEnoughAfterBeing {makeFakePlanExecutor(), kTestNss, {}, + APIParameters(), {}, repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), BSONObj(), @@ -403,6 +414,34 @@ TEST_F(CursorManagerTest, CursorShouldNotTimeOutUntilIdleForLongEnoughAfterBeing } /** + * Test that a cursor correctly stores API parameters. + */ +TEST_F(CursorManagerTest, CursorStoresAPIParameters) { + APIParameters apiParams = APIParameters(); + apiParams.setAPIVersion("2"); + apiParams.setAPIStrict(true); + apiParams.setAPIDeprecationErrors(true); + + CursorManager* cursorManager = useCursorManager(); + auto cursorPin = cursorManager->registerCursor( + _opCtx.get(), + {makeFakePlanExecutor(), + kTestNss, + {}, + apiParams, + {}, + repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern), + BSONObj(), + PrivilegeVector()}); + + auto storedAPIParams = cursorPin->getAPIParameters(); + + ASSERT_EQ(apiParams.getAPIVersion(), storedAPIParams.getAPIVersion()); + ASSERT_EQ(apiParams.getAPIStrict(), storedAPIParams.getAPIStrict()); + ASSERT_EQ(apiParams.getAPIDeprecationErrors(), storedAPIParams.getAPIDeprecationErrors()); +} + +/** * Test that cursors inherit the logical session id from their operation context */ TEST_F(CursorManagerTestCustomOpCtx, LogicalSessionIdOnOperationCtxTest) { diff --git a/src/mongo/s/query/SConscript b/src/mongo/s/query/SConscript index 0b924534119..05daa81d443 100644 --- a/src/mongo/s/query/SConscript +++ b/src/mongo/s/query/SConscript @@ -47,6 +47,7 @@ env.Library( "cluster_client_cursor_impl.cpp", ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/shared_request_handling', "router_exec_stage", ], ) @@ -93,6 +94,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/shared_request_handling', ], ) @@ -123,6 +125,7 @@ env.Library( '$BUILD_DIR/mongo/db/kill_sessions', '$BUILD_DIR/mongo/db/logical_session_cache', '$BUILD_DIR/mongo/db/logical_session_id', + '$BUILD_DIR/mongo/db/shared_request_handling', ], ) diff --git a/src/mongo/s/query/cluster_aggregation_planner.cpp b/src/mongo/s/query/cluster_aggregation_planner.cpp index 8271c404702..6632dee755a 100644 --- a/src/mongo/s/query/cluster_aggregation_planner.cpp +++ b/src/mongo/s/query/cluster_aggregation_planner.cpp @@ -250,8 +250,10 @@ BSONObj establishMergingMongosCursor(OperationContext* opCtx, std::unique_ptr<Pipeline, PipelineDeleter> pipelineForMerging, const PrivilegeVector& privileges) { - ClusterClientCursorParams params( - requestedNss, ReadPreferenceSetting::get(opCtx), ReadConcernArgs::get(opCtx)); + ClusterClientCursorParams params(requestedNss, + APIParameters::get(opCtx), + ReadPreferenceSetting::get(opCtx), + ReadConcernArgs::get(opCtx)); params.originatingCommandObj = CurOp::get(opCtx)->opDescription().getOwned(); params.tailableMode = pipelineForMerging->getContext()->tailableMode; diff --git a/src/mongo/s/query/cluster_client_cursor.h b/src/mongo/s/query/cluster_client_cursor.h index 6dc1ae8419e..44aae05e34d 100644 --- a/src/mongo/s/query/cluster_client_cursor.h +++ b/src/mongo/s/query/cluster_client_cursor.h @@ -33,6 +33,7 @@ #include "mongo/client/read_preference.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" @@ -176,6 +177,11 @@ public: virtual boost::optional<TxnNumber> getTxnNumber() const = 0; /** + * Returns the APIParameters for this cursor. + */ + virtual APIParameters getAPIParameters() const = 0; + + /** * Returns the readPreference for this cursor. */ virtual boost::optional<ReadPreferenceSetting> getReadPreference() const = 0; diff --git a/src/mongo/s/query/cluster_client_cursor_impl.cpp b/src/mongo/s/query/cluster_client_cursor_impl.cpp index fefd913f8a7..d0cc5fd88b2 100644 --- a/src/mongo/s/query/cluster_client_cursor_impl.cpp +++ b/src/mongo/s/query/cluster_client_cursor_impl.cpp @@ -205,6 +205,10 @@ void ClusterClientCursorImpl::incNBatches() { ++_nBatchesReturned; } +APIParameters ClusterClientCursorImpl::getAPIParameters() const { + return _params.apiParameters; +} + boost::optional<ReadPreferenceSetting> ClusterClientCursorImpl::getReadPreference() const { return _params.readPreference; } diff --git a/src/mongo/s/query/cluster_client_cursor_impl.h b/src/mongo/s/query/cluster_client_cursor_impl.h index 23f1c351fda..e3a853db464 100644 --- a/src/mongo/s/query/cluster_client_cursor_impl.h +++ b/src/mongo/s/query/cluster_client_cursor_impl.h @@ -102,6 +102,8 @@ public: boost::optional<TxnNumber> getTxnNumber() const final; + APIParameters getAPIParameters() const final; + boost::optional<ReadPreferenceSetting> getReadPreference() const final; boost::optional<ReadConcernArgs> getReadConcern() const final; diff --git a/src/mongo/s/query/cluster_client_cursor_impl_test.cpp b/src/mongo/s/query/cluster_client_cursor_impl_test.cpp index 0119abcbd68..630182bda5f 100644 --- a/src/mongo/s/query/cluster_client_cursor_impl_test.cpp +++ b/src/mongo/s/query/cluster_client_cursor_impl_test.cpp @@ -56,10 +56,11 @@ TEST_F(ClusterClientCursorImplTest, NumReturnedSoFar) { mockStage->queueResult(BSON("a" << i)); } - ClusterClientCursorImpl cursor(_opCtx.get(), - std::move(mockStage), - ClusterClientCursorParams(NamespaceString("unused"), {}), - boost::none); + ClusterClientCursorImpl cursor( + _opCtx.get(), + std::move(mockStage), + ClusterClientCursorParams(NamespaceString("unused"), APIParameters(), {}), + boost::none); ASSERT_EQ(cursor.getNumReturnedSoFar(), 0); @@ -251,6 +252,24 @@ TEST_F(ClusterClientCursorImplTest, ShouldStoreLSIDIfSetOnOpCtx) { } } +TEST_F(ClusterClientCursorImplTest, ShouldStoreAPIParameters) { + auto mockStage = std::make_unique<RouterStageMock>(_opCtx.get()); + + APIParameters apiParams = APIParameters(); + apiParams.setAPIVersion("2"); + apiParams.setAPIStrict(true); + apiParams.setAPIDeprecationErrors(true); + + ClusterClientCursorParams params(NamespaceString("test"), apiParams, {}); + ClusterClientCursorImpl cursor( + _opCtx.get(), std::move(mockStage), std::move(params), boost::none); + auto storedAPIParams = cursor.getAPIParameters(); + + ASSERT_EQ(apiParams.getAPIVersion(), storedAPIParams.getAPIVersion()); + ASSERT_EQ(apiParams.getAPIStrict(), storedAPIParams.getAPIStrict()); + ASSERT_EQ(apiParams.getAPIDeprecationErrors(), storedAPIParams.getAPIDeprecationErrors()); +} + } // namespace } // namespace mongo diff --git a/src/mongo/s/query/cluster_client_cursor_mock.cpp b/src/mongo/s/query/cluster_client_cursor_mock.cpp index e6f86dccade..9391f57f4fc 100644 --- a/src/mongo/s/query/cluster_client_cursor_mock.cpp +++ b/src/mongo/s/query/cluster_client_cursor_mock.cpp @@ -149,6 +149,14 @@ boost::optional<TxnNumber> ClusterClientCursorMock::getTxnNumber() const { return _txnNumber; } +void ClusterClientCursorMock::setAPIParameters(APIParameters& apiParameters) { + _apiParameters = apiParameters; +} + +APIParameters ClusterClientCursorMock::getAPIParameters() const { + return _apiParameters; +} + boost::optional<ReadPreferenceSetting> ClusterClientCursorMock::getReadPreference() const { return boost::none; } diff --git a/src/mongo/s/query/cluster_client_cursor_mock.h b/src/mongo/s/query/cluster_client_cursor_mock.h index f72551a24da..a34ce2376eb 100644 --- a/src/mongo/s/query/cluster_client_cursor_mock.h +++ b/src/mongo/s/query/cluster_client_cursor_mock.h @@ -90,6 +90,10 @@ public: boost::optional<TxnNumber> getTxnNumber() const final; + void setAPIParameters(APIParameters& apiParameters); + + APIParameters getAPIParameters() const final; + boost::optional<ReadPreferenceSetting> getReadPreference() const final; boost::optional<ReadConcernArgs> getReadConcern() const final; @@ -141,6 +145,8 @@ private: Date_t _lastUseDate; std::uint64_t _nBatchesReturned = 0; + + APIParameters _apiParameters = APIParameters(); }; } // namespace mongo diff --git a/src/mongo/s/query/cluster_client_cursor_params.h b/src/mongo/s/query/cluster_client_cursor_params.h index cd6563f842c..d8bb0ae8da0 100644 --- a/src/mongo/s/query/cluster_client_cursor_params.h +++ b/src/mongo/s/query/cluster_client_cursor_params.h @@ -39,6 +39,7 @@ #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" @@ -68,9 +69,10 @@ using repl::ReadConcernArgs; */ struct ClusterClientCursorParams { ClusterClientCursorParams(NamespaceString nss, + APIParameters apiParameters, boost::optional<ReadPreferenceSetting> readPref = boost::none, boost::optional<ReadConcernArgs> readConcernArgs = boost::none) - : nsString(std::move(nss)) { + : nsString(std::move(nss)), apiParameters(std::move(apiParameters)) { if (readPref) { readPreference = std::move(readPref.get()); } @@ -147,6 +149,9 @@ struct ClusterClientCursorParams { // set. TailableModeEnum tailableMode = TailableModeEnum::kNormal; + // The API parameters associated with the cursor. + APIParameters apiParameters; + // Set if a readPreference must be respected throughout the lifetime of the cursor. boost::optional<ReadPreferenceSetting> readPreference; diff --git a/src/mongo/s/query/cluster_cursor_manager_test.cpp b/src/mongo/s/query/cluster_cursor_manager_test.cpp index aa8974161cc..1c68a2b3d2a 100644 --- a/src/mongo/s/query/cluster_cursor_manager_test.cpp +++ b/src/mongo/s/query/cluster_cursor_manager_test.cpp @@ -964,6 +964,32 @@ TEST_F(ClusterCursorManagerTest, DoNotDestroyKilledPinnedCursors) { ASSERT(isMockCursorKilled(0)); } +// Test that a cursor correctly stores API parameters. +TEST_F(ClusterCursorManagerTest, CursorStoresAPIParameters) { + APIParameters apiParams = APIParameters(); + apiParams.setAPIVersion("2"); + apiParams.setAPIStrict(true); + apiParams.setAPIDeprecationErrors(true); + + auto cursor = allocateMockCursor(); + cursor->setAPIParameters(apiParams); + + auto cursorId = + assertGet(getManager()->registerCursor(_opCtx.get(), + std::move(cursor), + nss, + ClusterCursorManager::CursorType::SingleTarget, + ClusterCursorManager::CursorLifetime::Mortal, + UserNameIterator())); + auto pinnedCursor = + assertGet(getManager()->checkOutCursor(nss, cursorId, _opCtx.get(), successAuthChecker)); + auto storedAPIParams = pinnedCursor->getAPIParameters(); + + ASSERT_EQ(apiParams.getAPIVersion(), storedAPIParams.getAPIVersion()); + ASSERT_EQ(apiParams.getAPIStrict(), storedAPIParams.getAPIStrict()); + ASSERT_EQ(apiParams.getAPIDeprecationErrors(), storedAPIParams.getAPIDeprecationErrors()); +} + TEST_F(ClusterCursorManagerTest, CannotRegisterCursorDuringShutdown) { ASSERT_OK(getManager()->registerCursor(_opCtx.get(), allocateMockCursor(), diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp index 6f374202b8f..8ceba8426ed 100644 --- a/src/mongo/s/query/cluster_find.cpp +++ b/src/mongo/s/query/cluster_find.cpp @@ -248,7 +248,8 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, // Construct the query and parameters. Defer setting skip and limit here until // we determine if the query is targeting multi-shards or a single shard below. - ClusterClientCursorParams params(query.nss(), readPref, ReadConcernArgs::get(opCtx)); + ClusterClientCursorParams params( + query.nss(), APIParameters::get(opCtx), readPref, ReadConcernArgs::get(opCtx)); params.originatingCommandObj = CurOp::get(opCtx)->opDescription().getOwned(); params.batchSize = query.getQueryRequest().getEffectiveBatchSize(); params.tailableMode = query.getQueryRequest().getTailableMode(); @@ -440,6 +441,8 @@ Status setUpOperationContextStateForGetMore(OperationContext* opCtx, ReadConcernArgs::get(opCtx) = *readConcern; } + 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. auto comment = cursor->getOriginatingCommand()["comment"]; diff --git a/src/mongo/s/query/store_possible_cursor.cpp b/src/mongo/s/query/store_possible_cursor.cpp index b76a47cddbe..6b6391dbb27 100644 --- a/src/mongo/s/query/store_possible_cursor.cpp +++ b/src/mongo/s/query/store_possible_cursor.cpp @@ -99,8 +99,10 @@ StatusWith<BSONObj> storePossibleCursor(OperationContext* opCtx, return cmdResult; } - ClusterClientCursorParams params( - incomingCursorResponse.getValue().getNSS(), boost::none, ReadConcernArgs::get(opCtx)); + ClusterClientCursorParams params(incomingCursorResponse.getValue().getNSS(), + APIParameters::get(opCtx), + boost::none, + ReadConcernArgs::get(opCtx)); params.remotes.emplace_back(); auto& remoteCursor = params.remotes.back(); remoteCursor.setShardId(shardId.toString()); |