std::vector<AsyncRequestsSender::Response> TransactionRouter::abortTransaction( OperationContext* opCtx, bool isImplicit) { // The router has yet to send any commands to a remote shard for this transaction. // Return the same error that would have been returned by a shard. uassert(ErrorCodes::NoSuchTransaction, "no known command has been sent by this router for this transaction", !_participants.empty()); auto abortCmd = BSON("abortTransaction" << 1); std::vector<AsyncRequestsSender::Request> abortRequests; for (const auto& participantEntry : _participants) { abortRequests.emplace_back(ShardId(participantEntry.first), abortCmd); } // Implicit aborts log earlier. if (!isImplicit) { LOG(0) << _txnIdToString() << " Aborting transaction on " << _participants.size() << " shard(s)"; } return gatherResponses(opCtx, NamespaceString::kAdminDb, ReadPreferenceSetting{ReadPreference::PrimaryOnly}, Shard::RetryPolicy::kIdempotent, abortRequests); }
std::vector<AsyncRequestsSender::Response> scatterGatherUnversionedTargetAllShards( OperationContext* opCtx, StringData dbName, const BSONObj& cmdObj, const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy) { return gatherResponses( opCtx, dbName, readPref, retryPolicy, buildUnversionedRequestsForAllShards(opCtx, cmdObj)); }
std::vector<AsyncRequestsSender::Response> scatterGatherVersionedTargetByRoutingTable( OperationContext* opCtx, StringData dbName, const NamespaceString& nss, const CachedCollectionRoutingInfo& routingInfo, const BSONObj& cmdObj, const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy, const BSONObj& query, const BSONObj& collation) { const auto requests = buildVersionedRequestsForTargetedShards(opCtx, nss, routingInfo, cmdObj, query, collation); return gatherResponses(opCtx, dbName, readPref, retryPolicy, requests); }
AsyncRequestsSender::Response executeCommandAgainstDatabasePrimary( OperationContext* opCtx, StringData dbName, const CachedDatabaseInfo& dbInfo, const BSONObj& cmdObj, const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy) { auto responses = gatherResponses(opCtx, dbName, readPref, retryPolicy, buildUnversionedRequestsForShards( opCtx, {dbInfo.primaryId()}, appendDbVersionIfPresent(cmdObj, dbInfo))); return std::move(responses.front()); }
std::vector<AsyncRequestsSender::Response> scatterGatherOnlyVersionIfUnsharded( OperationContext* opCtx, const NamespaceString& nss, const BSONObj& cmdObj, const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy) { auto routingInfo = uassertStatusOK(Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo(opCtx, nss)); std::vector<AsyncRequestsSender::Request> requests; if (routingInfo.cm()) { // An unversioned request on a sharded collection can cause a shard that has not owned data // for the collection yet to implicitly create the collection without all the collection // options. So, we signal to shards that they should not implicitly create the collection. requests = buildUnversionedRequestsForAllShards(opCtx, cmdObj); } else { requests = buildVersionedRequestsForTargetedShards( opCtx, nss, routingInfo, cmdObj, BSONObj(), BSONObj()); } return gatherResponses(opCtx, nss.db(), readPref, retryPolicy, requests); }
void TransactionRouter::_clearPendingParticipants(OperationContext* opCtx) { const auto pendingParticipants = _getPendingParticipants(); // Send abort to each pending participant. This resets their transaction state and guarantees no // transactions will be left open if the retry does not re-target any of these shards. std::vector<AsyncRequestsSender::Request> abortRequests; for (const auto& participant : pendingParticipants) { abortRequests.emplace_back(participant, BSON("abortTransaction" << 1)); } auto responses = gatherResponses(opCtx, NamespaceString::kAdminDb, ReadPreferenceSetting{ReadPreference::PrimaryOnly}, Shard::RetryPolicy::kIdempotent, abortRequests); // Verify each abort succeeded or failed with NoSuchTransaction, which may happen if the // transaction was already implicitly aborted on the shard. for (const auto& response : responses) { _assertAbortStatusIsOkOrNoSuchTransaction(response); } // Remove each aborted participant from the participant list. Remove after sending abort, so // they are not added back to the participant list by the transaction tracking inside the ARS. for (const auto& participant : pendingParticipants) { invariant(_participants.erase(participant)); } // If there are no more participants, also clear the coordinator id because a new one must be // chosen by the retry. if (_participants.empty()) { _coordinatorId.reset(); return; } // If participants were created by an earlier command, the coordinator must be one of them. invariant(_coordinatorId); invariant(_participants.count(*_coordinatorId) == 1); }