diff options
author | Dianna Hohensee <dianna.hohensee@10gen.com> | 2018-12-06 16:22:08 -0500 |
---|---|---|
committer | Dianna Hohensee <dianna.hohensee@10gen.com> | 2018-12-10 17:13:00 -0500 |
commit | ef7a91c26cdfd85f1d170219a724c16edaab6b9c (patch) | |
tree | 08d78b21c1f68f2797c390dcc287adb692887e55 | |
parent | 1108946e1c2fb8922c384fa5171165868b1162a8 (diff) | |
download | mongo-ef7a91c26cdfd85f1d170219a724c16edaab6b9c.tar.gz |
SERVER-37939 Add a temporary twoPhaseCreateIndexes command in order to selectively exercise simultaneous index builds during development
-rw-r--r-- | jstests/core/views/views_all_commands.js | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/create_indexes.cpp | 480 | ||||
-rw-r--r-- | src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp | 1 |
3 files changed, 270 insertions, 215 deletions
diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js index a5acda0c1de..baf28db424b 100644 --- a/jstests/core/views/views_all_commands.js +++ b/jstests/core/views/views_all_commands.js @@ -519,6 +519,10 @@ command: {touch: "view", data: true}, expectFailure: true, }, + twoPhaseCreateIndexes: { + command: {twoPhaseCreateIndexes: "view", indexes: [{key: {x: 1}, name: "x_1"}]}, + expectFailure: true, + }, unsetSharding: {skip: isAnInternalCommand}, update: {command: {update: "view", updates: [{q: {x: 1}, u: {x: 2}}]}, expectFailure: true}, updateRole: { diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index 9dcf9ebd467..2edd7fc6d5a 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -79,6 +79,7 @@ namespace { const StringData kIndexesFieldName = "indexes"_sd; const StringData kCommandName = "createIndexes"_sd; +const StringData kTwoPhaseCommandName = "twoPhaseCreateIndexes"_sd; /** * Parses the index specifications from 'cmdObj', validates them, and returns equivalent index @@ -141,7 +142,8 @@ StatusWith<std::vector<BSONObj>> parseAndValidateIndexSpecs( } hasIndexesField = true; - } else if (kCommandName == cmdElemFieldName || isGenericArgument(cmdElemFieldName)) { + } else if (kCommandName == cmdElemFieldName || kTwoPhaseCommandName == cmdElemFieldName || + isGenericArgument(cmdElemFieldName)) { continue; } else { return {ErrorCodes::BadValue, @@ -242,271 +244,319 @@ std::vector<BSONObj> resolveDefaultsAndRemoveExistingIndexes(OperationContext* o return specs; } -/** - * { createIndexes : "bar", indexes : [ { ns : "test.bar", key : { x : 1 }, name: "x_1" } ] } - */ -class CmdCreateIndex : public ErrmsgCommandDeprecated { -public: - CmdCreateIndex() : ErrmsgCommandDeprecated(kCommandName) {} +void checkUniqueIndexConstraints(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& newIdxKey) { + invariant(opCtx->lockState()->isCollectionLockedForMode(nss.ns(), MODE_X)); + + const auto metadata = CollectionShardingState::get(opCtx, nss)->getCurrentMetadata(); + if (!metadata->isSharded()) + return; + + const ShardKeyPattern shardKeyPattern(metadata->getKeyPattern()); + uassert(ErrorCodes::CannotCreateIndex, + str::stream() << "cannot create unique index over " << newIdxKey + << " with shard key pattern " + << shardKeyPattern.toBSON(), + shardKeyPattern.isUniqueIndexCompatible(newIdxKey)); +} - bool supportsWriteConcern(const BSONObj& cmd) const override { - return true; +bool runCreateIndexes(OperationContext* opCtx, + const string& dbname, + const BSONObj& cmdObj, + string& errmsg, + BSONObjBuilder& result, + bool runTwoPhaseBuild) { + const NamespaceString ns(CommandHelpers::parseNsCollectionRequired(dbname, cmdObj)); + uassertStatusOK(userAllowedWriteNS(ns)); + + // Disallow users from creating new indexes on config.transactions since the sessions code + // was optimized to not update indexes + uassert(ErrorCodes::IllegalOperation, + str::stream() << "not allowed to create index on " << ns.ns(), + ns != NamespaceString::kSessionTransactionsTableNamespace); + + auto specs = uassertStatusOK( + parseAndValidateIndexSpecs(opCtx, ns, cmdObj, serverGlobalParams.featureCompatibility)); + + // Do not use AutoGetOrCreateDb because we may relock the database in mode X. + Lock::DBLock dbLock(opCtx, ns.db(), MODE_IX); + if (!repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, ns)) { + uasserted(ErrorCodes::NotMaster, + str::stream() << "Not primary while creating indexes in " << ns.ns()); } - AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { - return AllowedOnSecondary::kNever; + const auto indexesAlreadyExist = [&result](int numIndexes) { + result.append("numIndexesBefore", numIndexes); + result.append("numIndexesAfter", numIndexes); + result.append("note", "all indexes already exist"); + return true; + }; + + // Before potentially taking an exclusive database lock, check if all indexes already exist + // while holding an intent lock. Only continue if new indexes need to be built and the + // database should be re-locked in exclusive mode. + { + AutoGetCollection autoColl(opCtx, ns, MODE_IX); + if (auto collection = autoColl.getCollection()) { + auto specsCopy = resolveDefaultsAndRemoveExistingIndexes(opCtx, collection, specs); + if (specsCopy.size() == 0) { + return indexesAlreadyExist(collection->getIndexCatalog()->numIndexesTotal(opCtx)); + } + } } - Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) const override { - ActionSet actions; - actions.addAction(ActionType::createIndex); - Privilege p(parseResourcePattern(dbname, cmdObj), actions); - if (AuthorizationSession::get(client)->isAuthorizedForPrivilege(p)) - return Status::OK(); - return Status(ErrorCodes::Unauthorized, "Unauthorized"); - } + // Relocking temporarily releases the Database lock while holding a Global IX lock. This + // prevents the replication state from changing, but requires abandoning the current + // snapshot in case indexes change during the period of time where no database lock is held. + opCtx->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_X); - bool errmsgRun(OperationContext* opCtx, - const string& dbname, - const BSONObj& cmdObj, - string& errmsg, - BSONObjBuilder& result) override { - const NamespaceString ns(CommandHelpers::parseNsCollectionRequired(dbname, cmdObj)); - uassertStatusOK(userAllowedWriteNS(ns)); - - // Disallow users from creating new indexes on config.transactions since the sessions code - // was optimized to not update indexes - uassert(ErrorCodes::IllegalOperation, - str::stream() << "not allowed to create index on " << ns.ns(), - ns != NamespaceString::kSessionTransactionsTableNamespace); - - auto specs = uassertStatusOK( - parseAndValidateIndexSpecs(opCtx, ns, cmdObj, serverGlobalParams.featureCompatibility)); - - // Do not use AutoGetOrCreateDb because we may relock the database in mode X. - Lock::DBLock dbLock(opCtx, ns.db(), MODE_IX); - if (!repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, ns)) { - uasserted(ErrorCodes::NotMaster, - str::stream() << "Not primary while creating indexes in " << ns.ns()); - } + // Allow the strong lock acquisition above to be interrupted, but from this point forward do + // not allow locks or re-locks to be interrupted. + UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - const auto indexesAlreadyExist = [&result](int numIndexes) { - result.append("numIndexesBefore", numIndexes); - result.append("numIndexesAfter", numIndexes); - result.append("note", "all indexes already exist"); - return true; - }; - - // Before potentially taking an exclusive database lock, check if all indexes already exist - // while holding an intent lock. Only continue if new indexes need to be built and the - // database should be re-locked in exclusive mode. - { - AutoGetCollection autoColl(opCtx, ns, MODE_IX); - if (auto collection = autoColl.getCollection()) { - auto specsCopy = resolveDefaultsAndRemoveExistingIndexes(opCtx, collection, specs); - if (specsCopy.size() == 0) { - return indexesAlreadyExist( - collection->getIndexCatalog()->numIndexesTotal(opCtx)); - } - } + Database* db = DatabaseHolder::getDatabaseHolder().get(opCtx, ns.db()); + if (!db) { + db = DatabaseHolder::getDatabaseHolder().openDb(opCtx, ns.db()); + } + DatabaseShardingState::get(db).checkDbVersion(opCtx); + + Collection* collection = db->getCollection(opCtx, ns); + if (collection) { + result.appendBool("createdCollectionAutomatically", false); + } else { + if (db->getViewCatalog()->lookup(opCtx, ns.ns())) { + errmsg = "Cannot create indexes on a view"; + uasserted(ErrorCodes::CommandNotSupportedOnView, errmsg); } - // Relocking temporarily releases the Database lock while holding a Global IX lock. This - // prevents the replication state from changing, but requires abandoning the current - // snapshot in case indexes change during the period of time where no database lock is held. - opCtx->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_X); + uassertStatusOK(userAllowedCreateNS(ns.db(), ns.coll())); - // Allow the strong lock acquisition above to be interrupted, but from this point forward do - // not allow locks or re-locks to be interrupted. - UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - - Database* db = DatabaseHolder::getDatabaseHolder().get(opCtx, ns.db()); - if (!db) { - db = DatabaseHolder::getDatabaseHolder().openDb(opCtx, ns.db()); - } - DatabaseShardingState::get(db).checkDbVersion(opCtx); + writeConflictRetry(opCtx, kCommandName, ns.ns(), [&] { + WriteUnitOfWork wunit(opCtx); + collection = db->createCollection(opCtx, ns.ns(), CollectionOptions()); + invariant(collection); + wunit.commit(); + }); + result.appendBool("createdCollectionAutomatically", true); + } - Collection* collection = db->getCollection(opCtx, ns); - if (collection) { - result.appendBool("createdCollectionAutomatically", false); - } else { - if (db->getViewCatalog()->lookup(opCtx, ns.ns())) { - errmsg = "Cannot create indexes on a view"; - uasserted(ErrorCodes::CommandNotSupportedOnView, errmsg); - } + // Use AutoStatsTracker to update Top. + boost::optional<AutoStatsTracker> statsTracker; + const boost::optional<int> dbProfilingLevel = boost::none; + statsTracker.emplace(opCtx, + ns, + Top::LockType::WriteLocked, + AutoStatsTracker::LogMode::kUpdateTopAndCurop, + dbProfilingLevel); - uassertStatusOK(userAllowedCreateNS(ns.db(), ns.coll())); - writeConflictRetry(opCtx, kCommandName, ns.ns(), [&] { - WriteUnitOfWork wunit(opCtx); - collection = db->createCollection(opCtx, ns.ns(), CollectionOptions()); - invariant(collection); - wunit.commit(); - }); - result.appendBool("createdCollectionAutomatically", true); - } + MultiIndexBlock indexer(opCtx, collection); + indexer.allowBackgroundBuilding(); + indexer.allowInterruption(); - // Use AutoStatsTracker to update Top. - boost::optional<AutoStatsTracker> statsTracker; - const boost::optional<int> dbProfilingLevel = boost::none; - statsTracker.emplace(opCtx, - ns, - Top::LockType::WriteLocked, - AutoStatsTracker::LogMode::kUpdateTopAndCurop, - dbProfilingLevel); + const size_t origSpecsSize = specs.size(); + specs = resolveDefaultsAndRemoveExistingIndexes(opCtx, collection, std::move(specs)); + const int numIndexesBefore = collection->getIndexCatalog()->numIndexesTotal(opCtx); + if (specs.size() == 0) { + return indexesAlreadyExist(numIndexesBefore); + } - MultiIndexBlock indexer(opCtx, collection); - indexer.allowBackgroundBuilding(); - indexer.allowInterruption(); + result.append("numIndexesBefore", numIndexesBefore); - const size_t origSpecsSize = specs.size(); - specs = resolveDefaultsAndRemoveExistingIndexes(opCtx, collection, std::move(specs)); + if (specs.size() != origSpecsSize) { + result.append("note", "index already exists"); + } - const int numIndexesBefore = collection->getIndexCatalog()->numIndexesTotal(opCtx); - if (specs.size() == 0) { - return indexesAlreadyExist(numIndexesBefore); + for (size_t i = 0; i < specs.size(); i++) { + const BSONObj& spec = specs[i]; + if (spec["unique"].trueValue()) { + checkUniqueIndexConstraints(opCtx, ns, spec["key"].Obj()); } + } - result.append("numIndexesBefore", numIndexesBefore); + std::vector<BSONObj> indexInfoObjs = + writeConflictRetry(opCtx, kCommandName, ns.ns(), [&indexer, &specs] { + return uassertStatusOK(indexer.init(specs)); + }); - if (specs.size() != origSpecsSize) { - result.append("note", "index already exists"); - } + // If we're a background index, replace exclusive db lock with an intent lock, so that + // other readers and writers can proceed during this phase. + if (indexer.getBuildInBackground()) { + opCtx->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_IX); + } - for (size_t i = 0; i < specs.size(); i++) { - const BSONObj& spec = specs[i]; - if (spec["unique"].trueValue()) { - _checkUniqueIndexConstraints(opCtx, ns, spec["key"].Obj()); + auto relockOnErrorGuard = MakeGuard([&] { + // Must have exclusive DB lock before we clean up the index build via the + // destructor of 'indexer'. + if (indexer.getBuildInBackground()) { + try { + // This function cannot throw today, but we will preemptively prepare for + // that day, to avoid data corruption due to lack of index cleanup. + opCtx->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_X); + } catch (...) { + std::terminate(); } } + }); - std::vector<BSONObj> indexInfoObjs = - writeConflictRetry(opCtx, kCommandName, ns.ns(), [&indexer, &specs] { - return uassertStatusOK(indexer.init(specs)); - }); + // Collection scan and insert into index, followed by a drain of writes received in the + // background. + { + Lock::CollectionLock colLock(opCtx->lockState(), ns.ns(), MODE_IX); + uassertStatusOK(indexer.insertAllDocumentsInCollection()); + } - // If we're a background index, replace exclusive db lock with an intent lock, so that - // other readers and writers can proceed during this phase. - if (indexer.getBuildInBackground()) { - opCtx->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_IX); - } + if (MONGO_FAIL_POINT(hangAfterIndexBuildDumpsInsertsFromBulk)) { + log() << "Hanging after dumping inserts from bulk builder"; + MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterIndexBuildDumpsInsertsFromBulk); + } - auto relockOnErrorGuard = MakeGuard([&] { - // Must have exclusive DB lock before we clean up the index build via the - // destructor of 'indexer'. - if (indexer.getBuildInBackground()) { - try { - // This function cannot throw today, but we will preemptively prepare for - // that day, to avoid data corruption due to lack of index cleanup. - opCtx->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_X); - } catch (...) { - std::terminate(); - } - } - }); + // Perform the first drain while holding an intent lock. + { + opCtx->recoveryUnit()->abandonSnapshot(); + Lock::CollectionLock colLock(opCtx->lockState(), ns.ns(), MODE_IS); - // Collection scan and insert into index, followed by a drain of writes received in the - // background. - { - Lock::CollectionLock colLock(opCtx->lockState(), ns.ns(), MODE_IX); - uassertStatusOK(indexer.insertAllDocumentsInCollection()); - } + LOG(1) << "performing first index build drain"; + uassertStatusOK(indexer.drainBackgroundWritesIfNeeded()); + } - if (MONGO_FAIL_POINT(hangAfterIndexBuildDumpsInsertsFromBulk)) { - log() << "Hanging after dumping inserts from bulk builder"; - MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterIndexBuildDumpsInsertsFromBulk); - } + if (MONGO_FAIL_POINT(hangAfterIndexBuildFirstDrain)) { + log() << "Hanging after index build first drain"; + MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterIndexBuildFirstDrain); + } - // Perform the first drain while holding an intent lock. - { - opCtx->recoveryUnit()->abandonSnapshot(); - Lock::CollectionLock colLock(opCtx->lockState(), ns.ns(), MODE_IS); + // Perform the second drain while stopping writes on the collection. + { + opCtx->recoveryUnit()->abandonSnapshot(); + Lock::CollectionLock colLock(opCtx->lockState(), ns.ns(), MODE_S); - LOG(1) << "performing first index build drain"; - uassertStatusOK(indexer.drainBackgroundWritesIfNeeded()); - } + LOG(1) << "performing second index build drain"; + uassertStatusOK(indexer.drainBackgroundWritesIfNeeded()); + } - if (MONGO_FAIL_POINT(hangAfterIndexBuildFirstDrain)) { - log() << "Hanging after index build first drain"; - MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterIndexBuildFirstDrain); - } + if (MONGO_FAIL_POINT(hangAfterIndexBuildSecondDrain)) { + log() << "Hanging after index build second drain"; + MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterIndexBuildSecondDrain); + } - // Perform the second drain while stopping writes on the collection. - { - opCtx->recoveryUnit()->abandonSnapshot(); - Lock::CollectionLock colLock(opCtx->lockState(), ns.ns(), MODE_S); + relockOnErrorGuard.Dismiss(); - LOG(1) << "performing second index build drain"; - uassertStatusOK(indexer.drainBackgroundWritesIfNeeded()); - } + // Need to return db lock back to exclusive, to complete the index build. + if (indexer.getBuildInBackground()) { + opCtx->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_X); - if (MONGO_FAIL_POINT(hangAfterIndexBuildSecondDrain)) { - log() << "Hanging after index build second drain"; - MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterIndexBuildSecondDrain); + Database* db = DatabaseHolder::getDatabaseHolder().get(opCtx, ns.db()); + if (db) { + DatabaseShardingState::get(db).checkDbVersion(opCtx); } - relockOnErrorGuard.Dismiss(); + invariant(db); + invariant(db->getCollection(opCtx, ns)); + } - // Need to return db lock back to exclusive, to complete the index build. - if (indexer.getBuildInBackground()) { - opCtx->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_X); + // Perform the third and final drain after releasing a shared lock and reacquiring an + // exclusive lock on the database. + LOG(1) << "performing final index build drain"; + uassertStatusOK(indexer.drainBackgroundWritesIfNeeded()); - Database* db = DatabaseHolder::getDatabaseHolder().get(opCtx, ns.db()); - if (db) { - DatabaseShardingState::get(db).checkDbVersion(opCtx); - } + writeConflictRetry(opCtx, kCommandName, ns.ns(), [&] { + WriteUnitOfWork wunit(opCtx); - invariant(db); - invariant(db->getCollection(opCtx, ns)); - } + uassertStatusOK(indexer.commit([opCtx, &ns, collection](const BSONObj& spec) { + opCtx->getServiceContext()->getOpObserver()->onCreateIndex( + opCtx, ns, *(collection->uuid()), spec, false); + })); - // Perform the third and final drain after releasing a shared lock and reacquiring an - // exclusive lock on the database. - LOG(1) << "performing final index build drain"; - uassertStatusOK(indexer.drainBackgroundWritesIfNeeded()); + wunit.commit(); + }); - writeConflictRetry(opCtx, kCommandName, ns.ns(), [&] { - WriteUnitOfWork wunit(opCtx); - - uassertStatusOK(indexer.commit([opCtx, &ns, collection](const BSONObj& spec) { - opCtx->getServiceContext()->getOpObserver()->onCreateIndex( - opCtx, ns, *(collection->uuid()), spec, false); - })); + result.append("numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal(opCtx)); - wunit.commit(); - }); + return true; +} - result.append("numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal(opCtx)); +/** + * { createIndexes : "bar", indexes : [ { ns : "test.bar", key : { x : 1 }, name: "x_1" } ] } + */ +class CmdCreateIndex : public ErrmsgCommandDeprecated { +public: + CmdCreateIndex() : ErrmsgCommandDeprecated(kCommandName) {} + bool supportsWriteConcern(const BSONObj& cmd) const override { return true; } -private: - static void _checkUniqueIndexConstraints(OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& newIdxKey) { - invariant(opCtx->lockState()->isCollectionLockedForMode(nss.ns(), MODE_X)); - - const auto metadata = CollectionShardingState::get(opCtx, nss)->getCurrentMetadata(); - if (!metadata->isSharded()) - return; - - const ShardKeyPattern shardKeyPattern(metadata->getKeyPattern()); - uassert(ErrorCodes::CannotCreateIndex, - str::stream() << "cannot create unique index over " << newIdxKey - << " with shard key pattern " - << shardKeyPattern.toBSON(), - shardKeyPattern.isUniqueIndexCompatible(newIdxKey)); + AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { + return AllowedOnSecondary::kNever; + } + + Status checkAuthForCommand(Client* client, + const std::string& dbname, + const BSONObj& cmdObj) const override { + ActionSet actions; + actions.addAction(ActionType::createIndex); + Privilege p(parseResourcePattern(dbname, cmdObj), actions); + if (AuthorizationSession::get(client)->isAuthorizedForPrivilege(p)) + return Status::OK(); + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + bool errmsgRun(OperationContext* opCtx, + const string& dbname, + const BSONObj& cmdObj, + string& errmsg, + BSONObjBuilder& result) override { + return runCreateIndexes(opCtx, dbname, cmdObj, errmsg, result, false /*two phase build*/); } } cmdCreateIndex; +/** + * A temporary duplicate of the createIndexes command that runs two phase index builds for gradual + * testing purposes. Otherwise, all of the necessary replication changes for the Simultaneous Index + * Builds project would have to be turned on all at once because so much testing already exists that + * would break with incremental changes. + * + * {twoPhaseCreateIndexes : "bar", indexes : [ { ns : "test.bar", key : { x : 1 }, name: "x_1" } ]} + */ +class CmdTwoPhaseCreateIndex : public ErrmsgCommandDeprecated { +public: + CmdTwoPhaseCreateIndex() : ErrmsgCommandDeprecated(kTwoPhaseCommandName) {} + + bool supportsWriteConcern(const BSONObj& cmd) const override { + return true; + } + + AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { + return AllowedOnSecondary::kNever; + } + + Status checkAuthForCommand(Client* client, + const std::string& dbname, + const BSONObj& cmdObj) const override { + ActionSet actions; + actions.addAction(ActionType::createIndex); + Privilege p(parseResourcePattern(dbname, cmdObj), actions); + if (AuthorizationSession::get(client)->isAuthorizedForPrivilege(p)) + return Status::OK(); + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + bool errmsgRun(OperationContext* opCtx, + const string& dbname, + const BSONObj& cmdObj, + string& errmsg, + BSONObjBuilder& result) override { + return runCreateIndexes(opCtx, dbname, cmdObj, errmsg, result, true /*two phase build*/); + } + +} cmdTwoPhaseCreateIndex; + } // namespace } // namespace mongo diff --git a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp index 8ced4508e2f..7d1eed8a6b8 100644 --- a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp +++ b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp @@ -609,6 +609,7 @@ TEST_F(MongodbCAPITest, RunListCommands) { "sleep", "startSession", "trimMemory", + "twoPhaseCreateIndexes", "update", "validate", }; |