diff options
author | Matthew Saltz <matthew.saltz@mongodb.com> | 2020-04-14 19:29:24 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-05-11 20:15:12 +0000 |
commit | a434867901150513736daa876b362ab9f7a22195 (patch) | |
tree | 57f8faaf4618623c886e3607a27140c272ebbc73 | |
parent | 927de0e9066200b82c6316804d15795978489587 (diff) | |
download | mongo-a434867901150513736daa876b362ab9f7a22195.tar.gz |
SERVER-45367 Log open cursors for a namespace when submitting tasks to the range deleter
(cherry picked from commit 2c7a53f74cd6f588cca2ab968a9263d59c56684c)
-rw-r--r-- | src/mongo/db/cursor_manager.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.h | 5 | ||||
-rw-r--r-- | src/mongo/db/s/cleanup_orphaned_cmd.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/s/collection_sharding_state.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/s/collection_sharding_state.h | 6 | ||||
-rw-r--r-- | src/mongo/db/s/metadata_manager.cpp | 54 | ||||
-rw-r--r-- | src/mongo/db/s/metadata_manager.h | 5 | ||||
-rw-r--r-- | src/mongo/db/s/metadata_manager_test.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/s/migration_source_manager.cpp | 3 | ||||
-rw-r--r-- | src/mongo/dbtests/cursor_manager_test.cpp | 39 |
10 files changed, 143 insertions, 14 deletions
diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index bfb27d07254..6be2ae666e0 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -640,6 +640,22 @@ void CursorManager::appendActiveCursors(std::vector<GenericCursor>* cursors) con } } +std::vector<CursorId> CursorManager::getCursorIdsForNamespace(const NamespaceString& nss) const { + std::vector<CursorId> cursors; + + auto allPartitions = _cursorMap->lockAllPartitions(); + for (auto&& partition : allPartitions) { + for (auto&& entry : partition) { + auto cursor = entry.second; + if (cursor->nss() == nss) { + cursors.emplace_back(cursor->cursorid()); + } + } + } + + return cursors; +} + stdx::unordered_set<CursorId> CursorManager::getCursorsForSession(LogicalSessionId lsid) const { stdx::unordered_set<CursorId> cursors; diff --git a/src/mongo/db/cursor_manager.h b/src/mongo/db/cursor_manager.h index 299b1f3fa65..1ecb19694b9 100644 --- a/src/mongo/db/cursor_manager.h +++ b/src/mongo/db/cursor_manager.h @@ -196,6 +196,11 @@ public: */ void appendActiveCursors(std::vector<GenericCursor>* cursors) const; + /** + * Returns a vector of all open cursors for the given namespace. + */ + std::vector<CursorId> getCursorIdsForNamespace(const NamespaceString& nss) const; + /* * Returns a list of all open cursors for the given session. */ diff --git a/src/mongo/db/s/cleanup_orphaned_cmd.cpp b/src/mongo/db/s/cleanup_orphaned_cmd.cpp index a54d3c03aac..d8cf1728f5b 100644 --- a/src/mongo/db/s/cleanup_orphaned_cmd.cpp +++ b/src/mongo/db/s/cleanup_orphaned_cmd.cpp @@ -111,7 +111,8 @@ CleanupResult cleanupOrphanedData(OperationContext* opCtx, *stoppedAtKey = targetRange->getMax(); - notifn = css->cleanUpRange(*targetRange, CollectionShardingState::kNow); + notifn = css->cleanUpRange( + opCtx, autoColl.getCollection(), *targetRange, CollectionShardingState::kNow); } // Sleep waiting for our own deletion. We don't actually care about any others, so there is no diff --git a/src/mongo/db/s/collection_sharding_state.cpp b/src/mongo/db/s/collection_sharding_state.cpp index 5215d47b698..df365b79800 100644 --- a/src/mongo/db/s/collection_sharding_state.cpp +++ b/src/mongo/db/s/collection_sharding_state.cpp @@ -235,11 +235,13 @@ void CollectionShardingState::forgetReceive(const ChunkRange& range) { _metadataManager->forgetReceive(range); } -auto CollectionShardingState::cleanUpRange(ChunkRange const& range, CleanWhen when) - -> CleanupNotification { +auto CollectionShardingState::cleanUpRange(OperationContext* opCtx, + const Collection* collection, + ChunkRange const& range, + CleanWhen when) -> CleanupNotification { Date_t time = (when == kNow) ? Date_t{} : Date_t::now() + stdx::chrono::seconds{orphanCleanupDelaySecs.load()}; - return _metadataManager->cleanUpRange(range, time); + return _metadataManager->cleanUpRange(opCtx, collection, range, time); } std::vector<ScopedCollectionMetadata> CollectionShardingState::overlappingMetadata( diff --git a/src/mongo/db/s/collection_sharding_state.h b/src/mongo/db/s/collection_sharding_state.h index eeef74e62b6..e6072916cab 100644 --- a/src/mongo/db/s/collection_sharding_state.h +++ b/src/mongo/db/s/collection_sharding_state.h @@ -152,7 +152,11 @@ public: * result.abandon(), instead of waitStatus, to ignore the outcome. */ enum CleanWhen { kNow, kDelayed }; - auto cleanUpRange(ChunkRange const& range, CleanWhen) -> CleanupNotification; + auto cleanUpRange(OperationContext* opCtx, + const Collection* collection, + ChunkRange const& range, + CleanWhen) -> CleanupNotification; + /** * Returns a vector of ScopedCollectionMetadata objects representing metadata instances in use diff --git a/src/mongo/db/s/metadata_manager.cpp b/src/mongo/db/s/metadata_manager.cpp index 87e59ee84e9..ba8eefd6cd3 100644 --- a/src/mongo/db/s/metadata_manager.cpp +++ b/src/mongo/db/s/metadata_manager.cpp @@ -38,6 +38,8 @@ #include "mongo/bson/simple_bsonobj_comparator.h" #include "mongo/bson/util/builder.h" #include "mongo/db/bson/dotted_path_support.h" +#include "mongo/db/cursor_manager.h" +#include "mongo/db/db_raii.h" #include "mongo/db/query/internal_plans.h" #include "mongo/db/range_arithmetic.h" #include "mongo/db/s/collection_sharding_state.h" @@ -150,6 +152,49 @@ void scheduleCleanup(executor::TaskExecutor* executor, } } +void logRangeDeletionWaitingOnOpenCursors(const OperationContext* opCtx, + const Collection* collection, + const NamespaceString& nss, + const ChunkRange& range) { + invariant(opCtx->lockState()->isCollectionLockedForMode(nss.toString(), MODE_IS)); + std::vector<CursorId> cursorIds; + // If the collection exists, gather a list of all cursors related to the collection. + if (collection) { + auto cursorIdsFromCollectionCursorManager = + collection->getCursorManager()->getCursorIdsForNamespace(nss); + cursorIds.insert(cursorIds.end(), + cursorIdsFromCollectionCursorManager.begin(), + cursorIdsFromCollectionCursorManager.end()); + + // Aggregation cursors are registered on the global cursor manager. A cursor on + // the global cursor manager can involve any number of collections, but is registered with + // the namespace of the aggregate command. This works well for this purpose since any other + // namespaces would come through a $lookup or $graphLookup which cannot read from sharded + // collections and so could not be contributing to the delay of a range deletion. The + // namespace of the aggregate command can be sharded, so we do want to include those + // aggregation cursors in this message. + auto cursorIdsFromGlobalCursorManager = + CursorManager::getGlobalCursorManager()->getCursorIdsForNamespace(nss); + cursorIds.insert(cursorIds.end(), + cursorIdsFromGlobalCursorManager.begin(), + cursorIdsFromGlobalCursorManager.end()); + } + + // Join cursorIds as a comma-separated list. + std::string cursorIdList = + cursorIds.empty() ? "" : std::accumulate(std::next(cursorIds.begin()), + cursorIds.end(), + std::to_string(cursorIds[0]), + [](std::string a, CursorId b) { + return std::move(a) + ',' + std::to_string(b); + }); + + log() << "Deletion of " << nss.ns() << " range " << redact(range.toString()) + << " will be scheduled after all possibly dependent queries finish. " + "All open cursors for namespace: [" + << cursorIdList << "]"; +} + } // namespace MetadataManager::MetadataManager(ServiceContext* serviceContext, @@ -417,8 +462,10 @@ void MetadataManager::forgetReceive(ChunkRange const& range) { _pushRangeToClean(lg, range, Date_t{}).abandon(); } -auto MetadataManager::cleanUpRange(ChunkRange const& range, Date_t whenToDelete) - -> CleanupNotification { +auto MetadataManager::cleanUpRange(OperationContext* opCtx, + const Collection* collection, + ChunkRange const& range, + Date_t whenToDelete) -> CleanupNotification { stdx::lock_guard<stdx::mutex> lg(_managerLock); invariant(!_metadata.empty()); @@ -444,8 +491,7 @@ auto MetadataManager::cleanUpRange(ChunkRange const& range, Date_t whenToDelete) return _pushRangeToClean(lg, range, whenToDelete); } - log() << "Deletion of " << _nss.ns() << " range " << redact(range.toString()) - << " will be scheduled after all possibly dependent queries finish"; + logRangeDeletionWaitingOnOpenCursors(opCtx, collection, _nss, range); // Put it on the oldest metadata permissible; the current one might live a long time. auto& orphans = overlapMetadata->orphans; diff --git a/src/mongo/db/s/metadata_manager.h b/src/mongo/db/s/metadata_manager.h index 5a6cbda9d38..53d124c2467 100644 --- a/src/mongo/db/s/metadata_manager.h +++ b/src/mongo/db/s/metadata_manager.h @@ -118,7 +118,10 @@ public: * * Call waitStatus(opCtx) on the result to wait for the deletion to complete or fail. */ - CleanupNotification cleanUpRange(ChunkRange const& range, Date_t whenToDelete); + CleanupNotification cleanUpRange(OperationContext* opCtx, + const Collection* collection, + ChunkRange const& range, + Date_t whenToDelete); /** * Returns a vector of ScopedCollectionMetadata objects representing metadata instances in use diff --git a/src/mongo/db/s/metadata_manager_test.cpp b/src/mongo/db/s/metadata_manager_test.cpp index 3f8ce528555..b070c8b3219 100644 --- a/src/mongo/db/s/metadata_manager_test.cpp +++ b/src/mongo/db/s/metadata_manager_test.cpp @@ -80,6 +80,13 @@ protected: configTargeter()->setFindHostReturnValue(dummyHost); _manager = std::make_shared<MetadataManager>(getServiceContext(), kNss, executor()); + _autoColl.emplace(operationContext(), kNss, MODE_IS); + } + + void tearDown() override { + _autoColl.reset(); + _manager.reset(); + ShardingMongodTestFixture::tearDown(); } std::shared_ptr<RemoteCommandTargeterMock> configTargeter() const { @@ -146,7 +153,12 @@ protected: return cm2Ptr; } + Collection* collection() { + return _autoColl->getCollection(); + } + std::shared_ptr<MetadataManager> _manager; + boost::optional<AutoGetCollection> _autoColl; }; // In the following tests, the ranges-to-clean is not drained by the background deleter thread @@ -172,7 +184,7 @@ TEST_F(MetadataManagerTest, AddRangeNotificationsBlockAndYield) { _manager->refreshActiveMetadata(makeEmptyMetadata()); ChunkRange cr1(BSON("key" << 0), BSON("key" << 10)); - auto notifn1 = _manager->cleanUpRange(cr1, Date_t{}); + auto notifn1 = _manager->cleanUpRange(operationContext(), collection(), cr1, Date_t{}); ASSERT_FALSE(notifn1.ready()); ASSERT_EQ(_manager->numberOfRangesToClean(), 1UL); auto optNotifn = _manager->trackOrphanedDataCleanup(cr1); @@ -218,7 +230,7 @@ TEST_F(MetadataManagerTest, NotificationBlocksUntilDeletion) { ASSERT_EQ(_manager->numberOfMetadataSnapshots(), 3UL); ASSERT_EQ(_manager->numberOfRangesToClean(), 0UL); // not yet... - optNotif = _manager->cleanUpRange(cr1, Date_t{}); + optNotif = _manager->cleanUpRange(operationContext(), collection(), cr1, Date_t{}); ASSERT_EQ(_manager->numberOfMetadataSnapshots(), 3UL); ASSERT_EQ(_manager->numberOfRangesToClean(), 1UL); } // scm1,2,3 destroyed, refcount of each metadata goes to zero @@ -316,7 +328,7 @@ TEST_F(MetadataManagerTest, RangesToCleanMembership) { ASSERT(_manager->numberOfRangesToClean() == 0UL); ChunkRange cr1 = ChunkRange(BSON("key" << 0), BSON("key" << 10)); - auto notifn = _manager->cleanUpRange(cr1, Date_t{}); + auto notifn = _manager->cleanUpRange(operationContext(), collection(), cr1, Date_t{}); ASSERT(!notifn.ready()); ASSERT(_manager->numberOfRangesToClean() == 1UL); notifn.abandon(); diff --git a/src/mongo/db/s/migration_source_manager.cpp b/src/mongo/db/s/migration_source_manager.cpp index bbc2ead1cea..be76f9102ac 100644 --- a/src/mongo/db/s/migration_source_manager.cpp +++ b/src/mongo/db/s/migration_source_manager.cpp @@ -637,7 +637,8 @@ Status MigrationSourceManager::commitChunkMetadataOnConfig(OperationContext* opC auto const whenToClean = _args.getWaitForDelete() ? CollectionShardingState::kNow : CollectionShardingState::kDelayed; AutoGetCollection autoColl(opCtx, getNss(), MODE_IS); - return CollectionShardingState::get(opCtx, getNss())->cleanUpRange(range, whenToClean); + return CollectionShardingState::get(opCtx, getNss()) + ->cleanUpRange(opCtx, autoColl.getCollection(), range, whenToClean); }(); if (!MONGO_FAIL_POINT(doNotRefreshRecipientAfterCommit)) { diff --git a/src/mongo/dbtests/cursor_manager_test.cpp b/src/mongo/dbtests/cursor_manager_test.cpp index a861f89bfb8..e89cc66a35f 100644 --- a/src/mongo/dbtests/cursor_manager_test.cpp +++ b/src/mongo/dbtests/cursor_manager_test.cpp @@ -585,5 +585,44 @@ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsMultipleSessions) { ASSERT(cursors2.find(cursor2) != cursors2.end()); } +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<CursorId> 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 |