diff options
author | Benety Goh <benety@mongodb.com> | 2021-06-02 18:31:57 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-07-20 19:56:18 +0000 |
commit | 1928c8d20941bdaf094507d0b1616ca58d530330 (patch) | |
tree | b5990bdf73fa240de7f106a64bbace3e2bd2bb64 | |
parent | 522024f39b5bc1a4f5a7b49306d20b10bf87fe80 (diff) | |
download | mongo-1928c8d20941bdaf094507d0b1616ca58d530330.tar.gz |
SERVER-56877 track uncommitted changes to multikey paths with a counter
This change applies the technique in commit 3566db153ea61fb10d3ef11ea917fc7bc93eac4d
for the isMultikey boolean flags to the reader and writer multikey paths.
This commit is designed to support pre-5.0 backports and will be superseded by a
new patch that uses the Collection metadata introduced in 5.0.
(cherry picked from commit 5d8ac7127e207e0ef1aeaa90dcc62cda6896b71e)
-rw-r--r-- | jstests/noPassthrough/validate_multikey_compound.js | 6 | ||||
-rw-r--r-- | jstests/noPassthrough/validate_multikey_restart.js | 16 | ||||
-rw-r--r-- | jstests/noPassthrough/validate_multikey_stepdown.js | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_catalog_entry_impl.cpp | 37 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_catalog_entry_impl.h | 14 |
5 files changed, 53 insertions, 26 deletions
diff --git a/jstests/noPassthrough/validate_multikey_compound.js b/jstests/noPassthrough/validate_multikey_compound.js index e354b9af3de..f923b94e562 100644 --- a/jstests/noPassthrough/validate_multikey_compound.js +++ b/jstests/noPassthrough/validate_multikey_compound.js @@ -71,9 +71,9 @@ assert(result.indexDetails.c_hashed.valid, tojson(result)); // Check multikey index. assert.eq(6, result.keysPerIndex.a_1_b_1, tojson(result)); -assert(!result.indexDetails.a_1_b_1.valid, tojson(result)); +assert(result.indexDetails.a_1_b_1.valid, tojson(result)); -assert(!result.valid, tojson(result)); +assert(result.valid, tojson(result)); -rst.stopSet(/*signal=*/undefined, /*forRestart=*/undefined, {skipValidation: true}); +rst.stopSet(); })(); diff --git a/jstests/noPassthrough/validate_multikey_restart.js b/jstests/noPassthrough/validate_multikey_restart.js index 05cb8d2eb48..e57f84c2229 100644 --- a/jstests/noPassthrough/validate_multikey_restart.js +++ b/jstests/noPassthrough/validate_multikey_restart.js @@ -56,22 +56,14 @@ docs = testColl.find().sort({_id: 1}).toArray(); assert.eq(1, docs.length, 'too many docs in collection: ' + tojson(docs)); assert.eq(1, docs[0]._id, 'unexpected document content in collection: ' + tojson(docs)); -// TODO(SERVER-56877): When the catalog inconsistency is fixed, we should expect to get -// only one document from the query, which matches the query results before restarting. jsTestLog('Checking multikey query after restart'); multikeyQueryDocs = testColl.find({a: {$in: [4, 5, 6]}}).toArray(); -assert.eq(3, +assert.eq(1, multikeyQueryDocs.length, 'too many docs in multikey query result: ' + tojson(multikeyQueryDocs)); assert.eq(1, multikeyQueryDocs[0]._id, 'unexpected document content in multikey query result: ' + tojson(multikeyQueryDocs)); -assert.eq(1, - multikeyQueryDocs[1]._id, - 'unexpected document content in multikey query result: ' + tojson(multikeyQueryDocs)); -assert.eq(1, - multikeyQueryDocs[2]._id, - 'unexpected document content in multikey query result: ' + tojson(multikeyQueryDocs)); jsTestLog('Validating collection after restart'); const result = assert.commandWorked(testColl.validate({full: true})); @@ -90,9 +82,9 @@ assert(result.indexDetails.b_hashed.valid, tojson(result)); // Check multikey index. assert.eq(3, result.keysPerIndex.a_1, tojson(result)); -assert(!result.indexDetails.a_1.valid, tojson(result)); +assert(result.indexDetails.a_1.valid, tojson(result)); -assert(!result.valid, tojson(result)); +assert(result.valid, tojson(result)); -rst.stopSet(/*signal=*/undefined, /*forRestart=*/undefined, {skipValidation: true}); +rst.stopSet(); })(); diff --git a/jstests/noPassthrough/validate_multikey_stepdown.js b/jstests/noPassthrough/validate_multikey_stepdown.js index b05ec37fa06..9ca5012a656 100644 --- a/jstests/noPassthrough/validate_multikey_stepdown.js +++ b/jstests/noPassthrough/validate_multikey_stepdown.js @@ -121,9 +121,9 @@ assert(result.indexDetails._id_.valid, tojson(result)); // Check geo index. assert.lt(1, result.keysPerIndex.geo_2dsphere, tojson(result)); -assert(!result.indexDetails.geo_2dsphere.valid, tojson(result)); +assert(result.indexDetails.geo_2dsphere.valid, tojson(result)); -assert(!result.valid, tojson(result)); +assert(result.valid, tojson(result)); -rst.stopSet(/*signal=*/undefined, /*forRestart=*/undefined, {skipValidation: true}); +rst.stopSet(); })(); diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp index 7ba3a22f1e4..4189698ea09 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp @@ -78,10 +78,12 @@ IndexCatalogEntryImpl::IndexCatalogEntryImpl(OperationContext* const opCtx, { stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); - const bool isMultikey = _catalogIsMultikey(opCtx, collection, &_indexMultikeyPathsForRead); + const bool isMultikey = _catalogIsMultikey(opCtx, collection, &_indexMultikeyPathsForWrite); _isMultikeyForRead.store(isMultikey); _isMultikeyForWrite.store(isMultikey); - _indexTracksMultikeyPathsInCatalog = !_indexMultikeyPathsForRead.empty(); + _indexMultikeyPathsForRead = _indexMultikeyPathsForWrite; + _numUncommittedCatalogMultikeyUpdates = 0; + _indexTracksMultikeyPathsInCatalog = !_indexMultikeyPathsForWrite.empty(); } auto nss = DurableCatalog::get(opCtx)->getEntry(_catalogId).nss; @@ -184,12 +186,12 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, if (_indexTracksMultikeyPathsInCatalog) { stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); - invariant(multikeyPaths.size() == _indexMultikeyPathsForRead.size()); + invariant(multikeyPaths.size() == _indexMultikeyPathsForWrite.size()); bool newPathIsMultikey = false; for (size_t i = 0; i < multikeyPaths.size(); ++i) { - if (!std::includes(_indexMultikeyPathsForRead[i].begin(), - _indexMultikeyPathsForRead[i].end(), + if (!std::includes(_indexMultikeyPathsForWrite[i].begin(), + _indexMultikeyPathsForWrite[i].end(), multikeyPaths[i].begin(), multikeyPaths[i].end())) { // If 'multikeyPaths' contains a new path component that causes this index to be @@ -274,10 +276,12 @@ void IndexCatalogEntryImpl::forceSetMultikey(OperationContext* const opCtx, { stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); const bool isMultikeyInCatalog = - _catalogIsMultikey(opCtx, coll, &_indexMultikeyPathsForRead); + _catalogIsMultikey(opCtx, coll, &_indexMultikeyPathsForWrite); _isMultikeyForRead.store(isMultikeyInCatalog); _isMultikeyForWrite.store(isMultikeyInCatalog); - _indexTracksMultikeyPathsInCatalog = !_indexMultikeyPathsForRead.empty(); + _indexMultikeyPathsForRead = _indexMultikeyPathsForWrite; + _numUncommittedCatalogMultikeyUpdates = 0; + _indexTracksMultikeyPathsInCatalog = !_indexMultikeyPathsForWrite.empty(); } // Since multikey metadata has changed, invalidate the query cache. @@ -394,9 +398,13 @@ void IndexCatalogEntryImpl::_catalogSetMultikey(OperationContext* opCtx, _isMultikeyForRead.store(true); if (_indexTracksMultikeyPathsInCatalog) { stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); + if (_numUncommittedCatalogMultikeyUpdates == 0) { + _indexMultikeyPathsForRead = _indexMultikeyPathsForWrite; + } for (size_t i = 0; i < multikeyPaths.size(); ++i) { _indexMultikeyPathsForRead[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end()); } + _numUncommittedCatalogMultikeyUpdates++; } if (indexMetadataHasChanged) { LOGV2_DEBUG(4718705, @@ -412,6 +420,21 @@ void IndexCatalogEntryImpl::_catalogSetMultikey(OperationContext* opCtx, // transaction successfully commits. Only after this point may a writer optimize out // flipping multikey. _isMultikeyForWrite.store(true); + if (_indexTracksMultikeyPathsInCatalog) { + stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); + _indexMultikeyPathsForWrite = _indexMultikeyPathsForRead; + _numUncommittedCatalogMultikeyUpdates--; + invariant(_numUncommittedCatalogMultikeyUpdates >= 0, + str::stream() << _ident << descriptor()->indexName()); + } + }); + opCtx->recoveryUnit()->onRollback([this] { + if (_indexTracksMultikeyPathsInCatalog) { + stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); + _numUncommittedCatalogMultikeyUpdates--; + invariant(_numUncommittedCatalogMultikeyUpdates >= 0, + str::stream() << _ident << ":" << descriptor()->indexName()); + } }); } diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.h b/src/mongo/db/catalog/index_catalog_entry_impl.h index 6a6f84ff3aa..fbfbcaf6d4a 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.h +++ b/src/mongo/db/catalog/index_catalog_entry_impl.h @@ -248,7 +248,8 @@ private: // this point, future writers do not need to update the catalog. mutable AtomicWord<bool> _isMultikeyForWrite; - // Controls concurrent access to '_indexMultikeyPathsForRead'. + // Controls concurrent access to '_indexMultikeyPathsForRead', '_indexMultikeyPathsForWrite', + // and '_numUncommittedCatalogMultikeyUpdates'. // We acquire this mutex rather than the RESOURCE_METADATA lock as a performance optimization // so that it is cheaper to detect whether there is actually any path-level multikey // information to update or not. @@ -263,6 +264,17 @@ private: // causes the index to be multikey. mutable MultikeyPaths _indexMultikeyPathsForRead; // May include paths not committed to catalog. + mutable MultikeyPaths + _indexMultikeyPathsForWrite; // Paths in catalog updated by a transaction commit. + + // If this counter is greater than zero, we have state in '_indexMultikeyPathsForRead' that have + // not been successfully updated in the durable catalog. This counter corresponds to the number + // of calls _catalogSetMultikey() with open storage transactions. + // It is possible for the read paths to contain path info that are not present in the durable + // catalog, but all the durable paths must be present in the in-memory state (read paths). This + // may lead to less than optimal query plans, but should not result in incorrect plans due to + // insufficient multikey coverage. + mutable int _numUncommittedCatalogMultikeyUpdates = 0; // The earliest snapshot that is allowed to read this index. boost::optional<Timestamp> _minVisibleSnapshot; |