From a691dacf5bca2d78714d4abebe18078e1473b485 Mon Sep 17 00:00:00 2001 From: Pierlauro Sciarelli Date: Thu, 9 Feb 2023 11:50:46 +0000 Subject: SERVER-71792 Log list of cursors a range deletion is going to wait on --- src/mongo/db/cursor_manager.cpp | 18 ++++++++++ src/mongo/db/cursor_manager.h | 11 ++++++ .../db/s/range_deleter_service_op_observer.cpp | 11 ++++++ src/mongo/dbtests/cursor_manager_test.cpp | 39 ++++++++++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index 214c1c2bf3f..eb2e3dad8bb 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -186,6 +186,24 @@ std::size_t CursorManager::timeoutCursors(OperationContext* opCtx, Date_t now) { return toDisposeWithoutMutex.size(); } +std::vector CursorManager::getCursorIdsForNamespace(const NamespaceString& nss) { + std::vector cursorIds; + + // Lock and inspect one partition at a time in order to avoid contention. It is acceptable for + // the output not to include info about cursors opened/closed while iterating. + for (size_t partitionId = 0; partitionId < kNumPartitions; ++partitionId) { + auto lockedPartition = _cursorMap->lockOnePartitionById(partitionId); + for (auto it = lockedPartition->begin(); it != lockedPartition->end(); ++it) { + auto* cursor = it->second; + if (cursor->nss() == nss) { + cursorIds.push_back(cursor->cursorid()); + } + } + } + + return cursorIds; +} + StatusWith CursorManager::pinCursor( OperationContext* opCtx, CursorId id, diff --git a/src/mongo/db/cursor_manager.h b/src/mongo/db/cursor_manager.h index bce3b05fdea..ed5cdb4e876 100644 --- a/src/mongo/db/cursor_manager.h +++ b/src/mongo/db/cursor_manager.h @@ -193,6 +193,17 @@ public: std::pair killCursorsWithMatchingSessions(OperationContext* opCtx, const SessionKiller::Matcher& matcher); + /** + * Returns a vector of open cursor ids registered on the `nss`. The result doesn't include + * cursors registered for a different namespace but also acting on `nss` (e.g. in presence of + * sub-pipelines). + * + * Locks/inspects one partition at a time, hence the result might not include new cursors being + * opened on the namespace. The only guarantee is that the result will include any cursor opened + * before calling this method and not closed before iterating the partition holding it. + */ + std::vector getCursorIdsForNamespace(const NamespaceString& nss); + /** * Set the CursorManager's ClockSource*. */ diff --git a/src/mongo/db/s/range_deleter_service_op_observer.cpp b/src/mongo/db/s/range_deleter_service_op_observer.cpp index 7c0d0084963..93de39a2377 100644 --- a/src/mongo/db/s/range_deleter_service_op_observer.cpp +++ b/src/mongo/db/s/range_deleter_service_op_observer.cpp @@ -30,6 +30,7 @@ #include "mongo/db/s/range_deleter_service_op_observer.h" #include "mongo/db/catalog_raii.h" +#include "mongo/db/cursor_manager.h" #include "mongo/db/persistent_task_store.h" #include "mongo/db/s/collection_sharding_runtime.h" #include "mongo/db/s/range_deleter_service.h" @@ -54,6 +55,16 @@ void registerTaskWithOngoingQueriesOnOpLogEntryCommit(OperationContext* opCtx, rdt.getNss()) ->getOngoingQueriesCompletionFuture(rdt.getCollectionUuid(), rdt.getRange()) .semi(); + if (!waitForActiveQueriesToComplete.isReady()) { + const auto openCursorsIds = + CursorManager::get(opCtx)->getCursorIdsForNamespace(rdt.getNss()); + LOGV2_INFO( + 7179200, + "Range deletion will be scheduled after all possibly dependent queries finish", + "namespace"_attr = rdt.getNss(), + "range"_attr = rdt.getRange(), + "cursorsDirectlyReferringTheNamespace"_attr = openCursorsIds); + } (void)RangeDeleterService::get(opCtx)->registerTask( rdt, std::move(waitForActiveQueriesToComplete)); } catch (const DBException& ex) { diff --git a/src/mongo/dbtests/cursor_manager_test.cpp b/src/mongo/dbtests/cursor_manager_test.cpp index fa85089ff26..61845775695 100644 --- a/src/mongo/dbtests/cursor_manager_test.cpp +++ b/src/mongo/dbtests/cursor_manager_test.cpp @@ -754,5 +754,44 @@ TEST_F(CursorManagerTestCustomOpCtx, CursorsMarkedAsKilledAreReturnedForOpKeyLoo auto cursors = useCursorManager()->getCursorsForOpKeys({opKey}); ASSERT_EQ(cursors.size(), size_t(1)); } + +TEST_F(CursorManagerTestCustomOpCtx, + GetCursorIdsForNamespaceReturnsSingleEntryForMatchingNamespace) { + auto opCtx = _queryServiceContext->makeOperationContext(); + auto pinned = makeCursor(opCtx.get()); + auto cursorId = pinned.getCursor()->cursorid(); + auto cursorsForNamespace = useCursorManager()->getCursorIdsForNamespace(kTestNss); + ASSERT_EQUALS(cursorsForNamespace.size(), 1ull); + ASSERT_EQUALS(cursorsForNamespace[0], cursorId); +} + +TEST_F(CursorManagerTestCustomOpCtx, + GetCursorIdsForNamespaceReturnsMultipleEntriesForMatchingNamespace) { + auto opCtx = _queryServiceContext->makeOperationContext(); + auto pinned1 = makeCursor(opCtx.get()); + auto pinned2 = makeCursor(opCtx.get()); + auto cursorId1 = pinned1.getCursor()->cursorid(); + auto cursorId2 = pinned2.getCursor()->cursorid(); + auto cursorsForNamespace = useCursorManager()->getCursorIdsForNamespace(kTestNss); + ASSERT_EQUALS(cursorsForNamespace.size(), 2ull); + // The results for cursorsForNamespace won't necessarily be the same as the order of insertion. + std::set cursorsForNamespaceSet(cursorsForNamespace.begin(), + cursorsForNamespace.end()); + + ASSERT_EQUALS(cursorsForNamespaceSet.count(cursorId1), 1ull); + ASSERT_EQUALS(cursorsForNamespaceSet.count(cursorId2), 1ull); +} + +TEST_F(CursorManagerTestCustomOpCtx, + GetCursorIdsForNamespaceDoesNotReturnEntriesForNonMatchingNamespace) { + auto opCtx = _queryServiceContext->makeOperationContext(); + // Add a cursor for kTestNss. + auto pinned = makeCursor(opCtx.get()); + // Get cursors for a different NamespaceString. + auto cursorsForNamespace = + useCursorManager()->getCursorIdsForNamespace(NamespaceString("somerandom.nss")); + ASSERT_EQUALS(cursorsForNamespace.size(), 0ull); +} + } // namespace } // namespace mongo -- cgit v1.2.1