summaryrefslogtreecommitdiff
path: root/src/mongo/db/transaction_participant.cpp
diff options
context:
space:
mode:
authorSiyuan Zhou <siyuan.zhou@mongodb.com>2018-11-14 18:34:27 -0500
committerSiyuan Zhou <siyuan.zhou@mongodb.com>2018-12-03 18:21:37 -0500
commit55e72b015e2aa7297c00db29e4d93451ea61a7ca (patch)
tree8f91b68f97adc99332688bfcfaa04f9818679851 /src/mongo/db/transaction_participant.cpp
parent74921ac92c1330f754eed39c8e7148955aca2be9 (diff)
downloadmongo-55e72b015e2aa7297c00db29e4d93451ea61a7ca.tar.gz
SERVER-37199 Yield locks of transactions in secondary application.r4.1.6
Diffstat (limited to 'src/mongo/db/transaction_participant.cpp')
-rw-r--r--src/mongo/db/transaction_participant.cpp31
1 files changed, 26 insertions, 5 deletions
diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp
index 05947daa982..cc9726c29b5 100644
--- a/src/mongo/db/transaction_participant.cpp
+++ b/src/mongo/db/transaction_participant.cpp
@@ -529,6 +529,12 @@ TransactionParticipant::TxnResources::TxnResources(OperationContext* opCtx, bool
}
_locker->unsetThreadId();
+ // On secondaries, we yield the locks for transactions.
+ if (!opCtx->writesAreReplicated()) {
+ _lockSnapshot = std::make_unique<Locker::LockSnapshot>();
+ _locker->releaseWriteUnitOfWork(_lockSnapshot.get());
+ }
+
// This thread must still respect the transaction lock timeout, since it can prevent the
// transaction from making progress.
auto maxTransactionLockMillis = maxTransactionLockRequestTimeoutMillis.load();
@@ -553,25 +559,35 @@ TransactionParticipant::TxnResources::~TxnResources() {
// when starting a new transaction before completing an old one. So we should
// be at WUOW nesting level 1 (only the top level WriteUnitOfWork).
_recoveryUnit->abortUnitOfWork();
- _locker->endWriteUnitOfWork();
+ // If locks are not yielded, release them.
+ if (!_lockSnapshot) {
+ _locker->endWriteUnitOfWork();
+ }
invariant(!_locker->inAWriteUnitOfWork());
}
}
void TransactionParticipant::TxnResources::release(OperationContext* opCtx) {
// Perform operations that can fail the release before marking the TxnResources as released.
+
+ // Restore locks if they are yielded.
+ if (_lockSnapshot) {
+ invariant(!_locker->isLocked());
+ // opCtx is passed in to enable the restoration to be interrupted.
+ _locker->restoreWriteUnitOfWork(opCtx, *_lockSnapshot);
+ _lockSnapshot.reset(nullptr);
+ }
_locker->reacquireTicket(opCtx);
invariant(!_released);
_released = true;
- // We intentionally do not capture the return value of swapLockState(), which is just an empty
- // locker. At the end of the operation, if the transaction is not complete, we will stash the
- // operation context's locker and replace it with a new empty locker.
-
// It is necessary to lock the client to change the Locker on the OperationContext.
stdx::lock_guard<Client> lk(*opCtx->getClient());
invariant(opCtx->lockState()->getClientState() == Locker::ClientState::kInactive);
+ // We intentionally do not capture the return value of swapLockState(), which is just an empty
+ // locker. At the end of the operation, if the transaction is not complete, we will stash the
+ // operation context's locker and replace it with a new empty locker.
opCtx->swapLockState(std::move(_locker));
opCtx->lockState()->updateThreadIdToCurrentThread();
@@ -701,6 +717,11 @@ void TransactionParticipant::unstashTransactionResources(OperationContext* opCtx
: SpeculativeTransactionOpTime::kLastApplied);
}
+ // All locks of transactions must be acquired inside the global WUOW so that we can
+ // yield and restore all locks on state transition. Otherwise, we'd have to remember
+ // which locks are managed by WUOW.
+ invariant(!opCtx->lockState()->isLocked());
+
// Stashed transaction resources do not exist for this in-progress multi-document
// transaction. Set up the transaction resources on the opCtx.
opCtx->setWriteUnitOfWork(std::make_unique<WriteUnitOfWork>(opCtx));