summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDianna Hohensee <dianna.hohensee@10gen.com>2018-11-01 13:41:57 -0400
committerDianna Hohensee <dianna.hohensee@10gen.com>2018-11-28 13:01:04 -0500
commitac6d01aeea9a7ee989b7d1bc879d25e18c5b32a6 (patch)
treef37347e28802a36f1d4e43a2ec0af705c356e10a
parent18452b94f497e4f25493477ee45e759d449871e9 (diff)
downloadmongo-ac6d01aeea9a7ee989b7d1bc879d25e18c5b32a6.tar.gz
SERVER-37636 Establish an index builds interface through which to access and affect index builds
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/db/SConscript26
-rw-r--r--src/mongo/db/catalog/SConscript22
-rw-r--r--src/mongo/db/catalog/index_builds_manager.cpp174
-rw-r--r--src/mongo/db/catalog/index_builds_manager.h179
-rw-r--r--src/mongo/db/catalog/index_builds_manager_test.cpp101
-rw-r--r--src/mongo/db/catalog/multi_index_block.h1
-rw-r--r--src/mongo/db/catalog/multi_index_block_impl.h3
-rw-r--r--src/mongo/db/collection_index_builds_tracker.cpp109
-rw-r--r--src/mongo/db/collection_index_builds_tracker.h112
-rw-r--r--src/mongo/db/database_index_builds_tracker.cpp78
-rw-r--r--src/mongo/db/database_index_builds_tracker.h102
-rw-r--r--src/mongo/db/db.cpp6
-rw-r--r--src/mongo/db/index_builds_coordinator.cpp585
-rw-r--r--src/mongo/db/index_builds_coordinator.h438
-rw-r--r--src/mongo/db/index_builds_coordinator_test.cpp323
-rw-r--r--src/mongo/db/repl_index_build_state.h123
17 files changed, 2380 insertions, 3 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 8dff7dd8bc4..9998f435060 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -341,6 +341,7 @@ mongod = env.Program(
'db/ftdc/ftdc_mongod',
'db/fts/ftsmongod',
'db/index/index_access_method',
+ 'db/index_builds_coordinator',
'db/index/index_descriptor',
'db/index_d',
'db/initialize_snmp',
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 8a71699022b..ec14f2c6113 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -892,6 +892,32 @@ env.Library(
)
env.Library(
+ target="index_builds_coordinator",
+ source=[
+ "collection_index_builds_tracker.cpp",
+ "database_index_builds_tracker.cpp",
+ "index_builds_coordinator.cpp",
+ ],
+ LIBDEPS=[
+ "$BUILD_DIR/mongo/base",
+ '$BUILD_DIR/mongo/db/catalog_raii',
+ "$BUILD_DIR/mongo/db/catalog/index_builds_manager",
+ "$BUILD_DIR/mongo/util/concurrency/thread_pool",
+ ],
+)
+
+env.CppUnitTest(
+ target="index_builds_coordinator_test",
+ source=[
+ "index_builds_coordinator_test.cpp",
+ ],
+ LIBDEPS=[
+ "$BUILD_DIR/mongo/db/catalog/catalog_test_fixture",
+ "index_builds_coordinator",
+ ]
+)
+
+env.Library(
target="cloner",
source=[
"cloner.cpp",
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 06ef36e5bce..2e3fb91c150 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -103,6 +103,28 @@ env.Library(
);
env.Library(
+ target='index_builds_manager',
+ source=[
+ 'index_builds_manager.cpp',
+ ],
+ LIBDEPS=[
+ 'multi_index_block',
+ '$BUILD_DIR/mongo/base',
+ ],
+)
+
+env.CppUnitTest(
+ target='index_builds_manager_test',
+ source=[
+ 'index_builds_manager_test.cpp',
+ ],
+ LIBDEPS=[
+ 'catalog_test_fixture',
+ 'index_builds_manager',
+ ]
+)
+
+env.Library(
target='index_key_validate',
source=[
"index_key_validate.cpp",
diff --git a/src/mongo/db/catalog/index_builds_manager.cpp b/src/mongo/db/catalog/index_builds_manager.cpp
new file mode 100644
index 00000000000..15138cae0df
--- /dev/null
+++ b/src/mongo/db/catalog/index_builds_manager.cpp
@@ -0,0 +1,174 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/catalog/index_builds_manager.h"
+
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/multi_index_block.h"
+#include "mongo/db/catalog/multi_index_block_impl.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+using std::shared_ptr;
+
+IndexBuildsManager::~IndexBuildsManager() {
+ invariant(_builders.empty());
+}
+
+Status IndexBuildsManager::setUpIndexBuild(OperationContext* opCtx,
+ Collection* collection,
+ const NamespaceString& nss,
+ const std::vector<BSONObj>& specs,
+ const UUID& buildUUID) {
+ _registerIndexBuild(opCtx, collection, buildUUID);
+
+ // TODO: Not yet implemented.
+
+ return Status::OK();
+}
+
+StatusWith<IndexBuildRecoveryState> IndexBuildsManager::recoverIndexBuild(
+ const NamespaceString& nss, const UUID& buildUUID, std::vector<std::string> indexNames) {
+
+ // TODO: Not yet implemented.
+
+ return IndexBuildRecoveryState::Building;
+}
+
+Status IndexBuildsManager::startBuildingIndex(const UUID& buildUUID) {
+ auto multiIndexBlockPtr = _getBuilder(buildUUID);
+ // TODO: verify that the index builder is in the expected state.
+
+ // TODO: Not yet implemented.
+
+ return Status::OK();
+}
+
+Status IndexBuildsManager::finishbBuildingPhase(const UUID& buildUUID) {
+ auto multiIndexBlockPtr = _getBuilder(buildUUID);
+ // TODO: verify that the index builder is in the expected state.
+
+ // TODO: Not yet implemented.
+
+ return Status::OK();
+}
+
+Status IndexBuildsManager::checkIndexConstraintViolations(const UUID& buildUUID) {
+ auto multiIndexBlockPtr = _getBuilder(buildUUID);
+ // TODO: verify that the index builder is in the expected state.
+
+ // TODO: Not yet implemented.
+
+ return Status::OK();
+}
+
+Status IndexBuildsManager::finishConstraintPhase(const UUID& buildUUID) {
+ auto multiIndexBlockPtr = _getBuilder(buildUUID);
+ // TODO: verify that the index builder is in the expected state.
+
+ // TODO: Not yet implemented.
+
+ return Status::OK();
+}
+
+Status IndexBuildsManager::commitIndexBuild(const UUID& buildUUID) {
+ auto multiIndexBlockPtr = _getBuilder(buildUUID);
+ // TODO: verify that the index builder is in the expected state.
+
+ // TODO: Not yet implemented.
+
+ return Status::OK();
+}
+
+bool IndexBuildsManager::abortIndexBuild(const UUID& buildUUID, const std::string& reason) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto builderIt = _builders.find(buildUUID);
+ if (builderIt == _builders.end()) {
+ return false;
+ }
+ builderIt->second->abort(reason);
+ return true;
+}
+
+bool IndexBuildsManager::interruptIndexBuild(const UUID& buildUUID, const std::string& reason) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto builderIt = _builders.find(buildUUID);
+ if (builderIt == _builders.end()) {
+ return false;
+ }
+
+ // TODO: Not yet implemented.
+ return true;
+}
+
+void IndexBuildsManager::tearDownIndexBuild(const UUID& buildUUID) {
+ // TODO verify that the index builder is in a finished state before allowing its destruction.
+ _unregisterIndexBuild(buildUUID);
+}
+
+void IndexBuildsManager::verifyNoIndexBuilds_forTestOnly() {
+ invariant(_builders.empty());
+}
+
+void IndexBuildsManager::_registerIndexBuild(OperationContext* opCtx,
+ Collection* collection,
+ UUID buildUUID) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ std::shared_ptr<MultiIndexBlockImpl> mib =
+ std::make_shared<MultiIndexBlockImpl>(opCtx, collection);
+ invariant(_builders.insert(std::make_pair(buildUUID, mib)).second);
+}
+
+void IndexBuildsManager::_unregisterIndexBuild(const UUID& buildUUID) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto builderIt = _builders.find(buildUUID);
+ invariant(builderIt != _builders.end());
+ _builders.erase(builderIt);
+}
+
+std::shared_ptr<MultiIndexBlock> IndexBuildsManager::_getBuilder(const UUID& buildUUID) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ auto builderIt = _builders.find(buildUUID);
+ invariant(builderIt != _builders.end());
+ return builderIt->second;
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/index_builds_manager.h b/src/mongo/db/catalog/index_builds_manager.h
new file mode 100644
index 00000000000..fed73c8fe85
--- /dev/null
+++ b/src/mongo/db/catalog/index_builds_manager.h
@@ -0,0 +1,179 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/stdx/mutex.h"
+
+namespace mongo {
+
+class Collection;
+class MultiIndexBlock;
+class OperationContext;
+class ServiceContext;
+
+enum IndexBuildRecoveryState { Building, Verifying, Committing };
+
+/**
+ * This is the interface through which to act on index builders. Index builder life times are
+ * managed here, and all actions taken on index builders pass through this interface. Index builder
+ * state is set up and then cleaned up by this class.
+ */
+class IndexBuildsManager {
+ MONGO_DISALLOW_COPYING(IndexBuildsManager);
+
+public:
+ IndexBuildsManager() = default;
+ ~IndexBuildsManager();
+
+ /**
+ * Sets up the index build state and registers it in the manager.
+ *
+ * TODO: Not yet implemented. Only instantiates and registers a builder in the manager. Does not
+ * set up index build state.
+ */
+ Status setUpIndexBuild(OperationContext* opCtx,
+ Collection* collection,
+ const NamespaceString& nss,
+ const std::vector<BSONObj>& specs,
+ const UUID& buildUUID);
+
+ /**
+ * Recovers the index build from its persisted state and sets it up to run again.
+ *
+ * Returns an enum reflecting the point up to which the build was recovered, so the caller knows
+ * where to recommence.
+ *
+ * TODO: Not yet implemented.
+ */
+ StatusWith<IndexBuildRecoveryState> recoverIndexBuild(const NamespaceString& nss,
+ const UUID& buildUUID,
+ std::vector<std::string> indexNames);
+
+ /**
+ * Runs the scanning/insertion phase of the index build..
+ *
+ * TODO: Not yet implemented.
+ */
+ Status startBuildingIndex(const UUID& buildUUID);
+
+ /**
+ * Persists information in the index catalog entry to reflect the successful completion of the
+ * scanning/insertion phase.
+ *
+ * TODO: Not yet implemented.
+ */
+ Status finishbBuildingPhase(const UUID& buildUUID);
+
+ /**
+ * Runs the index constraint violation checking phase of the index build..
+ *
+ * TODO: Not yet implemented.
+ */
+ Status checkIndexConstraintViolations(const UUID& buildUUID);
+
+ /**
+ * Persists information in the index catalog entry to reflect the successful completion of the
+ * index constraint violation checking phase..
+ *
+ * TODO: Not yet implemented.
+ */
+ Status finishConstraintPhase(const UUID& buildUUID);
+
+ /**
+ * Persists information in the index catalog entry that the index is ready for use, as well as
+ * updating the in-memory index catalog entry for this index to ready.
+ *
+ * TODO: Not yet implemented.
+ */
+ Status commitIndexBuild(const UUID& buildUUID);
+
+ /**
+ * Signals the index build to be aborted and returns without waiting for completion.
+ *
+ * Returns true if a build existed to be signaled, as opposed to having already finished and
+ * been cleared away, or not having yet started..
+ *
+ * TODO: Not yet fully implemented. The MultiIndexBlock::abort function that is called is
+ * not yet implemented.
+ */
+ bool abortIndexBuild(const UUID& buildUUID, const std::string& reason);
+
+ /**
+ * Signals the index build to be interrupted and returns without waiting for it to stop. Does
+ * nothing if the index build has already been cleared away.
+ *
+ * Returns true if a build existed to be signaled, as opposed to having already finished and
+ * been cleared away, or not having yet started..
+ *
+ * TODO: Not yet implemented.
+ */
+ bool interruptIndexBuild(const UUID& buildUUID, const std::string& reason);
+
+ /**
+ * Cleans up the index build state and unregisters it from the manager.
+ */
+ void tearDownIndexBuild(const UUID& buildUUID);
+
+ /**
+ * Checks via invariant that the manager has no index builds presently.
+ */
+ void verifyNoIndexBuilds_forTestOnly();
+
+private:
+ /**
+ * Creates and registers a new builder in the _builders map, mapped by the provided buildUUID.
+ */
+ void _registerIndexBuild(OperationContext* opCtx, Collection* collection, UUID buildUUID);
+
+ /**
+ * Unregisters the builder associcated with the given buildUUID from the _builders map.
+ */
+ void _unregisterIndexBuild(const UUID& buildUUID);
+
+ /**
+ * Returns a shared pointer to the builder. Invariants if the builder does not exist.
+ */
+ std::shared_ptr<MultiIndexBlock> _getBuilder(const UUID& buildUUID);
+
+ // Protects the map data structures below.
+ mutable stdx::mutex _mutex;
+
+ // Map of index builders by build UUID. Allows access to the builders so that actions can be
+ // taken on and information passed to and from index builds.
+ std::map<UUID, std::shared_ptr<MultiIndexBlock>> _builders;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/index_builds_manager_test.cpp b/src/mongo/db/catalog/index_builds_manager_test.cpp
new file mode 100644
index 00000000000..90f78e013c7
--- /dev/null
+++ b/src/mongo/db/catalog/index_builds_manager_test.cpp
@@ -0,0 +1,101 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/catalog/index_builds_manager.h"
+
+#include "mongo/db/catalog/catalog_test_fixture.h"
+#include "mongo/db/catalog/multi_index_block.h"
+#include "mongo/db/catalog/multi_index_block_impl.h"
+#include "mongo/db/catalog_raii.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+using unittest::log;
+
+namespace {
+
+class IndexBuildsManagerTest : public CatalogTestFixture {
+private:
+ void setUp() override;
+ void tearDown() override;
+
+public:
+ void createCollection(const NamespaceString& nss);
+
+ const UUID _buildUUID = UUID::gen();
+ const NamespaceString _nss = NamespaceString("test.foo");
+ IndexBuildsManager _indexBuildsManager;
+};
+
+void IndexBuildsManagerTest::setUp() {
+ CatalogTestFixture::setUp();
+ createCollection(_nss);
+}
+
+void IndexBuildsManagerTest::tearDown() {
+ _indexBuildsManager.verifyNoIndexBuilds_forTestOnly();
+ // All databases are dropped during tear down.
+ CatalogTestFixture::tearDown();
+}
+
+void IndexBuildsManagerTest::createCollection(const NamespaceString& nss) {
+ ASSERT_OK(storageInterface()->createCollection(operationContext(), nss, CollectionOptions()));
+}
+
+std::vector<BSONObj> makeSpecs(const NamespaceString& nss, std::vector<std::string> keys) {
+ ASSERT(keys.size());
+ std::vector<BSONObj> indexSpecs;
+ for (auto keyName : keys) {
+ indexSpecs.push_back(BSON("ns" << nss.toString() << "v" << 2 << "key" << BSON(keyName << 1)
+ << "name"
+ << (keyName + "_1")));
+ }
+ return indexSpecs;
+}
+
+TEST_F(IndexBuildsManagerTest, IndexBuildsManagerSetUpAndTearDown) {
+ AutoGetCollection autoColl(operationContext(), _nss, MODE_X);
+
+ ASSERT_OK(_indexBuildsManager.setUpIndexBuild(operationContext(),
+ autoColl.getCollection(),
+ _nss,
+ makeSpecs(_nss, {"a", "b"}),
+ _buildUUID));
+
+ _indexBuildsManager.tearDownIndexBuild(_buildUUID);
+}
+
+} // namespace
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/multi_index_block.h b/src/mongo/db/catalog/multi_index_block.h
index 70d2d4f0ce9..b2468a15c9b 100644
--- a/src/mongo/db/catalog/multi_index_block.h
+++ b/src/mongo/db/catalog/multi_index_block.h
@@ -214,4 +214,5 @@ public:
virtual bool getBuildInBackground() const = 0;
};
+
} // namespace mongo
diff --git a/src/mongo/db/catalog/multi_index_block_impl.h b/src/mongo/db/catalog/multi_index_block_impl.h
index a1c7f726cba..ab008f9692c 100644
--- a/src/mongo/db/catalog/multi_index_block_impl.h
+++ b/src/mongo/db/catalog/multi_index_block_impl.h
@@ -56,9 +56,6 @@ class MultiIndexBlockImpl : public MultiIndexBlock {
MONGO_DISALLOW_COPYING(MultiIndexBlockImpl);
public:
- /**
- * Neither pointer is owned.
- */
MultiIndexBlockImpl(OperationContext* opCtx, Collection* collection);
~MultiIndexBlockImpl() override;
diff --git a/src/mongo/db/collection_index_builds_tracker.cpp b/src/mongo/db/collection_index_builds_tracker.cpp
new file mode 100644
index 00000000000..58b3fef7b1b
--- /dev/null
+++ b/src/mongo/db/collection_index_builds_tracker.cpp
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/collection_index_builds_tracker.h"
+
+#include "mongo/db/catalog/index_builds_manager.h"
+
+namespace mongo {
+
+CollectionIndexBuildsTracker::~CollectionIndexBuildsTracker() {
+ invariant(_buildStateByBuildUUID.empty());
+ invariant(_buildStateByIndexName.empty());
+}
+
+void CollectionIndexBuildsTracker::addIndexBuild(
+ WithLock, std::shared_ptr<ReplIndexBuildState> replIndexBuildState) {
+ // Ensure that a new entry is added.
+ invariant(
+ _buildStateByBuildUUID.emplace(replIndexBuildState->buildUUID, replIndexBuildState).second);
+
+ invariant(replIndexBuildState->indexNames.size());
+ for (auto& indexName : replIndexBuildState->indexNames) {
+ // Ensure that a new entry is added.
+ invariant(_buildStateByIndexName.emplace(indexName, replIndexBuildState).second);
+ }
+}
+
+void CollectionIndexBuildsTracker::removeIndexBuild(
+ WithLock, std::shared_ptr<ReplIndexBuildState> replIndexBuildState) {
+ invariant(_buildStateByBuildUUID.find(replIndexBuildState->buildUUID) !=
+ _buildStateByBuildUUID.end());
+ _buildStateByBuildUUID.erase(replIndexBuildState->buildUUID);
+
+ for (const auto& indexName : replIndexBuildState->indexNames) {
+ invariant(_buildStateByIndexName.find(indexName) != _buildStateByIndexName.end());
+ _buildStateByIndexName.erase(indexName);
+ }
+
+ if (_buildStateByBuildUUID.empty()) {
+ _noIndexBuildsRemainCondVar.notify_all();
+ }
+}
+
+std::shared_ptr<ReplIndexBuildState> CollectionIndexBuildsTracker::getIndexBuildState(
+ WithLock, StringData indexName) const {
+ auto it = _buildStateByIndexName.find(indexName.toString());
+ invariant(it != _buildStateByIndexName.end());
+ return it->second;
+}
+
+bool CollectionIndexBuildsTracker::hasIndexBuildState(WithLock, StringData indexName) const {
+ auto it = _buildStateByIndexName.find(indexName.toString());
+ if (it == _buildStateByIndexName.end()) {
+ return false;
+ }
+ return true;
+}
+
+void CollectionIndexBuildsTracker::runOperationOnAllBuilds(
+ WithLock lk,
+ IndexBuildsManager* indexBuildsManager,
+ std::function<void(WithLock,
+ IndexBuildsManager* indexBuildsManager,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState,
+ const std::string& reason)> func,
+ const std::string& reason) noexcept {
+ for (auto it = _buildStateByBuildUUID.begin(); it != _buildStateByBuildUUID.end(); ++it) {
+ func(lk, indexBuildsManager, it->second, reason);
+ }
+}
+
+int CollectionIndexBuildsTracker::getNumberOfIndexBuilds(WithLock) const {
+ return _buildStateByBuildUUID.size();
+}
+
+void CollectionIndexBuildsTracker::waitUntilNoIndexBuildsRemain(
+ stdx::unique_lock<stdx::mutex>& lk) {
+ _noIndexBuildsRemainCondVar.wait(lk, [&] { return _buildStateByBuildUUID.empty(); });
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/collection_index_builds_tracker.h b/src/mongo/db/collection_index_builds_tracker.h
new file mode 100644
index 00000000000..10b4f833f7c
--- /dev/null
+++ b/src/mongo/db/collection_index_builds_tracker.h
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/db/repl_index_build_state.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/util/concurrency/with_lock.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+class IndexBuildsManager;
+
+/**
+ * Tracks index builds for a particular collection. Provides access to index build information by
+ * collection and index name. Can be used to act on all index builds for a collection, wait upon the
+ * completion of all index builds, and provide collection level index build information.
+ *
+ * The owner of a CollectionIndexBuildsTracker instance must instantiate a mutex to use along with
+ * the data structure to ensure it remains consistent across single or multiple function accesses.
+ *
+ * This is intended to only be used by the IndexBuildsCoordinator class.
+ */
+class CollectionIndexBuildsTracker {
+ MONGO_DISALLOW_COPYING(CollectionIndexBuildsTracker);
+
+public:
+ CollectionIndexBuildsTracker() = default;
+ ~CollectionIndexBuildsTracker();
+
+ /**
+ * Starts tracking the specified index build on the collection.
+ */
+ void addIndexBuild(WithLock, std::shared_ptr<ReplIndexBuildState> buildInfo);
+
+ /**
+ * Stops tracking the specified index build on the collection.
+ */
+ void removeIndexBuild(WithLock, std::shared_ptr<ReplIndexBuildState> buildInfo);
+
+ std::shared_ptr<ReplIndexBuildState> getIndexBuildState(WithLock, StringData indexName) const;
+
+ bool hasIndexBuildState(WithLock, StringData indexName) const;
+
+ /**
+ * Runs the provided function operation on all this collection's index builds
+ *
+ * 'func' returns void and must not throw. It should not have failure state to return.
+ */
+ void runOperationOnAllBuilds(
+ WithLock,
+ IndexBuildsManager* indexBuildsManager,
+ std::function<void(WithLock,
+ IndexBuildsManager* indexBuildsManager,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState,
+ const std::string& reason)> func,
+ const std::string& reason) noexcept;
+
+ /**
+ * Note that this is the number of index builders, and that each index builder can be building
+ * several indexes.
+ */
+ int getNumberOfIndexBuilds(WithLock) const;
+
+ /**
+ * Returns when no index builds remain on this collection.
+ */
+ void waitUntilNoIndexBuildsRemain(stdx::unique_lock<stdx::mutex>& lk);
+
+private:
+ // Maps of index build states on the collection, by build UUID and index name.
+ stdx::unordered_map<UUID, std::shared_ptr<ReplIndexBuildState>, UUID::Hash>
+ _buildStateByBuildUUID;
+ stdx::unordered_map<std::string, std::shared_ptr<ReplIndexBuildState>> _buildStateByIndexName;
+
+ // Condition variable that is signaled when there are no active index builds remaining on the
+ // collection.
+ stdx::condition_variable _noIndexBuildsRemainCondVar;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/database_index_builds_tracker.cpp b/src/mongo/db/database_index_builds_tracker.cpp
new file mode 100644
index 00000000000..6e31c8ffeff
--- /dev/null
+++ b/src/mongo/db/database_index_builds_tracker.cpp
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/database_index_builds_tracker.h"
+
+#include "mongo/db/catalog/index_builds_manager.h"
+
+namespace mongo {
+
+DatabaseIndexBuildsTracker::~DatabaseIndexBuildsTracker() {
+ invariant(_allIndexBuilds.empty());
+}
+
+void DatabaseIndexBuildsTracker::addIndexBuild(
+ WithLock, std::shared_ptr<ReplIndexBuildState> replIndexBuildState) {
+ invariant(_allIndexBuilds.insert({replIndexBuildState->buildUUID, replIndexBuildState}).second);
+}
+
+void DatabaseIndexBuildsTracker::removeIndexBuild(WithLock, const UUID& buildUUID) {
+ auto it = _allIndexBuilds.find(buildUUID);
+ invariant(it != _allIndexBuilds.end());
+ _allIndexBuilds.erase(it);
+
+ if (_allIndexBuilds.empty()) {
+ _noIndexBuildsRemainCondVar.notify_all();
+ }
+}
+
+void DatabaseIndexBuildsTracker::runOperationOnAllBuilds(
+ WithLock lk,
+ IndexBuildsManager* indexBuildsManager,
+ std::function<void(WithLock,
+ IndexBuildsManager* indexBuildsManager,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState,
+ const std::string& reason)> func,
+ const std::string& reason) {
+ for (auto it = _allIndexBuilds.begin(); it != _allIndexBuilds.end(); ++it) {
+ func(lk, indexBuildsManager, it->second, reason);
+ }
+}
+
+int DatabaseIndexBuildsTracker::getNumberOfIndexBuilds(WithLock) const {
+ return _allIndexBuilds.size();
+}
+
+void DatabaseIndexBuildsTracker::waitUntilNoIndexBuildsRemain(stdx::unique_lock<stdx::mutex>& lk) {
+ _noIndexBuildsRemainCondVar.wait(lk, [&] { return _allIndexBuilds.empty(); });
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/database_index_builds_tracker.h b/src/mongo/db/database_index_builds_tracker.h
new file mode 100644
index 00000000000..000a58d9bde
--- /dev/null
+++ b/src/mongo/db/database_index_builds_tracker.h
@@ -0,0 +1,102 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/db/repl_index_build_state.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/util/concurrency/with_lock.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+class IndexBuildsManager;
+
+/**
+ * Tracks index builds for a particular database. Can be used to act on all index builds in the
+ * database, wait upon the completion of all index build for the database, and provide database
+ * level index build information.
+ *
+ * The owner of a DatabaseIndexBuildsTracker instance must instantiate a mutex to use along with the
+ * data structure to ensure it remains consistent across single or multiple function accesses.
+ *
+ * This is intended to only be used by the IndexBuildsCoordinator class.
+ */
+class DatabaseIndexBuildsTracker {
+public:
+ DatabaseIndexBuildsTracker() = default;
+ ~DatabaseIndexBuildsTracker();
+
+ /**
+ * Starts tracking the specified index build on the database.
+ */
+ void addIndexBuild(WithLock, std::shared_ptr<ReplIndexBuildState> buildInfo);
+
+ /**
+ * Stops tracking the specified index build on the database.
+ */
+ void removeIndexBuild(WithLock, const UUID& buildUUID);
+
+ /**
+ * Runs the provided function operation on all this database's index builds.
+ */
+ void runOperationOnAllBuilds(
+ WithLock,
+ IndexBuildsManager* indexBuildsManager,
+ std::function<void(WithLock,
+ IndexBuildsManager* indexBuildsManager,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState,
+ const std::string& reason)> func,
+ const std::string& reason);
+
+ /**
+ * Note that this is the number of index builders, and that each index builder can be building
+ * several indexes.
+ */
+ int getNumberOfIndexBuilds(WithLock) const;
+
+ /**
+ * Returns when no index builds remain on this database.
+ */
+ void waitUntilNoIndexBuildsRemain(stdx::unique_lock<stdx::mutex>& lk);
+
+private:
+ // Map of index build states on the database, by build UUID.
+ stdx::unordered_map<UUID, std::shared_ptr<ReplIndexBuildState>, UUID::Hash> _allIndexBuilds;
+
+ // Condition variable that is signaled when there are no active index builds remaining on the
+ // database.
+ stdx::condition_variable _noIndexBuildsRemainCondVar;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index edaa8416ab8..69471e9e65b 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -72,6 +72,7 @@
#include "mongo/db/free_mon/free_mon_mongod.h"
#include "mongo/db/ftdc/ftdc_mongod.h"
#include "mongo/db/global_settings.h"
+#include "mongo/db/index_builds_coordinator.h"
#include "mongo/db/index_names.h"
#include "mongo/db/index_rebuilder.h"
#include "mongo/db/initialize_server_global_state.h"
@@ -908,6 +909,11 @@ void shutdownTask() {
killSessionsLocalShutdownAllTransactions(opCtx);
}
+ // Interrupts all index builds, leaving the state intact to be recovered when the server
+ // restarts. This should be done after replication oplog application finishes, so foreground
+ // index builds begun by replication on secondaries do not invariant.
+ IndexBuildsCoordinator::get(serviceContext)->shutdown();
+
serviceContext->setKillAllOperations();
ReplicaSetMonitor::shutdown();
diff --git a/src/mongo/db/index_builds_coordinator.cpp b/src/mongo/db/index_builds_coordinator.cpp
new file mode 100644
index 00000000000..624efe9c34f
--- /dev/null
+++ b/src/mongo/db/index_builds_coordinator.cpp
@@ -0,0 +1,585 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/index_builds_coordinator.h"
+
+#include "mongo/db/catalog/uuid_catalog.h"
+#include "mongo/db/catalog_raii.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+namespace {
+
+/**
+ * Constructs the options for the loader thread pool.
+ */
+ThreadPool::Options makeDefaultThreadPoolOptions() {
+ ThreadPool::Options options;
+ options.poolName = "IndexBuildsCoordinator";
+ options.minThreads = 0;
+ options.maxThreads = 10;
+
+ // Ensure all threads have a client.
+ options.onCreateThread = [](const std::string& threadName) {
+ Client::initThread(threadName.c_str());
+ };
+
+ return options;
+}
+
+/**
+ * Returns the collection UUID for the given 'nss', or a NamespaceNotFound error.
+ *
+ * Momentarily takes the collection IS lock for 'nss' to access the collection UUID.
+ */
+StatusWith<UUID> getCollectionUUID(OperationContext* opCtx, const NamespaceString& nss) {
+ try {
+ AutoGetCollection autoColl(opCtx, nss, MODE_IS);
+ return autoColl.getCollection()->uuid().get();
+ } catch (const DBException& ex) {
+ invariant(ex.toStatus().code() == ErrorCodes::NamespaceNotFound);
+ return ex.toStatus();
+ }
+}
+
+/**
+ * Aborts the index build identified by the provided 'replIndexBuildState'.
+ *
+ * Sets a signal on the coordinator's repl index build state if the builder does not yet exist in
+ * the manager.
+ */
+void abortIndexBuild(WithLock lk,
+ IndexBuildsManager* indexBuildsManager,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState,
+ const std::string& reason) {
+ bool res = indexBuildsManager->abortIndexBuild(replIndexBuildState->buildUUID, reason);
+ if (res) {
+ return;
+ }
+ // The index builder was not found in the manager, so it only exists in the coordinator. In this
+ // case, set the abort signal on the coordinator index build state.
+ replIndexBuildState->aborted = true;
+ replIndexBuildState->abortReason = reason;
+}
+
+} // namespace
+
+const auto getIndexBuildsCoord = ServiceContext::declareDecoration<IndexBuildsCoordinator>();
+
+IndexBuildsCoordinator* IndexBuildsCoordinator::get(ServiceContext* serviceContext) {
+ return &getIndexBuildsCoord(serviceContext);
+}
+
+IndexBuildsCoordinator* IndexBuildsCoordinator::get(OperationContext* operationContext) {
+ return get(operationContext->getServiceContext());
+}
+
+IndexBuildsCoordinator::IndexBuildsCoordinator() : _threadPool(makeDefaultThreadPoolOptions()) {
+ _threadPool.startup();
+}
+
+IndexBuildsCoordinator::~IndexBuildsCoordinator() {
+ invariant(_databaseIndexBuilds.empty());
+ invariant(_disallowedDbs.empty());
+ invariant(_disallowedCollections.empty());
+ invariant(_collectionIndexBuilds.empty());
+}
+
+void IndexBuildsCoordinator::shutdown() {
+ // Stop new scheduling.
+ _threadPool.shutdown();
+
+ // Signal active builds to stop and wait for them to stop.
+ interruptAllIndexBuilds("Index build interrupted due to shutdown.");
+
+ // Wait for active threads to finish.
+ _threadPool.join();
+}
+
+StatusWith<Future<void>> IndexBuildsCoordinator::buildIndex(OperationContext* opCtx,
+ const NamespaceString& nss,
+ const std::vector<BSONObj>& specs,
+ const UUID& buildUUID) {
+ std::vector<std::string> indexNames;
+ for (auto& spec : specs) {
+ std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName);
+ if (name.empty()) {
+ return Status(
+ ErrorCodes::CannotCreateIndex,
+ str::stream() << "Cannot create an index for a spec '" << spec
+ << "' without a non-empty string value for the 'name' field");
+ }
+ indexNames.push_back(name);
+ }
+
+ UUID collectionUUID = [&] {
+ AutoGetCollection autoColl(opCtx, nss, MODE_IS);
+ return autoColl.getCollection()->uuid().get();
+ }();
+
+ auto pf = makePromiseFuture<void>();
+
+ auto replIndexBuildState = std::make_shared<ReplIndexBuildState>(
+ buildUUID, collectionUUID, indexNames, specs, std::move(pf.promise));
+
+ Status status = _registerIndexBuild(opCtx, replIndexBuildState);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = _threadPool.schedule([ this, buildUUID ]() noexcept {
+ auto opCtx = Client::getCurrent()->makeOperationContext();
+
+ // Sets up and runs the index build. Sets result and cleans up index build.
+ _runIndexBuild(opCtx.get(), buildUUID);
+ });
+
+ // Clean up the index build if we failed to schedule it.
+ if (!status.isOK()) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ // Unregister the index build before setting the promises, so callers do not see the build
+ // again.
+ _unregisterIndexBuild(lk, opCtx, replIndexBuildState);
+
+ // Set the promises in case another thread already joined the index build.
+ for (auto& promise : replIndexBuildState->promises) {
+ promise.setError(status);
+ }
+
+ return status;
+ }
+
+ return std::move(pf.future);
+}
+
+
+Future<void> IndexBuildsCoordinator::joinIndexBuilds(const NamespaceString& nss,
+ const std::vector<BSONObj>& indexSpecs) {
+ // TODO: implement. This code is just to make it compile.
+ auto pf = makePromiseFuture<void>();
+ auto promise = std::move(pf.promise);
+ return std::move(pf.future);
+}
+
+void IndexBuildsCoordinator::interruptAllIndexBuilds(const std::string& reason) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ // Signal all the index builds to stop.
+ for (auto& buildStateIt : _allIndexBuilds) {
+ _indexBuildsManager.interruptIndexBuild(buildStateIt.second->buildUUID, reason);
+ }
+
+ // Wait for all the index builds to stop.
+ for (auto& dbIt : _databaseIndexBuilds) {
+ dbIt.second->waitUntilNoIndexBuildsRemain(lk);
+ }
+}
+
+void IndexBuildsCoordinator::abortCollectionIndexBuilds(const UUID& collectionUUID,
+ const std::string& reason) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ // Ensure the caller correctly stopped any new index builds on the collection.
+ auto it = _disallowedCollections.find(collectionUUID);
+ invariant(it != _disallowedCollections.end());
+
+ auto collIndexBuildsIt = _collectionIndexBuilds.find(collectionUUID);
+ if (collIndexBuildsIt == _collectionIndexBuilds.end()) {
+ return;
+ }
+
+ collIndexBuildsIt->second->runOperationOnAllBuilds(
+ lk, &_indexBuildsManager, abortIndexBuild, reason);
+ collIndexBuildsIt->second->waitUntilNoIndexBuildsRemain(lk);
+}
+
+void IndexBuildsCoordinator::abortDatabaseIndexBuilds(StringData db, const std::string& reason) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ // Ensure the caller correctly stopped any new index builds on the database.
+ auto it = _disallowedDbs.find(db);
+ invariant(it != _disallowedDbs.end());
+
+ auto dbIndexBuilds = _databaseIndexBuilds[db];
+ if (!dbIndexBuilds) {
+ return;
+ }
+
+ dbIndexBuilds->runOperationOnAllBuilds(lk, &_indexBuildsManager, abortIndexBuild, reason);
+ dbIndexBuilds->waitUntilNoIndexBuildsRemain(lk);
+}
+
+Future<void> IndexBuildsCoordinator::abortIndexBuildByName(
+ const NamespaceString& nss,
+ const std::vector<std::string>& indexNames,
+ const std::string& reason) {
+ // TODO: not yet implemented. Some code to make it compile.
+ auto pf = makePromiseFuture<void>();
+ auto promise = std::move(pf.promise);
+ return std::move(pf.future);
+}
+
+Future<void> IndexBuildsCoordinator::abortIndexBuildByUUID(const UUID& buildUUID,
+ const std::string& reason) {
+ // TODO: not yet implemented. Some code to make it compile.
+ auto pf = makePromiseFuture<void>();
+ auto promise = std::move(pf.promise);
+ return std::move(pf.future);
+}
+
+void IndexBuildsCoordinator::signalChangeToPrimaryMode() {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ _replMode = ReplState::Primary;
+}
+
+void IndexBuildsCoordinator::signalChangeToSecondaryMode() {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ _replMode = ReplState::Secondary;
+}
+
+void IndexBuildsCoordinator::signalChangeToInitialSyncMode() {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ _replMode = ReplState::InitialSync;
+}
+
+void IndexBuildsCoordinator::voteCommitIndexBuild(const UUID& buildUUID,
+ const HostAndPort& hostAndPort) {}
+
+Status IndexBuildsCoordinator::setCommitQuorum(const NamespaceString& nss,
+ const std::vector<std::string>& indexNames,
+ const BSONObj& newCommitQuorum) {
+ // TODO: not yet implemented.
+ return Status::OK();
+}
+
+void IndexBuildsCoordinator::recoverIndexBuilds() {}
+
+int IndexBuildsCoordinator::numInProgForDb(StringData db) const {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto dbIndexBuildsIt = _databaseIndexBuilds.find(db);
+ if (dbIndexBuildsIt == _databaseIndexBuilds.end()) {
+ return 0;
+ }
+ return dbIndexBuildsIt->second->getNumberOfIndexBuilds(lk);
+}
+
+void IndexBuildsCoordinator::dump(std::ostream& ss) const {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ if (_collectionIndexBuilds.size()) {
+ ss << "\n<b>Background Jobs in Progress</b>\n";
+ // TODO: We should improve this to print index names per collection, not just collection
+ // names.
+ for (auto it = _collectionIndexBuilds.begin(); it != _collectionIndexBuilds.end(); ++it) {
+ ss << " " << it->first << '\n';
+ }
+ }
+
+ for (auto it = _databaseIndexBuilds.begin(); it != _databaseIndexBuilds.end(); ++it) {
+ ss << "database " << it->first << ": " << it->second->getNumberOfIndexBuilds(lk) << '\n';
+ }
+}
+
+bool IndexBuildsCoordinator::inProgForCollection(const UUID& collectionUUID) const {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ return _collectionIndexBuilds.find(collectionUUID) != _collectionIndexBuilds.end();
+}
+
+bool IndexBuildsCoordinator::inProgForDb(StringData db) const {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ return _databaseIndexBuilds.find(db) != _databaseIndexBuilds.end();
+}
+
+void IndexBuildsCoordinator::assertNoIndexBuildInProgForCollection(
+ const UUID& collectionUUID) const {
+ uassert(ErrorCodes::BackgroundOperationInProgressForNamespace,
+ mongoutils::str::stream()
+ << "cannot perform operation: an index build is currently running",
+ !inProgForCollection(collectionUUID));
+}
+
+void IndexBuildsCoordinator::assertNoBgOpInProgForDb(StringData db) const {
+ uassert(ErrorCodes::BackgroundOperationInProgressForDatabase,
+ mongoutils::str::stream()
+ << "cannot perform operation: an index build is currently running for "
+ "database "
+ << db,
+ !inProgForDb(db));
+}
+
+void IndexBuildsCoordinator::awaitNoBgOpInProgForNs(OperationContext* opCtx, StringData ns) const {
+ auto statusWithCollectionUUID = getCollectionUUID(opCtx, NamespaceString(ns));
+ if (!statusWithCollectionUUID.isOK()) {
+ // The collection does not exist, so there are no index builds on it.
+ invariant(statusWithCollectionUUID.getStatus().code() == ErrorCodes::NamespaceNotFound);
+ return;
+ }
+
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto collIndexBuildsIt = _collectionIndexBuilds.find(statusWithCollectionUUID.getValue());
+ if (collIndexBuildsIt == _collectionIndexBuilds.end()) {
+ return;
+ }
+
+ collIndexBuildsIt->second->waitUntilNoIndexBuildsRemain(lk);
+}
+
+void IndexBuildsCoordinator::awaitNoBgOpInProgForDb(StringData db) const {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto dbIndexBuildsIt = _databaseIndexBuilds.find(db);
+ if (dbIndexBuildsIt != _databaseIndexBuilds.end()) {
+ return;
+ }
+
+ dbIndexBuildsIt->second->waitUntilNoIndexBuildsRemain(lk);
+}
+
+void IndexBuildsCoordinator::sleepIndexBuilds_forTestOnly(bool sleep) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ _sleepForTest = sleep;
+}
+
+void IndexBuildsCoordinator::verifyNoIndexBuilds_forTestOnly() {
+ invariant(_databaseIndexBuilds.empty());
+ invariant(_disallowedDbs.empty());
+ invariant(_disallowedCollections.empty());
+ invariant(_collectionIndexBuilds.empty());
+}
+
+Status IndexBuildsCoordinator::_registerIndexBuild(
+ OperationContext* opCtx, std::shared_ptr<ReplIndexBuildState> replIndexBuildState) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ NamespaceString nss =
+ UUIDCatalog::get(opCtx).lookupNSSByUUID(replIndexBuildState->collectionUUID);
+ if (!nss.isValid()) {
+ return Status(ErrorCodes::NamespaceNotFound,
+ "The collection has been dropped since the index build began.");
+ }
+
+ auto itns = _disallowedCollections.find(replIndexBuildState->collectionUUID);
+ auto itdb = _disallowedDbs.find(nss.db());
+ if (itns != _disallowedCollections.end() || itdb != _disallowedDbs.end()) {
+ return Status(ErrorCodes::CannotCreateIndex,
+ str::stream() << "Collection '" << nss.toString()
+ << "' is in the process of being dropped. New index builds are "
+ "not currently allowed.");
+ }
+
+ // Check whether any indexes are already being built with the same index name(s). (Duplicate
+ // specs will be discovered by the index builder.)
+ auto collIndexBuildsIt = _collectionIndexBuilds.find(replIndexBuildState->collectionUUID);
+ if (collIndexBuildsIt != _collectionIndexBuilds.end()) {
+ for (const auto& name : replIndexBuildState->indexNames) {
+ if (collIndexBuildsIt->second->hasIndexBuildState(lk, name)) {
+ return Status(ErrorCodes::IndexKeySpecsConflict,
+ str::stream() << "There's already an index with name '" << name
+ << "' being built on the collection");
+ }
+ }
+ }
+
+ // Register the index build.
+
+ auto dbIndexBuilds = _databaseIndexBuilds[nss.db()];
+ if (!dbIndexBuilds) {
+ _databaseIndexBuilds[nss.db()] = std::make_shared<DatabaseIndexBuildsTracker>();
+ dbIndexBuilds = _databaseIndexBuilds[nss.db()];
+ }
+ dbIndexBuilds->addIndexBuild(lk, replIndexBuildState);
+
+ auto collIndexBuildsItAndRes = _collectionIndexBuilds.insert(
+ {replIndexBuildState->collectionUUID, std::make_shared<CollectionIndexBuildsTracker>()});
+ collIndexBuildsItAndRes.first->second->addIndexBuild(lk, replIndexBuildState);
+
+ invariant(_allIndexBuilds.emplace(replIndexBuildState->buildUUID, replIndexBuildState).second);
+
+ return Status::OK();
+}
+
+void IndexBuildsCoordinator::_unregisterIndexBuild(
+ WithLock lk,
+ OperationContext* opCtx,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState) {
+ NamespaceString nss =
+ UUIDCatalog::get(opCtx).lookupNSSByUUID(replIndexBuildState->collectionUUID);
+ invariant(!nss.isEmpty());
+
+ auto dbIndexBuilds = _databaseIndexBuilds[nss.db()];
+ invariant(dbIndexBuilds);
+ dbIndexBuilds->removeIndexBuild(lk, replIndexBuildState->buildUUID);
+ if (dbIndexBuilds->getNumberOfIndexBuilds(lk) == 0) {
+ _databaseIndexBuilds.erase(nss.db());
+ }
+
+ auto collIndexBuildsIt = _collectionIndexBuilds.find(replIndexBuildState->collectionUUID);
+ invariant(collIndexBuildsIt != _collectionIndexBuilds.end());
+ collIndexBuildsIt->second->removeIndexBuild(lk, replIndexBuildState);
+ if (collIndexBuildsIt->second->getNumberOfIndexBuilds(lk) == 0) {
+ _collectionIndexBuilds.erase(collIndexBuildsIt);
+ }
+
+ invariant(_allIndexBuilds.erase(replIndexBuildState->buildUUID));
+}
+
+void IndexBuildsCoordinator::_runIndexBuild(OperationContext* opCtx,
+ const UUID& buildUUID) noexcept {
+ auto replState = [&] {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ auto it = _allIndexBuilds.find(buildUUID);
+ invariant(it != _allIndexBuilds.end());
+ return it->second;
+ }();
+
+ {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ while (_sleepForTest) {
+ lk.unlock();
+ sleepmillis(100);
+ lk.lock();
+ }
+ }
+
+ // TODO: create scoped object to create the index builder, then destroy the builder, set the
+ // promises and unregister the build.
+
+ // TODO: implement.
+
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ _unregisterIndexBuild(lk, opCtx, replState);
+
+ for (auto& promise : replState->promises) {
+ promise.emplaceValue();
+ }
+
+ return;
+}
+
+Status IndexBuildsCoordinator::_finishScanningPhase() {
+ // TODO: implement.
+ return Status::OK();
+}
+
+Status IndexBuildsCoordinator::_finishVerificationPhase() {
+ // TODO: implement.
+ return Status::OK();
+}
+
+Status IndexBuildsCoordinator::_finishCommitPhase() {
+ // TODO: implement.
+ return Status::OK();
+}
+
+void IndexBuildsCoordinator::_stopIndexBuildsOnDatabase(StringData dbName) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto it = _disallowedDbs.find(dbName);
+ if (it != _disallowedDbs.end()) {
+ ++(it->second);
+ return;
+ }
+ _disallowedDbs[dbName] = 1;
+}
+
+void IndexBuildsCoordinator::_stopIndexBuildsOnCollection(const UUID& collectionUUID) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto it = _disallowedCollections.find(collectionUUID);
+ if (it != _disallowedCollections.end()) {
+ ++(it->second);
+ return;
+ }
+ _disallowedCollections[collectionUUID] = 1;
+}
+
+void IndexBuildsCoordinator::_allowIndexBuildsOnDatabase(StringData dbName) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto it = _disallowedDbs.find(dbName);
+ invariant(it != _disallowedDbs.end());
+ invariant(it->second);
+ if (--(it->second) == 0) {
+ _disallowedDbs.erase(it);
+ }
+}
+
+void IndexBuildsCoordinator::_allowIndexBuildsOnCollection(const UUID& collectionUUID) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ auto it = _disallowedCollections.find(collectionUUID);
+ invariant(it != _disallowedCollections.end());
+ invariant(it->second > 0);
+ if (--(it->second) == 0) {
+ _disallowedCollections.erase(it);
+ }
+}
+
+StatusWith<bool> IndexBuildsCoordinator::_checkCommitQuorum(
+ const BSONObj& commitQuorum, const std::vector<HostAndPort>& confirmedMembers) {
+ // TODO: not yet implemented.
+ return false;
+}
+
+void IndexBuildsCoordinator::_refreshReplStateFromPersisted(OperationContext* opCtx,
+ const UUID& buildUUID) {}
+
+ScopedStopNewDatabaseIndexBuilds::ScopedStopNewDatabaseIndexBuilds(
+ IndexBuildsCoordinator* indexBuildsCoordinator, StringData dbName)
+ : _indexBuildsCoordinatorPtr(indexBuildsCoordinator), _dbName(dbName.toString()) {
+ _indexBuildsCoordinatorPtr->_stopIndexBuildsOnDatabase(_dbName);
+}
+
+ScopedStopNewDatabaseIndexBuilds::~ScopedStopNewDatabaseIndexBuilds() {
+ _indexBuildsCoordinatorPtr->_allowIndexBuildsOnDatabase(_dbName);
+}
+
+ScopedStopNewCollectionIndexBuilds::ScopedStopNewCollectionIndexBuilds(
+ IndexBuildsCoordinator* indexBuildsCoordinator, const UUID& collectionUUID)
+ : _indexBuildsCoordinatorPtr(indexBuildsCoordinator), _collectionUUID(collectionUUID) {
+ _indexBuildsCoordinatorPtr->_stopIndexBuildsOnCollection(_collectionUUID);
+}
+
+ScopedStopNewCollectionIndexBuilds::~ScopedStopNewCollectionIndexBuilds() {
+ _indexBuildsCoordinatorPtr->_allowIndexBuildsOnCollection(_collectionUUID);
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/index_builds_coordinator.h b/src/mongo/db/index_builds_coordinator.h
new file mode 100644
index 00000000000..8780c2e2a69
--- /dev/null
+++ b/src/mongo/db/index_builds_coordinator.h
@@ -0,0 +1,438 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/string_data.h"
+#include "mongo/db/catalog/index_builds_manager.h"
+#include "mongo/db/collection_index_builds_tracker.h"
+#include "mongo/db/database_index_builds_tracker.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/repl_index_build_state.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/util/concurrency/thread_pool.h"
+#include "mongo/util/concurrency/with_lock.h"
+#include "mongo/util/future.h"
+#include "mongo/util/net/hostandport.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+class OperationContext;
+class ServiceContext;
+
+/**
+ * This is a coordinator for all things index builds. It has a threadpool that runs index builds
+ * asynchronously, returning results to waiting callers via Futures and Promises. Index builds can
+ * be externally affected, notified, waited upon and aborted through this interface. The coordinator
+ * uses the cross replica set index build state to control index build progression.
+ *
+ * The IndexBuildsCoordinator is instantiated on the ServiceContext as a decoration, and is always
+ * accessible via the ServiceContext. It owns an IndexBuildsManager that manages the
+ * MultiIndexBlockImpl index builder instances.
+ */
+class IndexBuildsCoordinator {
+ MONGO_DISALLOW_COPYING(IndexBuildsCoordinator);
+
+public:
+ /**
+ * Sets up the thread pool.
+ */
+ IndexBuildsCoordinator();
+
+ /**
+ * Invariants that there are no index builds in-progress.
+ */
+ ~IndexBuildsCoordinator();
+
+ /**
+ * Shuts down the thread pool, signals interrupt to all index builds, then waits for all of the
+ * threads to finish.
+ */
+ void shutdown();
+
+ static IndexBuildsCoordinator* get(ServiceContext* serviceContext);
+ static IndexBuildsCoordinator* get(OperationContext* operationContext);
+
+ /**
+ * Sets up the in-memory and persisted state of the index build, then passes the build off to an
+ * asynchronous thread to run. A Future is returned to await the result of the asynchronous
+ * thread.
+ *
+ * Returns an error status if there are any errors setting up the index build.
+ */
+ StatusWith<Future<void>> buildIndex(OperationContext* opCtx,
+ const NamespaceString& nss,
+ const std::vector<BSONObj>& specs,
+ const UUID& buildUUID);
+
+ /**
+ * TODO: not yet implemented.
+ */
+ Future<void> joinIndexBuilds(const NamespaceString& nss,
+ const std::vector<BSONObj>& indexSpecs);
+
+ /**
+ * Signals all the index builds to stop and then waits for them to finish. Leaves the index
+ * builds in a recoverable state.
+ *
+ * This should only be called when certain the server will not start any new index builds --
+ * i.e. when the server is not accepting user requests and no internal operations are
+ * concurrently starting new index builds.
+ *
+ * TODO: not yet fully implemented. IndexBuildsManager::interruptIndexBuild is not yet
+ * implemented.
+ */
+ void interruptAllIndexBuilds(const std::string& reason);
+
+ /**
+ * Signals all of the index builds on the specified collection to abort and then waits until the
+ * index builds are no longer running. Must identify the collection with a UUID and the caller
+ * must continue to operate on the collection by UUID to protect against rename collection. The
+ * provided 'reason' will be used in the error message that the index builders return to their
+ * callers.
+ *
+ * First create a ScopedStopNewCollectionIndexBuilds to block further index builds on the
+ * collection before calling this and for the duration of the drop collection operation.
+ *
+ * {
+ * ScopedStopNewCollectionIndexBuilds scopedStop(collectionUUID);
+ * indexBuildsCoord->abortCollectionIndexBuilds(collectionUUID, "...");
+ * AutoGetCollection autoColl(..., collectionUUID, ...);
+ * autoColl->dropCollection(...);
+ * }
+ *
+ * TODO: this is partially implemented. It calls IndexBuildsManager::abortIndexBuild that is not
+ * implemented.
+ */
+ void abortCollectionIndexBuilds(const UUID& collectionUUID, const std::string& reason);
+
+ /**
+ * Signals all of the index builds on the specified 'db' to abort and then waits until the index
+ * builds are no longer running. The provided 'reason' will be used in the error message that
+ * the index builders return to their callers.
+ *
+ * First create a ScopedStopNewDatabaseIndexBuilds to block further index builds on the
+ * specified
+ * database before calling this and for the duration of the drop database operation.
+ *
+ * {
+ * ScopedStopNewDatabaseIndexBuilds scopedStop(dbName);
+ * indexBuildsCoord->abortDatabaseIndexBuilds(dbName, "...");
+ * AutoGetDb autoDb(...);
+ * autoDb->dropDatabase(...);
+ * }
+ *
+ * TODO: this is partially implemented. It calls IndexBuildsManager::abortIndexBuild that is not
+ * implemented.
+ */
+ void abortDatabaseIndexBuilds(StringData db, const std::string& reason);
+
+ /**
+ * Aborts a given index build by name on the given collection.
+ *
+ * TODO: This is not yet implemented.
+ */
+ Future<void> abortIndexBuildByName(const NamespaceString& nss,
+ const std::vector<std::string>& indexNames,
+ const std::string& reason);
+
+ /**
+ * Aborts a given index build by index build UUID.
+ *
+ * TODO: This is not yet implemented.
+ */
+ Future<void> abortIndexBuildByUUID(const UUID& buildUUID, const std::string& reason);
+
+ void signalChangeToPrimaryMode();
+
+ void signalChangeToSecondaryMode();
+
+ void signalChangeToInitialSyncMode();
+
+ /**
+ * TODO: This is not yet implemented.
+ */
+ void voteCommitIndexBuild(const UUID& buildUUID, const HostAndPort& hostAndPort);
+
+ /**
+ * TODO: This is not yet implemented. (This will have to take a collection IS lock to look up
+ * the collection UUID.)
+ */
+ Status setCommitQuorum(const NamespaceString& nss,
+ const std::vector<std::string>& indexNames,
+ const BSONObj& newCommitQuorum);
+
+ /**
+ * TODO: This is not yet implemented.
+ */
+ void recoverIndexBuilds();
+
+ /**
+ * Returns the number of index builds that are running on the specified database.
+ */
+ int numInProgForDb(StringData db) const;
+
+ /**
+ * Prints out the names of collections on which index builds are running, and the number of
+ * index builds per database.
+ */
+ void dump(std::ostream&) const;
+
+ /**
+ * Returns true if an index build is in progress on the specified collection.
+ */
+ bool inProgForCollection(const UUID& collectionUUID) const;
+
+ /**
+ * Returns true if an index build is in progress on the specified database.
+ */
+ bool inProgForDb(StringData db) const;
+
+ /**
+ * Uasserts if any index builds is in progress on the specified collection.
+ */
+ void assertNoIndexBuildInProgForCollection(const UUID& collectionUUID) const;
+
+ /**
+ * Uasserts if any index builds is in progress on the specified database.
+ */
+ void assertNoBgOpInProgForDb(StringData db) const;
+
+ /**
+ * Waits for all index builds on a specified collection to finish.
+ *
+ * Momentarily takes the collection IS lock for 'ns', to fetch the collection UUID.
+ */
+ void awaitNoBgOpInProgForNs(OperationContext* opCtx, StringData ns) const;
+ void awaitNoBgOpInProgForNs(OperationContext* opCtx, const NamespaceString& ns) const {
+ awaitNoBgOpInProgForNs(opCtx, ns.ns());
+ }
+
+ /**
+ * Waits for all index builds on a specified database to finish.
+ */
+ void awaitNoBgOpInProgForDb(StringData db) const;
+
+ void sleepIndexBuilds_forTestOnly(bool sleep);
+
+ void verifyNoIndexBuilds_forTestOnly();
+
+private:
+ // Friend classes in order to be the only allowed callers of
+ //_stopIndexBuildsOnCollection/Database and _allowIndexBuildsOnCollection/Database.
+ friend class ScopedStopNewDatabaseIndexBuilds;
+ friend class ScopedStopNewCollectionIndexBuilds;
+
+ /**
+ * Keeps track of the relevant replica set member states. Index builds are managed differently
+ * depending on the state of the replica set member.
+ *
+ * These states follow the replica set member states, as maintained by MemberState in the
+ * ReplicationCoordinator. If not in Primary or InitialSync modes, then the default will be
+ * Secondary, with the expectation that a replica set member must always transition to Secondary
+ * before Primary.
+ */
+ enum class ReplState { Primary, Secondary, InitialSync };
+
+ /**
+ * Registers an index build so that the rest of the system can discover it.
+ *
+ * If stopIndexBuildsOnNsOrDb has been called on the index build's collection or database, then
+ * an error will be returned.
+ */
+ Status _registerIndexBuild(OperationContext* opCtx,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState);
+
+ /**
+ * Unregisters the index build.
+ */
+ void _unregisterIndexBuild(WithLock lk,
+ OperationContext* opCtx,
+ std::shared_ptr<ReplIndexBuildState> replIndexBuildState);
+
+ /**
+ * TODO: not yet implemented.
+ */
+ void _runIndexBuild(OperationContext* opCtx, const UUID& buildUUID) noexcept;
+
+ /**
+ * TODO: not yet implemented.
+ */
+ Status _finishScanningPhase();
+
+ /**
+ * TODO: not yet implemented.
+ */
+ Status _finishVerificationPhase();
+
+ /**
+ * TODO: not yet implemented.
+ */
+ Status _finishCommitPhase();
+
+ /**
+ * Prevents new index builds being registered on the provided collection or database.
+ *
+ * It is safe to call this on the same collection/database concurrently in different threads. It
+ * will still behave correctly.
+ */
+ void _stopIndexBuildsOnDatabase(StringData dbName);
+ void _stopIndexBuildsOnCollection(const UUID& collectionUUID);
+
+ /**
+ * Allows new index builds to again be registered on the provided collection or database. Should
+ * only be called after calling stopIndexBuildsOnCollection or stopIndexBuildsOnDatabase on the
+ * same collection or database, respectively.
+ */
+ void _allowIndexBuildsOnDatabase(StringData dbName);
+ void _allowIndexBuildsOnCollection(const UUID& collectionUUID);
+
+ /**
+ * TODO: not yet implemented.
+ */
+ StatusWith<bool> _checkCommitQuorum(const BSONObj& commitQuorum,
+ const std::vector<HostAndPort>& confirmedMembers);
+
+ /**
+ * TODO: not yet implemented.
+ */
+ void _refreshReplStateFromPersisted(OperationContext* opCtx, const UUID& buildUUID);
+
+ // Protects the below state.
+ mutable stdx::mutex _mutex;
+
+ // New index builds are not allowed on a collection or database if the collection or database is
+ // in either of these maps. These are used when concurrent operations need to abort index builds
+ // on a collection or database and must wait for the index builds to drain, without further
+ // index builds being allowed to begin.
+ StringMap<int> _disallowedDbs;
+ stdx::unordered_map<UUID, int, UUID::Hash> _disallowedCollections;
+
+ // Maps database name to database information. Tracks and accesses index builds on a database
+ // level. Can be used to abort and wait upon the completion of all index builds for a database.
+ //
+ // Maps shared_ptrs so that DatabaseIndexBuildsTracker instances can outlive being erased from
+ // this map when there are no longer any builds remaining on the database. This is necessary
+ // when callers must wait for all index builds to cease.
+ StringMap<std::shared_ptr<DatabaseIndexBuildsTracker>> _databaseIndexBuilds;
+
+ // Collection UUID to collection level index build information. Enables index build lookup and
+ // abort by collection UUID and index name, as well as collection level interruption.
+ //
+ // Maps shared_ptrs so that CollectionIndexBuildsTracker instances can outlive being erased from
+ // this map when there are no longer any builds remaining on the collection. This is necessary
+ // when callers must wait for and index build or all index builds to cease.
+ stdx::unordered_map<UUID, std::shared_ptr<CollectionIndexBuildsTracker>, UUID::Hash>
+ _collectionIndexBuilds;
+
+ // Build UUID to index build information map.
+ stdx::unordered_map<UUID, std::shared_ptr<ReplIndexBuildState>, UUID::Hash> _allIndexBuilds;
+
+ // Handles actually building the indexes.
+ IndexBuildsManager _indexBuildsManager;
+
+ // Replication hooks will call into the Coordinator to update this on relevant state
+ // transitions. The Coordinator will then use the setting to inform how the index build is run.
+ // Index builds have different inter node communication responsibilities and error checking
+ // requirements depending on the replica set member's state.
+ ReplState _replMode = ReplState::Secondary;
+
+ // Thread pool on which index builds are run.
+ ThreadPool _threadPool;
+
+ bool _sleepForTest = false;
+};
+
+/**
+ * For this object's lifetime no new index builds will be allowed on the specified database. An
+ * error will be returned by the IndexBuildsCoordinator to any caller attempting to register a new
+ * index build on the blocked collection or database.
+ *
+ * This should be used by operations like drop database, where the active index builds must be
+ * signaled to abort, but it takes time for them to wrap up, during which time no further index
+ * builds should be scheduled.
+ */
+class ScopedStopNewDatabaseIndexBuilds {
+ MONGO_DISALLOW_COPYING(ScopedStopNewDatabaseIndexBuilds);
+
+public:
+ /**
+ * Takes either the full collection namespace or a database name and will block further index
+ * builds on that collection or database.
+ */
+ ScopedStopNewDatabaseIndexBuilds(IndexBuildsCoordinator* indexBuildsCoordinator,
+ StringData dbName);
+
+ /**
+ * Allows new index builds on the collection or database that were previously disallowed.
+ */
+ ~ScopedStopNewDatabaseIndexBuilds();
+
+private:
+ IndexBuildsCoordinator* _indexBuildsCoordinatorPtr;
+ std::string _dbName;
+};
+
+/**
+ * For this object's lifetime no new index builds will be allowed on the specified collection. An
+ * error will be returned by the IndexBuildsCoordinator to any caller attempting to register a new
+ * index build on the blocked collection.
+ *
+ * This should be used by operations like drop collection, where the active index builds must be
+ * signaled to abort, but it takes time for them to wrap up, during which time no further index
+ * builds should be scheduled.
+ */
+class ScopedStopNewCollectionIndexBuilds {
+ MONGO_DISALLOW_COPYING(ScopedStopNewCollectionIndexBuilds);
+
+public:
+ /**
+ * Blocks further index builds on the specified collection.
+ */
+ ScopedStopNewCollectionIndexBuilds(IndexBuildsCoordinator* indexBuildsCoordinator,
+ const UUID& collectionUUID);
+
+ /**
+ * Allows new index builds on the collection that were previously disallowed.
+ */
+ ~ScopedStopNewCollectionIndexBuilds();
+
+private:
+ IndexBuildsCoordinator* _indexBuildsCoordinatorPtr;
+ UUID _collectionUUID;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/index_builds_coordinator_test.cpp b/src/mongo/db/index_builds_coordinator_test.cpp
new file mode 100644
index 00000000000..bcea0dcc982
--- /dev/null
+++ b/src/mongo/db/index_builds_coordinator_test.cpp
@@ -0,0 +1,323 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/index_builds_coordinator.h"
+
+#include "mongo/db/catalog/catalog_test_fixture.h"
+#include "mongo/db/catalog/multi_index_block.h"
+#include "mongo/db/catalog/multi_index_block_impl.h"
+#include "mongo/db/catalog_raii.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+using unittest::assertGet;
+using unittest::log;
+
+namespace {
+
+class IndexBuildsCoordinatorTest : public CatalogTestFixture {
+private:
+ void setUp() override;
+ void tearDown() override;
+
+public:
+ void createCollection(const NamespaceString& nss);
+
+ const NamespaceString _testFooNss = NamespaceString("test.foo");
+ const NamespaceString _testBarNss = NamespaceString("test.bar");
+ const NamespaceString _othertestFooNss = NamespaceString("othertest.foo");
+ std::unique_ptr<IndexBuildsCoordinator> _indexBuildsCoord;
+};
+
+void IndexBuildsCoordinatorTest::setUp() {
+ CatalogTestFixture::setUp();
+ createCollection(_testFooNss);
+ createCollection(_testBarNss);
+ createCollection(_othertestFooNss);
+ _indexBuildsCoord = std::make_unique<IndexBuildsCoordinator>();
+}
+
+void IndexBuildsCoordinatorTest::tearDown() {
+ _indexBuildsCoord->verifyNoIndexBuilds_forTestOnly();
+ _indexBuildsCoord.reset();
+ // All databases are dropped during tear down.
+ CatalogTestFixture::tearDown();
+}
+
+void IndexBuildsCoordinatorTest::createCollection(const NamespaceString& nss) {
+ ASSERT_OK(storageInterface()->createCollection(operationContext(), nss, CollectionOptions()));
+}
+
+UUID getCollectionUUID(OperationContext* opCtx, const NamespaceString& nss) {
+ AutoGetCollection autoColl(opCtx, nss, MODE_IS);
+ Collection* coll = autoColl.getCollection();
+ ASSERT(coll);
+ return coll->uuid().get();
+}
+
+std::vector<BSONObj> makeSpecs(const NamespaceString& nss, std::vector<std::string> keys) {
+ invariant(keys.size());
+ std::vector<BSONObj> indexSpecs;
+ for (auto keyName : keys) {
+ indexSpecs.push_back(BSON("ns" << nss.toString() << "v" << 2 << "key" << BSON(keyName << 1)
+ << "name"
+ << (keyName + "_1")));
+ }
+ return indexSpecs;
+}
+
+TEST_F(IndexBuildsCoordinatorTest, CannotBuildIndexWithSameIndexName) {
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(true);
+
+ // Register an index build on _testFooNss.
+ Future<void> testFoo1Future = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"a", "b"}), UUID::gen()));
+
+ // Attempt and fail to register an index build on _testFooNss with the same index name, while
+ // the prior build is still running.
+ ASSERT_EQ(ErrorCodes::IndexKeySpecsConflict,
+ _indexBuildsCoord
+ ->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"b"}), UUID::gen())
+ .getStatus());
+
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(false);
+ ASSERT_OK(testFoo1Future.getNoThrow());
+}
+
+// Incrementally registering index builds and checking both that the registration was successful and
+// that the access functions convey the expected state of the manager.
+TEST_F(IndexBuildsCoordinatorTest, IndexBuildsCoordinatorRegistration) {
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(true);
+
+ // Register an index build on _testFooNss.
+ UUID testFooUUID = getCollectionUUID(operationContext(), _testFooNss);
+ Future<void> testFoo1Future = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"a", "b"}), UUID::gen()));
+
+ ASSERT_EQ(_indexBuildsCoord->numInProgForDb(_testFooNss.db()), 1);
+ ASSERT(_indexBuildsCoord->inProgForCollection(testFooUUID));
+ ASSERT(_indexBuildsCoord->inProgForDb(_testFooNss.db()));
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoIndexBuildInProgForCollection(testFooUUID),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForNamespace);
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoBgOpInProgForDb(_testFooNss.db()),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForDatabase);
+
+ // Register a second index build on _testFooNss.
+ Future<void> testFoo2Future = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"c", "d"}), UUID::gen()));
+
+ ASSERT_EQ(_indexBuildsCoord->numInProgForDb(_testFooNss.db()), 2);
+ ASSERT(_indexBuildsCoord->inProgForCollection(testFooUUID));
+ ASSERT(_indexBuildsCoord->inProgForDb(_testFooNss.db()));
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoIndexBuildInProgForCollection(testFooUUID),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForNamespace);
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoBgOpInProgForDb(_testFooNss.db()),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForDatabase);
+
+ // Register an index build on a different collection _testBarNss.
+ UUID testBarUUID = getCollectionUUID(operationContext(), _testBarNss);
+ Future<void> testBarFuture = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testBarNss, makeSpecs(_testBarNss, {"x", "y"}), UUID::gen()));
+
+ ASSERT_EQ(_indexBuildsCoord->numInProgForDb(_testBarNss.db()), 3);
+ ASSERT(_indexBuildsCoord->inProgForCollection(testBarUUID));
+ ASSERT(_indexBuildsCoord->inProgForDb(_testBarNss.db()));
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoIndexBuildInProgForCollection(testBarUUID),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForNamespace);
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoBgOpInProgForDb(_testBarNss.db()),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForDatabase);
+
+ // Register an index build on a collection in a different database _othertestFoo.
+ UUID othertestFooUUID = getCollectionUUID(operationContext(), _othertestFooNss);
+ Future<void> othertestFooFuture =
+ assertGet(_indexBuildsCoord->buildIndex(operationContext(),
+ _othertestFooNss,
+ makeSpecs(_othertestFooNss, {"r", "s"}),
+ UUID::gen()));
+
+ ASSERT_EQ(_indexBuildsCoord->numInProgForDb(_othertestFooNss.db()), 1);
+ ASSERT(_indexBuildsCoord->inProgForCollection(othertestFooUUID));
+ ASSERT(_indexBuildsCoord->inProgForDb(_othertestFooNss.db()));
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoIndexBuildInProgForCollection(othertestFooUUID),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForNamespace);
+ ASSERT_THROWS_CODE(_indexBuildsCoord->assertNoBgOpInProgForDb(_othertestFooNss.db()),
+ AssertionException,
+ ErrorCodes::BackgroundOperationInProgressForDatabase);
+
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(false);
+
+ ASSERT_OK(testFoo1Future.getNoThrow());
+ ASSERT_OK(testFoo2Future.getNoThrow());
+ ASSERT_OK(testBarFuture.getNoThrow());
+ ASSERT_OK(othertestFooFuture.getNoThrow());
+
+ _indexBuildsCoord->assertNoIndexBuildInProgForCollection(testFooUUID);
+ _indexBuildsCoord->assertNoIndexBuildInProgForCollection(testBarUUID);
+ _indexBuildsCoord->assertNoIndexBuildInProgForCollection(othertestFooUUID);
+
+ _indexBuildsCoord->assertNoBgOpInProgForDb(_testFooNss.db());
+ _indexBuildsCoord->assertNoBgOpInProgForDb(_othertestFooNss.db());
+
+ ASSERT_NOT_EQUALS(_testFooNss, _testBarNss);
+ ASSERT_NOT_EQUALS(_testFooNss, _othertestFooNss);
+}
+
+// Exercises the stopIndexBuildsOnCollection/Database() and allowIndexBuildsOnCollection/Database()
+// functions, checking that they correctly disallow and allow index builds when
+// ScopedStopNewCollectionIndexBuilds and ScopedStopNewDatabaseIndexBuilds are present on a
+// collection or database name.
+TEST_F(IndexBuildsCoordinatorTest, IndexBuildsCoordinatorDisallowNewBuildsOnNamespace) {
+ UUID testFooUUID = getCollectionUUID(operationContext(), _testFooNss);
+
+ {
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(true);
+
+ // Create a scoped object to block new index builds ONLY on _testFooNss.
+ ScopedStopNewCollectionIndexBuilds scopedStop(_indexBuildsCoord.get(), testFooUUID);
+
+ // Registering an index build on _testFooNss should fail.
+ ASSERT_EQ(ErrorCodes::CannotCreateIndex,
+ _indexBuildsCoord
+ ->buildIndex(operationContext(),
+ _testFooNss,
+ makeSpecs(_testFooNss, {"a", "b"}),
+ UUID::gen())
+ .getStatus());
+
+ // Registering index builds on other collections and databases should still succeed.
+ Future<void> testBarFuture = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testBarNss, makeSpecs(_testBarNss, {"c", "d"}), UUID::gen()));
+ Future<void> othertestFooFuture =
+ assertGet(_indexBuildsCoord->buildIndex(operationContext(),
+ _othertestFooNss,
+ makeSpecs(_othertestFooNss, {"e", "f"}),
+ UUID::gen()));
+
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(false);
+
+ ASSERT_OK(testBarFuture.getNoThrow());
+ ASSERT_OK(othertestFooFuture.getNoThrow());
+ }
+
+ {
+ // Check that the scoped object correctly cleared.
+ Future<void> testFooFuture = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"a", "b"}), UUID::gen()));
+ ASSERT_OK(testFooFuture.getNoThrow());
+ }
+
+ {
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(true);
+
+ // Create a scoped object to block new index builds on the 'test' database.
+ ScopedStopNewDatabaseIndexBuilds scopedStop(_indexBuildsCoord.get(), _testFooNss.db());
+
+ // Registering an index build on any collection in the 'test' database should fail.
+ ASSERT_EQ(ErrorCodes::CannotCreateIndex,
+ _indexBuildsCoord
+ ->buildIndex(operationContext(),
+ _testFooNss,
+ makeSpecs(_testFooNss, {"a", "b"}),
+ UUID::gen())
+ .getStatus());
+ ASSERT_EQ(ErrorCodes::CannotCreateIndex,
+ _indexBuildsCoord
+ ->buildIndex(operationContext(),
+ _testBarNss,
+ makeSpecs(_testBarNss, {"c", "d"}),
+ UUID::gen())
+ .getStatus());
+
+ // Registering index builds on another database should still succeed.
+ Future<void> othertestFooFuture =
+ assertGet(_indexBuildsCoord->buildIndex(operationContext(),
+ _othertestFooNss,
+ makeSpecs(_othertestFooNss, {"e", "f"}),
+ UUID::gen()));
+
+ _indexBuildsCoord->sleepIndexBuilds_forTestOnly(false);
+
+ ASSERT_OK(othertestFooFuture.getNoThrow());
+ }
+
+ {
+ // Check that the scoped object correctly cleared.
+ Future<void> testFooFuture = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"a", "b"}), UUID::gen()));
+ ASSERT_OK(testFooFuture.getNoThrow());
+ }
+
+ {
+ // Test concurrency of multiple scoped objects to block an index builds.
+
+ ScopedStopNewCollectionIndexBuilds scopedStop(_indexBuildsCoord.get(), testFooUUID);
+ {
+ ScopedStopNewCollectionIndexBuilds scopedStop(_indexBuildsCoord.get(), testFooUUID);
+
+ ASSERT_EQ(ErrorCodes::CannotCreateIndex,
+ _indexBuildsCoord
+ ->buildIndex(operationContext(),
+ _testFooNss,
+ makeSpecs(_testFooNss, {"a", "b"}),
+ UUID::gen())
+ .getStatus());
+ }
+ ASSERT_EQ(ErrorCodes::CannotCreateIndex,
+ _indexBuildsCoord
+ ->buildIndex(operationContext(),
+ _testFooNss,
+ makeSpecs(_testFooNss, {"a", "b"}),
+ UUID::gen())
+ .getStatus());
+ }
+
+ {
+ // Check that the scoped object correctly cleared.
+ Future<void> testFooFuture = assertGet(_indexBuildsCoord->buildIndex(
+ operationContext(), _testFooNss, makeSpecs(_testFooNss, {"a", "b"}), UUID::gen()));
+ ASSERT_OK(testFooFuture.getNoThrow());
+ }
+}
+
+} // namespace
+
+} // namespace mongo
diff --git a/src/mongo/db/repl_index_build_state.h b/src/mongo/db/repl_index_build_state.h
new file mode 100644
index 00000000000..b2473ccb934
--- /dev/null
+++ b/src/mongo/db/repl_index_build_state.h
@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/write_concern_options.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/util/future.h"
+#include "mongo/util/net/hostandport.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+/**
+ * Tracks the cross replica set progress of a particular index build identified by a build UUID.
+ *
+ * This is intended to only be used by the IndexBuildsCoordinator class.
+ *
+ * TODO: pass in commit quorum setting and FCV to decide the twoPhaseIndexBuild setting.
+ */
+struct ReplIndexBuildState {
+ ReplIndexBuildState(const UUID& indexBuildUUID,
+ const UUID& collUUID,
+ const std::vector<std::string> names,
+ const std::vector<BSONObj>& specs,
+ Promise<void> promise)
+ : buildUUID(indexBuildUUID),
+ collectionUUID(collUUID),
+ indexNames(names),
+ indexSpecs(specs) {
+ promises.emplace_back(std::move(promise));
+
+ // Verify that the given index names and index specs match.
+ invariant(names.size() == specs.size());
+ for (auto& spec : specs) {
+ std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName);
+ invariant(std::find(names.begin(), names.end(), name) != names.end());
+ }
+ }
+
+ // Uniquely identifies this index build across replica set members.
+ const UUID buildUUID;
+
+ // Identifies the collection for which the index is being built. Collections can be renamed, so
+ // the collection UUID is used to maintain correct association.
+ const UUID collectionUUID;
+
+ // The names of the indexes being built.
+ const std::vector<std::string> indexNames;
+
+ // The specs of the index(es) being built. Facilitates new callers joining an active index
+ // build.
+ const std::vector<BSONObj> indexSpecs;
+
+ // Whether to do a two phase index build or a single phase index build like in v4.0. The FCV
+ // at the start of the index build will determine this setting.
+ bool twoPhaseIndexBuild = false;
+
+ // Protects the state below.
+ mutable stdx::mutex mutex;
+
+ // The quorum required of commit ready replica set members before the index build will be
+ // allowed to commit.
+ WriteConcernOptions commitQuorum;
+
+ // Whether or not the primary replica set member has signaled that it is okay to go ahead and
+ // verify index constraint violations have gone away.
+ bool prepareIndexBuild = false;
+
+ // Tracks the members of the replica set that have finished building the index(es) and are ready
+ // to commit the index(es).
+ std::vector<HostAndPort> commitReadyMembers;
+
+ // Communicates the final outcome of the index build to any callers waiting upon the associated
+ // Future(s).
+ std::vector<Promise<void>> promises;
+
+ // There is a period of time where the index build is registered on the coordinator, but an
+ // index builder does not yet exist. Since a signal cannot be set on the index builder at that
+ // time, it must be saved here.
+ bool aborted = false;
+ std::string abortReason = "";
+
+ // The coordinator for the index build will wait upon this when awaiting an external signal,
+ // such as commit or commit readiness signals.
+ stdx::condition_variable condVar;
+};
+
+} // namespace mongo