diff options
author | Benety Goh <benety@mongodb.com> | 2021-12-04 07:59:18 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-12-04 13:23:06 +0000 |
commit | a4cc735b9ec8d826e22cc6e3edeae17c2fd17261 (patch) | |
tree | e34dfde2b7dc24c29a626a9101e5da8039433665 | |
parent | cd2787ff5e94fc051750b0d7f0700c93fe867f6a (diff) | |
download | mongo-a4cc735b9ec8d826e22cc6e3edeae17c2fd17261.tar.gz |
SERVER-61486 collMod tracks side writes during unique index conversion
-rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 61 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod_index.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod_index.h | 7 |
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); |