diff options
-rw-r--r-- | src/mongo/db/catalog/index_catalog_entry_impl.cpp | 86 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_catalog_entry_impl.h | 7 |
2 files changed, 55 insertions, 38 deletions
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp index 56114d22e91..aca5e622a02 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp @@ -240,6 +240,19 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, return; } + if (opCtx->inMultiDocumentTransaction()) { + auto status = _setMultikeyInMultiDocumentTransaction(opCtx, paths); + // Retry without side transaction. + if (!status.isOK()) { + _catalogSetMultikey(opCtx, paths); + } + } else { + _catalogSetMultikey(opCtx, paths); + } +} + +Status IndexCatalogEntryImpl::_setMultikeyInMultiDocumentTransaction( + OperationContext* opCtx, const MultikeyPaths& multikeyPaths) { // 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 @@ -247,49 +260,46 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, // 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); - while (opCtx->inMultiDocumentTransaction()) { - TransactionParticipant::SideTransactionBlock sideTxn(opCtx); - - // If the index is not visible within the side transaction, the index may have been created, - // but not committed, in the parent transaction. Therefore, we abandon the side transaction - // and set the multikey flag in the parent transaction. - if (!_catalogIsPresent(opCtx)) { - break; - } - writeConflictRetry(opCtx, "set index multikey", ns().ns(), [&] { - WriteUnitOfWork wuow(opCtx); - - // If we have a prepare optime for recovery, then we always use that. During recovery of - // prepared transactions, the logical clock may not yet be initialized, so we use the - // prepare timestamp of the transaction for this write. This is safe since the prepare - // timestamp is always <= the commit timestamp of a transaction, which satisfies the - // correctness requirement for multikey writes i.e. they must occur at or before the - // first write that set the multikey flag. - auto recoveryPrepareOpTime = txnParticipant.getPrepareOpTimeForRecovery(); - Timestamp writeTs = recoveryPrepareOpTime.isNull() - ? LogicalClock::get(opCtx)->getClusterTime().asTimestamp() - : recoveryPrepareOpTime.getTimestamp(); - - auto status = opCtx->recoveryUnit()->setTimestamp(writeTs); - if (status.code() == ErrorCodes::BadValue) { - LOGV2(20352, - "Temporarily could not timestamp the multikey catalog write, retrying. " - "{status_reason}", - "status_reason"_attr = status.reason()); - throw WriteConflictException(); - } - fassert(31164, status); + TransactionParticipant::SideTransactionBlock sideTxn(opCtx); - _catalogSetMultikey(opCtx, paths); + // If the index is not visible within the side transaction, the index may have been created, + // but not committed, in the parent transaction. Therefore, we abandon the side transaction + // and set the multikey flag in the parent transaction. + if (!_catalogIsPresent(opCtx)) { + return {ErrorCodes::SnapshotUnavailable, "index not visible in side transaction"}; + } - wuow.commit(); - }); + writeConflictRetry(opCtx, "set index multikey", ns().ns(), [&] { + WriteUnitOfWork wuow(opCtx); + + // If we have a prepare optime for recovery, then we always use that. During recovery of + // prepared transactions, the logical clock may not yet be initialized, so we use the + // prepare timestamp of the transaction for this write. This is safe since the prepare + // timestamp is always <= the commit timestamp of a transaction, which satisfies the + // correctness requirement for multikey writes i.e. they must occur at or before the + // first write that set the multikey flag. + auto recoveryPrepareOpTime = txnParticipant.getPrepareOpTimeForRecovery(); + Timestamp writeTs = recoveryPrepareOpTime.isNull() + ? LogicalClock::get(opCtx)->getClusterTime().asTimestamp() + : recoveryPrepareOpTime.getTimestamp(); + + auto status = opCtx->recoveryUnit()->setTimestamp(writeTs); + if (status.code() == ErrorCodes::BadValue) { + LOGV2(20352, + "Temporarily could not timestamp the multikey catalog write, retrying. " + "{status_reason}", + "status_reason"_attr = status.reason()); + throw WriteConflictException(); + } + fassert(31164, status); - return; - } + _catalogSetMultikey(opCtx, multikeyPaths); + + wuow.commit(); + }); - _catalogSetMultikey(opCtx, paths); + return Status::OK(); } // ---- diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.h b/src/mongo/db/catalog/index_catalog_entry_impl.h index ff2d9dde2aa..7cb8d5c98c6 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.h +++ b/src/mongo/db/catalog/index_catalog_entry_impl.h @@ -186,6 +186,13 @@ public: void setMinimumVisibleSnapshot(Timestamp newMinimumVisibleSnapshot) final; private: + /** + * Sets this index to be multikey when we are running inside a multi-document transaction. + * Used by setMultikey() only. + */ + Status _setMultikeyInMultiDocumentTransaction(OperationContext* opCtx, + const MultikeyPaths& multikeyPaths); + bool _catalogIsReady(OperationContext* opCtx) const; bool _catalogIsPresent(OperationContext* opCtx) const; |