diff options
author | James Wahlin <james@mongodb.com> | 2018-07-20 09:05:04 -0400 |
---|---|---|
committer | James Wahlin <james@mongodb.com> | 2018-07-24 13:09:41 -0400 |
commit | 35528523c00b72a210dc5b78a427d90ed1c14331 (patch) | |
tree | e666ffa680ffc39ca0bdbce323ac2fb116dedf6a /src/mongo/db | |
parent | 9175c4deba82dc35606d14428d1bf0d8b43d7a6c (diff) | |
download | mongo-35528523c00b72a210dc5b78a427d90ed1c14331.tar.gz |
SERVER-35031 Return MaxTimeMSExpired for maxTimeMS timeout
Adds a new 'MaxTimeMSExpired' error code, returned when a timeout occurs
due to exceeding of maxTimeMS. Timeouts unrelated to maxTimeMS will
continue to return 'ExceededTimeLimit'.
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/commands/getmore_cmd.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/exec/cached_plan.h | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/subplan.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/exec/subplan.h | 2 | ||||
-rw-r--r-- | src/mongo/db/keys_collection_manager_sharding_test.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/operation_context.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/operation_context.h | 14 | ||||
-rw-r--r-- | src/mongo/db/operation_context_test.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/query/find.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/query/plan_executor.h | 2 | ||||
-rw-r--r-- | src/mongo/db/s/namespace_metadata_change_notifications_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_mongod.cpp | 2 |
13 files changed, 46 insertions, 44 deletions
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp index 4f0d297fc55..e296e4e0c97 100644 --- a/src/mongo/db/commands/getmore_cmd.cpp +++ b/src/mongo/db/commands/getmore_cmd.cpp @@ -401,7 +401,8 @@ public: opCtx->getServiceContext()->getPreciseClockSource()->now() + _request.awaitDataTimeout.value_or(Seconds{1}); } else if (cursor->getLeftoverMaxTimeMicros() < Microseconds::max()) { - opCtx->setDeadlineAfterNowBy(cursor->getLeftoverMaxTimeMicros()); + opCtx->setDeadlineAfterNowBy(cursor->getLeftoverMaxTimeMicros(), + ErrorCodes::MaxTimeMSExpired); } } if (!cursor->isAwaitData()) { diff --git a/src/mongo/db/exec/cached_plan.h b/src/mongo/db/exec/cached_plan.h index fa2f80a5e48..9d149860811 100644 --- a/src/mongo/db/exec/cached_plan.h +++ b/src/mongo/db/exec/cached_plan.h @@ -112,7 +112,7 @@ private: * * Returns a non-OK status if query planning fails. In particular, this function returns * ErrorCodes::QueryPlanKilled if the query plan was killed during a yield, or - * ErrorCodes::ExceededTimeLimit if the operation exceeded its time limit. + * ErrorCodes::MaxTimeMSExpired if the operation exceeded its time limit. */ Status tryYield(PlanYieldPolicy* yieldPolicy); diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index 41780eab96c..860adc4af20 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -428,13 +428,6 @@ Status SubplanStage::pickBestPlan(PlanYieldPolicy* yieldPolicy) { // Plan each branch of the $or. Status subplanningStatus = planSubqueries(); if (!subplanningStatus.isOK()) { - if (subplanningStatus == ErrorCodes::QueryPlanKilled || - subplanningStatus == ErrorCodes::ExceededTimeLimit) { - // Query planning cannot continue if the plan for one of the subqueries was killed - // because the collection or a candidate index may have been dropped, or if we've - // exceeded the operation's time limit. - return subplanningStatus; - } return choosePlanWholeQuery(yieldPolicy); } @@ -443,7 +436,7 @@ Status SubplanStage::pickBestPlan(PlanYieldPolicy* yieldPolicy) { Status subplanSelectStat = choosePlanForSubqueries(yieldPolicy); if (!subplanSelectStat.isOK()) { if (subplanSelectStat == ErrorCodes::QueryPlanKilled || - subplanSelectStat == ErrorCodes::ExceededTimeLimit) { + subplanSelectStat == ErrorCodes::MaxTimeMSExpired) { // Query planning cannot continue if the plan for one of the subqueries was killed // because the collection or a candidate index may have been dropped, or if we've // exceeded the operation's time limit. diff --git a/src/mongo/db/exec/subplan.h b/src/mongo/db/exec/subplan.h index c61fb3201d4..a05ac4e2955 100644 --- a/src/mongo/db/exec/subplan.h +++ b/src/mongo/db/exec/subplan.h @@ -103,7 +103,7 @@ public: * * Returns a non-OK status if query planning fails. In particular, this function returns * ErrorCodes::QueryPlanKilled if the query plan was killed during a yield, or - * ErrorCodes::ExceededTimeLimit if the operation has exceeded its time limit. + * ErrorCodes::MaxTimeMSExpired if the operation has exceeded its time limit. */ Status pickBestPlan(PlanYieldPolicy* yieldPolicy); diff --git a/src/mongo/db/keys_collection_manager_sharding_test.cpp b/src/mongo/db/keys_collection_manager_sharding_test.cpp index d21db07a5ff..005eaf049d8 100644 --- a/src/mongo/db/keys_collection_manager_sharding_test.cpp +++ b/src/mongo/db/keys_collection_manager_sharding_test.cpp @@ -87,7 +87,8 @@ private: }; TEST_F(KeysManagerShardedTest, GetKeyForValidationTimesOutIfRefresherIsNotRunning) { - operationContext()->setDeadlineAfterNowBy(Microseconds(250 * 1000)); + operationContext()->setDeadlineAfterNowBy(Microseconds(250 * 1000), + ErrorCodes::ExceededTimeLimit); ASSERT_THROWS(keyManager() ->getKeyForValidation(operationContext(), 1, LogicalTime(Timestamp(100, 0))) diff --git a/src/mongo/db/operation_context.cpp b/src/mongo/db/operation_context.cpp index f878eead361..091ffdd1ee4 100644 --- a/src/mongo/db/operation_context.cpp +++ b/src/mongo/db/operation_context.cpp @@ -83,11 +83,15 @@ OperationContext::OperationContext(Client* client, unsigned int opId) _elapsedTime(client ? client->getServiceContext()->getTickSource() : SystemTickSource::get()) {} -void OperationContext::setDeadlineAndMaxTime(Date_t when, Microseconds maxTime) { +void OperationContext::setDeadlineAndMaxTime(Date_t when, + Microseconds maxTime, + ErrorCodes::Error timeoutError) { invariant(!getClient()->isInDirectClient()); + invariant(ErrorCodes::isExceededTimeLimitError(timeoutError)); uassert(40120, "Illegal attempt to change operation deadline", !hasDeadline()); _deadline = when; _maxTime = maxTime; + _timeoutError = timeoutError; } Microseconds OperationContext::computeMaxTimeFromDeadline(Date_t when) { @@ -103,11 +107,11 @@ Microseconds OperationContext::computeMaxTimeFromDeadline(Date_t when) { return maxTime; } -void OperationContext::setDeadlineByDate(Date_t when) { - setDeadlineAndMaxTime(when, computeMaxTimeFromDeadline(when)); +void OperationContext::setDeadlineByDate(Date_t when, ErrorCodes::Error timeoutError) { + setDeadlineAndMaxTime(when, computeMaxTimeFromDeadline(when), timeoutError); } -void OperationContext::setDeadlineAfterNowBy(Microseconds maxTime) { +void OperationContext::setDeadlineAfterNowBy(Microseconds maxTime, ErrorCodes::Error timeoutError) { Date_t when; if (maxTime < Microseconds::zero()) { maxTime = Microseconds::zero(); @@ -121,7 +125,7 @@ void OperationContext::setDeadlineAfterNowBy(Microseconds maxTime) { when += clock->getPrecision() + maxTime; } } - setDeadlineAndMaxTime(when, maxTime); + setDeadlineAndMaxTime(when, maxTime, timeoutError); } bool OperationContext::hasDeadlineExpired() const { @@ -195,8 +199,8 @@ Status OperationContext::checkForInterruptNoAssert() { } if (hasDeadlineExpired()) { - markKilled(ErrorCodes::ExceededTimeLimit); - return Status(ErrorCodes::ExceededTimeLimit, "operation exceeded time limit"); + markKilled(_timeoutError); + return Status(_timeoutError, "operation exceeded time limit"); } MONGO_FAIL_POINT_BLOCK(checkForInterruptFail, scopedFailPoint) { @@ -298,7 +302,7 @@ StatusWith<stdx::cv_status> OperationContext::waitForConditionOrInterruptNoAsser // that we expect to time out at the same time as the existing deadline expires. If, when we // time out, we find that the op's deadline has not expired (as will always be the case if // maxTimeNeverTimeOut is set) then we assume that the incongruity is due to a clock mismatch - // and return ExceededTimeLimit regardless. To prevent this behaviour, only consider the op's + // and return _timeoutError regardless. To prevent this behaviour, only consider the op's // deadline in the event that the maxTimeNeverTimeOut failpoint is not set. bool opHasDeadline = (hasDeadline() && !MONGO_FAIL_POINT(maxTimeNeverTimeOut)); @@ -334,8 +338,8 @@ StatusWith<stdx::cv_status> OperationContext::waitForConditionOrInterruptNoAsser // is slightly ahead of the FastClock used in checkForInterrupt. In this case, // we treat the operation as though it has exceeded its time limit, just as if the // FastClock and system clock had agreed. - markKilled(ErrorCodes::ExceededTimeLimit); - return Status(ErrorCodes::ExceededTimeLimit, "operation exceeded time limit"); + markKilled(_timeoutError); + return Status(_timeoutError, "operation exceeded time limit"); } return waitStatus; } diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index c188f43c103..96c2fecc76e 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -377,22 +377,22 @@ public: * * To remove a deadline, pass in Date_t::max(). */ - void setDeadlineByDate(Date_t when); + void setDeadlineByDate(Date_t when, ErrorCodes::Error timeoutError); /** * Sets the deadline for this operation to the maxTime plus the current time reported * by the ServiceContext's fast clock source. */ - void setDeadlineAfterNowBy(Microseconds maxTime); + void setDeadlineAfterNowBy(Microseconds maxTime, ErrorCodes::Error timeoutError); template <typename D> - void setDeadlineAfterNowBy(D maxTime) { + void setDeadlineAfterNowBy(D maxTime, ErrorCodes::Error timeoutError) { if (maxTime <= D::zero()) { maxTime = D::zero(); } if (maxTime <= Microseconds::max()) { - setDeadlineAfterNowBy(duration_cast<Microseconds>(maxTime)); + setDeadlineAfterNowBy(duration_cast<Microseconds>(maxTime), timeoutError); } else { - setDeadlineByDate(Date_t::max()); + setDeadlineByDate(Date_t::max(), timeoutError); } } @@ -436,7 +436,7 @@ private: * Sets the deadline and maxTime as described. It is up to the caller to ensure that * these correctly correspond. */ - void setDeadlineAndMaxTime(Date_t when, Microseconds maxTime); + void setDeadlineAndMaxTime(Date_t when, Microseconds maxTime, ErrorCodes::Error timeoutError); /** * Compute maxTime based on the given deadline. @@ -501,6 +501,8 @@ private: Date_t _deadline = Date_t::max(); // The timepoint at which this operation exceeds its time limit. + ErrorCodes::Error _timeoutError = ErrorCodes::ExceededTimeLimit; + // Max operation time requested by the user or by the cursor in the case of a getMore with no // user-specified maxTime. This is tracked with microsecond granularity for the purpose of // assigning unused execution time back to a cursor at the end of an operation, only. The diff --git a/src/mongo/db/operation_context_test.cpp b/src/mongo/db/operation_context_test.cpp index 4660afedd21..b779102b702 100644 --- a/src/mongo/db/operation_context_test.cpp +++ b/src/mongo/db/operation_context_test.cpp @@ -217,7 +217,7 @@ public: TEST_F(OperationDeadlineTests, OperationDeadlineExpiration) { auto opCtx = client->makeOperationContext(); - opCtx->setDeadlineAfterNowBy(Seconds{1}); + opCtx->setDeadlineAfterNowBy(Seconds{1}, ErrorCodes::ExceededTimeLimit); mockClock->advance(Milliseconds{500}); ASSERT_OK(opCtx->checkForInterruptNoAssert()); @@ -245,7 +245,7 @@ TEST_F(OperationDeadlineTests, OperationDeadlineExpiration) { template <typename D> void assertLargeRelativeDeadlineLikeInfinity(Client& client, D maxTime) { auto opCtx = client.makeOperationContext(); - opCtx->setDeadlineAfterNowBy(maxTime); + opCtx->setDeadlineAfterNowBy(maxTime, ErrorCodes::ExceededTimeLimit); ASSERT_FALSE(opCtx->hasDeadline()) << "Tried to set maxTime to " << maxTime; } @@ -274,7 +274,7 @@ TEST_F(OperationDeadlineTests, VeryLargeRelativeDeadlinesNanoseconds) { // Nanoseconds::max() is less than Microseconds::max(), so it is possible to set // a deadline of that duration. auto opCtx = client->makeOperationContext(); - opCtx->setDeadlineAfterNowBy(Nanoseconds::max()); + opCtx->setDeadlineAfterNowBy(Nanoseconds::max(), ErrorCodes::ExceededTimeLimit); ASSERT_TRUE(opCtx->hasDeadline()); ASSERT_EQ(mockClock->now() + mockClock->getPrecision() + duration_cast<Milliseconds>(Nanoseconds::max()), @@ -283,7 +283,7 @@ TEST_F(OperationDeadlineTests, VeryLargeRelativeDeadlinesNanoseconds) { TEST_F(OperationDeadlineTests, WaitForMaxTimeExpiredCV) { auto opCtx = client->makeOperationContext(); - opCtx->setDeadlineByDate(mockClock->now()); + opCtx->setDeadlineByDate(mockClock->now(), ErrorCodes::ExceededTimeLimit); stdx::mutex m; stdx::condition_variable cv; stdx::unique_lock<stdx::mutex> lk(m); @@ -292,7 +292,7 @@ TEST_F(OperationDeadlineTests, WaitForMaxTimeExpiredCV) { TEST_F(OperationDeadlineTests, WaitForMaxTimeExpiredCVWithWaitUntilSet) { auto opCtx = client->makeOperationContext(); - opCtx->setDeadlineByDate(mockClock->now()); + opCtx->setDeadlineByDate(mockClock->now(), ErrorCodes::ExceededTimeLimit); stdx::mutex m; stdx::condition_variable cv; stdx::unique_lock<stdx::mutex> lk(m); @@ -323,7 +323,7 @@ TEST_F(OperationDeadlineTests, WaitForUntilExpiredCV) { TEST_F(OperationDeadlineTests, WaitForUntilExpiredCVWithMaxTimeSet) { auto opCtx = client->makeOperationContext(); - opCtx->setDeadlineByDate(mockClock->now() + Seconds{10}); + opCtx->setDeadlineByDate(mockClock->now() + Seconds{10}, ErrorCodes::ExceededTimeLimit); stdx::mutex m; stdx::condition_variable cv; stdx::unique_lock<stdx::mutex> lk(m); @@ -343,7 +343,7 @@ TEST_F(OperationDeadlineTests, WaitForDurationExpired) { TEST_F(OperationDeadlineTests, DuringWaitMaxTimeExpirationDominatesUntilExpiration) { auto opCtx = client->makeOperationContext(); - opCtx->setDeadlineByDate(mockClock->now()); + opCtx->setDeadlineByDate(mockClock->now(), ErrorCodes::ExceededTimeLimit); stdx::mutex m; stdx::condition_variable cv; stdx::unique_lock<stdx::mutex> lk(m); @@ -378,7 +378,7 @@ public: auto barrier = std::make_shared<unittest::Barrier>(2); auto task = stdx::packaged_task<bool()>([=] { if (maxTime < Date_t::max()) { - opCtx->setDeadlineByDate(maxTime); + opCtx->setDeadlineByDate(maxTime, ErrorCodes::ExceededTimeLimit); } auto predicate = [state] { return state->isSignaled; }; stdx::unique_lock<stdx::mutex> lk(state->mutex); diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp index 1d3462bef36..b295df1d5e7 100644 --- a/src/mongo/db/query/find.cpp +++ b/src/mongo/db/query/find.cpp @@ -367,7 +367,8 @@ Message getMore(OperationContext* opCtx, uassert(40136, "Illegal attempt to set operation deadline within DBDirectClient", !opCtx->getClient()->isInDirectClient()); - opCtx->setDeadlineAfterNowBy(cc->getLeftoverMaxTimeMicros()); + opCtx->setDeadlineAfterNowBy(cc->getLeftoverMaxTimeMicros(), + ErrorCodes::MaxTimeMSExpired); } opCtx->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. @@ -588,7 +589,7 @@ std::string runQuery(OperationContext* opCtx, uassert(40116, "Illegal attempt to set operation deadline within DBDirectClient", !opCtx->getClient()->isInDirectClient()); - opCtx->setDeadlineAfterNowBy(Milliseconds{qr.getMaxTimeMS()}); + opCtx->setDeadlineAfterNowBy(Milliseconds{qr.getMaxTimeMS()}, ErrorCodes::MaxTimeMSExpired); } opCtx->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. diff --git a/src/mongo/db/query/plan_executor.h b/src/mongo/db/query/plan_executor.h index 43510d651f5..0fe1f01fdf5 100644 --- a/src/mongo/db/query/plan_executor.h +++ b/src/mongo/db/query/plan_executor.h @@ -298,7 +298,7 @@ public: * Returns ErrorCodes::QueryPlanKilled if the PlanExecutor was killed while saved. * * If allowed, will yield and retry if a WriteConflictException is encountered. If the time - * limit is exceeded during this retry process, returns ErrorCodes::ExceededTimeLimit. If this + * limit is exceeded during this retry process, returns ErrorCodes::MaxTimeMSExpired. If this * PlanExecutor is killed during this retry process, returns ErrorCodes::QueryPlanKilled. In * this scenario, locks will have been released, and will not be held when control returns to * the caller. diff --git a/src/mongo/db/s/namespace_metadata_change_notifications_test.cpp b/src/mongo/db/s/namespace_metadata_change_notifications_test.cpp index 5e4cde64b6e..4d8dcad43a1 100644 --- a/src/mongo/db/s/namespace_metadata_change_notifications_test.cpp +++ b/src/mongo/db/s/namespace_metadata_change_notifications_test.cpp @@ -56,7 +56,7 @@ TEST_F(NamespaceMetadataChangeNotificationsTest, WaitForNotify) { { auto opCtx = getClient()->makeOperationContext(); - opCtx->setDeadlineAfterNowBy(Milliseconds{0}); + opCtx->setDeadlineAfterNowBy(Milliseconds{0}, ErrorCodes::ExceededTimeLimit); ASSERT_THROWS_CODE( scopedNotif.get(opCtx.get()), AssertionException, ErrorCodes::ExceededTimeLimit); } @@ -76,7 +76,7 @@ TEST_F(NamespaceMetadataChangeNotificationsTest, GiveUpWaitingForNotify) { auto scopedNotif = notifications.createNotification(kNss); auto opCtx = getClient()->makeOperationContext(); - opCtx->setDeadlineAfterNowBy(Milliseconds{0}); + opCtx->setDeadlineAfterNowBy(Milliseconds{0}, ErrorCodes::ExceededTimeLimit); ASSERT_THROWS_CODE( scopedNotif.get(opCtx.get()), AssertionException, ErrorCodes::ExceededTimeLimit); } @@ -92,7 +92,7 @@ TEST_F(NamespaceMetadataChangeNotificationsTest, MoveConstructionWaitForNotify) { auto opCtx = getClient()->makeOperationContext(); - opCtx->setDeadlineAfterNowBy(Milliseconds{0}); + opCtx->setDeadlineAfterNowBy(Milliseconds{0}, ErrorCodes::ExceededTimeLimit); ASSERT_THROWS_CODE( movedScopedNotif.get(opCtx.get()), AssertionException, ErrorCodes::ExceededTimeLimit); } diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index b87188cbc8a..ac6d691418b 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -807,7 +807,7 @@ void execCommandDatabase(OperationContext* opCtx, uassert(40119, "Illegal attempt to set operation deadline within DBDirectClient", !opCtx->getClient()->isInDirectClient()); - opCtx->setDeadlineAfterNowBy(Milliseconds{maxTimeMS}); + opCtx->setDeadlineAfterNowBy(Milliseconds{maxTimeMS}, ErrorCodes::MaxTimeMSExpired); } auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx); diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp index 4ba450faa74..488bdcba0bc 100644 --- a/src/mongo/db/service_entry_point_mongod.cpp +++ b/src/mongo/db/service_entry_point_mongod.cpp @@ -57,7 +57,7 @@ public: opCtx, repl::ReadConcernArgs::get(opCtx), invocation->allowsAfterClusterTime()); if (!rcStatus.isOK()) { - if (rcStatus == ErrorCodes::ExceededTimeLimit) { + if (ErrorCodes::isExceededTimeLimitError(rcStatus.code())) { const int debugLevel = serverGlobalParams.clusterRole == ClusterRole::ConfigServer ? 0 : 2; LOG(debugLevel) << "Command on database " << request.getDatabase() |