diff options
author | Maria van Keulen <maria@mongodb.com> | 2018-05-17 15:12:05 -0400 |
---|---|---|
committer | Maria van Keulen <maria@mongodb.com> | 2018-06-07 11:20:16 -0400 |
commit | b0309464e11c95eb512517cb644920e5f36e8eac (patch) | |
tree | 3a96f2c051e970e2c4fda71e210f312772965395 | |
parent | fad034552cc83890179ed2bbe8ec91a5e6743aed (diff) | |
download | mongo-b0309464e11c95eb512517cb644920e5f36e8eac.tar.gz |
SERVER-34615 Make UUIDCatalog updates for renameCollection atomic
(cherry picked from commit 7c12245583958023e35bce05d6b2212dcd7f24e3)
20 files changed, 456 insertions, 151 deletions
diff --git a/jstests/libs/parallel_shell_helpers.js b/jstests/libs/parallel_shell_helpers.js new file mode 100644 index 00000000000..70a60a5be0a --- /dev/null +++ b/jstests/libs/parallel_shell_helpers.js @@ -0,0 +1,5 @@ +/** + * Allows passing arguments to the function executed by startParallelShell. + */ +const funWithArgs = (fn, ...args) => + "(" + fn.toString() + ")(" + args.map(x => tojson(x)).reduce((x, y) => x + ", " + y) + ")"; diff --git a/jstests/multiVersion/upgrade_downgrade_while_creating_collection.js b/jstests/multiVersion/upgrade_downgrade_while_creating_collection.js index 8926cbdfa90..ba675fbae67 100644 --- a/jstests/multiVersion/upgrade_downgrade_while_creating_collection.js +++ b/jstests/multiVersion/upgrade_downgrade_while_creating_collection.js @@ -4,6 +4,7 @@ (function() { "use strict"; load("jstests/libs/feature_compatibility_version.js"); + load("jstests/libs/parallel_shell_helpers.js"); const rst = new ReplSetTest({nodes: 2}); rst.startSet(); @@ -42,9 +43,6 @@ return rawMongoProgramOutput().match("createCollection: test.mycoll"); }); - const funWithArgs = (fn, ...args) => "(" + fn.toString() + ")(" + - args.map(x => tojson(x)).reduce((x, y) => x + ", " + y) + ")"; - awaitUpgradeFCV = startParallelShell( funWithArgs(function(version) { assert.commandWorked( diff --git a/jstests/noPassthrough/find_by_uuid_and_rename.js b/jstests/noPassthrough/find_by_uuid_and_rename.js new file mode 100644 index 00000000000..d07c1da0ce6 --- /dev/null +++ b/jstests/noPassthrough/find_by_uuid_and_rename.js @@ -0,0 +1,56 @@ +// +// Run 'find' by UUID while renaming a collection concurrently. See SERVER-34615. +// + +(function() { + "use strict"; + const dbName = "do_concurrent_rename"; + const collName = "collA"; + const otherName = "collB"; + const repeatFind = 100; + load("jstests/noPassthrough/libs/concurrent_rename.js"); + load("jstests/libs/parallel_shell_helpers.js"); + + const conn = MongoRunner.runMongod({}); + assert.neq(null, conn, "mongod was unable to start up"); + jsTestLog("Create collection."); + let findRenameDB = conn.getDB(dbName); + findRenameDB.dropDatabase(); + assert.commandWorked(findRenameDB.runCommand({"create": collName})); + assert.commandWorked( + findRenameDB.runCommand({insert: collName, documents: [{fooField: 'FOO'}]})); + + let infos = findRenameDB.getCollectionInfos(); + let uuid = infos[0].info.uuid; + const findCmd = {"find": uuid}; + + // Assert 'find' command by UUID works. + assert.commandWorked(findRenameDB.runCommand(findCmd)); + + jsTestLog("Start parallel shell for renames."); + let renameShell = + startParallelShell(funWithArgs(doRenames, dbName, collName, otherName), conn.port); + + // Wait until we receive confirmation that the parallel shell has started. + assert.soon(() => conn.getDB("test").await_data.findOne({_id: "signal parent shell"}) !== null, + "Expected parallel shell to insert a document."); + + jsTestLog("Start 'find' commands."); + while (conn.getDB("test").await_data.findOne({_id: "rename has ended"}) == null) { + for (let i = 0; i < repeatFind; i++) { + let res = findRenameDB.runCommand(findCmd); + assert.commandWorked(res, "could not run " + tojson(findCmd)); + let cursor = new DBCommandCursor(findRenameDB, res); + let errMsg = "expected more data from command " + tojson(findCmd) + ", with result " + + tojson(res); + assert(cursor.hasNext(), errMsg); + let doc = cursor.next(); + assert.eq(doc.fooField, "FOO"); + assert(!cursor.hasNext(), + "expected to have exhausted cursor for results " + tojson(res)); + } + } + renameShell(); + MongoRunner.stopMongod(conn); + +}()); diff --git a/jstests/noPassthrough/libs/concurrent_rename.js b/jstests/noPassthrough/libs/concurrent_rename.js new file mode 100644 index 00000000000..79d1a0074c3 --- /dev/null +++ b/jstests/noPassthrough/libs/concurrent_rename.js @@ -0,0 +1,16 @@ +// Perform a set number of renames from collA to collB and vice versa. This function is to be called +// from a parallel shell, and is useful for simulating executions of functions concurrently with +// collection renames. +function doRenames(dbName, collName, otherName) { + const repeatRename = 200; + // Signal to the parent shell that the parallel shell has started. + assert.writeOK(db.await_data.insert({_id: "signal parent shell"})); + let renameDB = db.getSiblingDB(dbName); + for (let i = 0; i < repeatRename; i++) { + // Rename the collection back and forth. + assert.commandWorked(renameDB[collName].renameCollection(otherName)); + assert.commandWorked(renameDB[otherName].renameCollection(collName)); + } + // Signal to the parent shell that the renames have completed. + assert.writeOK(db.await_data.insert({_id: "rename has ended"})); +} diff --git a/jstests/noPassthrough/list_databases_and_rename_collection.js b/jstests/noPassthrough/list_databases_and_rename_collection.js index 6d8351e0737..9faebcb7dc8 100644 --- a/jstests/noPassthrough/list_databases_and_rename_collection.js +++ b/jstests/noPassthrough/list_databases_and_rename_collection.js @@ -4,28 +4,13 @@ (function() { "use strict"; - const dbName = "list_databases_rename"; + const dbName = "do_concurrent_rename"; const collName = "collA"; + const otherName = "collB"; const repeatListDatabases = 20; const listDatabasesCmd = {"listDatabases": 1}; - - // To be called from startParallelShell. - function doRenames() { - const dbName = "list_databases_rename"; - const collName = "collA"; - const repeatRename = 200; - // Signal to the parent shell that the parallel shell has started. - assert.writeOK(db.await_data.insert({_id: "signal parent shell"})); - const otherName = "collB"; - let listRenameDB = db.getSiblingDB(dbName); - for (let i = 0; i < repeatRename; i++) { - // Rename the collection back and forth. - assert.commandWorked(listRenameDB[collName].renameCollection(otherName)); - assert.commandWorked(listRenameDB[otherName].renameCollection(collName)); - } - // Signal to the parent shell that the renames have completed. - assert.writeOK(db.await_data.insert({_id: "rename has ended"})); - } + load("jstests/noPassthrough/libs/concurrent_rename.js"); + load("jstests/libs/parallel_shell_helpers.js"); const conn = MongoRunner.runMongod({}); assert.neq(null, conn, "mongod was unable to start up"); @@ -46,7 +31,8 @@ "expected " + tojson(cmdRes) + " to include " + dbName); jsTestLog("Start parallel shell"); - let renameShell = startParallelShell(doRenames, conn.port); + let renameShell = + startParallelShell(funWithArgs(doRenames, dbName, collName, otherName), conn.port); // Wait until we receive confirmation that the parallel shell has started. assert.soon(() => conn.getDB("test").await_data.findOne({_id: "signal parent shell"}) !== null); diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index c41ea778782..305750104e7 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -258,7 +258,7 @@ Status renameCollectionCommon(OperationContext* opCtx, invariant(options.dropTarget); auto dropTargetUUID = targetColl->uuid(); invariant(dropTargetUUID); - auto renameOpTime = opObserver->onRenameCollection( + auto renameOpTime = opObserver->preRenameCollection( opCtx, source, target, sourceUUID, dropTargetUUID, options.stayTemp); if (!renameOpTimeFromApplyOps.isNull()) { @@ -286,6 +286,8 @@ Status renameCollectionCommon(OperationContext* opCtx, return status; } + opObserver->postRenameCollection( + opCtx, source, target, sourceUUID, dropTargetUUID, options.stayTemp); wunit.commit(); return Status::OK(); }); diff --git a/src/mongo/db/catalog/rename_collection_test.cpp b/src/mongo/db/catalog/rename_collection_test.cpp index ede724e4968..52bfaf4c8e8 100644 --- a/src/mongo/db/catalog/rename_collection_test.cpp +++ b/src/mongo/db/catalog/rename_collection_test.cpp @@ -94,13 +94,25 @@ public: const NamespaceString& collectionName, OptionalCollectionUUID uuid) override; - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override; - + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; // Operations written to the oplog. These are operations for which // ReplicationCoordinator::isOplogDisabled() returns false. std::vector<std::string> oplogEntries; @@ -167,21 +179,43 @@ repl::OpTime OpObserverMock::onDropCollection(OperationContext* opCtx, return {}; } -repl::OpTime OpObserverMock::onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) { - _logOp(opCtx, fromCollection, "rename"); - OpObserver::Times::get(opCtx).reservedOpTimes.push_back(renameOpTime); +void OpObserverMock::onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + preRenameCollection(opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); OpObserverNoop::onRenameCollection( opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); onRenameCollectionCalled = true; onRenameCollectionDropTarget = dropTargetUUID; - return {}; } +void OpObserverMock::postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + OpObserverNoop::postRenameCollection( + opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + onRenameCollectionCalled = true; + onRenameCollectionDropTarget = dropTargetUUID; +} + +repl::OpTime OpObserverMock::preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + _logOp(opCtx, fromCollection, "rename"); + OpObserver::Times::get(opCtx).reservedOpTimes.push_back(renameOpTime); + OpObserverNoop::preRenameCollection( + opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + return {}; +} void OpObserverMock::_logOp(OperationContext* opCtx, const NamespaceString& nss, const std::string& operationName) { @@ -566,6 +600,10 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetDo ASSERT_FALSE(_collectionExists(_opCtx.get(), collC)); // B (originally A) should exist ASSERT_TRUE(_collectionExists(_opCtx.get(), collB)); + // collAUUID should be associated with collB's NamespaceString in the UUIDCatalog. + auto newCollNS = _getCollectionNssFromUUID(_opCtx.get(), collAUUID); + ASSERT_TRUE(newCollNS.isValid()); + ASSERT_EQUALS(newCollNS, collB); } TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetExists) { @@ -718,7 +756,7 @@ TEST_F(RenameCollectionTest, repl::UnreplicatedWritesBlock uwb(_opCtx.get()); ASSERT_FALSE(_opCtx->writesAreReplicated()); - // OpObserver::onRenameCollection() must return a null OpTime when writes are not replicated. + // OpObserver::preRenameCollection() must return a null OpTime when writes are not replicated. _opObserver->renameOpTime = {}; _createCollection(_opCtx.get(), _sourceNss); diff --git a/src/mongo/db/catalog/uuid_catalog.cpp b/src/mongo/db/catalog/uuid_catalog.cpp index 7b43be4f1b1..91595b5c69b 100644 --- a/src/mongo/db/catalog/uuid_catalog.cpp +++ b/src/mongo/db/catalog/uuid_catalog.cpp @@ -82,26 +82,42 @@ repl::OpTime UUIDCatalogObserver::onDropCollection(OperationContext* opCtx, return {}; } -repl::OpTime UUIDCatalogObserver::onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) { +void UUIDCatalogObserver::onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { if (!uuid) - return {}; - auto getNewCollection = [opCtx, toCollection] { - auto db = DatabaseHolder::getDatabaseHolder().get(opCtx, toCollection.db()); - auto newColl = db->getCollection(opCtx, toCollection); - invariant(newColl); - return newColl; - }; + return; + auto db = DatabaseHolder::getDatabaseHolder().get(opCtx, toCollection.db()); + auto newColl = db->getCollection(opCtx, toCollection); + invariant(newColl); UUIDCatalog& catalog = UUIDCatalog::get(opCtx); - catalog.onRenameCollection(opCtx, getNewCollection, uuid.get()); + catalog.onRenameCollection(opCtx, newColl, uuid.get()); +} + +repl::OpTime UUIDCatalogObserver::preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { return {}; } +void UUIDCatalogObserver::postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + // postRenameCollection and onRenameCollection are semantically equivalent from the perspective + // of the UUIDCatalogObserver. + onRenameCollection(opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); +} + UUIDCatalog& UUIDCatalog::get(ServiceContext* svcCtx) { return getCatalog(svcCtx); } @@ -124,25 +140,12 @@ void UUIDCatalog::onDropCollection(OperationContext* opCtx, CollectionUUID uuid) } void UUIDCatalog::onRenameCollection(OperationContext* opCtx, - GetNewCollectionFunction getNewCollection, + Collection* coll, CollectionUUID uuid) { - Collection* oldColl = removeUUIDCatalogEntry(uuid); - opCtx->recoveryUnit()->onCommit([this, getNewCollection, uuid](boost::optional<Timestamp>) { - // Reset current UUID entry in case some other operation updates the UUID catalog before the - // WUOW is committed. registerUUIDCatalogEntry() is a no-op if there's an existing UUID - // entry. - removeUUIDCatalogEntry(uuid); - auto newColl = getNewCollection(); - invariant(newColl); - registerUUIDCatalogEntry(uuid, newColl); - }); - opCtx->recoveryUnit()->onRollback([this, oldColl, uuid] { - // Reset current UUID entry in case some other operation updates the UUID catalog before the - // WUOW is rolled back. registerUUIDCatalogEntry() is a no-op if there's an existing UUID - // entry. - removeUUIDCatalogEntry(uuid); - registerUUIDCatalogEntry(uuid, oldColl); - }); + invariant(coll); + Collection* oldColl = replaceUUIDCatalogEntry(uuid, coll); + opCtx->recoveryUnit()->onRollback( + [this, oldColl, uuid] { replaceUUIDCatalogEntry(uuid, oldColl); }); } void UUIDCatalog::onCloseDatabase(Database* db) { @@ -192,6 +195,27 @@ NamespaceString UUIDCatalog::lookupNSSByUUID(CollectionUUID uuid) const { return NamespaceString(); } +Collection* UUIDCatalog::replaceUUIDCatalogEntry(CollectionUUID uuid, Collection* coll) { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + invariant(coll); + + auto foundIt = _catalog.find(uuid); + invariant(foundIt != _catalog.end()); + // Invalidate the source database's ordering, since we're deleting a UUID. + _orderedCollections.erase(foundIt->second->ns().db()); + + Collection* oldColl = foundIt->second; + LOG(2) << "unregistering collection " << oldColl->ns() << " with UUID " << uuid.toString(); + _catalog.erase(foundIt); + + // Invalidate the destination database's ordering, since we're adding a new UUID. + _orderedCollections.erase(coll->ns().db()); + + std::pair<CollectionUUID, Collection*> entry = std::make_pair(uuid, coll); + LOG(2) << "registering collection " << coll->ns() << " with UUID " << uuid.toString(); + invariant(_catalog.insert(entry).second == true); + return oldColl; +} void UUIDCatalog::registerUUIDCatalogEntry(CollectionUUID uuid, Collection* coll) { stdx::lock_guard<stdx::mutex> lock(_catalogLock); diff --git a/src/mongo/db/catalog/uuid_catalog.h b/src/mongo/db/catalog/uuid_catalog.h index aa864eb2571..a96210856fc 100644 --- a/src/mongo/db/catalog/uuid_catalog.h +++ b/src/mongo/db/catalog/uuid_catalog.h @@ -89,12 +89,24 @@ public: OptionalCollectionUUID uuid, const std::string& indexName, const BSONObj& idxDescriptor) override {} - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override; + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; void onApplyOps(OperationContext* opCtx, const std::string& dbName, const BSONObj& applyOpCmd) override {} @@ -136,20 +148,18 @@ public: void onDropCollection(OperationContext* opCtx, CollectionUUID uuid); /** - * Combination of onDropCollection and onCreateCollection. - * 'getNewCollection' is a function that returns collection to be registered when the current - * write unit of work is committed. + * This function atomically removes any existing entry for uuid from the UUID catalog and adds + * a new entry for uuid associated with the Collection coll. It is called by the op observer + * when a collection is renamed. */ - using GetNewCollectionFunction = stdx::function<Collection*()>; - void onRenameCollection(OperationContext* opCtx, - GetNewCollectionFunction getNewCollection, - CollectionUUID uuid); + void onRenameCollection(OperationContext* opCtx, Collection* coll, CollectionUUID uuid); /** * Implies onDropCollection for all collections in db, but is not transactional. */ void onCloseDatabase(Database* db); + Collection* replaceUUIDCatalogEntry(CollectionUUID uuid, Collection* coll); void registerUUIDCatalogEntry(CollectionUUID uuid, Collection* coll); Collection* removeUUIDCatalogEntry(CollectionUUID uuid); diff --git a/src/mongo/db/catalog/uuid_catalog_test.cpp b/src/mongo/db/catalog/uuid_catalog_test.cpp index 0028f7d38f4..097aea9c029 100644 --- a/src/mongo/db/catalog/uuid_catalog_test.cpp +++ b/src/mongo/db/catalog/uuid_catalog_test.cpp @@ -110,6 +110,19 @@ TEST_F(UUIDCatalogTest, OnDropCollection) { ASSERT(catalog.lookupCollectionByUUID(colUUID) == nullptr); } +TEST_F(UUIDCatalogTest, OnRenameCollection) { + auto oldUUID = CollectionUUID::gen(); + NamespaceString oldNss(nss.db(), "oldcol"); + Collection oldCol(stdx::make_unique<CollectionMock>(oldNss)); + catalog.onCreateCollection(&opCtx, &oldCol, oldUUID); + ASSERT_EQUALS(catalog.lookupCollectionByUUID(oldUUID), &oldCol); + + NamespaceString newNss(nss.db(), "newcol"); + Collection newCol(stdx::make_unique<CollectionMock>(newNss)); + catalog.onRenameCollection(&opCtx, &newCol, oldUUID); + ASSERT_EQUALS(catalog.lookupCollectionByUUID(oldUUID), &newCol); +} + TEST_F(UUIDCatalogTest, NonExistingNextCol) { ASSERT_FALSE(catalog.next(nss.db(), colUUID)); ASSERT_FALSE(catalog.next(nss.db(), nextUUID)); diff --git a/src/mongo/db/free_mon/free_mon_op_observer.h b/src/mongo/db/free_mon/free_mon_op_observer.h index acdb03d4890..c084c440dd2 100644 --- a/src/mongo/db/free_mon/free_mon_op_observer.h +++ b/src/mongo/db/free_mon/free_mon_op_observer.h @@ -101,15 +101,27 @@ public: const std::string& indexName, const BSONObj& indexInfo) final {} - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) final { + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) final {} + + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) final { return repl::OpTime(); } - + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) final {} void onApplyOps(OperationContext* opCtx, const std::string& dbName, const BSONObj& applyOpCmd) final {} diff --git a/src/mongo/db/op_observer.h b/src/mongo/db/op_observer.h index d65bb57bb7b..9a543aa0aa4 100644 --- a/src/mongo/db/op_observer.h +++ b/src/mongo/db/op_observer.h @@ -214,16 +214,42 @@ public: /** * This function logs an oplog entry when a 'renameCollection' command on a collection is - * executed. + * executed. It should be used specifically in instances where the optime is necessary to + * be obtained prior to performing the actual rename, and should only be used in conjunction + * with postRenameCollection. * Returns the optime of the oplog entry successfully written to the oplog. * Returns a null optime if an oplog entry was not written for this operation. */ - virtual repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) = 0; + virtual repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) = 0; + /** + * This function performs all op observer handling for a 'renameCollection' command except for + * logging the oplog entry. It should be used specifically in instances where the optime is + * necessary to be obtained prior to performing the actual rename, and should only be used in + * conjunction with preRenameCollection. + */ + virtual void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) = 0; + /** + * This function logs an oplog entry when a 'renameCollection' command on a collection is + * executed. It calls preRenameCollection to log the entry and postRenameCollection to do all + * other handling. + */ + virtual void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) = 0; + virtual void onApplyOps(OperationContext* opCtx, const std::string& dbName, const BSONObj& applyOpCmd) = 0; diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp index 655cc73bbef..a09d280e71b 100644 --- a/src/mongo/db/op_observer_impl.cpp +++ b/src/mongo/db/op_observer_impl.cpp @@ -766,12 +766,13 @@ void OpObserverImpl::onDropIndex(OperationContext* opCtx, ->logOp(opCtx, "c", cmdNss, cmdObj, &indexInfo); } -repl::OpTime OpObserverImpl::onRenameCollection(OperationContext* const opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) { + +repl::OpTime OpObserverImpl::preRenameCollection(OperationContext* const opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { const auto cmdNss = fromCollection.getCommandNS(); BSONObjBuilder builder; @@ -796,6 +797,27 @@ repl::OpTime OpObserverImpl::onRenameCollection(OperationContext* const opCtx, kUninitializedStmtId, {}); + return {}; +} + +void OpObserverImpl::postRenameCollection(OperationContext* const opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + const auto cmdNss = fromCollection.getCommandNS(); + + BSONObjBuilder builder; + builder.append("renameCollection", fromCollection.ns()); + builder.append("to", toCollection.ns()); + builder.append("stayTemp", stayTemp); + if (dropTargetUUID) { + dropTargetUUID->appendToBuilder(&builder, "dropTarget"); + } + + const auto cmdObj = builder.done(); + if (fromCollection.isSystemDotViews()) DurableViewCatalog::onExternalChange(opCtx, fromCollection); if (toCollection.isSystemDotViews()) @@ -810,8 +832,16 @@ repl::OpTime OpObserverImpl::onRenameCollection(OperationContext* const opCtx, cache.evictNamespace(toCollection); opCtx->recoveryUnit()->onRollback( [&cache, toCollection]() { cache.evictNamespace(toCollection); }); +} - return {}; +void OpObserverImpl::onRenameCollection(OperationContext* const opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + preRenameCollection(opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + postRenameCollection(opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); } void OpObserverImpl::onApplyOps(OperationContext* opCtx, diff --git a/src/mongo/db/op_observer_impl.h b/src/mongo/db/op_observer_impl.h index 4d62e4d8b3a..fb67efbe104 100644 --- a/src/mongo/db/op_observer_impl.h +++ b/src/mongo/db/op_observer_impl.h @@ -85,12 +85,24 @@ public: OptionalCollectionUUID uuid, const std::string& indexName, const BSONObj& indexInfo) override; - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override; + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override; void onApplyOps(OperationContext* opCtx, const std::string& dbName, const BSONObj& applyOpCmd) override; diff --git a/src/mongo/db/op_observer_noop.h b/src/mongo/db/op_observer_noop.h index 67682888d1a..5d74fa914af 100644 --- a/src/mongo/db/op_observer_noop.h +++ b/src/mongo/db/op_observer_noop.h @@ -82,14 +82,26 @@ public: OptionalCollectionUUID uuid, const std::string& indexName, const BSONObj& idxDescriptor) override {} - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override { + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override {} + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { return {}; } + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override {} void onApplyOps(OperationContext* opCtx, const std::string& dbName, const BSONObj& applyOpCmd) override {} diff --git a/src/mongo/db/op_observer_registry.h b/src/mongo/db/op_observer_registry.h index d02025ffd98..2ab2b971219 100644 --- a/src/mongo/db/op_observer_registry.h +++ b/src/mongo/db/op_observer_registry.h @@ -160,22 +160,45 @@ public: o->onDropIndex(opCtx, nss, uuid, indexName, idxDescriptor); } - repl::OpTime onRenameCollection(OperationContext* const opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override { + + void onRenameCollection(OperationContext* const opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { + ReservedTimes times{opCtx}; + for (auto& o : _observers) + o->onRenameCollection( + opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + } + + repl::OpTime preRenameCollection(OperationContext* const opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { ReservedTimes times{opCtx}; for (auto& observer : this->_observers) { - const auto time = observer->onRenameCollection( + const auto time = observer->preRenameCollection( opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); invariant(time.isNull()); } - return _getOpTimeToReturn(times.get().reservedOpTimes); } + void postRenameCollection(OperationContext* const opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { + ReservedTimes times{opCtx}; + for (auto& o : _observers) + o->postRenameCollection( + opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + } void onApplyOps(OperationContext* const opCtx, const std::string& dbName, const BSONObj& applyOpCmd) override { diff --git a/src/mongo/db/op_observer_registry_test.cpp b/src/mongo/db/op_observer_registry_test.cpp index 445c4789aab..f1873b25c94 100644 --- a/src/mongo/db/op_observer_registry_test.cpp +++ b/src/mongo/db/op_observer_registry_test.cpp @@ -59,15 +59,30 @@ struct TestObserver : public OpObserverNoop { OpObserver::Times::get(opCtx).reservedOpTimes.push_back(opTime); return {}; } - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) { + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { + preRenameCollection(opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + postRenameCollection(opCtx, fromCollection, toCollection, uuid, dropTargetUUID, stayTemp); + } + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) { OpObserver::Times::get(opCtx).reservedOpTimes.push_back(opTime); return {}; } + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) {} }; struct ThrowingObserver : public TestObserver { @@ -156,12 +171,14 @@ TEST_F(OpObserverRegistryTest, OnDropCollectionObserverResultReturnsRightTime) { checkConsistentOpTime(op); } -TEST_F(OpObserverRegistryTest, OnRenameCollectionObserverResultReturnsRightTime) { +TEST_F(OpObserverRegistryTest, PreRenameCollectionObserverResultReturnsRightTime) { OperationContextNoop opCtx; registry.addObserver(std::move(unique1)); registry.addObserver(std::make_unique<OpObserverNoop>()); auto op = [&]() -> repl::OpTime { - return registry.onRenameCollection(&opCtx, testNss, testNss, {}, {}, false); + auto opTime = registry.preRenameCollection(&opCtx, testNss, testNss, {}, {}, false); + registry.postRenameCollection(&opCtx, testNss, testNss, {}, {}, false); + return opTime; }; checkConsistentOpTime(op); } @@ -174,12 +191,14 @@ DEATH_TEST_F(OpObserverRegistryTest, OnDropCollectionReturnsInconsistentTime, "i checkInconsistentOpTime(op); } -DEATH_TEST_F(OpObserverRegistryTest, OnRenameCollectionReturnsInconsistentTime, "invariant") { +DEATH_TEST_F(OpObserverRegistryTest, PreRenameCollectionReturnsInconsistentTime, "invariant") { OperationContextNoop opCtx; registry.addObserver(std::move(unique1)); registry.addObserver(std::move(unique2)); auto op = [&]() -> repl::OpTime { - return registry.onRenameCollection(&opCtx, testNss, testNss, {}, {}, false); + auto opTime = registry.preRenameCollection(&opCtx, testNss, testNss, {}, {}, false); + registry.postRenameCollection(&opCtx, testNss, testNss, {}, {}, false); + return opTime; }; checkInconsistentOpTime(op); } diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp index c7dc39c8b68..d271adf849d 100644 --- a/src/mongo/db/repl/storage_interface_impl.cpp +++ b/src/mongo/db/repl/storage_interface_impl.cpp @@ -491,8 +491,7 @@ Status StorageInterfaceImpl::renameCollection(OperationContext* opCtx, auto newColl = autoDB.getDb()->getCollection(opCtx, toNS); if (newColl->uuid()) { - UUIDCatalog::get(opCtx).onRenameCollection( - opCtx, [newColl] { return newColl; }, newColl->uuid().get()); + UUIDCatalog::get(opCtx).onRenameCollection(opCtx, newColl, newColl->uuid().get()); } wunit.commit(); return status; diff --git a/src/mongo/db/s/config_server_op_observer.h b/src/mongo/db/s/config_server_op_observer.h index d2e60094f7d..91870bf4732 100644 --- a/src/mongo/db/s/config_server_op_observer.h +++ b/src/mongo/db/s/config_server_op_observer.h @@ -101,14 +101,26 @@ public: const std::string& indexName, const BSONObj& indexInfo) override {} - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override { + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override {} + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { return repl::OpTime(); } + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override {} void onApplyOps(OperationContext* opCtx, const std::string& dbName, diff --git a/src/mongo/db/s/shard_server_op_observer.h b/src/mongo/db/s/shard_server_op_observer.h index 5f5e6723516..413373446bd 100644 --- a/src/mongo/db/s/shard_server_op_observer.h +++ b/src/mongo/db/s/shard_server_op_observer.h @@ -102,14 +102,26 @@ public: const std::string& indexName, const BSONObj& indexInfo) override {} - repl::OpTime onRenameCollection(OperationContext* opCtx, - const NamespaceString& fromCollection, - const NamespaceString& toCollection, - OptionalCollectionUUID uuid, - OptionalCollectionUUID dropTargetUUID, - bool stayTemp) override { + void onRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override {} + repl::OpTime preRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override { return repl::OpTime(); } + void postRenameCollection(OperationContext* opCtx, + const NamespaceString& fromCollection, + const NamespaceString& toCollection, + OptionalCollectionUUID uuid, + OptionalCollectionUUID dropTargetUUID, + bool stayTemp) override {} void onApplyOps(OperationContext* opCtx, const std::string& dbName, |