summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2021-12-04 07:59:18 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-12-04 13:23:06 +0000
commita4cc735b9ec8d826e22cc6e3edeae17c2fd17261 (patch)
treee34dfde2b7dc24c29a626a9101e5da8039433665
parentcd2787ff5e94fc051750b0d7f0700c93fe867f6a (diff)
downloadmongo-a4cc735b9ec8d826e22cc6e3edeae17c2fd17261.tar.gz
SERVER-61486 collMod tracks side writes during unique index conversion
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp61
-rw-r--r--src/mongo/db/catalog/coll_mod_index.cpp5
-rw-r--r--src/mongo/db/catalog/coll_mod_index.h7
3 files changed, 70 insertions, 3 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index 7fe37a1f81f..4dcfe4293f2 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/catalog/clustered_collection_util.h"
#include "mongo/db/catalog/coll_mod_index.h"
+#include "mongo/db/catalog/coll_mod_write_ops_tracker.h"
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/catalog/create_collection.h"
#include "mongo/db/catalog/index_catalog.h"
@@ -535,6 +536,34 @@ Status _processCollModDryRunMode(OperationContext* opCtx,
return Status::OK();
}
+StatusWith<std::unique_ptr<CollModWriteOpsTracker::Token>> _setUpCollModIndexUnique(
+ OperationContext* opCtx, const NamespaceStringOrUUID& nsOrUUID, const CollMod& cmd) {
+ AutoGetCollection coll(opCtx, nsOrUUID, MODE_IS);
+ auto nss = coll.getNss();
+
+ const auto& collection = coll.getCollection();
+ if (!collection) {
+ return Status(ErrorCodes::NamespaceNotFound,
+ str::stream() << "ns does not exist for unique index conversion: " << nss);
+ }
+
+ // Install side write tracker.
+ auto opsTracker = CollModWriteOpsTracker::get(opCtx->getServiceContext());
+ auto writeOpsToken = opsTracker->startTracking(collection->uuid());
+
+ // Scan index for duplicates without exclusive access.
+ BSONObjBuilder unused;
+ auto statusW = parseCollModRequest(opCtx, nss, collection, cmd, &unused);
+ if (!statusW.isOK()) {
+ return statusW.getStatus();
+ }
+ const auto& cmr = statusW.getValue();
+ auto idx = cmr.indexRequest.idx;
+ scanIndexForDuplicates(opCtx, collection, idx);
+
+ return std::move(writeOpsToken);
+}
+
Status _collModInternal(OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
const CollMod& cmd,
@@ -544,6 +573,19 @@ Status _collModInternal(OperationContext* opCtx,
return _processCollModDryRunMode(opCtx, nsOrUUID, cmd, result, mode);
}
+ // Before acquiring exclusive access to the collection for unique index conversion, we will
+ // track concurrent writes while performing a preliminary index scan here. After we obtain
+ // exclusive access for the actual conversion, we will reconcile the concurrent writes with
+ // the state of the index before updating the catalog.
+ std::unique_ptr<CollModWriteOpsTracker::Token> writeOpsToken;
+ if (cmd.getIndex() && cmd.getIndex()->getUnique().value_or(false) && !mode) {
+ auto statusW = _setUpCollModIndexUnique(opCtx, nsOrUUID, cmd);
+ if (!statusW.isOK()) {
+ return statusW.getStatus();
+ }
+ writeOpsToken = std::move(statusW.getValue());
+ }
+
AutoGetCollection coll(opCtx, nsOrUUID, MODE_X, AutoGetCollectionViewMode::kViewsPermitted);
auto nss = coll.getNss();
StringData dbName = nss.db();
@@ -621,6 +663,16 @@ Status _collModInternal(OperationContext* opCtx,
createChangeStreamPreImagesCollection(opCtx);
}
+ // With exclusive access to the collection, we can take ownership of the modified docs observed
+ // by the side write tracker if a unique index conversion is requested.
+ // This step releases the resources associated with the token and therefore should not be
+ // performed inside the write conflict retry loop.
+ std::unique_ptr<CollModWriteOpsTracker::Docs> docsForUniqueIndex;
+ if (writeOpsToken) {
+ auto opsTracker = CollModWriteOpsTracker::get(opCtx->getServiceContext());
+ docsForUniqueIndex = opsTracker->stopTracking(std::move(writeOpsToken));
+ }
+
return writeConflictRetry(opCtx, "collMod", nss.ns(), [&] {
WriteUnitOfWork wunit(opCtx);
@@ -679,8 +731,13 @@ Status _collModInternal(OperationContext* opCtx,
}
// Handle index modifications.
- processCollModIndexRequest(
- opCtx, &coll, cmrNew.indexRequest, &indexCollModInfo, result, mode);
+ processCollModIndexRequest(opCtx,
+ &coll,
+ cmrNew.indexRequest,
+ docsForUniqueIndex.get(),
+ &indexCollModInfo,
+ result,
+ mode);
if (cmrNew.collValidator) {
coll.getWritableCollection()->setValidator(opCtx, *cmrNew.collValidator);
diff --git a/src/mongo/db/catalog/coll_mod_index.cpp b/src/mongo/db/catalog/coll_mod_index.cpp
index e96bedb555e..c9f0eae963b 100644
--- a/src/mongo/db/catalog/coll_mod_index.cpp
+++ b/src/mongo/db/catalog/coll_mod_index.cpp
@@ -106,6 +106,7 @@ void _processCollModIndexRequestUnique(OperationContext* opCtx,
AutoGetCollection* autoColl,
const IndexDescriptor* idx,
boost::optional<repl::OplogApplication::Mode> mode,
+ const CollModWriteOpsTracker::Docs* docsForUniqueIndex,
BSONElement indexUnique,
BSONElement* newUnique) {
// Do not update catalog if index is already unique.
@@ -128,6 +129,7 @@ void _processCollModIndexRequestUnique(OperationContext* opCtx,
void processCollModIndexRequest(OperationContext* opCtx,
AutoGetCollection* autoColl,
const ParsedCollModIndexRequest& collModIndexRequest,
+ const CollModWriteOpsTracker::Docs* docsForUniqueIndex,
boost::optional<IndexCollModInfo>* indexCollModInfo,
BSONObjBuilder* result,
boost::optional<repl::OplogApplication::Mode> mode) {
@@ -162,7 +164,8 @@ void processCollModIndexRequest(OperationContext* opCtx,
// User wants to convert an index to be unique.
if (indexUnique) {
- _processCollModIndexRequestUnique(opCtx, autoColl, idx, mode, indexUnique, &newUnique);
+ _processCollModIndexRequestUnique(
+ opCtx, autoColl, idx, mode, docsForUniqueIndex, indexUnique, &newUnique);
}
*indexCollModInfo = IndexCollModInfo{
diff --git a/src/mongo/db/catalog/coll_mod_index.h b/src/mongo/db/catalog/coll_mod_index.h
index c911db47f61..ad9769da0ec 100644
--- a/src/mongo/db/catalog/coll_mod_index.h
+++ b/src/mongo/db/catalog/coll_mod_index.h
@@ -28,6 +28,7 @@
*/
#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/catalog/coll_mod_write_ops_tracker.h"
#include "mongo/db/catalog_raii.h"
#include "mongo/db/coll_mod_gen.h"
#include "mongo/db/index/index_descriptor.h"
@@ -59,11 +60,17 @@ struct ParsedCollModIndexRequest {
*
* The appropriate collection locks should be acquired before calling this function.
*
+ * 'docsForUniqueIndex' contains documents captured by the side writes tracker for unique index
+ * conversion.
+ * If no side writes tracker was installed (because a unique index conversion was not requested),
+ * 'docsForUniqueIndex' will be null.
+ *
* Used by collMod implementation only.
*/
void processCollModIndexRequest(OperationContext* opCtx,
AutoGetCollection* autoColl,
const ParsedCollModIndexRequest& collModIndexRequest,
+ const CollModWriteOpsTracker::Docs* docsForUniqueIndex,
boost::optional<IndexCollModInfo>* indexCollModInfo,
BSONObjBuilder* result,
boost::optional<repl::OplogApplication::Mode> mode);