diff options
-rw-r--r-- | jstests/noPassthrough/point_in_time_lookups.js | 3 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_catalog_test.cpp | 37 | ||||
-rw-r--r-- | src/mongo/db/db_raii.cpp | 26 |
3 files changed, 62 insertions, 4 deletions
diff --git a/jstests/noPassthrough/point_in_time_lookups.js b/jstests/noPassthrough/point_in_time_lookups.js index 8367574c2e5..fda37331dcc 100644 --- a/jstests/noPassthrough/point_in_time_lookups.js +++ b/jstests/noPassthrough/point_in_time_lookups.js @@ -17,6 +17,9 @@ const replTest = new ReplSetTest({ // Set the history window to 1 hour to prevent the oldest timestamp from advancing. This // is necessary to avoid removing data files across restarts for this test. minSnapshotHistoryWindowInSeconds: 60 * 60, + // Exercise yielding and restoring point-in-time collections. + internalQueryExecYieldIterations: 0, + internalQueryExecYieldPeriodMS: 0 } } }); diff --git a/src/mongo/db/catalog/collection_catalog_test.cpp b/src/mongo/db/catalog/collection_catalog_test.cpp index 343a10ae9f3..421e854969c 100644 --- a/src/mongo/db/catalog/collection_catalog_test.cpp +++ b/src/mongo/db/catalog/collection_catalog_test.cpp @@ -3669,5 +3669,42 @@ TEST_F(CollectionCatalogTimestampTest, OpenCollectionBetweenIndexBuildInProgress coll); } } + +TEST_F(CollectionCatalogTimestampTest, ResolveNamespaceStringOrUUIDAtLatest) { + RAIIServerParameterControllerForTest featureFlagController( + "featureFlagPointInTimeCatalogLookups", true); + + const NamespaceString nss = NamespaceString::createNamespaceString_forTest("a.b"); + const Timestamp createCollectionTs = Timestamp(10, 10); + const UUID uuid = createCollection(opCtx.get(), nss, createCollectionTs); + const NamespaceStringOrUUID nssOrUUID = NamespaceStringOrUUID(nss.db(), uuid); + + NamespaceString resolvedNss = + CollectionCatalog::get(opCtx.get())->resolveNamespaceStringOrUUID(opCtx.get(), nssOrUUID); + ASSERT_EQ(resolvedNss, nss); + + const Timestamp dropCollectionTs = Timestamp(20, 20); + dropCollection(opCtx.get(), nss, dropCollectionTs); + + // Resolving the UUID throws NamespaceNotFound as the collection is no longer in the latest + // collection catalog. + ASSERT_THROWS_CODE( + CollectionCatalog::get(opCtx.get())->resolveNamespaceStringOrUUID(opCtx.get(), nssOrUUID), + DBException, + ErrorCodes::NamespaceNotFound); + + { + OneOffRead oor(opCtx.get(), createCollectionTs); + Lock::GlobalLock globalLock(opCtx.get(), MODE_IS); + + CollectionCatalog::get(opCtx.get()) + ->establishConsistentCollection(opCtx.get(), nss, createCollectionTs); + + // Resolving the UUID looks in OpenedCollections to try to resolve the UUID. + resolvedNss = CollectionCatalog::get(opCtx.get()) + ->resolveNamespaceStringOrUUID(opCtx.get(), nssOrUUID); + ASSERT_EQ(resolvedNss, nss); + } +} } // namespace } // namespace mongo diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp index 4d65ddeba24..f9d8bb2e446 100644 --- a/src/mongo/db/db_raii.cpp +++ b/src/mongo/db/db_raii.cpp @@ -1084,10 +1084,28 @@ ConsistentCatalogAndSnapshot getConsistentCatalogAndSnapshot( const auto catalogBeforeSnapshot = CollectionCatalog::get(opCtx); - // It is incorrect to resolve the UUID here as we haven't established a consistent view of - // this UUID yet. During a concurrent rename it can be wrong. This namespace is only used to - // determine if it is an internal namespace. - const auto nss = catalogBeforeSnapshot->resolveNamespaceStringOrUUID(opCtx, nsOrUUID); + // When a query yields it releases its snapshot, and any point-in-time instantiated + // collections stored on the snapshot decoration are destructed. At the start of a query, + // collections are fetched using a namespace. However, when a query is restoring from + // yield it attempts to fetch collections by UUID. It's possible for a UUID to no longer + // resolve to a namespace in the latest collection catalog if that collection was dropped + // while the query was yielding. This doesn't conclude that the collection is inaccessible + // at an earlier point-in-time as the data files may still be on disk. This namespace is + // used to determine if the read source needs to be changed and we only do this if the + // original read source is kNoTimestamp or kLastApplied. If it's neither of the two we can + // safely continue. + NamespaceString nss; + try { + nss = catalogBeforeSnapshot->resolveNamespaceStringOrUUID(opCtx, nsOrUUID); + } catch (const ExceptionFor<ErrorCodes::NamespaceNotFound>&) { + invariant(nsOrUUID.uuid()); + + const auto readSource = opCtx->recoveryUnit()->getTimestampReadSource(); + if (readSource == RecoveryUnit::ReadSource::kNoTimestamp || + readSource == RecoveryUnit::ReadSource::kLastApplied) { + throw; + } + } // This may modify the read source on the recovery unit for opCtx if the current read source // is either kNoTimestamp or kLastApplied. |