diff options
author | Maria van Keulen <maria.vankeulen@mongodb.com> | 2019-09-10 21:59:36 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-09-10 21:59:36 +0000 |
commit | bfac8d909877600c395dccc575effbfbae76a82a (patch) | |
tree | 92a6502d4ee5e0b2168e4bbde5e0313a47df7381 /src | |
parent | 3f6ba750fc8374968c3c06e31c67ac2839e9a736 (diff) | |
download | mongo-bfac8d909877600c395dccc575effbfbae76a82a.tar.gz |
SERVER-39708 Optimize Btree multikey updates
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/catalog/multi_index_block.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/index/index_build_interceptor.cpp | 34 | ||||
-rw-r--r-- | src/mongo/db/index/index_build_interceptor.h | 3 | ||||
-rw-r--r-- | src/mongo/db/multi_key_path_tracker.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/multi_key_path_tracker.h | 6 |
5 files changed, 56 insertions, 2 deletions
diff --git a/src/mongo/db/catalog/multi_index_block.cpp b/src/mongo/db/catalog/multi_index_block.cpp index 1eb834bd5bb..ee75f9ed64e 100644 --- a/src/mongo/db/catalog/multi_index_block.cpp +++ b/src/mongo/db/catalog/multi_index_block.cpp @@ -838,6 +838,12 @@ Status MultiIndexBlock::commit(OperationContext* opCtx, boost::optional<MultikeyPaths>(MultikeyPathTracker::get(opCtx).getMultikeyPathInfo( collection->ns(), _indexes[i].block->getIndexName())); if (multikeyPaths) { + // Upon reaching this point, multikeyPaths must either have at least one (possibly + // empty) element unless it is of an index with a type that doesn't support tracking + // multikeyPaths via the multikeyPaths array or has "special" multikey semantics. + invariant(!multikeyPaths.get().empty() || + !IndexBuildInterceptor::typeCanFastpathMultikeyUpdates( + _indexes[i].block->getEntry()->descriptor()->getIndexType())); _indexes[i].block->getEntry()->setMultikey(opCtx, *multikeyPaths); } } diff --git a/src/mongo/db/index/index_build_interceptor.cpp b/src/mongo/db/index/index_build_interceptor.cpp index c55a569442f..048163d6c6e 100644 --- a/src/mongo/db/index/index_build_interceptor.cpp +++ b/src/mongo/db/index/index_build_interceptor.cpp @@ -55,6 +55,18 @@ namespace mongo { MONGO_FAIL_POINT_DEFINE(hangDuringIndexBuildDrainYield); +bool IndexBuildInterceptor::typeCanFastpathMultikeyUpdates(IndexType indexType) { + // Ensure no new indexes are added without considering whether they use the multikeyPaths + // vector. + invariant(indexType == INDEX_BTREE || indexType == INDEX_2D || indexType == INDEX_HAYSTACK || + indexType == INDEX_2DSPHERE || indexType == INDEX_TEXT || indexType == INDEX_HASHED || + indexType == INDEX_WILDCARD); + // Only BTREE indexes are guaranteed to use the multikeyPaths vector. Other index types either + // do not track path-level multikey information or have "special" handling of multikey + // information. + return (indexType == INDEX_BTREE); +} + IndexBuildInterceptor::IndexBuildInterceptor(OperationContext* opCtx, IndexCatalogEntry* entry) : _indexCatalogEntry(entry), _sideWritesTable( @@ -64,6 +76,15 @@ IndexBuildInterceptor::IndexBuildInterceptor(OperationContext* opCtx, IndexCatal if (entry->descriptor()->unique()) { _duplicateKeyTracker = std::make_unique<DuplicateKeyTracker>(opCtx, entry); } + // `mergeMultikeyPaths` is sensitive to the two inputs having the same multikey + // "shape". Initialize `_multikeyPaths` with the right shape from the IndexCatalogEntry. + auto indexType = entry->descriptor()->getIndexType(); + if (typeCanFastpathMultikeyUpdates(indexType)) { + auto numFields = entry->descriptor()->getNumFields(); + _multikeyPaths = MultikeyPaths{}; + auto it = _multikeyPaths->begin(); + _multikeyPaths->insert(it, numFields, {}); + } } void IndexBuildInterceptor::deleteTemporaryTables(OperationContext* opCtx) { @@ -379,12 +400,17 @@ Status IndexBuildInterceptor::sideWrite(OperationContext* opCtx, Op op, int64_t* const numKeysOut) { invariant(opCtx->lockState()->inAWriteUnitOfWork()); - // Maintain parity with IndexAccessMethods handling of key counting. Only include // `multikeyMetadataKeys` when inserting. *numKeysOut = keys.size() + (op == Op::kInsert ? multikeyMetadataKeys.size() : 0); - if (op == Op::kInsert) { + auto indexType = _indexCatalogEntry->descriptor()->getIndexType(); + + // No need to take the multikeyPaths mutex if this is a trivial multikey update. + bool canBypassMultikeyMutex = typeCanFastpathMultikeyUpdates(indexType) && + MultikeyPathTracker::isMultikeyPathsTrivial(multikeyPaths); + + if (op == Op::kInsert && !canBypassMultikeyMutex) { // SERVER-39705: It's worth noting that a document may not generate any keys, but be // described as being multikey. This step must be done to maintain parity with `validate`s // expectations. @@ -392,6 +418,10 @@ Status IndexBuildInterceptor::sideWrite(OperationContext* opCtx, if (_multikeyPaths) { MultikeyPathTracker::mergeMultikeyPaths(&_multikeyPaths.get(), multikeyPaths); } else { + // All indexes that support pre-initialization of _multikeyPaths during + // IndexBuildInterceptor construction time should have been initialized already. + invariant(!typeCanFastpathMultikeyUpdates(indexType)); + // `mergeMultikeyPaths` is sensitive to the two inputs having the same multikey // "shape". Initialize `_multikeyPaths` with the right shape from the first result. _multikeyPaths = multikeyPaths; diff --git a/src/mongo/db/index/index_build_interceptor.h b/src/mongo/db/index/index_build_interceptor.h index 4bdaaebf6f0..97dca244576 100644 --- a/src/mongo/db/index/index_build_interceptor.h +++ b/src/mongo/db/index/index_build_interceptor.h @@ -48,6 +48,8 @@ class IndexBuildInterceptor { public: enum class Op { kInsert, kDelete }; + static bool typeCanFastpathMultikeyUpdates(IndexType type); + /** * Creates a temporary table for writes during an index build. Additionally creates a temporary * table to store any duplicate key constraint violations found during the build, if the index @@ -91,6 +93,7 @@ public: */ Status checkDuplicateKeyConstraints(OperationContext* opCtx) const; + /** * Performs a resumable scan on the side writes table, and either inserts or removes each key * from the underlying IndexAccessMethod. This will only insert as many records as are visible diff --git a/src/mongo/db/multi_key_path_tracker.cpp b/src/mongo/db/multi_key_path_tracker.cpp index d78271932e5..003a600e6bf 100644 --- a/src/mongo/db/multi_key_path_tracker.cpp +++ b/src/mongo/db/multi_key_path_tracker.cpp @@ -68,6 +68,15 @@ void MultikeyPathTracker::mergeMultikeyPaths(MultikeyPaths* toMergeInto, } } +bool MultikeyPathTracker::isMultikeyPathsTrivial(const MultikeyPaths& paths) { + for (auto&& path : paths) { + if (!path.empty()) { + return false; + } + } + return true; +} + void MultikeyPathTracker::addMultikeyPathInfo(MultikeyPathInfo info) { invariant(_trackMultikeyPathInfo); // Merge the `MultikeyPathInfo` input into the accumulated value being tracked for the diff --git a/src/mongo/db/multi_key_path_tracker.h b/src/mongo/db/multi_key_path_tracker.h index 1fb1ee20fac..b155bc1fe58 100644 --- a/src/mongo/db/multi_key_path_tracker.h +++ b/src/mongo/db/multi_key_path_tracker.h @@ -63,6 +63,12 @@ public: static void mergeMultikeyPaths(MultikeyPaths* toMergeInto, const MultikeyPaths& newPaths); + /** + * Returns whether paths contains only empty sets, i.e., {{}, {}, {}}. This includes the case + * where the MultikeyPaths vector itself has no elements, e.g., {}. + */ + static bool isMultikeyPathsTrivial(const MultikeyPaths& paths); + // Decoration requires a default constructor. MultikeyPathTracker() = default; |