diff options
author | Pavi Vetriselvan <pvselvan@umich.edu> | 2019-07-02 14:24:43 -0400 |
---|---|---|
committer | Pavi Vetriselvan <pvselvan@umich.edu> | 2019-07-02 14:24:43 -0400 |
commit | f93894b2307f7eec7cdc0b53d3ac58f971837328 (patch) | |
tree | 96f92a83bebdb2443dd77f4451f4bc4b20e04238 /src/mongo/db | |
parent | 89a719b5fcac9bbe5ce9369939cba2bc24735b26 (diff) | |
parent | e549b451eb4ba9aa11f79a2356061f5dab6c943b (diff) | |
download | mongo-f93894b2307f7eec7cdc0b53d3ac58f971837328.tar.gz |
Merge branch 'v4.2' of github.com:mongodb/mongo into v4.2
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/catalog/index_catalog_entry_impl.cpp | 78 |
1 files changed, 58 insertions, 20 deletions
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp index c7616877e04..6c6370f62ed 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp @@ -42,6 +42,7 @@ #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" +#include "mongo/db/logical_clock.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/multi_key_path_tracker.h" @@ -267,31 +268,68 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, // CollectionCatalogEntry::setIndexIsMultikey() requires that we discard the path-level // multikey information in order to avoid unintentionally setting path-level multikey // information on an index created before 3.4. - const bool indexMetadataHasChanged = DurableCatalog::get(opCtx)->setIndexIsMultikey( - opCtx, _collection->ns(), _descriptor->indexName(), paths); - - // When the recovery unit commits, update the multikey paths if needed and clear the plan cache - // if the index metadata has changed. - opCtx->recoveryUnit()->onCommit( - [this, multikeyPaths, indexMetadataHasChanged](boost::optional<Timestamp>) { - _isMultikey.store(true); - - if (_indexTracksPathLevelMultikeyInfo) { - stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex); - for (size_t i = 0; i < multikeyPaths.size(); ++i) { - _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end()); - } + bool indexMetadataHasChanged; + + // The commit handler for a transaction that sets the multikey flag. When the recovery unit + // commits, update the multikey paths if needed and clear the plan cache if the index metadata + // has changed. + auto onMultikeyCommitFn = [this, multikeyPaths](bool indexMetadataHasChanged) { + _isMultikey.store(true); + + if (_indexTracksPathLevelMultikeyInfo) { + stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex); + for (size_t i = 0; i < multikeyPaths.size(); ++i) { + _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end()); } + } - if (indexMetadataHasChanged && _infoCache) { - LOG(1) << _ns << ": clearing plan cache - index " << _descriptor->keyPattern() - << " set to multi key."; - _infoCache->clearQueryCache(); + if (indexMetadataHasChanged && _infoCache) { + LOG(1) << _ns << ": clearing plan cache - index " << _descriptor->keyPattern() + << " set to multi key."; + _infoCache->clearQueryCache(); + } + }; + + // If we are inside a multi-document transaction, we write the on-disk multikey update in a + // separate transaction so that it will not generate prepare conflicts with other operations + // that try to set the multikey flag. In general, it should always be safe to update the + // multikey flag earlier than necessary, and so we are not concerned with the atomicity of the + // multikey flag write and the parent transaction. We can do this write separately and commit it + // before the parent transaction commits. + auto txnParticipant = TransactionParticipant::get(opCtx); + if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) { + TransactionParticipant::SideTransactionBlock sideTxn(opCtx); + writeConflictRetry(opCtx, "set index multikey", _collection->ns().ns(), [&] { + WriteUnitOfWork wuow(opCtx); + auto writeTs = LogicalClock::get(opCtx)->getClusterTime().asTimestamp(); + auto status = opCtx->recoveryUnit()->setTimestamp(writeTs); + if (status.code() == ErrorCodes::BadValue) { + log() << "Temporarily could not timestamp the multikey catalog write, retrying. " + << status.reason(); + throw WriteConflictException(); } + fassert(31164, status); + indexMetadataHasChanged = DurableCatalog::get(opCtx)->setIndexIsMultikey( + opCtx, _collection->ns(), _descriptor->indexName(), paths); + opCtx->recoveryUnit()->onCommit([onMultikeyCommitFn, indexMetadataHasChanged]( + boost::optional<Timestamp>) { onMultikeyCommitFn(indexMetadataHasChanged); }); + wuow.commit(); }); + } else { + indexMetadataHasChanged = DurableCatalog::get(opCtx)->setIndexIsMultikey( + opCtx, _collection->ns(), _descriptor->indexName(), paths); + } - // Keep multikey changes in memory to correctly service later reads using this index. - auto txnParticipant = TransactionParticipant::get(opCtx); + opCtx->recoveryUnit()->onCommit([onMultikeyCommitFn, indexMetadataHasChanged]( + boost::optional<Timestamp>) { onMultikeyCommitFn(indexMetadataHasChanged); }); + + // Within a multi-document transaction, reads should be able to see the effect of previous + // writes done within that transaction. If a previous write in a transaction has set the index + // to be multikey, then a subsequent read MUST know that fact in order to return correct + // results. This is true in general for multikey writes. Since we don't update the in-memory + // multikey flag until after the transaction commits, we track extra information here to let + // subsequent readers within the same transaction know if this index was set as multikey by a + // previous write in the transaction. if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) { txnParticipant.addUncommittedMultikeyPathInfo( MultikeyPathInfo{_collection->ns(), _descriptor->indexName(), std::move(paths)}); |