Status applyAbortTransaction(OperationContext* opCtx, const OplogEntry& entry, repl::OplogApplication::Mode mode) { // Return error if run via applyOps command. uassert(50972, "abortTransaction is only used internally by secondaries.", mode != repl::OplogApplication::Mode::kApplyOpsCmd); // We don't put transactions into the prepare state until the end of recovery and initial sync, // so there is no transaction to abort. if (mode == repl::OplogApplication::Mode::kRecovering || mode == repl::OplogApplication::Mode::kInitialSync) { return Status::OK(); } invariant(mode == repl::OplogApplication::Mode::kSecondary); // Transaction operations are in its own batch, so we can modify their opCtx. invariant(entry.getSessionId()); invariant(entry.getTxnNumber()); opCtx->setLogicalSessionId(*entry.getSessionId()); opCtx->setTxnNumber(*entry.getTxnNumber()); // The write on transaction table may be applied concurrently, so refreshing state // from disk may read that write, causing starting a new transaction on an existing // txnNumber. Thus, we start a new transaction without refreshing state from disk. MongoDOperationContextSessionWithoutRefresh sessionCheckout(opCtx); auto transaction = TransactionParticipant::get(opCtx); transaction.unstashTransactionResources(opCtx, "abortTransaction"); transaction.abortActiveTransaction(opCtx); return Status::OK(); }
Status applyCommitTransaction(OperationContext* opCtx, const OplogEntry& entry, repl::OplogApplication::Mode mode) { // Return error if run via applyOps command. uassert(50987, "commitTransaction is only used internally by secondaries.", mode != repl::OplogApplication::Mode::kApplyOpsCmd); IDLParserErrorContext ctx("commitTransaction"); auto commitOplogEntryOpTime = entry.getOpTime(); auto commitCommand = CommitTransactionOplogObject::parse(ctx, entry.getObject()); const bool prepared = !commitCommand.getPrepared() || *commitCommand.getPrepared(); if (!prepared) return Status::OK(); invariant(commitCommand.getCommitTimestamp()); if (mode == repl::OplogApplication::Mode::kRecovering || mode == repl::OplogApplication::Mode::kInitialSync) { return _applyTransactionFromOplogChain(opCtx, entry, mode, *commitCommand.getCommitTimestamp(), commitOplogEntryOpTime.getTimestamp()); } invariant(mode == repl::OplogApplication::Mode::kSecondary); // Transaction operations are in its own batch, so we can modify their opCtx. invariant(entry.getSessionId()); invariant(entry.getTxnNumber()); opCtx->setLogicalSessionId(*entry.getSessionId()); opCtx->setTxnNumber(*entry.getTxnNumber()); // The write on transaction table may be applied concurrently, so refreshing state // from disk may read that write, causing starting a new transaction on an existing // txnNumber. Thus, we start a new transaction without refreshing state from disk. MongoDOperationContextSessionWithoutRefresh sessionCheckout(opCtx); auto transaction = TransactionParticipant::get(opCtx); invariant(transaction); transaction.unstashTransactionResources(opCtx, "commitTransaction"); transaction.commitPreparedTransaction( opCtx, *commitCommand.getCommitTimestamp(), commitOplogEntryOpTime); return Status::OK(); }