From 6355a6fa756fb72ec80c0cd3c39e5f195d201bfe Mon Sep 17 00:00:00 2001 From: Yury Smolski <140245+ysmolski@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:37:26 +0200 Subject: [PATCH 1/2] fix: propagate actualListSizes via resolver context It is needed to make router work with setting the header for actual cost via special hook that is called before writing to the client happens. --- execution/engine/execution_engine.go | 2 +- v2/pkg/engine/resolve/context.go | 5 +++++ v2/pkg/engine/resolve/resolve.go | 10 ++-------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/execution/engine/execution_engine.go b/execution/engine/execution_engine.go index 5c03e926a1..8d61bf2433 100644 --- a/execution/engine/execution_engine.go +++ b/execution/engine/execution_engine.go @@ -238,7 +238,7 @@ func (e *ExecutionEngine) Execute(ctx context.Context, operation *graphql.Reques return err } if resp != nil { - operation.ComputeActualCost(costCalculator, e.config.plannerConfig, resp.ActualListSizes) + operation.ComputeActualCost(costCalculator, e.config.plannerConfig, execContext.resolveContext.ActualListSizes) } return nil case *plan.SubscriptionResponsePlan: diff --git a/v2/pkg/engine/resolve/context.go b/v2/pkg/engine/resolve/context.go index dfa481b281..5a7193dc2f 100644 --- a/v2/pkg/engine/resolve/context.go +++ b/v2/pkg/engine/resolve/context.go @@ -45,6 +45,11 @@ type Context struct { SubgraphHeadersBuilder SubgraphHeadersBuilder + // ActualListSizes is populated by the resolver after resolution completes, + // before the response body is written. Maps JSON path to actual list size. + // Used to compute the actual cost. + ActualListSizes map[string]int + // GetDeduplicationData is called after the leader of an inbound singleflight request // finishes resolving. It extracts data from the leader's context (e.g. accumulated // response headers) that should be shared with all follower requests. diff --git a/v2/pkg/engine/resolve/resolve.go b/v2/pkg/engine/resolve/resolve.go index 6ce431b50a..f735752ef9 100644 --- a/v2/pkg/engine/resolve/resolve.go +++ b/v2/pkg/engine/resolve/resolve.go @@ -319,10 +319,6 @@ type GraphQLResolveInfo struct { // ResolveDeduplicated indicates whether the resolution of the entire operation was deduplicated via single flight ResolveDeduplicated bool - - // ActualListSizes maps the JSON path to the actual list size in the response. - // Used to compute the actual cost. - ActualListSizes map[string]int } func (r *Resolver) ResolveGraphQLResponse(ctx *Context, response *GraphQLResponse, data []byte, writer io.Writer) (*GraphQLResolveInfo, error) { @@ -354,7 +350,7 @@ func (r *Resolver) ResolveGraphQLResponse(ctx *Context, response *GraphQLRespons return nil, err } - resp.ActualListSizes = t.resolvable.actualListSizes + ctx.ActualListSizes = t.resolvable.actualListSizes return resp, err } @@ -415,6 +411,7 @@ func (r *Resolver) ArenaResolveGraphQLResponse(ctx *Context, response *GraphQLRe r.responseBufferPool.Release(responseArena) return nil, err } + ctx.ActualListSizes = t.resolvable.actualListSizes // first release resolverArena // all data is resolved and written into the response arena @@ -436,9 +433,6 @@ func (r *Resolver) ArenaResolveGraphQLResponse(ctx *Context, response *GraphQLRe // all data is written to the client // we're safe to release our buffer r.responseBufferPool.Release(responseArena) - - resp.ActualListSizes = t.resolvable.actualListSizes - return resp, err } From 198f90cad57825e1f96082bb298a00e3969954a0 Mon Sep 17 00:00:00 2001 From: Yury Smolski <140245+ysmolski@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:03:37 +0200 Subject: [PATCH 2/2] reset in free --- execution/engine/execution_engine.go | 4 ---- v2/pkg/engine/resolve/context.go | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/execution/engine/execution_engine.go b/execution/engine/execution_engine.go index 8d61bf2433..27c7a46df1 100644 --- a/execution/engine/execution_engine.go +++ b/execution/engine/execution_engine.go @@ -53,10 +53,6 @@ func (e *internalExecutionContext) setVariables(variables []byte) { } } -func (e *internalExecutionContext) reset() { - e.resolveContext.Free() -} - type ExecutionEngine struct { logger abstractlogger.Logger config Configuration diff --git a/v2/pkg/engine/resolve/context.go b/v2/pkg/engine/resolve/context.go index 5a7193dc2f..0b3e626f1b 100644 --- a/v2/pkg/engine/resolve/context.go +++ b/v2/pkg/engine/resolve/context.go @@ -320,6 +320,7 @@ func (c *Context) Free() { c.LoaderHooks = nil c.GetDeduplicationData = nil c.SetDeduplicationData = nil + c.ActualListSizes = nil } type traceStartKey struct{}