summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaria van Keulen <maria@mongodb.com>2018-05-17 15:12:05 -0400
committerMaria van Keulen <maria@mongodb.com>2018-06-07 11:20:16 -0400
commitb0309464e11c95eb512517cb644920e5f36e8eac (patch)
tree3a96f2c051e970e2c4fda71e210f312772965395
parentfad034552cc83890179ed2bbe8ec91a5e6743aed (diff)
downloadmongo-b0309464e11c95eb512517cb644920e5f36e8eac.tar.gz
SERVER-34615 Make UUIDCatalog updates for renameCollection atomic
(cherry picked from commit 7c12245583958023e35bce05d6b2212dcd7f24e3)
-rw-r--r--jstests/libs/parallel_shell_helpers.js5
-rw-r--r--jstests/multiVersion/upgrade_downgrade_while_creating_collection.js4
-rw-r--r--jstests/noPassthrough/find_by_uuid_and_rename.js56
-rw-r--r--jstests/noPassthrough/libs/concurrent_rename.js16
-rw-r--r--jstests/noPassthrough/list_databases_and_rename_collection.js26
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp4
-rw-r--r--src/mongo/db/catalog/rename_collection_test.cpp72
-rw-r--r--src/mongo/db/catalog/uuid_catalog.cpp88
-rw-r--r--src/mongo/db/catalog/uuid_catalog.h36
-rw-r--r--src/mongo/db/catalog/uuid_catalog_test.cpp13
-rw-r--r--src/mongo/db/free_mon/free_mon_op_observer.h26
-rw-r--r--src/mongo/db/op_observer.h40
-rw-r--r--src/mongo/db/op_observer_impl.cpp44
-rw-r--r--src/mongo/db/op_observer_impl.h24
-rw-r--r--src/mongo/db/op_observer_noop.h24
-rw-r--r--src/mongo/db/op_observer_registry.h39
-rw-r--r--src/mongo/db/op_observer_registry_test.cpp39
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp3
-rw-r--r--src/mongo/db/s/config_server_op_observer.h24
-rw-r--r--src/mongo/db/s/shard_server_op_observer.h24
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,