diff options
Diffstat (limited to 'src/mongo/s/commands/cluster_find_and_modify_cmd.cpp')
-rw-r--r-- | src/mongo/s/commands/cluster_find_and_modify_cmd.cpp | 128 |
1 files changed, 84 insertions, 44 deletions
diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp index 3d223e1e3fc..600b2ff3dd3 100644 --- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp +++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp @@ -517,6 +517,7 @@ Status FindAndModifyCmd::explain(OperationContext* opCtx, rpc::ReplyBuilderInterface* result) const { const DatabaseName dbName = DatabaseNameUtil::deserialize(request.getValidatedTenantId(), request.getDatabase()); + auto bodyBuilder = result->getBodyBuilder(); const BSONObj& cmdObj = [&]() { // Check whether the query portion needs to be rewritten for FLE. auto findAndModifyRequest = write_ops::FindAndModifyCommandRequest::parse( @@ -538,20 +539,11 @@ Status FindAndModifyCmd::explain(OperationContext* opCtx, const auto& cm = cri.cm; std::shared_ptr<Shard> shard; - if (cm.isSharded()) { - const BSONObj query = cmdObj.getObjectField("query"); - const BSONObj collation = getCollation(cmdObj); - const auto let = getLet(cmdObj); - const auto rc = getLegacyRuntimeConstants(cmdObj); - const BSONObj shardKey = - getShardKey(makeExpCtx(opCtx, nss, collation, boost::none, let, rc), cm, query); - const auto chunk = cm.findIntersectingChunk(shardKey, collation); - - shard = - uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getShard(opCtx, chunk.getShardId())); - } else { - shard = uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getShard(opCtx, cm.dbPrimary())); - } + const BSONObj query = cmdObj.getObjectField("query"); + const BSONObj collation = getCollation(cmdObj); + const auto isUpsert = cmdObj.getBoolField("upsert"); + const auto let = getLet(cmdObj); + const auto rc = getLegacyRuntimeConstants(cmdObj); const auto explainCmd = ClusterExplain::wrapAsExplain( appendLegacyRuntimeConstantsToCommandObject(opCtx, cmdObj), verbosity); @@ -561,16 +553,31 @@ Status FindAndModifyCmd::explain(OperationContext* opCtx, BSONObjBuilder bob; if (cm.isSharded()) { - _runCommand(opCtx, - shard->getId(), - cri.getShardVersion(shard->getId()), - boost::none, - nss, - applyReadWriteConcern(opCtx, false, false, explainCmd), - true /* isExplain */, - boost::none /* allowShardKeyUpdatesWithoutFullShardKeyInQuery */, - &bob); + if (write_without_shard_key::useTwoPhaseProtocol( + opCtx, nss, false /* isUpdateOrDelete */, isUpsert, query, collation, let, rc)) { + _runExplainWithoutShardKey(opCtx, nss, explainCmd, verbosity, &bob); + bodyBuilder.appendElementsUnique(bob.obj()); + return Status::OK(); + } else { + const BSONObj shardKey = + getShardKey(makeExpCtx(opCtx, nss, collation, boost::none, let, rc), cm, query); + const auto chunk = cm.findIntersectingChunk(shardKey, collation); + shard = uassertStatusOK( + Grid::get(opCtx)->shardRegistry()->getShard(opCtx, chunk.getShardId())); + + _runCommand(opCtx, + shard->getId(), + cri.getShardVersion(shard->getId()), + boost::none, + nss, + applyReadWriteConcern(opCtx, false, false, explainCmd), + true /* isExplain */, + boost::none /* allowShardKeyUpdatesWithoutFullShardKeyInQuery */, + &bob); + } } else { + shard = uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getShard(opCtx, cm.dbPrimary())); + _runCommand(opCtx, shard->getId(), boost::make_optional(!cm.dbVersion().isFixed(), ShardVersion::UNSHARDED()), @@ -591,7 +598,6 @@ Status FindAndModifyCmd::explain(OperationContext* opCtx, AsyncRequestsSender::Response arsResponse{ shard->getId(), response, shard->getConnString().getServers().front()}; - auto bodyBuilder = result->getBodyBuilder(); return ClusterExplain::buildExplainResult( opCtx, {arsResponse}, ClusterExplain::kSingleShard, millisElapsed, cmdObj, &bodyBuilder); } @@ -681,12 +687,8 @@ bool FindAndModifyCmd::run(OperationContext* opCtx, cmdObjForShard = replaceNamespaceByBucketNss(cmdObjForShard, nss); } - _runCommandWithoutShardKey(opCtx, - nss, - applyReadWriteConcern(opCtx, this, cmdObjForShard), - false /* isExplain */, - allowShardKeyUpdatesWithoutFullShardKeyInQuery, - &result); + _runCommandWithoutShardKey( + opCtx, nss, applyReadWriteConcern(opCtx, this, cmdObjForShard), &result); } } else { findAndModifyTargetedShardedCount.increment(1); @@ -801,19 +803,18 @@ void FindAndModifyCmd::_constructResult(OperationContext* opCtx, } // Two-phase protocol to run a findAndModify command without a shard key or _id. -void FindAndModifyCmd::_runCommandWithoutShardKey( - OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& cmdObj, - bool isExplain, - boost::optional<bool> allowShardKeyUpdatesWithoutFullShardKeyInQuery, - BSONObjBuilder* result) { +void FindAndModifyCmd::_runCommandWithoutShardKey(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& cmdObj, + BSONObjBuilder* result) { + auto allowShardKeyUpdatesWithoutFullShardKeyInQuery = + opCtx->isRetryableWrite() || opCtx->inMultiDocumentTransaction(); auto cmdObjForPassthrough = prepareCmdObjForPassthrough(opCtx, cmdObj, nss, - isExplain, + false /* isExplain */, boost::none /* dbVersion */, boost::none /* shardVersion */, allowShardKeyUpdatesWithoutFullShardKeyInQuery); @@ -855,6 +856,50 @@ void FindAndModifyCmd::_runCommandWithoutShardKey( result); } +// Two-phase protocol to run an explain for a findAndModify command without a shard key or _id. +void FindAndModifyCmd::_runExplainWithoutShardKey(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& cmdObj, + ExplainOptions::Verbosity verbosity, + BSONObjBuilder* result) { + auto cmdObjForPassthrough = prepareCmdObjForPassthrough( + opCtx, + cmdObj, + nss, + true /* isExplain */, + boost::none /* dbVersion */, + boost::none /* shardVersion */, + boost::none /* allowShardKeyUpdatesWithoutFullShardKeyInQuery */); + + // Explain currently cannot be run within a transaction, so each command is instead run + // separately outside of a transaction, and we compose the results at the end. + auto clusterQueryWithoutShardKeyExplainRes = [&] { + ClusterQueryWithoutShardKey clusterQueryWithoutShardKeyCommand(cmdObjForPassthrough); + const auto explainClusterQueryWithoutShardKeyCmd = + ClusterExplain::wrapAsExplain(clusterQueryWithoutShardKeyCommand.toBSON({}), verbosity); + auto opMsg = OpMsgRequest::fromDBAndBody(nss.db(), explainClusterQueryWithoutShardKeyCmd); + return CommandHelpers::runCommandDirectly(opCtx, opMsg).getOwned(); + }(); + + auto clusterWriteWithoutShardKeyExplainRes = [&] { + // Since 'explain' does not return the results of the query, we do not have an _id + // document to target by from the 'Read Phase'. We instead will use a dummy _id + // target document for the 'Write Phase'. + ClusterWriteWithoutShardKey clusterWriteWithoutShardKeyCommand( + cmdObjForPassthrough, + clusterQueryWithoutShardKeyExplainRes.getStringField("targetShardId").toString(), + write_without_shard_key::targetDocForExplain); + const auto explainClusterWriteWithoutShardKeyCmd = + ClusterExplain::wrapAsExplain(clusterWriteWithoutShardKeyCommand.toBSON({}), verbosity); + auto opMsg = OpMsgRequest::fromDBAndBody(nss.db(), explainClusterWriteWithoutShardKeyCmd); + return CommandHelpers::runCommandDirectly(opCtx, opMsg).getOwned(); + }(); + + auto output = write_without_shard_key::generateExplainResponseForTwoPhaseWriteProtocol( + clusterQueryWithoutShardKeyExplainRes, clusterWriteWithoutShardKeyExplainRes); + result->appendElementsUnique(output); +} + // Command invocation to be used if a shard key is specified or the collection is unsharded. void FindAndModifyCmd::_runCommand( OperationContext* opCtx, @@ -938,12 +983,7 @@ void FindAndModifyCmd::_handleWouldChangeOwningShardErrorRetryableWriteLegacy( getLet(cmdObj), getLegacyRuntimeConstants(cmdObj))) { findAndModifyNonTargetedShardedCount.increment(1); - _runCommandWithoutShardKey(opCtx, - nss, - stripWriteConcern(cmdObj), - false /* isExplain */, - true /* allowShardKeyUpdatesWithoutFullShardKeyInQuery */, - result); + _runCommandWithoutShardKey(opCtx, nss, stripWriteConcern(cmdObj), result); } else { findAndModifyTargetedShardedCount.increment(1); |