diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/index | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/index')
| -rw-r--r-- | src/mongo/db/index/SConscript | 123 | ||||
| -rw-r--r-- | src/mongo/db/index/index_access_method.cpp | 157 | ||||
| -rw-r--r-- | src/mongo/db/index/index_access_method.h | 28 | ||||
| -rw-r--r-- | src/mongo/db/index/index_access_method_factory.cpp (renamed from src/mongo/db/index/multikey_paths.cpp) | 53 | ||||
| -rw-r--r-- | src/mongo/db/index/index_access_method_factory_impl.cpp | 72 | ||||
| -rw-r--r-- | src/mongo/db/index/index_access_method_factory_impl.h (renamed from src/mongo/db/index/multikey_paths_test.cpp) | 48 | ||||
| -rw-r--r-- | src/mongo/db/index/index_build_interceptor.cpp | 4 | ||||
| -rw-r--r-- | src/mongo/db/index/index_descriptor.cpp | 34 | ||||
| -rw-r--r-- | src/mongo/db/index/index_descriptor.h | 39 | ||||
| -rw-r--r-- | src/mongo/db/index/multikey_paths.h | 7 | ||||
| -rw-r--r-- | src/mongo/db/index/s2_key_generator_test.cpp | 24 | ||||
| -rw-r--r-- | src/mongo/db/index/skipped_record_tracker.cpp | 2 | ||||
| -rw-r--r-- | src/mongo/db/index/wildcard_access_method.cpp | 10 |
13 files changed, 272 insertions, 329 deletions
diff --git a/src/mongo/db/index/SConscript b/src/mongo/db/index/SConscript index 9207e32aa49..48ab6f723cc 100644 --- a/src/mongo/db/index/SConscript +++ b/src/mongo/db/index/SConscript @@ -4,12 +4,57 @@ Import("env") env = env.Clone() +env.Library( + target='index_descriptor', + source=[ + 'index_descriptor.cpp', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/catalog/index_catalog', + '$BUILD_DIR/mongo/db/index_names', + '$BUILD_DIR/mongo/db/matcher/expressions', + '$BUILD_DIR/mongo/db/namespace_string', + '$BUILD_DIR/mongo/db/query/collation/collator_factory_interface', + ], +) + +env.Library( + target='key_generator', + source=[ + 'btree_key_generator.cpp', + 'column_key_generator.cpp', + 'expression_keys_private.cpp', + 'sort_key_generator.cpp', + 'wildcard_key_generator.cpp', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/bson/dotted_path_support', + '$BUILD_DIR/mongo/db/exec/projection_executor', + '$BUILD_DIR/mongo/db/exec/working_set', + '$BUILD_DIR/mongo/db/fts/base_fts', + '$BUILD_DIR/mongo/db/geo/geoparser', + '$BUILD_DIR/mongo/db/index_names', + '$BUILD_DIR/mongo/db/mongohasher', + '$BUILD_DIR/mongo/db/pipeline/document_path_support', + '$BUILD_DIR/mongo/db/query/collation/collator_interface', + '$BUILD_DIR/mongo/db/query/projection_ast', + '$BUILD_DIR/mongo/db/query/sort_pattern', + '$BUILD_DIR/mongo/db/record_id_helpers', + '$BUILD_DIR/mongo/db/timeseries/timeseries_conversion_util', + '$BUILD_DIR/third_party/s2/s2', + 'expression_params', + 'index_descriptor', + ], +) + env.Benchmark( target='key_gen_bm', source='key_gen_bm.cpp', LIBDEPS=[ '$BUILD_DIR/mongo/base', - 'index_access_method', + 'key_generator', ], ) @@ -31,72 +76,76 @@ env.Library( ] ) +env.Library( + target='index_access_method_factory', + source=[ + 'index_access_method_factory.cpp', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/index_names', + ], +) + serveronlyEnv = env.Clone() serveronlyEnv.InjectThirdParty(libraries=['snappy']) serveronlyEnv.Library( target="index_access_method", source=[ - '2d_access_method.cpp', - 'btree_access_method.cpp', - 'btree_key_generator.cpp', - 'column_key_generator.cpp', 'duplicate_key_tracker.cpp', - 'expression_keys_private.cpp', - 'fts_access_method.cpp', - 'hash_access_method.cpp', 'index_access_method.cpp', 'index_build_interceptor.cpp', 'index_build_interceptor.idl', - 'index_descriptor.cpp', - 'multikey_paths.cpp', - 's2_access_method.cpp', - 's2_bucket_access_method.cpp', 'skipped_record_tracker.cpp', - 'sort_key_generator.cpp', - 'wildcard_access_method.cpp', - 'wildcard_key_generator.cpp', ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/bson/dotted_path_support', - '$BUILD_DIR/mongo/db/catalog/collection', - '$BUILD_DIR/mongo/db/catalog/index_catalog', '$BUILD_DIR/mongo/db/catalog/index_catalog_entry', - '$BUILD_DIR/mongo/db/concurrency/exception_util', - '$BUILD_DIR/mongo/db/exec/projection_executor', - '$BUILD_DIR/mongo/db/exec/working_set', - '$BUILD_DIR/mongo/db/fts/base_fts', - '$BUILD_DIR/mongo/db/geo/geoparser', - '$BUILD_DIR/mongo/db/mongohasher', + '$BUILD_DIR/mongo/db/concurrency/write_conflict_exception', + '$BUILD_DIR/mongo/db/curop', '$BUILD_DIR/mongo/db/multi_key_path_tracker', - '$BUILD_DIR/mongo/db/pipeline/document_path_support', - '$BUILD_DIR/mongo/db/query/collation/collator_factory_interface', - '$BUILD_DIR/mongo/db/query/collation/collator_interface', - '$BUILD_DIR/mongo/db/query/projection_ast', - '$BUILD_DIR/mongo/db/query/query_stats/query_stats', - '$BUILD_DIR/mongo/db/query/sort_pattern', - '$BUILD_DIR/mongo/db/record_id_helpers', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', '$BUILD_DIR/mongo/db/resumable_index_builds_idl', '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/db/sorter/sorter_idl', - '$BUILD_DIR/mongo/db/sorter/sorter_stats', '$BUILD_DIR/mongo/db/storage/encryption_hooks', '$BUILD_DIR/mongo/db/storage/execution_context', '$BUILD_DIR/mongo/db/storage/index_entry_comparison', '$BUILD_DIR/mongo/db/storage/key_string', '$BUILD_DIR/mongo/db/storage/record_store_base', '$BUILD_DIR/mongo/db/storage/storage_options', - '$BUILD_DIR/mongo/db/timeseries/timeseries_conversion_util', '$BUILD_DIR/mongo/db/vector_clock', '$BUILD_DIR/mongo/idl/server_parameter', '$BUILD_DIR/mongo/util/progress_meter', - '$BUILD_DIR/third_party/s2/s2', '$BUILD_DIR/third_party/shim_snappy', - 'expression_params', + 'index_descriptor', ], ) +env.Library( + target="index_access_methods", + source=[ + "2d_access_method.cpp", + "btree_access_method.cpp", + "fts_access_method.cpp", + "hash_access_method.cpp", + "index_access_method_factory_impl.cpp", + "s2_access_method.cpp", + "s2_bucket_access_method.cpp", + "wildcard_access_method.cpp", + ], + LIBDEPS=[ + 'index_access_method', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/fts/base_fts', + '$BUILD_DIR/mongo/db/index_names', + 'expression_params', + 'key_generator', + ] +) + env.CppUnitTest( target='db_index_test', source=[ @@ -104,7 +153,6 @@ env.CppUnitTest( 'btree_key_generator_test.cpp', 'column_key_generator_test.cpp', 'hash_key_generator_test.cpp', - 'multikey_paths_test.cpp', 's2_key_generator_test.cpp', 's2_bucket_key_generator_test.cpp', 'sort_key_generator_test.cpp', @@ -119,8 +167,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/db/query/query_test_service_context', '$BUILD_DIR/mongo/db/query/sort_pattern', '$BUILD_DIR/mongo/db/record_id_helpers', - '$BUILD_DIR/mongo/db/sorter/sorter_stats', 'expression_params', - 'index_access_method', + 'key_generator', ], ) diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp index 4d454f2b5a3..63148efb8cf 100644 --- a/src/mongo/db/index/index_access_method.cpp +++ b/src/mongo/db/index/index_access_method.cpp @@ -31,7 +31,7 @@ #include "mongo/platform/basic.h" -#include "mongo/db/index/index_access_method.h" +#include "mongo/db/index/btree_access_method.h" #include <utility> #include <vector> @@ -42,17 +42,10 @@ #include "mongo/db/catalog/index_consistency.h" #include "mongo/db/client.h" #include "mongo/db/commands/server_status.h" -#include "mongo/db/concurrency/exception_util.h" +#include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/curop.h" -#include "mongo/db/index/2d_access_method.h" -#include "mongo/db/index/btree_access_method.h" -#include "mongo/db/index/fts_access_method.h" -#include "mongo/db/index/hash_access_method.h" #include "mongo/db/index/index_build_interceptor.h" #include "mongo/db/index/index_descriptor.h" -#include "mongo/db/index/s2_access_method.h" -#include "mongo/db/index/s2_bucket_access_method.h" -#include "mongo/db/index/wildcard_access_method.h" #include "mongo/db/jsobj.h" #include "mongo/db/keypattern.h" #include "mongo/db/operation_context.h" @@ -60,7 +53,6 @@ #include "mongo/db/repl/timestamp_block.h" #include "mongo/db/sorter/sorter.h" #include "mongo/db/storage/execution_context.h" -#include "mongo/db/storage/kv/kv_engine.h" #include "mongo/db/storage/storage_options.h" #include "mongo/logv2/log.h" #include "mongo/platform/atomic_word.h" @@ -79,35 +71,6 @@ MONGO_FAIL_POINT_DEFINE(hangIndexBuildDuringBulkLoadPhaseSecond); MONGO_FAIL_POINT_DEFINE(hangDuringIndexBuildBulkLoadYield); MONGO_FAIL_POINT_DEFINE(hangDuringIndexBuildBulkLoadYieldSecond); -/** - * Static factory method that constructs and returns an appropriate IndexAccessMethod depending on - * the type of the index. - */ -std::unique_ptr<IndexAccessMethod> IndexAccessMethod::make( - IndexCatalogEntry* entry, std::unique_ptr<SortedDataInterface> sortedDataInterface) { - auto desc = entry->descriptor(); - const std::string& type = desc->getAccessMethodName(); - if ("" == type) - return std::make_unique<BtreeAccessMethod>(entry, std::move(sortedDataInterface)); - else if (IndexNames::HASHED == type) - return std::make_unique<HashAccessMethod>(entry, std::move(sortedDataInterface)); - else if (IndexNames::GEO_2DSPHERE == type) - return std::make_unique<S2AccessMethod>(entry, std::move(sortedDataInterface)); - else if (IndexNames::GEO_2DSPHERE_BUCKET == type) - return std::make_unique<S2BucketAccessMethod>(entry, std::move(sortedDataInterface)); - else if (IndexNames::TEXT == type) - return std::make_unique<FTSAccessMethod>(entry, std::move(sortedDataInterface)); - else if (IndexNames::GEO_2D == type) - return std::make_unique<TwoDAccessMethod>(entry, std::move(sortedDataInterface)); - else if (IndexNames::WILDCARD == type) - return std::make_unique<WildcardAccessMethod>(entry, std::move(sortedDataInterface)); - LOGV2(20688, - "Can't find index for keyPattern {keyPattern}", - "Can't find index for keyPattern", - "keyPattern"_attr = desc->keyPattern()); - fassertFailed(31021); -} - namespace { /** @@ -136,10 +99,6 @@ public: builder.append("resumed", resumed.loadRelaxed()); builder.append("filesOpenedForExternalSort", sorterFileStats.opened.loadRelaxed()); builder.append("filesClosedForExternalSort", sorterFileStats.closed.loadRelaxed()); - builder.append("spilledRanges", sorterTracker.spilledRanges.loadRelaxed()); - builder.append("bytesSpilledUncompressed", - sorterTracker.bytesSpilledUncompressed.loadRelaxed()); - builder.append("bytesSpilled", sorterTracker.bytesSpilled.loadRelaxed()); return builder.obj(); } @@ -150,15 +109,11 @@ public: // This value should not exceed 'count'. AtomicWord<long long> resumed; - // Sorter statistics that are aggregate of all sorters. - SorterTracker sorterTracker; - // Number of times the external sorter opened/closed a file handle to spill data to disk. // This pair of counters in aggregate indicate the number of open file handles used by // the external sorter and may be useful in diagnosing situations where the process is // close to exhausting this finite resource. - SorterFileStats sorterFileStats = {&sorterTracker}; - + SorterFileStats sorterFileStats; } indexBulkBuilderSSS; /** @@ -177,9 +132,7 @@ SortOptions makeSortOptions(size_t maxMemoryUsageBytes, StringData dbName) { .TempDir(storageGlobalParams.dbpath + "/_tmp") .ExtSortAllowed() .MaxMemoryUsageBytes(maxMemoryUsageBytes) - .UseMemoryPool(true) .FileStats(&indexBulkBuilderSSS.sorterFileStats) - .Tracker(&indexBulkBuilderSSS.sorterTracker) .DBName(dbName.toString()); } @@ -412,13 +365,6 @@ void SortedDataIndexAccessMethod::removeOneKey(OperationContext* opCtx, try { _newInterface->unindex(opCtx, keyString, dupsAllowed); } catch (AssertionException& e) { - if (e.code() == ErrorCodes::DataCorruptionDetected) { - // DataCorruptionDetected errors are expected to have logged an error and added an entry - // to the health log with the stack trace at the location where the error was initially - // thrown. No need to do so again. - throw; - } - NamespaceString ns = _indexCatalogEntry->getNSSFromCatalog(opCtx); LOGV2(20683, "Assertion failure: _unindex failed on: {namespace} for index: {indexName}. " @@ -500,13 +446,9 @@ RecordId SortedDataIndexAccessMethod::findSingle(OperationContext* opCtx, void SortedDataIndexAccessMethod::validate(OperationContext* opCtx, int64_t* numKeys, IndexValidateResults* fullResults) const { - if (numKeys) { - long long keys = 0; - _newInterface->fullValidate(opCtx, &keys, fullResults); - *numKeys = keys; - } else { - _newInterface->fullValidate(opCtx, nullptr, fullResults); - } + long long keys = 0; + _newInterface->fullValidate(opCtx, &keys, fullResults); + *numKeys = keys; } bool SortedDataIndexAccessMethod::appendCustomStats(OperationContext* opCtx, @@ -638,8 +580,7 @@ Status SortedDataIndexAccessMethod::doUpdate(OperationContext* opCtx, // Add all new data keys into the index. for (const auto& keyString : ticket.added) { - bool dupsAllowed = (!_descriptor->prepareUnique() || !opCtx->isEnforcingConstraints()) && - ticket.dupsAllowed; + bool dupsAllowed = !_descriptor->prepareUnique() && ticket.dupsAllowed; auto status = _newInterface->insert(opCtx, keyString, dupsAllowed); if (!status.isOK()) return status; @@ -668,23 +609,6 @@ Ident* SortedDataIndexAccessMethod::getIdentPtr() const { return this->_newInterface.get(); } -void IndexAccessMethod::BulkBuilder::countNewBuildInStats() { - indexBulkBuilderSSS.count.addAndFetch(1); -} - -void IndexAccessMethod::BulkBuilder::countResumedBuildInStats() { - indexBulkBuilderSSS.count.addAndFetch(1); - indexBulkBuilderSSS.resumed.addAndFetch(1); -} - -SorterFileStats* IndexAccessMethod::BulkBuilder::bulkBuilderFileStats() { - return &indexBulkBuilderSSS.sorterFileStats; -} - -SorterTracker* IndexAccessMethod::BulkBuilder::bulkBuilderTracker() { - return &indexBulkBuilderSSS.sorterTracker; -} - class SortedDataIndexAccessMethod::BulkBuilderImpl final : public IndexAccessMethod::BulkBuilder { public: using Sorter = mongo::Sorter<KeyString::Value, mongo::NullValue>; @@ -700,6 +624,7 @@ public: Status insert(OperationContext* opCtx, const CollectionPtr& collection, + SharedBufferFragmentBuilder& pooledBuilder, const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, @@ -783,6 +708,7 @@ SortedDataIndexAccessMethod::BulkBuilderImpl::BulkBuilderImpl(SortedDataIndexAcc Status SortedDataIndexAccessMethod::BulkBuilderImpl::insert( OperationContext* opCtx, const CollectionPtr& collection, + SharedBufferFragmentBuilder& pooledBuilder, const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, @@ -796,7 +722,7 @@ Status SortedDataIndexAccessMethod::BulkBuilderImpl::insert( try { _iam->getKeys(opCtx, collection, - _sorter->memPool(), + pooledBuilder, obj, options.getKeysMode, GetKeysContext::kAddingKeys, @@ -917,23 +843,26 @@ void SortedDataIndexAccessMethod::BulkBuilderImpl::_yield(OperationContext* opCt auto locker = opCtx->lockState(); Locker::LockSnapshot snapshot; - locker->saveLockStateAndUnlock(&snapshot); - - // Track the number of yields in CurOp. - CurOp::get(opCtx)->yielded(); - - auto failPointHang = [opCtx, &ns](FailPoint* fp) { - fp->executeIf( - [fp](auto&&) { - LOGV2(5180600, "Hanging index build during bulk load yield"); - fp->pauseWhileSet(); - }, - [opCtx, &ns](auto&& config) { return config.getStringField("namespace") == ns.ns(); }); - }; - failPointHang(&hangDuringIndexBuildBulkLoadYield); - failPointHang(&hangDuringIndexBuildBulkLoadYieldSecond); - - locker->restoreLockState(opCtx, snapshot); + if (locker->saveLockStateAndUnlock(&snapshot)) { + + // Track the number of yields in CurOp. + CurOp::get(opCtx)->yielded(); + + auto failPointHang = [opCtx, &ns](FailPoint* fp) { + fp->executeIf( + [fp](auto&&) { + LOGV2(5180600, "Hanging index build during bulk load yield"); + fp->pauseWhileSet(); + }, + [opCtx, &ns](auto&& config) { + return config.getStringField("namespace") == ns.ns(); + }); + }; + failPointHang(&hangDuringIndexBuildBulkLoadYield); + failPointHang(&hangDuringIndexBuildBulkLoadYieldSecond); + + locker->restoreLockState(opCtx, snapshot); + } yieldable->restore(); } @@ -1215,16 +1144,6 @@ Status SortedDataIndexAccessMethod::_indexKeysOrWriteToSideTable( *keysInsertedOut += inserted; } } else { - // Ensure that our snapshot is compatible with the index's minimum visibile snapshot. - const auto minVisibleTimestamp = _indexCatalogEntry->getMinimumVisibleSnapshot(); - const auto readTimestamp = - opCtx->recoveryUnit()->getPointInTimeReadTimestamp(opCtx).value_or( - opCtx->recoveryUnit()->getCatalogConflictingTimestamp()); - if (minVisibleTimestamp && !readTimestamp.isNull() && - readTimestamp < *minVisibleTimestamp) { - throw WriteConflictException(); - } - int64_t numInserted = 0; status = insertKeysAndUpdateMultikeyPaths( opCtx, @@ -1280,16 +1199,12 @@ void SortedDataIndexAccessMethod::_unindexKeysOrWriteToSideTable( // are allowed in unique indexes, WiredTiger does not do blind unindexing, and instead confirms // that the recordid matches the element we are removing. // - // We need to disable blind-deletes if 'checkRecordId' is explicitly set 'On'. - options.dupsAllowed = options.dupsAllowed || checkRecordId == CheckRecordId::On; - - // Ensure that our snapshot is compatible with the index's minimum visibile snapshot. - const auto minVisibleTimestamp = _indexCatalogEntry->getMinimumVisibleSnapshot(); - const auto readTimestamp = opCtx->recoveryUnit()->getPointInTimeReadTimestamp(opCtx).value_or( - opCtx->recoveryUnit()->getCatalogConflictingTimestamp()); - if (minVisibleTimestamp && !readTimestamp.isNull() && readTimestamp < *minVisibleTimestamp) { - throw WriteConflictException(); - } + // We need to disable blind-deletes if 'checkRecordId' is explicitly set 'On', or for + // in-progress indexes, in order to force recordid-matching for unindex operations, since + // initial sync can build an index over a collection with duplicates. See SERVER-17487 for more + // details. + options.dupsAllowed = options.dupsAllowed || !_indexCatalogEntry->isReady(opCtx) || + (checkRecordId == CheckRecordId::On); int64_t removed = 0; Status status = removeKeys(opCtx, keys, options, &removed); diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h index 18d96a2218d..ad61cc5a25e 100644 --- a/src/mongo/db/index/index_access_method.h +++ b/src/mongo/db/index/index_access_method.h @@ -40,7 +40,6 @@ #include "mongo/db/jsobj.h" #include "mongo/db/operation_context.h" #include "mongo/db/record_id.h" -#include "mongo/db/sorter/sorter.h" #include "mongo/db/storage/sorted_data_interface.h" #include "mongo/db/yieldable.h" @@ -73,9 +72,6 @@ public: IndexAccessMethod() = default; virtual ~IndexAccessMethod() = default; - static std::unique_ptr<IndexAccessMethod> make( - IndexCatalogEntry* entry, std::unique_ptr<SortedDataInterface> sortedDataInterface); - /** * Equivalent to (but shorter and faster than): dynamic_cast<SortedDataIndexAccessMethod*>(this) */ @@ -189,6 +185,7 @@ public: */ virtual Status insert(OperationContext* opCtx, const CollectionPtr& collection, + SharedBufferFragmentBuilder& pooledBuilder, const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, @@ -221,12 +218,6 @@ public: * Persists on disk the keys that have been inserted using this BulkBuilder. */ virtual IndexStateInfo persistDataForShutdown() = 0; - - protected: - static void countNewBuildInStats(); - static void countResumedBuildInStats(); - static SorterFileStats* bulkBuilderFileStats(); - static SorterTracker* bulkBuilderTracker(); }; /** @@ -249,6 +240,23 @@ public: }; /** + * Factory class that constructs an IndexAccessMethod depending on the type of index. + */ +class IndexAccessMethodFactory { +public: + IndexAccessMethodFactory() = default; + virtual ~IndexAccessMethodFactory() = default; + + static IndexAccessMethodFactory* get(ServiceContext* service); + static IndexAccessMethodFactory* get(OperationContext* opCtx); + static void set(ServiceContext* service, + std::unique_ptr<IndexAccessMethodFactory> collectionFactory); + + virtual std::unique_ptr<IndexAccessMethod> make( + IndexCatalogEntry* entry, std::unique_ptr<SortedDataInterface> sortedDataInterface) = 0; +}; + +/** * Updates are two steps: verify that it's a valid update, and perform it. * prepareUpdate fills out the UpdateStatus and update actually applies it. */ diff --git a/src/mongo/db/index/multikey_paths.cpp b/src/mongo/db/index/index_access_method_factory.cpp index 52fdc54e99c..986ed2d8967 100644 --- a/src/mongo/db/index/multikey_paths.cpp +++ b/src/mongo/db/index/index_access_method_factory.cpp @@ -27,48 +27,27 @@ * it in the license file. */ -#include "mongo/db/index/multikey_paths.h" +#include "mongo/platform/basic.h" -#include "mongo/util/str.h" +#include "mongo/db/index/index_access_method.h" namespace mongo { +namespace { +const auto getFactory = + ServiceContext::declareDecoration<std::unique_ptr<IndexAccessMethodFactory>>(); +} // namespace -std::string multikeyPathsToString(MultikeyPaths paths) { - str::stream builder; - builder << "["; - auto pathIt = paths.begin(); - while (true) { - if (pathIt == paths.end()) { - break; - } - - builder << "{"; - - auto pathSet = *pathIt; - auto setIt = pathSet.begin(); - while (true) { - if (setIt == pathSet.end()) { - break; - } - - builder << *setIt; - if (++setIt == pathSet.end()) { - break; - } else { - builder << ","; - } - } - - builder << "}"; +IndexAccessMethodFactory* IndexAccessMethodFactory::get(ServiceContext* service) { + return getFactory(service).get(); +} - if (++pathIt == paths.end()) { - break; - } else { - builder << ","; - } - } - builder << "]"; - return builder; +IndexAccessMethodFactory* IndexAccessMethodFactory::get(OperationContext* opCtx) { + return getFactory(opCtx->getServiceContext()).get(); } +void IndexAccessMethodFactory::set(ServiceContext* service, + std::unique_ptr<IndexAccessMethodFactory> newFactory) { + auto& factory = getFactory(service); + factory = std::move(newFactory); +} } // namespace mongo diff --git a/src/mongo/db/index/index_access_method_factory_impl.cpp b/src/mongo/db/index/index_access_method_factory_impl.cpp new file mode 100644 index 00000000000..2f1610deee6 --- /dev/null +++ b/src/mongo/db/index/index_access_method_factory_impl.cpp @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2019-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_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kIndex + +#include "mongo/platform/basic.h" + +#include "mongo/db/index/index_access_method_factory_impl.h" + +#include "mongo/db/index/2d_access_method.h" +#include "mongo/db/index/btree_access_method.h" +#include "mongo/db/index/fts_access_method.h" +#include "mongo/db/index/hash_access_method.h" +#include "mongo/db/index/s2_access_method.h" +#include "mongo/db/index/s2_bucket_access_method.h" +#include "mongo/db/index/wildcard_access_method.h" +#include "mongo/logv2/log.h" + +namespace mongo { + +std::unique_ptr<IndexAccessMethod> IndexAccessMethodFactoryImpl::make( + IndexCatalogEntry* entry, std::unique_ptr<SortedDataInterface> sortedDataInterface) { + auto desc = entry->descriptor(); + const std::string& type = desc->getAccessMethodName(); + if ("" == type) + return std::make_unique<BtreeAccessMethod>(entry, std::move(sortedDataInterface)); + else if (IndexNames::HASHED == type) + return std::make_unique<HashAccessMethod>(entry, std::move(sortedDataInterface)); + else if (IndexNames::GEO_2DSPHERE == type) + return std::make_unique<S2AccessMethod>(entry, std::move(sortedDataInterface)); + else if (IndexNames::GEO_2DSPHERE_BUCKET == type) + return std::make_unique<S2BucketAccessMethod>(entry, std::move(sortedDataInterface)); + else if (IndexNames::TEXT == type) + return std::make_unique<FTSAccessMethod>(entry, std::move(sortedDataInterface)); + else if (IndexNames::GEO_2D == type) + return std::make_unique<TwoDAccessMethod>(entry, std::move(sortedDataInterface)); + else if (IndexNames::WILDCARD == type) + return std::make_unique<WildcardAccessMethod>(entry, std::move(sortedDataInterface)); + LOGV2(20688, + "Can't find index for keyPattern {keyPattern}", + "Can't find index for keyPattern", + "keyPattern"_attr = desc->keyPattern()); + fassertFailed(31021); +} + +} // namespace mongo diff --git a/src/mongo/db/index/multikey_paths_test.cpp b/src/mongo/db/index/index_access_method_factory_impl.h index 1ce040f90bb..dae1a2204ac 100644 --- a/src/mongo/db/index/multikey_paths_test.cpp +++ b/src/mongo/db/index/index_access_method_factory_impl.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2023-present MongoDB, Inc. + * Copyright (C) 2019-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, @@ -27,47 +27,19 @@ * it in the license file. */ -#include "mongo/db/index/multikey_paths.h" -#include "mongo/unittest/unittest.h" +#pragma once -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest +#include "mongo/db/index/index_access_method.h" namespace mongo { -namespace { -TEST(MultikeyPaths, PrintEmptyPaths) { - MultikeyPaths paths; - ASSERT_EQ(multikeyPathsToString(paths), "[]"); -} +class IndexAccessMethodFactoryImpl : public IndexAccessMethodFactory { +public: + IndexAccessMethodFactoryImpl() = default; + ~IndexAccessMethodFactoryImpl() = default; -TEST(MultikeyPaths, PrintEmptySetPaths) { - MultikeyPaths paths; - paths.resize(1); - ASSERT_EQ(multikeyPathsToString(paths), "[{}]"); -} + std::unique_ptr<IndexAccessMethod> make( + IndexCatalogEntry* entry, std::unique_ptr<SortedDataInterface> SortedDataInterface) final; +}; -TEST(MultikeyPaths, PrintEmptySetsPaths) { - MultikeyPaths paths; - paths.resize(2); - ASSERT_EQ(multikeyPathsToString(paths), "[{},{}]"); -} - -TEST(MultikeyPaths, PrintNonEmptySetPaths) { - MultikeyPaths paths; - paths.resize(2); - paths[1].insert(2); - ASSERT_EQ(multikeyPathsToString(paths), "[{},{2}]"); -} - -TEST(MultikeyPaths, PrintNonEmptySetsPaths) { - MultikeyPaths paths; - paths.resize(4); - paths[1].insert(2); - paths[3].insert(0); - paths[3].insert(1); - paths[3].insert(2); - ASSERT_EQ(multikeyPathsToString(paths), "[{},{2},{},{0,1,2}]"); -} - -} // namespace } // namespace mongo diff --git a/src/mongo/db/index/index_build_interceptor.cpp b/src/mongo/db/index/index_build_interceptor.cpp index c90ac194d8b..455496c0c98 100644 --- a/src/mongo/db/index/index_build_interceptor.cpp +++ b/src/mongo/db/index/index_build_interceptor.cpp @@ -37,7 +37,7 @@ #include "mongo/bson/bsonobj.h" #include "mongo/db/catalog_raii.h" -#include "mongo/db/concurrency/exception_util.h" +#include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/curop.h" #include "mongo/db/db_raii.h" #include "mongo/db/index/index_access_method.h" @@ -338,7 +338,7 @@ void IndexBuildInterceptor::_yield(OperationContext* opCtx, const Yieldable* yie auto locker = opCtx->lockState(); Locker::LockSnapshot snapshot; - locker->saveLockStateAndUnlock(&snapshot); + invariant(locker->saveLockStateAndUnlock(&snapshot)); // Track the number of yields in CurOp. diff --git a/src/mongo/db/index/index_descriptor.cpp b/src/mongo/db/index/index_descriptor.cpp index ee520ff5d15..3f4dbd088ed 100644 --- a/src/mongo/db/index/index_descriptor.cpp +++ b/src/mongo/db/index/index_descriptor.cpp @@ -38,8 +38,6 @@ #include "mongo/bson/simple_bsonelement_comparator.h" #include "mongo/bson/unordered_fields_bsonobj_comparator.h" #include "mongo/db/catalog/index_catalog_entry.h" -#include "mongo/db/index/column_key_generator.h" -#include "mongo/db/index/wildcard_key_generator.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/query/collation/collator_factory_interface.h" #include "mongo/db/server_options.h" @@ -111,11 +109,6 @@ constexpr StringData IndexDescriptor::kHiddenFieldName; constexpr StringData IndexDescriptor::kWeightsFieldName; constexpr StringData IndexDescriptor::kPrepareUniqueFieldName; -/** - * Constructs an IndexDescriptor object. Arguments: - * accessMethodName - one of the 'IndexNames::XXX' constants from index_names.cpp - * infoObj - options information - */ IndexDescriptor::IndexDescriptor(const std::string& accessMethodName, BSONObj infoObj) : _accessMethodName(accessMethodName), _indexType(IndexNames::nameToType(accessMethodName)), @@ -144,18 +137,12 @@ IndexDescriptor::IndexDescriptor(const std::string& accessMethodName, BSONObj in } if (BSONElement prepareUniqueElement = _infoObj[kPrepareUniqueFieldName]) { + uassert( + ErrorCodes::InvalidOptions, + "Index does not support the 'prepareUnique' field", + feature_flags::gCollModIndexUnique.isEnabled(serverGlobalParams.featureCompatibility)); _prepareUnique = prepareUniqueElement.trueValue(); } - - // If there is a wildcardProjection, compute and store the normalized version in - // '_normalizedProjection'. - BSONElement wildcardProjection = infoObj[IndexDescriptor::kPathProjectionFieldName]; - if (wildcardProjection) { - WildcardProjection indexPathProjection = WildcardKeyGenerator::createProjectionExecutor( - BSON("$**" << 1), wildcardProjection.Obj()); - _normalizedProjection = - indexPathProjection.exec()->serializeTransformation(boost::none).toBson(); - } } bool IndexDescriptor::isIndexVersionSupported(IndexVersion indexVersion) { @@ -175,6 +162,10 @@ IndexDescriptor::Comparison IndexDescriptor::compareIndexOptions( OperationContext* opCtx, const NamespaceString& ns, const IndexCatalogEntry* existingIndex) const { + // The compareIndexOptions method can only be reliably called on a candidate index which is + // being compared against an index that already exists in the catalog. + tassert(4765900, "This object must be a candidate index", !getEntry()); + auto existingIndexDesc = existingIndex->descriptor(); // We first check whether the key pattern is identical for both indexes. @@ -183,15 +174,8 @@ IndexDescriptor::Comparison IndexDescriptor::compareIndexOptions( return Comparison::kDifferent; } - // If the candidate has a wildcardProjection, we must compare the normalized versions, not the - // versions from the catalog which are kept as the user gave them and thus may be semantically - // identical to but syntactically different from the normalized form. There are no other types - // of index projections. Thus, if there is no projection, both the original and normalized - // projections will be empty BSON objects, so we can still do the comparison based on the - // normalized projection. static const UnorderedFieldsBSONObjComparator kUnorderedBSONCmp; - if (kUnorderedBSONCmp.evaluate(_normalizedProjection != - existingIndexDesc->_normalizedProjection)) { + if (kUnorderedBSONCmp.evaluate(_projection != existingIndexDesc->_normalizedProjection)) { return Comparison::kDifferent; } diff --git a/src/mongo/db/index/index_descriptor.h b/src/mongo/db/index/index_descriptor.h index 0f1b5089409..f4f48cb35b9 100644 --- a/src/mongo/db/index/index_descriptor.h +++ b/src/mongo/db/index/index_descriptor.h @@ -70,7 +70,6 @@ public: static constexpr StringData k2dsphereFinestIndexedLevel = "finestIndexedLevel"_sd; static constexpr StringData k2dsphereVersionFieldName = "2dsphereIndexVersion"_sd; static constexpr StringData kBackgroundFieldName = "background"_sd; - static constexpr StringData kBucketSizeFieldName = "bucketSize"_sd; static constexpr StringData kCollationFieldName = "collation"_sd; static constexpr StringData kDefaultLanguageFieldName = "default_language"_sd; static constexpr StringData kDropDuplicatesFieldName = "dropDups"_sd; @@ -120,36 +119,15 @@ public: } /** - * Return the path projection spec, if one exists. This is only applicable for wildcard ('$**') - * indexes. It is kept as originally specified by the createIndex() call, not normalized. - * - * It contains only the projection object that was contained in the field listed below from the - * original createIndex() parameters object, but it does NOT preserve the field name: - * - "wildcardProjection" (IndexDescriptor::kPathProjectionFieldName) - * - * This is set by the IndexDescriptor constructor and never changes after that. - * - * Example: db.a.createIndex({"$**":1}, {"name": "i1", "wildcardProjection": {"a.b": 1}}) - * return (unnormalized) object: {"a.b":{"$numberDouble":"1"}} + * Return the path projection spec, if one exists. This is only applicable for '$**' indexes. */ const BSONObj& pathProjection() const { return _projection; } /** - * Returns the normalized path projection spec, if one exists. This is only applicable for - * wildcard ('$**') indexes. It is the normalized version of the path projection and is used to - * determine whether a new index candidate from createIndex() duplicates an existing index. - * - * It contains the normalized projection object based on the original object that was contained - * in the field listed below from the original createIndex() parameters object, but it does NOT - * preserve the field name: - * - "wildcardProjection" (IndexDescriptor::kPathProjectionFieldName) - * - * This is set by the IndexDescriptor constructor and never changes after that. - * - * Example: db.a.createIndex({"$**":1}, {"name": "i1", "wildcardProjection": {"a.b": 1}}) - * return (normalized) object: {"a":{"b":true},"_id":false} + * Returns the normalized path projection spec, if one exists. This is only applicable for '$**' + * indexes. */ const BSONObj& normalizedPathProjection() const { return _normalizedProjection; @@ -273,6 +251,12 @@ public: } private: + // This method should only ever be called by WildcardAccessMethod, to set the + // '_normalizedProjection' for descriptors associated with an existing IndexCatalogEntry. + void _setNormalizedPathProjection(BSONObj&& proj) { + _normalizedProjection = std::move(proj); + } + // What access method should we use for this index? std::string _accessMethodName; @@ -285,8 +269,8 @@ private: int64_t _numFields; // How many fields are indexed? BSONObj _keyPattern; - BSONObj _projection; // for wildcardProjection / columnstoreProjection; never changes - BSONObj _normalizedProjection; // for wildcardProjection / columnstoreProjection; never changes + BSONObj _projection; + BSONObj _normalizedProjection; std::string _indexName; bool _isIdIndex; bool _sparse; @@ -305,6 +289,7 @@ private: friend class IndexCatalog; friend class IndexCatalogEntryImpl; friend class IndexCatalogEntryContainer; + friend class WildcardAccessMethod; }; } // namespace mongo diff --git a/src/mongo/db/index/multikey_paths.h b/src/mongo/db/index/multikey_paths.h index 91b9ad6e88d..4c29f45360e 100644 --- a/src/mongo/db/index/multikey_paths.h +++ b/src/mongo/db/index/multikey_paths.h @@ -29,12 +29,10 @@ #pragma once -#include <cstddef> -#include <functional> -#include <string> - #include <boost/container/flat_set.hpp> #include <boost/container/small_vector.hpp> +#include <cstddef> +#include <vector> #include "mongo/bson/bson_depth.h" @@ -69,5 +67,4 @@ using MultikeyComponents = boost::container::flat_set< constexpr std::size_t kFewCompoundIndexFields = 4; using MultikeyPaths = boost::container::small_vector<MultikeyComponents, kFewCompoundIndexFields>; -std::string multikeyPathsToString(MultikeyPaths paths); } // namespace mongo diff --git a/src/mongo/db/index/s2_key_generator_test.cpp b/src/mongo/db/index/s2_key_generator_test.cpp index 11e701243b5..e3e027049ac 100644 --- a/src/mongo/db/index/s2_key_generator_test.cpp +++ b/src/mongo/db/index/s2_key_generator_test.cpp @@ -34,7 +34,6 @@ #include "mongo/db/index/expression_keys_private.h" #include <algorithm> -#include <s2.h> #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/simple_bsonobj_comparator.h" @@ -686,27 +685,4 @@ TEST_F(S2KeyGeneratorTest, MidPathSingleElementArrayIsConsideredMultikey) { assertMultikeyPathsEqual(MultikeyPaths{{0U}, MultikeyComponents{}}, actualMultikeyPaths); } -// Test which verifies that the rounding functions used by s2 follow 'round to even' rounding -// behavior. -TEST_F(S2KeyGeneratorTest, VerifyS2RoundingBehavior) { - const double roundDownToEven = 2.5; - ASSERT_EQ(2, MathUtil::FastIntRound(roundDownToEven)); - ASSERT_EQ(2LL, MathUtil::FastInt64Round(roundDownToEven)); - - const double roundUpToEven = 3.5; - ASSERT_EQ(4, MathUtil::FastIntRound(roundUpToEven)); - ASSERT_EQ(4LL, MathUtil::FastInt64Round(roundUpToEven)); - - const double roundDownToEvenNegative = -3.5; - ASSERT_EQ(-4, MathUtil::FastIntRound(roundDownToEvenNegative)); - ASSERT_EQ(-4LL, MathUtil::FastInt64Round(roundDownToEvenNegative)); - - const double roundUpToEvenNegative = -2.5; - ASSERT_EQ(-2, MathUtil::FastIntRound(roundUpToEvenNegative)); - ASSERT_EQ(-2LL, MathUtil::FastInt64Round(roundUpToEvenNegative)); - - const double point = 944920918.5; - ASSERT_EQ(944920918, MathUtil::FastIntRound(point)); - ASSERT_EQ(944920918LL, MathUtil::FastInt64Round(point)); -} } // namespace diff --git a/src/mongo/db/index/skipped_record_tracker.cpp b/src/mongo/db/index/skipped_record_tracker.cpp index ebbc7b97516..cd6ebef448d 100644 --- a/src/mongo/db/index/skipped_record_tracker.cpp +++ b/src/mongo/db/index/skipped_record_tracker.cpp @@ -32,7 +32,7 @@ #include "mongo/db/index/skipped_record_tracker.h" #include "mongo/db/catalog/collection.h" -#include "mongo/db/concurrency/exception_util.h" +#include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/curop.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/multi_key_path_tracker.h" diff --git a/src/mongo/db/index/wildcard_access_method.cpp b/src/mongo/db/index/wildcard_access_method.cpp index cdab87cbb04..4ea3cfc261c 100644 --- a/src/mongo/db/index/wildcard_access_method.cpp +++ b/src/mongo/db/index/wildcard_access_method.cpp @@ -45,7 +45,15 @@ WildcardAccessMethod::WildcardAccessMethod(IndexCatalogEntry* wildcardState, _indexCatalogEntry->getCollator(), getSortedDataInterface()->getKeyStringVersion(), getSortedDataInterface()->getOrdering(), - getSortedDataInterface()->rsKeyFormat()) {} + getSortedDataInterface()->rsKeyFormat()) { + // Normalize the 'wildcardProjection' index option to facilitate its comparison as part of + // index signature. + if (!_descriptor->pathProjection().isEmpty()) { + auto* projExec = getWildcardProjection()->exec(); + wildcardState->descriptor()->_setNormalizedPathProjection( + projExec->serializeTransformation(boost::none).toBson()); + } +} bool WildcardAccessMethod::shouldMarkIndexAsMultikey(size_t numberOfKeys, const KeyStringSet& multikeyMetadataKeys, |
