From 3e59bad319da75f2ad51a13ac3239bd21e74c41e Mon Sep 17 00:00:00 2001 From: Jack Mulrow Date: Wed, 12 Feb 2020 16:20:43 +0000 Subject: SERVER-45900 Check for unsharded collection when submitting range deletion task --- src/mongo/db/s/migration_util.cpp | 21 ++++++---- src/mongo/db/s/migration_util_test.cpp | 73 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) (limited to 'src/mongo') diff --git a/src/mongo/db/s/migration_util.cpp b/src/mongo/db/s/migration_util.cpp index ff3d10e6537..58f9198057c 100644 --- a/src/mongo/db/s/migration_util.cpp +++ b/src/mongo/db/s/migration_util.cpp @@ -144,15 +144,18 @@ ExecutorFuture submitRangeDeletionTask(OperationContext* opCtx, autoColl.emplace(opCtx, deletionTask.getNss(), MODE_IS); auto css = CollectionShardingRuntime::get(opCtx, deletionTask.getNss()); - if (!css->getCurrentMetadataIfKnown() || + if (!css->getCurrentMetadataIfKnown() || !css->getCurrentMetadata()->isSharded() || !css->getCurrentMetadata()->uuidMatches(deletionTask.getCollectionUuid())) { - // If the collection's filtering metadata is not known or its UUID does not match - // the UUID of the deletion task, force a filtering metadata refresh once, because - // this node may have just stepped up and therefore may have a stale cache. + // If the collection's filtering metadata is not known, is unsharded, or its UUID + // does not match the UUID of the deletion task, force a filtering metadata refresh + // once, because this node may have just stepped up and therefore may have a stale + // cache. LOG(0) << "Filtering metadata for namespace in deletion task " << deletionTask.toBSON() << (css->getCurrentMetadataIfKnown() - ? " has UUID that does not match UUID of the deletion task" + ? (css->getCurrentMetadata()->isSharded() + ? " has UUID that does not match UUID of the deletion task" + : " is unsharded") : " is not known") << ", forcing a refresh of " << deletionTask.getNss(); @@ -173,14 +176,16 @@ ExecutorFuture submitRangeDeletionTask(OperationContext* opCtx, } autoColl.emplace(opCtx, deletionTask.getNss(), MODE_IS); - if (!css->getCurrentMetadataIfKnown() || + if (!css->getCurrentMetadataIfKnown() || !css->getCurrentMetadata()->isSharded() || !css->getCurrentMetadata()->uuidMatches(deletionTask.getCollectionUuid())) { LOG(0) << "Even after forced refresh, filtering metadata for namespace in deletion " "task " << deletionTask.toBSON() << (css->getCurrentMetadataIfKnown() - ? "has UUID that does not match UUID of the deletion task" - : "is not known") + ? (css->getCurrentMetadata()->isSharded() + ? " has UUID that does not match UUID of the deletion task" + : " is unsharded") + : " is not known") << ", deleting the task."; autoColl.reset(); diff --git a/src/mongo/db/s/migration_util_test.cpp b/src/mongo/db/s/migration_util_test.cpp index 14547873f36..fa8b6197552 100644 --- a/src/mongo/db/s/migration_util_test.cpp +++ b/src/mongo/db/s/migration_util_test.cpp @@ -534,6 +534,54 @@ TEST_F(SubmitRangeDeletionTaskTest, ASSERT_EQ(store.count(opCtx), 0); } +TEST_F(SubmitRangeDeletionTaskTest, FailsAndDeletesTaskIfNamespaceIsUnshardedEvenAfterRefresh) { + auto opCtx = operationContext(); + + auto deletionTask = createDeletionTask(kNss, kDefaultUUID, 0, 10); + + PersistentTaskStore store(opCtx, NamespaceString::kRangeDeletionNamespace); + store.add(opCtx, deletionTask); + ASSERT_EQ(store.count(opCtx), 1); + + // Make the refresh triggered by submitting the task return an empty result when loading the + // collection so it is considered unsharded. + _mockCatalogCacheLoader->setDatabaseRefreshReturnValue(kDefaultDatabaseType); + _mockCatalogCacheLoader->setCollectionRefreshReturnValue( + Status(ErrorCodes::NamespaceNotFound, "dummy errmsg")); + + auto submitTaskFuture = migrationutil::submitRangeDeletionTask(opCtx, deletionTask); + + // The task should not have been submitted, and the task's entry should have been removed from + // the persistent store. + ASSERT_FALSE(submitTaskFuture.get(opCtx)); + ASSERT_EQ(store.count(opCtx), 0); +} + +TEST_F(SubmitRangeDeletionTaskTest, + FailsAndDeletesTaskIfNamespaceIsUnshardedBeforeAndAfterRefresh) { + auto opCtx = operationContext(); + + auto deletionTask = createDeletionTask(kNss, kDefaultUUID, 0, 10); + + PersistentTaskStore store(opCtx, NamespaceString::kRangeDeletionNamespace); + store.add(opCtx, deletionTask); + ASSERT_EQ(store.count(opCtx), 1); + + // Mock an empty result for the task's collection and force a refresh so the node believes the + // collection is unsharded. + _mockCatalogCacheLoader->setDatabaseRefreshReturnValue(kDefaultDatabaseType); + _mockCatalogCacheLoader->setCollectionRefreshReturnValue( + Status(ErrorCodes::NamespaceNotFound, "dummy errmsg")); + forceShardFilteringMetadataRefresh(opCtx, kNss, true); + + auto submitTaskFuture = migrationutil::submitRangeDeletionTask(opCtx, deletionTask); + + // The task should not have been submitted, and the task's entry should have been removed from + // the persistent store. + ASSERT_FALSE(submitTaskFuture.get(opCtx)); + ASSERT_EQ(store.count(opCtx), 0); +} + TEST_F(SubmitRangeDeletionTaskTest, SucceedsIfFilteringMetadataUUIDMatchesTaskUUID) { auto opCtx = operationContext(); @@ -573,6 +621,31 @@ TEST_F( ASSERT(submitTaskFuture.get(opCtx)); } +TEST_F(SubmitRangeDeletionTaskTest, + SucceedsIfTaskNamespaceInitiallyUnshardedButUUIDMatchesAfterRefresh) { + auto opCtx = operationContext(); + + // Force a metadata refresh with no collection entry so the node believes the namespace is + // unsharded when the task is submitted. + _mockCatalogCacheLoader->setDatabaseRefreshReturnValue(kDefaultDatabaseType); + _mockCatalogCacheLoader->setCollectionRefreshReturnValue( + Status(ErrorCodes::NamespaceNotFound, "dummy errmsg")); + forceShardFilteringMetadataRefresh(opCtx, kNss, true); + + auto deletionTask = createDeletionTask(kNss, kDefaultUUID, 0, 10); + + // Make the refresh triggered by submitting the task return a UUID that matches the task's UUID. + auto matchingColl = makeCollectionType(kDefaultUUID, kEpoch); + _mockCatalogCacheLoader->setCollectionRefreshReturnValue(matchingColl); + _mockCatalogCacheLoader->setChunkRefreshReturnValue( + makeChangedChunks(ChunkVersion(10, 0, kEpoch))); + _mockCatalogClient->setCollections({matchingColl}); + + // The task should have been submitted successfully. + auto submitTaskFuture = migrationutil::submitRangeDeletionTask(opCtx, deletionTask); + ASSERT(submitTaskFuture.get(opCtx)); +} + TEST_F(SubmitRangeDeletionTaskTest, SucceedsIfFilteringMetadataUUIDInitiallyDifferentFromTaskUUIDButMatchesAfterRefresh) { auto opCtx = operationContext(); -- cgit v1.2.1