fix(GWC-29): drop the wasted request clone on the Invoke hot path
Invoke deep-cloned the whole MxCommandRequest — including its command payload, potentially a large bulk-write graph — only to overwrite the cloned command with commandToInvoke and discard it. MapCommand then did the one clone actually needed. Net cost: a full wasted command deep-clone per Invoke, worst for exactly the bulk writes that are largest. Adds a MapCommand(MxCommand) overload (MapCommand reads nothing else off the request) and has Invoke pass commandToInvoke directly; the request overload delegates so other callers are untouched. The remaining clone inside MapCommand stays and is now documented as required rather than incidental: commandToInvoke may be the gRPC-owned request.Command, and the caller reads it again after dispatch via TrackCommandReply, so ownership transfer (à la GWC-07) is not safe here. That clone is what keeps WorkerClient.CreateCommandEnvelope's no-aliasing invariant true. Tests: MxAccessGrpcMapperTests.MapCommandFromCommandClonesPayload (mutating the input leaves the mapped command untouched; both overloads produce equal results under a fixed TimeProvider).
This commit is contained in:
@@ -116,9 +116,12 @@ public sealed class MxAccessGatewayService(
|
||||
return bulkConstraintPlan.CreateDeniedReply(request);
|
||||
}
|
||||
|
||||
MxCommandRequest invokeRequest = request.Clone();
|
||||
invokeRequest.Command = commandToInvoke;
|
||||
WorkerCommand workerCommand = mapper.MapCommand(invokeRequest);
|
||||
// Map from the command alone: cloning the whole request only to overwrite its command with
|
||||
// commandToInvoke deep-cloned the (potentially large) original payload for nothing, since
|
||||
// MapCommand reads nothing but the command (GWC-29). The one clone that matters still
|
||||
// happens inside MapCommand, which is what keeps the worker-bound graph unaliased from
|
||||
// commandToInvoke — the caller still reads it below via TrackCommandReply.
|
||||
WorkerCommand workerCommand = mapper.MapCommand(commandToInvoke);
|
||||
WorkerCommandReply workerReply = await sessionManager
|
||||
.InvokeAsync(request.SessionId, workerCommand, context.CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user