@@ -65,6 +65,11 @@ const OperationContext::Decoration<bool> shouldWaitForInserts =
6565const OperationContext::Decoration<repl::OpTime> clientsLastKnownCommittedOpTime =
6666 OperationContext::declareDecoration<repl::OpTime>();
6767
68+ struct CappedInsertNotifierData {
69+ shared_ptr<CappedInsertNotifier> notifier;
70+ uint64_t lastEOFVersion = ~0 ;
71+ };
72+
6873namespace {
6974
7075namespace {
@@ -407,30 +412,45 @@ bool PlanExecutor::shouldWaitForInserts() {
407412 return false ;
408413}
409414
410- bool PlanExecutor::waitForInserts () {
411- // If we cannot yield, we should retry immediately.
415+ std::shared_ptr<CappedInsertNotifier> PlanExecutor::getCappedInsertNotifier () {
416+ // If we cannot yield, we should retry immediately when we hit EOF, so do not get
417+ // a CappedInsertNotifier.
412418 if (!_yieldPolicy->canReleaseLocksDuringExecution ())
413- return true ;
419+ return nullptr ;
414420
415- // We can only wait if we have a collection; otherwise retry immediately.
421+ // We can only wait if we have a collection; otherwise we should retry immediately when
422+ // we hit EOF.
416423 dassert (_opCtx->lockState ()->isCollectionLockedForMode (_nss.ns (), MODE_IS));
417424 auto db = dbHolder ().get (_opCtx, _nss.db ());
418425 if (!db)
419- return true ;
426+ return nullptr ;
420427 auto collection = db->getCollection (_opCtx, _nss);
421428 if (!collection)
429+ return nullptr ;
430+
431+ return collection->getCappedInsertNotifier ();
432+ }
433+
434+ bool PlanExecutor::waitForInserts (CappedInsertNotifierData* notifierData) {
435+ // We tested to see if we could wait when getting the CappedInsertNotifier.
436+ if (!notifierData->notifier )
422437 return true ;
423438
424- auto notifier = collection->getCappedInsertNotifier ();
425- uint64_t notifierVersion = notifier->getVersion ();
439+ // The notifier wait() method will not wait unless the version passed to it matches the
440+ // current version of the notifier. Since the version passed to it is the current version
441+ // of the notifier at the time of the previous EOF, we require two EOFs in a row with no
442+ // notifier version change in order to wait. This is sufficient to ensure we never wait
443+ // when data is available.
426444 auto curOp = CurOp::get (_opCtx);
427445 curOp->pauseTimer ();
428446 ON_BLOCK_EXIT ([curOp] { curOp->resumeTimer (); });
429447 auto opCtx = _opCtx;
430- bool yieldResult = _yieldPolicy->yield (nullptr , [opCtx, notifier, notifierVersion] {
448+ uint64_t currentNotifierVersion = notifierData->notifier ->getVersion ();
449+ bool yieldResult = _yieldPolicy->yield (nullptr , [opCtx, notifierData] {
431450 const auto timeout = opCtx->getRemainingMaxTimeMicros ();
432- notifier->wait (notifierVersion , timeout);
451+ notifierData-> notifier ->wait (notifierData-> lastEOFVersion , timeout);
433452 });
453+ notifierData->lastEOFVersion = currentNotifierVersion;
434454 return yieldResult;
435455}
436456
@@ -471,6 +491,13 @@ PlanExecutor::ExecState PlanExecutor::getNextImpl(Snapshotted<BSONObj>* objOut,
471491 // Incremented on every writeConflict, reset to 0 on any successful call to _root->work.
472492 size_t writeConflictsInARow = 0 ;
473493
494+ // Capped insert data; declared outside the loop so we hold a shared pointer to the capped
495+ // insert notifier the entire time we are in the loop. Holding a shared pointer to the capped
496+ // insert notifier is necessary for the notifierVersion to advance.
497+ CappedInsertNotifierData cappedInsertNotifierData;
498+ if (shouldWaitForInserts ()) {
499+ cappedInsertNotifierData.notifier = getCappedInsertNotifier ();
500+ }
474501 for (;;) {
475502 // These are the conditions which can cause us to yield:
476503 // 1) The yield policy's timer elapsed, or
@@ -563,7 +590,7 @@ PlanExecutor::ExecState PlanExecutor::getNextImpl(Snapshotted<BSONObj>* objOut,
563590 // Fall through to yield check at end of large conditional.
564591 } else if (PlanStage::IS_EOF == code) {
565592 if (shouldWaitForInserts ()) {
566- const bool locksReacquiredAfterYield = waitForInserts ();
593+ const bool locksReacquiredAfterYield = waitForInserts (&cappedInsertNotifierData );
567594 if (locksReacquiredAfterYield) {
568595 // There may be more results, try to get more data.
569596 continue ;
0 commit comments