/** * Copyright (C) 2022-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 * . * * 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/db/global_index.h" #include "mongo/bson/bsonobj.h" #include "mongo/db/catalog/clustered_collection_util.h" #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/concurrency/exception_util.h" #include "mongo/db/index_builds_coordinator.h" #include "mongo/db/namespace_string.h" #include "mongo/db/op_observer/op_observer.h" #include "mongo/db/operation_context.h" #include "mongo/db/transaction/retryable_writes_stats.h" #include "mongo/db/transaction/transaction_participant.h" #include "mongo/logv2/log.h" #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kIndex namespace mongo::global_index { void createContainer(OperationContext* opCtx, const UUID& indexUUID) { // StmtId will always be 0, as the command only replicates a createGlobalIndex oplog entry. constexpr StmtId stmtId = 0; if (opCtx->isRetryableWrite()) { const auto txnParticipant = TransactionParticipant::get(opCtx); if (txnParticipant.checkStatementExecuted(opCtx, stmtId)) { RetryableWritesStats::get(opCtx)->incrementRetriedCommandsCount(); RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount(); LOGV2_DEBUG(6789203, 1, "_shardsvrCreateGlobalIndex retried statement already executed", "indexUUID"_attr = indexUUID); return; } } const auto nss = NamespaceString::makeGlobalIndexNSS(indexUUID); LOGV2(6789200, "Create global index container", "indexUUID"_attr = indexUUID); // Create the container. return writeConflictRetry(opCtx, "createGlobalIndexContainer", nss.ns(), [&]() { const auto indexKeySpec = BSON("v" << 2 << "name" << "indexKey_1" << "key" << BSON("indexKey" << 1) << "unique" << true); WriteUnitOfWork wuow(opCtx); // createIndexesOnEmptyCollection requires the MODE_X collection lock. AutoGetCollection autoColl(opCtx, nss, MODE_X); if (!CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, nss)) { { repl::UnreplicatedWritesBlock unreplicatedWrites(opCtx); auto db = autoColl.ensureDbExists(opCtx); CollectionOptions collOptions; collOptions.clusteredIndex = clustered_util::makeDefaultClusteredIdIndex(); collOptions.uuid = indexUUID; db->createCollection(opCtx, nss, collOptions); CollectionWriter writer(opCtx, nss); IndexBuildsCoordinator::get(opCtx)->createIndexesOnEmptyCollection( opCtx, writer, {indexKeySpec}, false); } auto opObserver = opCtx->getServiceContext()->getOpObserver(); opObserver->onCreateGlobalIndex(opCtx, nss, indexUUID); wuow.commit(); } else { // Container already exists, this can happen when attempting to create a global index // that already exists. Sanity check its storage format. tassert(6789204, str::stream() << "Collection with UUID " << indexUUID << " already exists but it's not clustered.", autoColl->getCollectionOptions().clusteredIndex); tassert(6789205, str::stream() << "Collection with UUID " << indexUUID << " already exists but it's missing a unique index on " "'indexKey'.", autoColl->getIndexCatalog()->findIndexByKeyPatternAndOptions( opCtx, BSON("indexKey" << 1), indexKeySpec, IndexCatalog::InclusionPolicy::kReady)); tassert(6789206, str::stream() << "Collection with namespace " << nss.ns() << " already exists but it has inconsistent UUID " << autoColl->uuid().toString() << ".", autoColl->uuid() == indexUUID); LOGV2(6789201, "Global index container already exists", "indexUUID"_attr = indexUUID); } return; }); } } // namespace mongo::global_index