summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2020-02-22 09:45:55 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-02-22 14:59:49 +0000
commit68dfffe822f1888f221b265dc7974e9f98ea383c (patch)
tree10e081b8500f707d8e9e62033bf7b729e739b175
parent0839064b93c75ee7a273dba296a286f521080da5 (diff)
downloadmongo-68dfffe822f1888f221b265dc7974e9f98ea383c.tar.gz
SERVER-46229 add IndexCatalogEntryImpl::_setMultikeyInMultiDocumentTransaction()
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.cpp86
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.h7
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;