summaryrefslogtreecommitdiff
path: root/src/mongo/db/index
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
commit4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch)
tree1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/index
parentaa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff)
parent8f0827553e09872941945a093b647a4211a9db7f (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/SConscript123
-rw-r--r--src/mongo/db/index/index_access_method.cpp157
-rw-r--r--src/mongo/db/index/index_access_method.h28
-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.cpp72
-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.cpp4
-rw-r--r--src/mongo/db/index/index_descriptor.cpp34
-rw-r--r--src/mongo/db/index/index_descriptor.h39
-rw-r--r--src/mongo/db/index/multikey_paths.h7
-rw-r--r--src/mongo/db/index/s2_key_generator_test.cpp24
-rw-r--r--src/mongo/db/index/skipped_record_tracker.cpp2
-rw-r--r--src/mongo/db/index/wildcard_access_method.cpp10
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,