diff options
author | Maria van Keulen <maria.vankeulen@mongodb.com> | 2019-09-13 20:17:27 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-09-13 20:17:27 +0000 |
commit | ade1c1f91effa7b86aa8b82daa8548d360b2bc68 (patch) | |
tree | c913ea7c9085f619beb300d2574d05291d6d1cd1 /src/mongo/db | |
parent | 21778d3c962bd069b29a3c2241bddbc625b9d959 (diff) | |
download | mongo-ade1c1f91effa7b86aa8b82daa8548d360b2bc68.tar.gz |
SERVER-39708 Optimize Btree multikey updates
(cherry picked from commit bfac8d909877600c395dccc575effbfbae76a82a)
Diffstat (limited to 'src/mongo/db')
-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 76dad0efcc2..61c3abd9471 100644 --- a/src/mongo/db/catalog/multi_index_block.cpp +++ b/src/mongo/db/catalog/multi_index_block.cpp @@ -828,6 +828,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 a035341c272..ace415aa6dd 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) { @@ -368,12 +389,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. @@ -381,6 +407,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 efbe6fa9459..d280d4e4234 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; |