summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJames Wahlin <james@mongodb.com>2018-03-14 12:39:18 -0400
committerJames Wahlin <james@mongodb.com>2018-03-15 16:28:49 -0400
commite1418190f72787f9a21d5251ad6290076dbc4ae9 (patch)
tree19c50dd49ceeb3d9769105bf65c0469628dc2ac2 /src/mongo
parentf5d2afc04499c49e13c486a64ec7077a3f4aef62 (diff)
downloadmongo-e1418190f72787f9a21d5251ad6290076dbc4ae9.tar.gz
SERVER-33698 batchSize:0 snapshot reads must allocate a WiredTiger transaction
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/session.cpp83
1 files changed, 52 insertions, 31 deletions
diff --git a/src/mongo/db/session.cpp b/src/mongo/db/session.cpp
index b20552fd234..db36244520e 100644
--- a/src/mongo/db/session.cpp
+++ b/src/mongo/db/session.cpp
@@ -271,6 +271,9 @@ boost::optional<repl::OplogEntry> createMatchingTransactionTableUpdate(
// will be allowed to commit.
MONGO_FP_DECLARE(onPrimaryTransactionalWrite);
+// Failpoint which will pause an operation just after allocating a point-in-time storage engine
+// transaction.
+MONGO_FP_DECLARE(hangAfterPreallocateSnapshot);
} // namespace
const BSONObj Session::kDeadEndSentinel(BSON("$incompleteOplogHistory" << 1));
@@ -605,41 +608,59 @@ void Session::unstashTransactionResources(OperationContext* opCtx) {
return;
}
- // We must lock the Client to change the Locker on the OperationContext and the Session mutex to
- // access Session state. We must lock the Client before the Session mutex, since the Client
- // effectively owns the Session. That is, a user might lock the Client to ensure it doesn't go
- // away, and then lock the Session owned by that client.
- stdx::lock_guard<Client> lk(*opCtx->getClient());
- stdx::lock_guard<stdx::mutex> lg(_mutex);
- if (opCtx->getTxnNumber() < _activeTxnNumber) {
- // The session is checked out, so _activeTxnNumber cannot advance due to a user operation.
- // However, when a chunk is migrated, session and transaction information is copied from the
- // donor shard to the recipient. This occurs outside of the check-out mechanism and can lead
- // to a higher _activeTxnNumber during the lifetime of a checkout. If that occurs, we abort
- // the current transaction. Note that it would indicate a user bug to have a newer
- // transaction on one shard while an older transaction is still active on another shard.
- _releaseStashedTransactionResources(lg);
- uasserted(ErrorCodes::TransactionAborted,
- str::stream() << "Transaction aborted. Active txnNumber is now "
- << _activeTxnNumber);
- return;
- }
+ bool snapshotPreallocated = false;
+ {
+ // We must lock the Client to change the Locker on the OperationContext and the Session
+ // mutex to access Session state. We must lock the Client before the Session mutex, since
+ // the Client effectively owns the Session. That is, a user might lock the Client to ensure
+ // it doesn't go away, and then lock the Session owned by that client.
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ stdx::lock_guard<stdx::mutex> lg(_mutex);
+ if (opCtx->getTxnNumber() < _activeTxnNumber) {
+ // The session is checked out, so _activeTxnNumber cannot advance due to a user
+ // operation.
+ // However, when a chunk is migrated, session and transaction information is copied from
+ // the donor shard to the recipient. This occurs outside of the check-out mechanism and
+ // can lead to a higher _activeTxnNumber during the lifetime of a checkout. If that
+ // occurs, we abort the current transaction. Note that it would indicate a user bug to
+ // have a newer transaction on one shard while an older transaction is still active on
+ // another shard.
+ _releaseStashedTransactionResources(lg);
+ uasserted(ErrorCodes::TransactionAborted,
+ str::stream() << "Transaction aborted. Active txnNumber is now "
+ << _activeTxnNumber);
+ return;
+ }
- if (_txnResourceStash) {
- invariant(_txnState != MultiDocumentTransactionState::kNone);
- _txnResourceStash->release(opCtx);
- _txnResourceStash = boost::none;
- } else {
- auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
- if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern ||
- _txnState == MultiDocumentTransactionState::kInProgress) {
- opCtx->setWriteUnitOfWork(std::make_unique<WriteUnitOfWork>(opCtx));
- if (_txnState != MultiDocumentTransactionState::kInProgress) {
- invariant(_txnState == MultiDocumentTransactionState::kNone);
- _txnState = MultiDocumentTransactionState::kInSnapshotRead;
+ if (_txnResourceStash) {
+ invariant(_txnState != MultiDocumentTransactionState::kNone);
+ _txnResourceStash->release(opCtx);
+ _txnResourceStash = boost::none;
+ } else {
+ auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
+ if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern ||
+ _txnState == MultiDocumentTransactionState::kInProgress) {
+ opCtx->setWriteUnitOfWork(std::make_unique<WriteUnitOfWork>(opCtx));
+
+ // Storage engine transactions may be started in a lazy manner. By explicitly
+ // starting here we ensure that a point-in-time snapshot is established during the
+ // first operation of a transaction.
+ opCtx->recoveryUnit()->preallocateSnapshot();
+ snapshotPreallocated = true;
+
+ if (_txnState != MultiDocumentTransactionState::kInProgress) {
+ invariant(_txnState == MultiDocumentTransactionState::kNone);
+ _txnState = MultiDocumentTransactionState::kInSnapshotRead;
+ }
}
}
}
+
+ if (snapshotPreallocated) {
+ // The Client lock must not be held when executing this failpoint as it will block currentOp
+ // execution.
+ MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangAfterPreallocateSnapshot);
+ }
}
void Session::abortIfSnapshotRead(TxnNumber txnNumber) {