summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/replsets/create_drop_database_different_casing.js57
-rw-r--r--jstests/replsets/rollback_drop_database.js11
-rw-r--r--src/mongo/db/catalog/drop_database.cpp25
3 files changed, 75 insertions, 18 deletions
diff --git a/jstests/replsets/create_drop_database_different_casing.js b/jstests/replsets/create_drop_database_different_casing.js
new file mode 100644
index 00000000000..6ba4465d8da
--- /dev/null
+++ b/jstests/replsets/create_drop_database_different_casing.js
@@ -0,0 +1,57 @@
+/**
+ * Ensures that the drop database oplog entry gets sent to the secondaries prior to dropping the
+ * in memory state of the database in the primary during the 'dropDatabase' command.
+ * Dropping the in memory state of the database before sending the drop database oplog entry to the
+ * secondaries first can result in a situation where the secondaries crash during replication.
+ * The secondaries would crash given this scenario:
+ * - At time 10: Primary drops database "A"'s in memory state.
+ * - At time 15: Primary creates database "a" and sends the create database oplog entries to the
+ * secondaries.
+ * - At time 20: Secondaries apply create database oplog entry and crash because database "A"
+ * still exists in-memory for them.
+ * - At time 25: Primary sends database "A"'s drop oplog entry to secondaries (already crashed).
+ *
+ * @tags: [requires_replication]
+ */
+(function() {
+'use strict';
+
+load("jstests/libs/check_log.js");
+
+const rst = new ReplSetTest({nodes: [{}, {rsConfig: {priority: 0}}]});
+rst.startSet();
+rst.initiate();
+
+const dbNameUpper = "A";
+const dbNameLower = "a";
+
+const primary = rst.getPrimary();
+
+let upperDB = primary.getDB(dbNameUpper);
+assert.commandWorked(upperDB.createCollection("test"));
+
+assert.commandWorked(upperDB.adminCommand(
+ {configureFailPoint: 'dropDatabaseHangBeforeInMemoryDrop', mode: "alwaysOn"}));
+let awaitDropUpper = startParallelShell(() => {
+ db.getSiblingDB("A").dropDatabase();
+}, primary.port);
+
+checkLog.contains(primary, "dropDatabase - fail point dropDatabaseHangBeforeInMemoryDrop enabled.");
+
+let lowerDB = primary.getDB(dbNameLower);
+
+// The oplog entry to the secondaries to drop database "A" was sent, but the primary has not yet
+// dropped "A" as it's hanging on the 'dropDatabaseHangBeforeInMemoryDrop' fail point.
+assert.commandFailedWithCode(lowerDB.createCollection("test"), ErrorCodes.DatabaseDifferCase);
+
+rst.awaitReplication();
+assert.commandWorked(
+ lowerDB.adminCommand({configureFailPoint: 'dropDatabaseHangBeforeInMemoryDrop', mode: "off"}));
+
+checkLog.contains(primary, "dropDatabase " + dbNameUpper + " - finished");
+assert.commandWorked(lowerDB.createCollection("test"));
+
+awaitDropUpper();
+
+rst.stopSet();
+})();
diff --git a/jstests/replsets/rollback_drop_database.js b/jstests/replsets/rollback_drop_database.js
index 58eb31ae257..15f26cceee1 100644
--- a/jstests/replsets/rollback_drop_database.js
+++ b/jstests/replsets/rollback_drop_database.js
@@ -26,8 +26,8 @@ assert.commandWorked(rollbackNode.getDB(oldDbName)["beforeRollback"].insert({"nu
// Set a failpoint on the original primary, so that it blocks after it commits the last
// 'dropCollection' entry but before the 'dropDatabase' entry is logged.
-assert.commandWorked(
- rollbackNode.adminCommand({configureFailPoint: "dropDatabaseHangBeforeLog", mode: "alwaysOn"}));
+assert.commandWorked(rollbackNode.adminCommand(
+ {configureFailPoint: "dropDatabaseHangBeforeInMemoryDrop", mode: "alwaysOn"}));
// Issue a 'dropDatabase' command.
let dropDatabaseFn = function() {
@@ -40,7 +40,8 @@ let dropDatabaseFn = function() {
let waitForDropDatabaseToFinish = startParallelShell(dropDatabaseFn, rollbackNode.port);
// Ensure that we've hit the failpoint before moving on.
-checkLog.contains(rollbackNode, "dropDatabase - fail point dropDatabaseHangBeforeLog enabled");
+checkLog.contains(rollbackNode,
+ "dropDatabase - fail point dropDatabaseHangBeforeInMemoryDrop enabled.");
// Wait for the secondary to finish dropping the collection (the last replicated entry).
// We use the default 10-minute timeout for this.
@@ -53,8 +54,8 @@ rollbackTest.transitionToRollbackOperations();
// Allow the final 'dropDatabase' entry to be logged on the now isolated primary.
// This is the rollback node's divergent oplog entry.
-assert.commandWorked(
- rollbackNode.adminCommand({configureFailPoint: "dropDatabaseHangBeforeLog", mode: "off"}));
+assert.commandWorked(rollbackNode.adminCommand(
+ {configureFailPoint: "dropDatabaseHangBeforeInMemoryDrop", mode: "off"}));
waitForDropDatabaseToFinish();
assert.eq(false, rollbackNode.getDB(oldDbName).getCollectionNames().includes("beforeRollback"));
jsTestLog("Database " + oldDbName + " successfully dropped on primary node " + rollbackNode.host);
diff --git a/src/mongo/db/catalog/drop_database.cpp b/src/mongo/db/catalog/drop_database.cpp
index 793d9a9f779..bb3747bfc23 100644
--- a/src/mongo/db/catalog/drop_database.cpp
+++ b/src/mongo/db/catalog/drop_database.cpp
@@ -54,8 +54,8 @@
namespace mongo {
-MONGO_FAIL_POINT_DEFINE(dropDatabaseHangBeforeLog);
MONGO_FAIL_POINT_DEFINE(dropDatabaseHangAfterAllCollectionsDrop);
+MONGO_FAIL_POINT_DEFINE(dropDatabaseHangBeforeInMemoryDrop);
namespace {
@@ -78,24 +78,23 @@ void _finishDropDatabase(OperationContext* opCtx,
BackgroundOperation::assertNoBgOpInProgForDb(dbName);
IndexBuildsCoordinator::get(opCtx)->assertNoBgOpInProgForDb(dbName);
+ writeConflictRetry(opCtx, "dropDatabase_database", dbName, [&] {
+ WriteUnitOfWork wunit(opCtx);
+ opCtx->getServiceContext()->getOpObserver()->onDropDatabase(opCtx, dbName);
+ wunit.commit();
+ });
+
+ if (MONGO_unlikely(dropDatabaseHangBeforeInMemoryDrop.shouldFail())) {
+ log() << "dropDatabase - fail point dropDatabaseHangBeforeInMemoryDrop enabled.";
+ dropDatabaseHangBeforeInMemoryDrop.pauseWhileSet();
+ }
+
auto databaseHolder = DatabaseHolder::get(opCtx);
databaseHolder->dropDb(opCtx, db);
dropPendingGuard.dismiss();
log() << "dropDatabase " << dbName << " - dropped " << numCollections << " collection(s)";
log() << "dropDatabase " << dbName << " - finished";
-
- if (MONGO_unlikely(dropDatabaseHangBeforeLog.shouldFail())) {
- log() << "dropDatabase - fail point dropDatabaseHangBeforeLog enabled. "
- "Blocking until fail point is disabled. ";
- dropDatabaseHangBeforeLog.pauseWhileSet();
- }
-
- writeConflictRetry(opCtx, "dropDatabase_database", dbName, [&] {
- WriteUnitOfWork wunit(opCtx);
- getGlobalServiceContext()->getOpObserver()->onDropDatabase(opCtx, dbName);
- wunit.commit();
- });
}
} // namespace