diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops.idl | 38 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 26 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_write_without_shard_key_cmd.cpp | 57 |
4 files changed, 122 insertions, 13 deletions
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index bf0189073a5..2d0082285eb 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -684,8 +684,18 @@ write_ops::FindAndModifyCommandReply CmdFindAndModify::Invocation::typedRun( } if (analyze_shard_key::supportsPersistingSampledQueries() && req.getSampleId()) { - analyze_shard_key::QueryAnalysisWriter::get(opCtx)->addFindAndModifyQuery(req).getAsync( - [](auto) {}); + auto findAndModifyOp = req; + + // If the initial query was a write without shard key, the two phase write protocol + // modifies the query in the write phase. In order to get correct metrics, we need to + // reconstruct the original query prior to sampling. + if (req.getOriginalQuery()) { + findAndModifyOp.setQuery(*req.getOriginalQuery()); + findAndModifyOp.setCollation(req.getOriginalCollation()); + } + analyze_shard_key::QueryAnalysisWriter::get(opCtx) + ->addFindAndModifyQuery(findAndModifyOp) + .getAsync([](auto) {}); } if (MONGO_unlikely(failAllFindAndModify.shouldFail())) { diff --git a/src/mongo/db/ops/write_ops.idl b/src/mongo/db/ops/write_ops.idl index 5f793cb9780..4fe3d0d7650 100644 --- a/src/mongo/db/ops/write_ops.idl +++ b/src/mongo/db/ops/write_ops.idl @@ -205,6 +205,25 @@ structs: type: EncryptionInformation optional: true stability: unstable + $_originalQuery: + description: "The original write query. This is used for updateOne/deleteOne + without shard key during the write phase of the two phase protocol in + order to make sure the shard key query analysis stores the correct + client query." + type: object + optional: true + cpp_name: originalQuery + stability: internal + $_originalCollation: + description: "The original write query. This is used for updateOne/deleteOne + without shard key during the write phase of the two phase protocol in + order to make sure the shard key query analysis stores the correct + client collation." + type: object + optional: true + cpp_name: originalCollation + stability: internal + UpdateOpEntry: description: "Parser for the entries in the 'updates' array of an update command." @@ -550,3 +569,22 @@ commands: type: uuid optional: true stability: unstable + $_originalQuery: + description: "The original write query. This is used for findAndModify without shard + key during the write phase of the two phase protocol in order to make + sure the shard key query analysis stores the correct client + query." + type: object + optional: true + cpp_name: originalQuery + stability: internal + $_originalCollation: + description: "The original collation. This is used for findAndModify without shard + key during the write phase of the two phase protocol in order to make + sure the shard key query analysis stores the correct client + collation." + type: object + optional: true + cpp_name: originalCollation + stability: internal + diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index dcb72a4ca93..71d6531de1a 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -1106,8 +1106,18 @@ WriteResult performUpdates(OperationContext* opCtx, }); if (analyze_shard_key::supportsPersistingSampledQueries() && singleOp.getSampleId()) { + auto updateOp = wholeOp; + + // If the initial query was a write without shard key, the two phase write protocol + // modifies the query in the write phase. In order to get correct metrics, we need to + // reconstruct the original query prior to sampling. + if (wholeOp.getOriginalQuery()) { + updateOp.getUpdates().front().setQ(*wholeOp.getOriginalQuery()); + updateOp.getUpdates().front().setCollation(wholeOp.getOriginalCollation()); + } + analyze_shard_key::QueryAnalysisWriter::get(opCtx) - ->addUpdateQuery(wholeOp, currentOpIndex) + ->addUpdateQuery(updateOp, currentOpIndex) .getAsync([](auto) {}); } @@ -1354,8 +1364,18 @@ WriteResult performDeletes(OperationContext* opCtx, }); if (analyze_shard_key::supportsPersistingSampledQueries() && singleOp.getSampleId()) { + auto deleteOp = wholeOp; + + // If the initial query was a write without shard key, the two phase write protocol + // modifies the query in the write phase. In order to get correct metrics, we need to + // reconstruct the original query prior to sampling. + if (wholeOp.getOriginalQuery()) { + deleteOp.getDeletes().front().setQ(*wholeOp.getOriginalQuery()); + deleteOp.getDeletes().front().setCollation(wholeOp.getOriginalCollation()); + } + analyze_shard_key::QueryAnalysisWriter::get(opCtx) - ->addDeleteQuery(wholeOp, currentOpIndex) + ->addDeleteQuery(deleteOp, currentOpIndex) .getAsync([](auto) {}); } @@ -1389,7 +1409,7 @@ WriteResult performDeletes(OperationContext* opCtx, } return out; -} +} // namespace mongo::write_ops_exec Status performAtomicTimeseriesWrites( OperationContext* opCtx, diff --git a/src/mongo/s/commands/cluster_write_without_shard_key_cmd.cpp b/src/mongo/s/commands/cluster_write_without_shard_key_cmd.cpp index 3250f90b0af..7d9a470dc4a 100644 --- a/src/mongo/s/commands/cluster_write_without_shard_key_cmd.cpp +++ b/src/mongo/s/commands/cluster_write_without_shard_key_cmd.cpp @@ -49,30 +49,71 @@ BSONObj _createCmdObj(const BSONObj& writeCmd, const StringData& commandName, const BSONObj& targetDocId, const NamespaceString& nss) { - // Drop collation and writeConcern as - // targeting by _id uses default collation and writeConcern cannot be specified for - // commands run in internal transactions. This object will be used to construct the command - // request used by clusterWriteWithoutShardKey. + + // Drop the writeConcern as it cannot be specified for commands run in internal transactions. + // This object will be used to construct the command request used by + // _clusterWriteWithoutShardKey. BSONObjBuilder writeCmdObjBuilder( - writeCmd.removeFields(std::set<std::string>{"collation", "writeConcern"})); + writeCmd.removeField(WriteConcernOptions::kWriteConcernField)); writeCmdObjBuilder.appendElementsUnique(BSON("$db" << nss.dbName().toString())); auto writeCmdObj = writeCmdObjBuilder.obj(); // Parse original write command and set _id as query filter for new command object. if (commandName == "update") { auto parsedUpdateRequest = write_ops::UpdateCommandRequest::parse( - IDLParserContext("_clusterWriteWithoutShardKey"), writeCmdObj); + IDLParserContext("_clusterWriteWithoutShardKeyForUpdate"), writeCmdObj); + + // The original query and collation are sent along with the modified command for the + // purposes of query sampling. + if (parsedUpdateRequest.getUpdates().front().getSampleId()) { + auto writeCommandRequestBase = write_ops::WriteCommandRequestBase( + parsedUpdateRequest.getWriteCommandRequestBase()); + writeCommandRequestBase.setOriginalQuery( + parsedUpdateRequest.getUpdates().front().getQ()); + writeCommandRequestBase.setOriginalCollation( + parsedUpdateRequest.getUpdates().front().getCollation()); + parsedUpdateRequest.setWriteCommandRequestBase(writeCommandRequestBase); + } + parsedUpdateRequest.getUpdates().front().setQ(targetDocId); + // Unset the collation because targeting by _id uses default collation. + parsedUpdateRequest.getUpdates().front().setCollation(boost::none); return parsedUpdateRequest.toBSON(BSONObj()); } else if (commandName == "delete") { auto parsedDeleteRequest = write_ops::DeleteCommandRequest::parse( - IDLParserContext("_clusterWriteWithoutShardKey"), writeCmdObj); + IDLParserContext("_clusterWriteWithoutShardKeyForDelete"), writeCmdObj); + + // The original query and collation are sent along with the modified command for the + // purposes of query sampling. + if (parsedDeleteRequest.getDeletes().front().getSampleId()) { + auto writeCommandRequestBase = write_ops::WriteCommandRequestBase( + parsedDeleteRequest.getWriteCommandRequestBase()); + writeCommandRequestBase.setOriginalQuery( + parsedDeleteRequest.getDeletes().front().getQ()); + writeCommandRequestBase.setOriginalCollation( + parsedDeleteRequest.getDeletes().front().getCollation()); + parsedDeleteRequest.setWriteCommandRequestBase(writeCommandRequestBase); + } + parsedDeleteRequest.getDeletes().front().setQ(targetDocId); + // Unset the collation because targeting by _id uses default collation. + parsedDeleteRequest.getDeletes().front().setCollation(boost::none); return parsedDeleteRequest.toBSON(BSONObj()); } else if (commandName == "findandmodify" || commandName == "findAndModify") { auto parsedFindAndModifyRequest = write_ops::FindAndModifyCommandRequest::parse( - IDLParserContext("_clusterWriteWithoutShardKey"), writeCmdObj); + IDLParserContext("_clusterWriteWithoutShardKeyForFindAndModify"), writeCmdObj); + + // The original query and collation are sent along with the modified command for the + // purposes of query sampling. + if (parsedFindAndModifyRequest.getSampleId()) { + parsedFindAndModifyRequest.setOriginalQuery(parsedFindAndModifyRequest.getQuery()); + parsedFindAndModifyRequest.setOriginalCollation( + parsedFindAndModifyRequest.getCollation()); + } + parsedFindAndModifyRequest.setQuery(targetDocId); + // Unset the collation because targeting by _id uses default collation. + parsedFindAndModifyRequest.setCollation(boost::none); return parsedFindAndModifyRequest.toBSON(BSONObj()); } else { uasserted(ErrorCodes::InvalidOptions, |