diff options
-rw-r--r-- | jstests/replsets/create_drop_database_different_casing.js | 57 | ||||
-rw-r--r-- | jstests/replsets/rollback_drop_database.js | 11 | ||||
-rw-r--r-- | src/mongo/db/catalog/drop_database.cpp | 25 |
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 |