summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/noPassthrough/drop_collection_aborts_in_progress_index_builds.js64
-rw-r--r--src/mongo/db/catalog/drop_collection.cpp205
-rw-r--r--src/mongo/db/catalog/drop_collection.h23
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp32
-rw-r--r--src/mongo/db/commands/dbcommands.cpp1
-rw-r--r--src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp5
-rw-r--r--src/mongo/db/pipeline/process_interface/standalone_process_interface.cpp5
-rw-r--r--src/mongo/db/repl/oplog.cpp8
-rw-r--r--src/mongo/dbtests/rollbacktests.cpp24
9 files changed, 300 insertions, 67 deletions
diff --git a/jstests/noPassthrough/drop_collection_aborts_in_progress_index_builds.js b/jstests/noPassthrough/drop_collection_aborts_in_progress_index_builds.js
new file mode 100644
index 00000000000..91c3e0be1d3
--- /dev/null
+++ b/jstests/noPassthrough/drop_collection_aborts_in_progress_index_builds.js
@@ -0,0 +1,64 @@
+/**
+ * Tests that the "drop" command can abort in-progress index builds.
+ */
+(function() {
+"use strict";
+
+load("jstests/noPassthrough/libs/index_build.js");
+
+const mongodOptions = {};
+const conn = MongoRunner.runMongod(mongodOptions);
+
+if (!IndexBuildTest.supportsTwoPhaseIndexBuild(conn)) {
+ jsTest.log("Not running because two phase index builds are not supported.");
+ MongoRunner.stopMongod(conn);
+ return;
+}
+
+const dbName = "drop_collection_aborts_in_progress_index_builds";
+const collName = "test";
+
+TestData.dbName = dbName;
+TestData.collName = collName;
+
+const testDB = conn.getDB(dbName);
+testDB.getCollection(collName).drop();
+
+assert.commandWorked(testDB.createCollection(collName));
+const coll = testDB.getCollection(collName);
+
+assert.commandWorked(coll.insert({a: 1}));
+assert.commandWorked(coll.insert({b: 1}));
+
+assert.commandWorked(coll.createIndex({a: 1}));
+
+jsTest.log("Starting two index builds and freezing them.");
+IndexBuildTest.pauseIndexBuilds(testDB.getMongo());
+
+const awaitFirstIndexBuild = IndexBuildTest.startIndexBuild(
+ testDB.getMongo(), coll.getFullName(), {a: 1, b: 1}, {}, [ErrorCodes.IndexBuildAborted]);
+IndexBuildTest.waitForIndexBuildToScanCollection(testDB, collName, "a_1_b_1");
+
+const awaitSecondIndexBuild = IndexBuildTest.startIndexBuild(
+ testDB.getMongo(), coll.getFullName(), {b: 1}, {}, [ErrorCodes.IndexBuildAborted]);
+IndexBuildTest.waitForIndexBuildToScanCollection(testDB, collName, "b_1");
+
+jsTest.log("Dropping collection " + dbName + "." + collName + " with in-progress index builds");
+const awaitDrop = startParallelShell(() => {
+ const testDB = db.getSiblingDB(TestData.dbName);
+ assert.commandWorked(testDB.runCommand({drop: TestData.collName}));
+}, conn.port);
+
+try {
+ checkLog.contains(testDB.getMongo(),
+ "About to abort all index builders on collection with UUID");
+} finally {
+ IndexBuildTest.resumeIndexBuilds(testDB.getMongo());
+}
+
+awaitFirstIndexBuild();
+awaitSecondIndexBuild();
+awaitDrop();
+
+MongoRunner.stopMongod(conn);
+}());
diff --git a/src/mongo/db/catalog/drop_collection.cpp b/src/mongo/db/catalog/drop_collection.cpp
index 1f18d42eec0..ca7a93a945b 100644
--- a/src/mongo/db/catalog/drop_collection.cpp
+++ b/src/mongo/db/catalog/drop_collection.cpp
@@ -54,6 +54,20 @@ namespace mongo {
MONGO_FAIL_POINT_DEFINE(hangDropCollectionBeforeLockAcquisition);
MONGO_FAIL_POINT_DEFINE(hangDuringDropCollection);
+Status _checkNssAndReplState(OperationContext* opCtx, Collection* coll) {
+ if (!coll) {
+ return Status(ErrorCodes::NamespaceNotFound, "ns not found");
+ }
+
+ if (opCtx->writesAreReplicated() &&
+ !repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, coll->ns())) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while dropping collection " << coll->ns());
+ }
+
+ return Status::OK();
+}
+
Status _dropView(OperationContext* opCtx,
Database* db,
const NamespaceString& collectionName,
@@ -104,6 +118,129 @@ Status _dropView(OperationContext* opCtx,
return Status::OK();
}
+Status _abortIndexBuildsAndDropCollection(OperationContext* opCtx,
+ const NamespaceString& startingNss,
+ DropCollectionSystemCollectionMode systemCollectionMode,
+ BSONObjBuilder& result) {
+ // We only need to hold an intent lock to send abort signals to the active index builder on this
+ // collection.
+ boost::optional<AutoGetDb> autoDb;
+ autoDb.emplace(opCtx, startingNss.db(), MODE_IX);
+
+ boost::optional<Lock::CollectionLock> collLock;
+ collLock.emplace(opCtx, startingNss, MODE_IX);
+
+ // Abandon the snapshot as the index catalog will compare the in-memory state to the disk state,
+ // which may have changed when we released the collection lock temporarily.
+ opCtx->recoveryUnit()->abandonSnapshot();
+
+ Collection* coll =
+ CollectionCatalog::get(opCtx).lookupCollectionByNamespace(opCtx, startingNss);
+ Status status = _checkNssAndReplState(opCtx, coll);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (MONGO_unlikely(hangDuringDropCollection.shouldFail())) {
+ LOGV2(518090,
+ "hangDuringDropCollection fail point enabled. Blocking until fail point is "
+ "disabled.");
+ hangDuringDropCollection.pauseWhileSet();
+ }
+
+ AutoStatsTracker statsTracker(opCtx,
+ startingNss,
+ Top::LockType::NotLocked,
+ AutoStatsTracker::LogMode::kUpdateCurOp,
+ autoDb->getDb()->getProfilingLevel());
+
+ IndexBuildsCoordinator* indexBuildsCoord = IndexBuildsCoordinator::get(opCtx);
+ const UUID collectionUUID = coll->uuid();
+ const NamespaceStringOrUUID dbAndUUID{coll->ns().db().toString(), coll->uuid()};
+ const int numIndexes = coll->getIndexCatalog()->numIndexesTotal(opCtx);
+
+ while (true) {
+ // Send the abort signal to any active index builds on the collection.
+ indexBuildsCoord->abortCollectionIndexBuildsNoWait(
+ collectionUUID,
+ str::stream() << "Collection " << coll->ns() << "(" << collectionUUID
+ << ") is being dropped");
+
+ // Now that the abort signals were sent out to the active index builders for this
+ // collection, we need to release the lock temporarily to allow those index builders to
+ // process the abort signal. Holding a lock here will cause the index builders to block
+ // indefinitely.
+ collLock = boost::none;
+ autoDb = boost::none;
+
+ indexBuildsCoord->awaitNoIndexBuildInProgressForCollection(collectionUUID);
+
+ // Take an exclusive lock to finish the collection drop.
+ autoDb.emplace(opCtx, startingNss.db(), MODE_IX);
+ collLock.emplace(opCtx, dbAndUUID, MODE_X);
+
+ // Abandon the snapshot as the index catalog will compare the in-memory state to the
+ // disk state, which may have changed when we released the collection lock temporarily.
+ opCtx->recoveryUnit()->abandonSnapshot();
+
+ coll = CollectionCatalog::get(opCtx).lookupCollectionByUUID(opCtx, collectionUUID);
+ status = _checkNssAndReplState(opCtx, coll);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ // Check if any new index builds were started while releasing the collection lock
+ // temporarily, if so, we need to abort the new index builders.
+ const bool abortAgain = indexBuildsCoord->inProgForCollection(collectionUUID);
+ if (!abortAgain) {
+ break;
+ }
+
+ // We only need to hold an intent lock to send an abort signal to the active index
+ // builders on this collection.
+ collLock = boost::none;
+ autoDb = boost::none;
+
+ autoDb.emplace(opCtx, startingNss.db(), MODE_IX);
+ collLock.emplace(opCtx, dbAndUUID, MODE_IX);
+
+ // Abandon the snapshot as the index catalog will compare the in-memory state to the
+ // disk state, which may have changed when we released the collection lock temporarily.
+ opCtx->recoveryUnit()->abandonSnapshot();
+
+ coll = CollectionCatalog::get(opCtx).lookupCollectionByUUID(opCtx, collectionUUID);
+ status = _checkNssAndReplState(opCtx, coll);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
+ // It's possible for the given collection to be drop pending after obtaining the locks again, if
+ // that is the case, then the collection is already registered to be dropped. Return early.
+ const NamespaceString resolvedNss = coll->ns();
+ if (resolvedNss.isDropPendingNamespace()) {
+ return Status::OK();
+ }
+
+ WriteUnitOfWork wunit(opCtx);
+
+ invariant(coll->getIndexCatalog()->numIndexesInProgress(opCtx) == 0);
+ status =
+ systemCollectionMode == DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops
+ ? autoDb->getDb()->dropCollection(opCtx, resolvedNss, {})
+ : autoDb->getDb()->dropCollectionEvenIfSystem(opCtx, resolvedNss, {});
+
+ if (!status.isOK()) {
+ return status;
+ }
+ wunit.commit();
+
+ result.append("nIndexesWas", numIndexes);
+ result.append("ns", resolvedNss.ns());
+
+ return Status::OK();
+}
+
Status _dropCollection(OperationContext* opCtx,
Database* db,
const NamespaceString& collectionName,
@@ -113,8 +250,9 @@ Status _dropCollection(OperationContext* opCtx,
Lock::CollectionLock collLock(opCtx, collectionName, MODE_X);
Collection* coll =
CollectionCatalog::get(opCtx).lookupCollectionByNamespace(opCtx, collectionName);
- if (!coll) {
- return Status(ErrorCodes::NamespaceNotFound, "ns not found");
+ Status status = _checkNssAndReplState(opCtx, coll);
+ if (!status.isOK()) {
+ return status;
}
if (MONGO_unlikely(hangDuringDropCollection.shouldFail())) {
@@ -130,18 +268,12 @@ Status _dropCollection(OperationContext* opCtx,
AutoStatsTracker::LogMode::kUpdateCurOp,
db->getProfilingLevel());
- if (opCtx->writesAreReplicated() &&
- !repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, collectionName)) {
- return Status(ErrorCodes::NotMaster,
- str::stream() << "Not primary while dropping collection " << collectionName);
- }
-
WriteUnitOfWork wunit(opCtx);
int numIndexes = coll->getIndexCatalog()->numIndexesTotal(opCtx);
BackgroundOperation::assertNoBgOpInProgForNs(collectionName.ns());
IndexBuildsCoordinator::get(opCtx)->assertNoIndexBuildInProgForCollection(coll->uuid());
- Status status =
+ status =
systemCollectionMode == DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops
? db->dropCollection(opCtx, collectionName, dropOpTime)
: db->dropCollectionEvenIfSystem(opCtx, collectionName, dropOpTime);
@@ -160,9 +292,56 @@ Status _dropCollection(OperationContext* opCtx,
Status dropCollection(OperationContext* opCtx,
const NamespaceString& collectionName,
BSONObjBuilder& result,
- const repl::OpTime& dropOpTime,
DropCollectionSystemCollectionMode systemCollectionMode) {
if (!serverGlobalParams.quiet.load()) {
+ LOGV2(518070, "CMD: drop {collectionName}", "collectionName"_attr = collectionName);
+ }
+
+ if (MONGO_unlikely(hangDropCollectionBeforeLockAcquisition.shouldFail())) {
+ LOGV2(518080, "Hanging drop collection before lock acquisition while fail point is set");
+ hangDropCollectionBeforeLockAcquisition.pauseWhileSet();
+ }
+
+ try {
+ return writeConflictRetry(opCtx, "drop", collectionName.ns(), [&] {
+ {
+ AutoGetDb autoDb(opCtx, collectionName.db(), MODE_IX);
+ Database* db = autoDb.getDb();
+ if (!db) {
+ return Status(ErrorCodes::NamespaceNotFound, "ns not found");
+ }
+
+ Collection* coll = CollectionCatalog::get(opCtx).lookupCollectionByNamespace(
+ opCtx, collectionName);
+
+ if (!coll) {
+ return _dropView(opCtx, db, collectionName, result);
+ }
+
+ // Only abort in-progress index builds if two phase index builds are supported.
+ bool supportsTwoPhaseIndexBuild =
+ IndexBuildsCoordinator::get(opCtx)->supportsTwoPhaseIndexBuild();
+ if (!supportsTwoPhaseIndexBuild) {
+ return _dropCollection(
+ opCtx, db, collectionName, {}, systemCollectionMode, result);
+ }
+ }
+
+ return _abortIndexBuildsAndDropCollection(
+ opCtx, collectionName, systemCollectionMode, result);
+ });
+ } catch (ExceptionFor<ErrorCodes::NamespaceNotFound>&) {
+ // The shell requires that NamespaceNotFound error codes return the "ns not found"
+ // string.
+ return Status(ErrorCodes::NamespaceNotFound, "ns not found");
+ }
+}
+
+Status dropCollectionForApplyOps(OperationContext* opCtx,
+ const NamespaceString& collectionName,
+ const repl::OpTime& dropOpTime,
+ DropCollectionSystemCollectionMode systemCollectionMode) {
+ if (!serverGlobalParams.quiet.load()) {
LOGV2(20332, "CMD: drop {collectionName}", "collectionName"_attr = collectionName);
}
@@ -179,11 +358,13 @@ Status dropCollection(OperationContext* opCtx,
Collection* coll =
CollectionCatalog::get(opCtx).lookupCollectionByNamespace(opCtx, collectionName);
+
+ BSONObjBuilder unusedBuilder;
if (!coll) {
- return _dropView(opCtx, db, collectionName, result);
+ return _dropView(opCtx, db, collectionName, unusedBuilder);
} else {
return _dropCollection(
- opCtx, db, collectionName, dropOpTime, systemCollectionMode, result);
+ opCtx, db, collectionName, dropOpTime, systemCollectionMode, unusedBuilder);
}
});
}
diff --git a/src/mongo/db/catalog/drop_collection.h b/src/mongo/db/catalog/drop_collection.h
index 2646c632aa1..5446bbef922 100644
--- a/src/mongo/db/catalog/drop_collection.h
+++ b/src/mongo/db/catalog/drop_collection.h
@@ -38,21 +38,28 @@ namespace repl {
class OpTime;
} // namespace repl
-/**
- * Drops the collection "collectionName" and populates "result" with statistics about what
- * was removed.
- *
- * If we are applying an oplog entry for a collection drop on a secondary, 'dropOpTime' is set
- * to the optime in the oplog entry.
- */
enum class DropCollectionSystemCollectionMode {
kDisallowSystemCollectionDrops,
kAllowSystemCollectionDrops
};
+
+/**
+ * Drops the collection "collectionName" and populates "result" with statistics about what
+ * was removed. Aborts in-progress index builds on the collection if two phase index builds are
+ * supported.
+ */
Status dropCollection(OperationContext* opCtx,
const NamespaceString& collectionName,
BSONObjBuilder& result,
- const repl::OpTime& dropOpTime,
DropCollectionSystemCollectionMode systemCollectionMode);
+/**
+ * Drops the collection "collectionName". When applying a 'drop' oplog entry on a secondary, the
+ * 'dropOpTime' will contain the optime of the oplog entry.
+ */
+Status dropCollectionForApplyOps(OperationContext* opCtx,
+ const NamespaceString& collectionName,
+ const repl::OpTime& dropOpTime,
+ DropCollectionSystemCollectionMode systemCollectionMode);
+
} // namespace mongo
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp
index 36b5e941a35..16af3071493 100644
--- a/src/mongo/db/catalog/rename_collection.cpp
+++ b/src/mongo/db/catalog/rename_collection.cpp
@@ -581,15 +581,13 @@ Status renameBetweenDBs(OperationContext* opCtx,
// Dismissed on success
auto tmpCollectionDropper = makeGuard([&] {
- BSONObjBuilder unusedResult;
Status status = Status::OK();
try {
- status =
- dropCollection(opCtx,
- tmpName,
- unusedResult,
- {},
- DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
+ status = dropCollectionForApplyOps(
+ opCtx,
+ tmpName,
+ {},
+ DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
} catch (...) {
status = exceptionToStatus();
}
@@ -738,13 +736,8 @@ Status renameBetweenDBs(OperationContext* opCtx,
return status;
tmpCollectionDropper.dismiss();
-
- BSONObjBuilder unusedResult;
- return dropCollection(opCtx,
- source,
- unusedResult,
- {},
- DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
+ return dropCollectionForApplyOps(
+ opCtx, source, {}, DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
}
} // namespace
@@ -941,12 +934,11 @@ Status renameCollectionForApplyOps(OperationContext* opCtx,
// Downgrade renameCollection to dropCollection.
if (dropTargetNss) {
- BSONObjBuilder unusedResult;
- return dropCollection(opCtx,
- *dropTargetNss,
- unusedResult,
- renameOpTime,
- DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
+ return dropCollectionForApplyOps(
+ opCtx,
+ *dropTargetNss,
+ renameOpTime,
+ DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
}
return Status(ErrorCodes::NamespaceNotFound,
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index a7015ebf7ec..5b880adfdd9 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -255,7 +255,6 @@ public:
dropCollection(opCtx,
nsToDrop,
result,
- {},
DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
return true;
}
diff --git a/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp b/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp
index bed008e597e..002e261099e 100644
--- a/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp
+++ b/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp
@@ -167,9 +167,8 @@ void ReplicaSetNodeProcessInterface::createCollection(OperationContext* opCtx,
void ReplicaSetNodeProcessInterface::dropCollection(OperationContext* opCtx,
const NamespaceString& ns) {
- BSONObjBuilder result;
- uassertStatusOK(mongo::dropCollection(
- opCtx, ns, result, {}, DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
+ uassertStatusOK(mongo::dropCollectionForApplyOps(
+ opCtx, ns, {}, DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
}
std::unique_ptr<Pipeline, PipelineDeleter>
diff --git a/src/mongo/db/pipeline/process_interface/standalone_process_interface.cpp b/src/mongo/db/pipeline/process_interface/standalone_process_interface.cpp
index f4a805a595f..a5d38eb6fc1 100644
--- a/src/mongo/db/pipeline/process_interface/standalone_process_interface.cpp
+++ b/src/mongo/db/pipeline/process_interface/standalone_process_interface.cpp
@@ -145,9 +145,8 @@ void StandaloneProcessInterface::createCollection(OperationContext* opCtx,
void StandaloneProcessInterface::dropCollection(OperationContext* opCtx,
const NamespaceString& ns) {
- BSONObjBuilder result;
- uassertStatusOK(mongo::dropCollection(
- opCtx, ns, result, {}, DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
+ uassertStatusOK(mongo::dropCollectionForApplyOps(
+ opCtx, ns, {}, DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
}
std::unique_ptr<Pipeline, PipelineDeleter> StandaloneProcessInterface::attachCursorSourceToPipeline(
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 8150dfbeb70..d2d92c32693 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -795,7 +795,6 @@ const StringMap<ApplyOpMetadata> kOpsMap = {
{ErrorCodes::NamespaceNotFound}}},
{"drop",
{[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status {
- BSONObjBuilder resultWeDontCareAbout;
const auto& cmd = entry.getObject();
auto nss = extractNsFromUUIDorNs(opCtx, entry.getNss(), entry.getUuid(), cmd);
if (nss.isDropPendingNamespace()) {
@@ -812,11 +811,8 @@ const StringMap<ApplyOpMetadata> kOpsMap = {
if (!opCtx->writesAreReplicated()) {
opTime = entry.getOpTime();
}
- return dropCollection(opCtx,
- nss,
- resultWeDontCareAbout,
- opTime,
- DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
+ return dropCollectionForApplyOps(
+ opCtx, nss, opTime, DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
},
{ErrorCodes::NamespaceNotFound}}},
// deleteIndex(es) is deprecated but still works as of April 10, 2015
diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp
index 72affc48683..ebd8ce2e5cb 100644
--- a/src/mongo/dbtests/rollbacktests.cpp
+++ b/src/mongo/dbtests/rollbacktests.cpp
@@ -337,13 +337,11 @@ public:
{
WriteUnitOfWork uow(&opCtx);
- BSONObjBuilder result;
- ASSERT_OK(
- dropCollection(&opCtx,
- target,
- result,
- {},
- DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
+ ASSERT_OK(dropCollectionForApplyOps(
+ &opCtx,
+ target,
+ {},
+ DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
ASSERT_OK(renameCollection(&opCtx, source, target));
ASSERT(!collectionExists(&opCtx, &ctx, source.ns()));
ASSERT(collectionExists(&opCtx, &ctx, target.ns()));
@@ -398,13 +396,11 @@ public:
{
WriteUnitOfWork uow(&opCtx);
- BSONObjBuilder result;
- ASSERT_OK(
- dropCollection(&opCtx,
- nss,
- result,
- {},
- DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
+ ASSERT_OK(dropCollectionForApplyOps(
+ &opCtx,
+ nss,
+ {},
+ DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
ASSERT(!collectionExists(&opCtx, &ctx, nss.ns()));
CollectionOptions collectionOptions =
assertGet(CollectionOptions::parse(BSONObj(), CollectionOptions::parseForCommand));