summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-03-12 12:26:31 -0400
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-03-13 08:14:11 -0400
commit796ae56683f818810fa5eab8ba446c71dcf54900 (patch)
treed63d8a71d44de9fa738a744b06a911f2519515ef
parent330c59b671ba62ad72f0a80e0c32beca7c69be2b (diff)
downloadmongo-796ae56683f818810fa5eab8ba446c71dcf54900.tar.gz
SERVER-39419 Stepdown interrupting a running dropDatabase command could leave unclean state
-rw-r--r--jstests/noPassthrough/step_down_during_drop_database.js60
-rw-r--r--src/mongo/db/catalog/database.h2
-rw-r--r--src/mongo/db/catalog/database_impl.cpp4
-rw-r--r--src/mongo/db/catalog/database_test.cpp9
4 files changed, 62 insertions, 13 deletions
diff --git a/jstests/noPassthrough/step_down_during_drop_database.js b/jstests/noPassthrough/step_down_during_drop_database.js
new file mode 100644
index 00000000000..51a21afce76
--- /dev/null
+++ b/jstests/noPassthrough/step_down_during_drop_database.js
@@ -0,0 +1,60 @@
+/**
+ * Tests that performing a stepdown on the primary during a dropDatabase command doesn't have any
+ * negative effects when the new primary runs the same dropDatabase command while the old primary
+ * is still in the midst of dropping the database.
+ *
+ * @tags: [requires_replication]
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/check_log.js");
+
+ const dbName = "test";
+ const collName = "coll";
+
+ const replSet = new ReplSetTest({nodes: 2});
+ replSet.startSet();
+ replSet.initiate();
+
+ let primary = replSet.getPrimary();
+ let testDB = primary.getDB(dbName);
+
+ const size = 5;
+ jsTest.log("Creating " + size + " test documents.");
+ var bulk = testDB.getCollection(collName).initializeUnorderedBulkOp();
+ for (var i = 0; i < size; ++i) {
+ bulk.insert({i: i});
+ }
+ assert.writeOK(bulk.execute());
+ replSet.awaitReplication();
+
+ const failpoint = "dropDatabaseHangAfterAllCollectionsDrop";
+ assert.commandWorked(primary.adminCommand({configureFailPoint: failpoint, mode: "alwaysOn"}));
+
+ // Run the dropDatabase command and stepdown the primary while it is running.
+ const awaitShell = startParallelShell(() => {
+ db.dropDatabase();
+ }, testDB.getMongo().port);
+
+ // Ensure the dropDatabase command has begun before stepping down.
+ checkLog.contains(primary,
+ "dropDatabase - fail point dropDatabaseHangAfterAllCollectionsDrop " +
+ "enabled. Blocking until fail point is disabled.");
+
+ assert.commandWorked(testDB.adminCommand({replSetStepDown: 60, force: true}));
+ replSet.waitForState(primary, ReplSetTest.State.SECONDARY);
+
+ assert.commandWorked(primary.adminCommand({configureFailPoint: failpoint, mode: "off"}));
+ awaitShell();
+
+ primary = replSet.getPrimary();
+ testDB = primary.getDB(dbName);
+
+ // Run dropDatabase on the new primary. The secondary (formerly the primary) should be able to
+ // drop the database too.
+ testDB.dropDatabase();
+ replSet.awaitReplication();
+
+ replSet.stopSet();
+})();
diff --git a/src/mongo/db/catalog/database.h b/src/mongo/db/catalog/database.h
index 30648c41bd9..5e4a248305a 100644
--- a/src/mongo/db/catalog/database.h
+++ b/src/mongo/db/catalog/database.h
@@ -152,8 +152,6 @@ public:
* Sets the 'drop-pending' state of this Database.
* This is done at the beginning of a dropDatabase operation and is used to reject subsequent
* collection creation requests on this database.
- * Throws a UserAssertion if this is called on a Database that is already in a 'drop-pending'
- * state.
* The database must be locked in MODE_X when calling this function.
*/
virtual void setDropPending(OperationContext* opCtx, bool dropPending) = 0;
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index e8db845ce50..4ca2102ff0f 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -370,10 +370,6 @@ Status DatabaseImpl::setProfilingLevel(OperationContext* opCtx, int newLevel) {
void DatabaseImpl::setDropPending(OperationContext* opCtx, bool dropPending) {
if (dropPending) {
invariant(opCtx->lockState()->isDbLockedForMode(name(), MODE_X));
- uassert(ErrorCodes::DatabaseDropPending,
- str::stream() << "Unable to drop database " << name()
- << " because it is already in the process of being dropped.",
- !_dropPending);
_dropPending = true;
} else {
invariant(opCtx->lockState()->isDbLockedForMode(name(), MODE_IX));
diff --git a/src/mongo/db/catalog/database_test.cpp b/src/mongo/db/catalog/database_test.cpp
index e1743a76488..c5f5caf1968 100644
--- a/src/mongo/db/catalog/database_test.cpp
+++ b/src/mongo/db/catalog/database_test.cpp
@@ -128,13 +128,8 @@ TEST_F(DatabaseTest, SetDropPendingThrowsExceptionIfDatabaseIsAlreadyInADropPend
db->setDropPending(_opCtx.get(), true);
ASSERT_TRUE(db->isDropPending(_opCtx.get()));
- ASSERT_THROWS_CODE_AND_WHAT(
- db->setDropPending(_opCtx.get(), true),
- AssertionException,
- ErrorCodes::DatabaseDropPending,
- (StringBuilder() << "Unable to drop database " << _nss.db()
- << " because it is already in the process of being dropped.")
- .stringData());
+ db->setDropPending(_opCtx.get(), true);
+ ASSERT_TRUE(db->isDropPending(_opCtx.get()));
db->setDropPending(_opCtx.get(), false);
ASSERT_FALSE(db->isDropPending(_opCtx.get()));