예제 #1
0
파일: do_txn.cpp 프로젝트: RyanBard/mongo
Status doTxn(OperationContext* opCtx,
             const std::string& dbName,
             const BSONObj& doTxnCmd,
             BSONObjBuilder* result) {
    auto txnNumber = opCtx->getTxnNumber();
    uassert(ErrorCodes::InvalidOptions, "doTxn can only be run with a transaction ID.", txnNumber);
    auto txnParticipant = TransactionParticipant::get(opCtx);
    uassert(ErrorCodes::InvalidOptions, "doTxn must be run within a transaction", txnParticipant);
    invariant(txnParticipant->inMultiDocumentTransaction());
    invariant(opCtx->getWriteUnitOfWork());
    uassert(
        ErrorCodes::InvalidOptions, "doTxn supports only CRUD opts.", _areOpsCrudOnly(doTxnCmd));
    auto hasPrecondition = _hasPrecondition(doTxnCmd);


    // Acquire global lock in IX mode so that the replication state check will remain valid.
    Lock::GlobalLock globalLock(opCtx, MODE_IX);

    auto replCoord = repl::ReplicationCoordinator::get(opCtx);
    bool userInitiatedWritesAndNotPrimary =
        opCtx->writesAreReplicated() && !replCoord->canAcceptWritesForDatabase(opCtx, dbName);

    if (userInitiatedWritesAndNotPrimary)
        return Status(ErrorCodes::NotMaster,
                      str::stream() << "Not primary while applying ops to database " << dbName);

    int numApplied = 0;

    try {
        BSONObjBuilder intermediateResult;

        // The transaction takes place in a global unit of work, so the precondition check
        // and the writes will share the same snapshot.
        if (hasPrecondition) {
            uassertStatusOK(_checkPrecondition(opCtx, doTxnCmd, result));
        }

        numApplied = 0;
        uassertStatusOK(_doTxn(opCtx, dbName, doTxnCmd, &intermediateResult, &numApplied));
        txnParticipant->commitUnpreparedTransaction(opCtx);
        result->appendElements(intermediateResult.obj());
    } catch (const DBException& ex) {
        txnParticipant->abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
        BSONArrayBuilder ab;
        ++numApplied;
        for (int j = 0; j < numApplied; j++)
            ab.append(false);
        result->append("applied", numApplied);
        result->append("code", ex.code());
        result->append("codeName", ErrorCodes::errorString(ex.code()));
        result->append("errmsg", ex.what());
        result->append("results", ab.arr());
        return Status(ErrorCodes::UnknownError, ex.what());
    }

    return Status::OK();
}
예제 #2
0
void killSessionsAbortUnpreparedTransactions(OperationContext* opCtx,
                                             const SessionKiller::Matcher& matcher,
                                             ErrorCodes::Error reason) {
    killSessionsAction(
        opCtx,
        matcher,
        [](const ObservableSession& session) {
            auto participant = TransactionParticipant::get(session);
            return participant.inMultiDocumentTransaction() && !participant.transactionIsPrepared();
        },
        [](OperationContext* opCtx, const SessionToKill& session) {
            TransactionParticipant::get(session).abortTransactionIfNotPrepared(opCtx);
        },
        reason);
}
예제 #3
0
void MongoDSessionCatalog::onStepUp(OperationContext* opCtx) {
    // Invalidate sessions that could have a retryable write on it, so that we can refresh from disk
    // in case the in-memory state was out of sync.
    const auto catalog = SessionCatalog::get(opCtx);
    // The use of shared_ptr here is in order to work around the limitation of stdx::function that
    // the functor must be copyable.
    auto sessionKillTokens = std::make_shared<std::vector<SessionCatalog::KillToken>>();

    // Scan all sessions and reacquire locks for prepared transactions.
    // There may be sessions that are checked out during this scan, but none of them
    // can be prepared transactions, since only oplog application can make transactions
    // prepared on secondaries and oplog application has been stopped at this moment.
    std::vector<LogicalSessionId> sessionIdToReacquireLocks;

    SessionKiller::Matcher matcher(
        KillAllSessionsByPatternSet{makeKillAllSessionsByPattern(opCtx)});
    catalog->scanSessions(matcher, [&](const ObservableSession& session) {
        const auto txnParticipant = TransactionParticipant::get(session.get());
        if (!txnParticipant->inMultiDocumentTransaction()) {
            sessionKillTokens->emplace_back(session.kill());
        }

        if (txnParticipant->transactionIsPrepared()) {
            sessionIdToReacquireLocks.emplace_back(session.getSessionId());
        }
    });
    killSessionTokensFunction(opCtx, sessionKillTokens);

    {
        // Create a new opCtx because we need an empty locker to refresh the locks.
        auto newClient = opCtx->getServiceContext()->makeClient("restore-prepared-txn");
        AlternativeClientRegion acr(newClient);
        for (const auto& sessionId : sessionIdToReacquireLocks) {
            auto newOpCtx = cc().makeOperationContext();
            newOpCtx->setLogicalSessionId(sessionId);
            MongoDOperationContextSession ocs(newOpCtx.get());
            auto txnParticipant =
                TransactionParticipant::get(OperationContextSession::get(newOpCtx.get()));
            txnParticipant->refreshLocksForPreparedTransaction(newOpCtx.get(), false);
        }
    }

    const size_t initialExtentSize = 0;
    const bool capped = false;
    const bool maxSize = 0;

    BSONObj result;

    DBDirectClient client(opCtx);

    if (client.createCollection(NamespaceString::kSessionTransactionsTableNamespace.ns(),
                                initialExtentSize,
                                capped,
                                maxSize,
                                &result)) {
        return;
    }

    const auto status = getStatusFromCommandResult(result);

    if (status == ErrorCodes::NamespaceExists) {
        return;
    }

    uassertStatusOKWithContext(status,
                               str::stream()
                                   << "Failed to create the "
                                   << NamespaceString::kSessionTransactionsTableNamespace.ns()
                                   << " collection");
}