Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion router-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ require (
github.com/wundergraph/cosmo/router v0.0.0-20260710155145-803a4bc06d92
github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e
github.com/wundergraph/cosmo/speedtrap v0.0.0-00010101000000-000000000000
github.com/wundergraph/graphql-go-tools/v2 v2.15.0
github.com/wundergraph/graphql-go-tools/v2 v2.15.2-0.20260812110546-307924a9b18c
go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/sdk/metric v1.44.0
Expand Down
4 changes: 2 additions & 2 deletions router-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV
github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw=
github.com/wundergraph/go-arena v1.3.0 h1:n0ng5a1vbd8YGq1u3rMr0vPU5f6AZ1BXIiUhL1UIok8=
github.com/wundergraph/go-arena v1.3.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw=
github.com/wundergraph/graphql-go-tools/v2 v2.15.0 h1:AG4l/QZrj1IBqxxPzz40r31fmKBopXQw+UjsgHYu8j4=
github.com/wundergraph/graphql-go-tools/v2 v2.15.0/go.mod h1:zREIKLmpjfNcGSubndaW/913r0Y8XbbYOXQeZFkwHdo=
github.com/wundergraph/graphql-go-tools/v2 v2.15.2-0.20260812110546-307924a9b18c h1:r6jdzvZVLVPqrQb6eooE/xL53lJIIcZRT7sOP97d6Qw=
github.com/wundergraph/graphql-go-tools/v2 v2.15.2-0.20260812110546-307924a9b18c/go.mod h1:zREIKLmpjfNcGSubndaW/913r0Y8XbbYOXQeZFkwHdo=
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
Expand Down
68 changes: 68 additions & 0 deletions router-tests/operations/multi_fetch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package integration

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/wundergraph/cosmo/router-tests/testenv"
"github.com/wundergraph/cosmo/router/pkg/config"
)

func TestMultiFetch(t *testing.T) {
t.Parallel()

// multiFetchQuery resolves Consultancy.lead from the employees subgraph, then needs
// two follow-up entity fetches: Employee.derivedMood and Consultancy.isLeadAvailable
const multiFetchQuery = `query Requires {
products {
__typename
... on Consultancy {
lead {
__typename
id
derivedMood
}
isLeadAvailable
}
}
}`
const multiFetchExpectedResponse = `{"data":{"products":[{"__typename":"Consultancy","lead":{"__typename":"Employee","id":1,"derivedMood":"HAPPY"},"isLeadAvailable":false},{"__typename":"Cosmo"},{"__typename":"SDK"}]}}`

t.Run("merges same-wave entity fetches to the same subgraph when enabled", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
ModifyEngineExecutionConfiguration: func(cfg *config.EngineExecutionConfiguration) {
cfg.EnableMultiFetch = true
},
}, func(t *testing.T, xEnv *testenv.Environment) {
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{Query: multiFetchQuery})
require.JSONEq(t, multiFetchExpectedResponse, res.Body)

// Root fetch + one merged entity fetch.
require.EqualValues(t, 2, xEnv.SubgraphRequestCount.Employees.Load())

require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Mood.Load())
require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Availability.Load())
require.EqualValues(t, 4, xEnv.SubgraphRequestCount.Global.Load())
})
})

t.Run("sends one request per entity fetch by default", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{Query: multiFetchQuery})
// Merging must not change the result: same response as the merged case above.
require.JSONEq(t, multiFetchExpectedResponse, res.Body)

// Root fetch + two separate entity fetches.
require.EqualValues(t, 3, xEnv.SubgraphRequestCount.Employees.Load())

require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Mood.Load())
require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Availability.Load())
require.EqualValues(t, 5, xEnv.SubgraphRequestCount.Global.Load())
})
})
}
25 changes: 19 additions & 6 deletions router/core/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/wundergraph/graphql-go-tools/v2/pkg/asttransform"
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/introspection_datasource"
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/plan"
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/postprocess"
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
"github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
)
Expand Down Expand Up @@ -49,6 +50,9 @@ type Executor struct {
Resolver *resolve.Resolver
RenameTypeNames []resolve.RenameTypeName
TrackUsageInfo bool
// PostprocessorOptions configure the plan postprocessor from the engine
// execution configuration (multi-fetch merging, fetch scheduling).
PostprocessorOptions []postprocess.ProcessorOption
}

type ExecutorBuildOptions struct {
Expand Down Expand Up @@ -209,13 +213,22 @@ func (b *ExecutorConfigurationBuilder) Build(ctx context.Context, opts *Executor
}
}

var postprocessorOptions []postprocess.ProcessorOption
if opts.RouterEngineConfig.Execution.EnableMultiFetch {
postprocessorOptions = append(postprocessorOptions, postprocess.EnableMultiFetch())
}
if opts.RouterEngineConfig.Execution.EnableScheduleFetches {
postprocessorOptions = append(postprocessorOptions, postprocess.EnableScheduleFetches())
}

return &Executor{
PlanConfig: *planConfig,
ClientSchema: clientSchemaDefinition,
RouterSchema: &routerSchemaDefinition,
Resolver: resolver,
RenameTypeNames: renameTypeNames,
TrackUsageInfo: b.trackUsageInfo,
PlanConfig: *planConfig,
ClientSchema: clientSchemaDefinition,
RouterSchema: &routerSchemaDefinition,
Resolver: resolver,
RenameTypeNames: renameTypeNames,
TrackUsageInfo: b.trackUsageInfo,
PostprocessorOptions: postprocessorOptions,
}, providers, nil
}

Expand Down
3 changes: 3 additions & 0 deletions router/core/factoryresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ func (l *Loader) Load(engineConfig *nodev1.EngineConfiguration, subgraphs []*nod
var outConfig plan.Configuration
// attach field usage information to the plan
outConfig.DefaultFlushIntervalMillis = engineConfig.DefaultFlushInterval
// EnableMultiFetch makes the planner record the subgraph operation artifacts
// the postprocessor's multi-fetch merge stage consumes.
outConfig.EnableMultiFetch = routerEngineConfig.Execution.EnableMultiFetch
for _, configuration := range engineConfig.FieldConfigurations {
var args []plan.ArgumentConfiguration
for _, argumentConfiguration := range configuration.ArgumentsConfiguration {
Expand Down
6 changes: 5 additions & 1 deletion router/core/operation_planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"errors"
"slices"
"strconv"
"time"

Expand Down Expand Up @@ -98,7 +99,10 @@ func (p *OperationPlanner) planOperation(content string, name string, includeQue
}

// postprocess query plan to get its final state
post := postprocess.NewProcessor(postprocess.CollectDataSourceInfo())
post := postprocess.NewProcessor(append(
slices.Clone(p.executor.PostprocessorOptions),
postprocess.CollectDataSourceInfo(),
)...)
post.Process(preparedPlan)

return &planWithMetaData{
Expand Down
2 changes: 2 additions & 0 deletions router/core/plan_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ func (pl *Planner) PlanPreparedOperation(operation *ast.Document) (planNode *Pla
return nil, opTimes, errors.New(report.Error())
}

// Mirror the router's default engine execution configuration: multi-fetch
// merging and fetch scheduling are opt-in and default off (see Executor build).
post := postprocess.NewProcessor()
post.Process(preparedPlan)
// measure postprocessing time as part of planning time
Expand Down
12 changes: 6 additions & 6 deletions router/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ require (
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
github.com/twmb/franz-go v1.16.1
github.com/wundergraph/graphql-go-tools/v2 v2.15.0
github.com/wundergraph/graphql-go-tools/v2 v2.15.2-0.20260812110546-307924a9b18c
// Do not upgrade, it renames attributes we rely on
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0
go.opentelemetry.io/contrib/propagators/b3 v1.44.0
Expand All @@ -50,8 +50,8 @@ require (
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.27.0
go.withmatt.com/connect-brotli v0.4.0
golang.org/x/sync v0.21.0
golang.org/x/sys v0.46.0 // indirect
golang.org/x/sync v0.20.0
golang.org/x/sys v0.45.0 // indirect
google.golang.org/grpc v1.82.1
google.golang.org/protobuf v1.36.11
)
Expand Down Expand Up @@ -86,8 +86,8 @@ require (
github.com/wundergraph/go-arena v1.3.0
go.uber.org/goleak v1.3.0
go.uber.org/ratelimit v0.3.1
golang.org/x/net v0.56.0
golang.org/x/text v0.39.0
golang.org/x/net v0.55.0
golang.org/x/text v0.37.0
golang.org/x/time v0.15.0
)

Expand Down Expand Up @@ -169,7 +169,7 @@ require (
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/crypto v0.52.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
Expand Down
28 changes: 14 additions & 14 deletions router/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV
github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw=
github.com/wundergraph/go-arena v1.3.0 h1:n0ng5a1vbd8YGq1u3rMr0vPU5f6AZ1BXIiUhL1UIok8=
github.com/wundergraph/go-arena v1.3.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw=
github.com/wundergraph/graphql-go-tools/v2 v2.15.0 h1:AG4l/QZrj1IBqxxPzz40r31fmKBopXQw+UjsgHYu8j4=
github.com/wundergraph/graphql-go-tools/v2 v2.15.0/go.mod h1:zREIKLmpjfNcGSubndaW/913r0Y8XbbYOXQeZFkwHdo=
github.com/wundergraph/graphql-go-tools/v2 v2.15.2-0.20260812110546-307924a9b18c h1:r6jdzvZVLVPqrQb6eooE/xL53lJIIcZRT7sOP97d6Qw=
github.com/wundergraph/graphql-go-tools/v2 v2.15.2-0.20260812110546-307924a9b18c/go.mod h1:zREIKLmpjfNcGSubndaW/913r0Y8XbbYOXQeZFkwHdo=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
Expand Down Expand Up @@ -398,21 +398,21 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -429,19 +429,19 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
Expand Down
15 changes: 12 additions & 3 deletions router/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import (

"github.com/caarlos0/env/v11"
"github.com/goccy/go-yaml"
"go.uber.org/zap/zapcore"

"github.com/wundergraph/cosmo/router/internal/unique"
"github.com/wundergraph/cosmo/router/internal/yamlmerge"
"github.com/wundergraph/cosmo/router/pkg/otel/otelconfig"
"go.uber.org/zap/zapcore"
)

const (
Expand Down Expand Up @@ -479,8 +480,9 @@ type EngineExecutionConfiguration struct {
// Deprecated: EnableExecutionPlanCacheResponseHeader is deprecated, use EngineDebugConfiguration.EnableCacheResponseHeaders instead.
EnableExecutionPlanCacheResponseHeader bool `envDefault:"false" env:"ENGINE_ENABLE_EXECUTION_PLAN_CACHE_RESPONSE_HEADER" yaml:"enable_execution_plan_cache_response_header"`

MaxConcurrentResolvers int `envDefault:"1024" env:"ENGINE_MAX_CONCURRENT_RESOLVERS" yaml:"max_concurrent_resolvers,omitempty"`
EnableNetPoll bool `envDefault:"true" env:"ENGINE_ENABLE_NET_POLL" yaml:"enable_net_poll"`
MaxConcurrentResolvers int `envDefault:"1024" env:"ENGINE_MAX_CONCURRENT_RESOLVERS" yaml:"max_concurrent_resolvers,omitempty"`
EnableNetPoll bool `envDefault:"true" env:"ENGINE_ENABLE_NET_POLL" yaml:"enable_net_poll"`

ExecutionPlanCacheSize int64 `envDefault:"1024" env:"ENGINE_EXECUTION_PLAN_CACHE_SIZE" yaml:"execution_plan_cache_size,omitempty"`
SlowPlanCacheSize int64 `envDefault:"300" env:"ENGINE_SLOW_PLAN_CACHE_SIZE" yaml:"slow_plan_cache_size,omitempty"`
SlowPlanCacheThreshold time.Duration `envDefault:"100ms" env:"ENGINE_SLOW_PLAN_CACHE_THRESHOLD" yaml:"slow_plan_cache_threshold,omitempty"`
Expand All @@ -500,6 +502,13 @@ type EngineExecutionConfiguration struct {
SubscriptionFetchTimeout time.Duration `envDefault:"30s" env:"ENGINE_SUBSCRIPTION_FETCH_TIMEOUT" yaml:"subscription_fetch_timeout,omitempty"`
EnableDefer bool `envDefault:"false" env:"ENGINE_ENABLE_DEFER" yaml:"enable_defer"`

// EnableMultiFetch merges entity fetches to the same subgraph that execute
// in the same wave into a single batched request with aliased _entities fields.
EnableMultiFetch bool `envDefault:"false" env:"ENGINE_ENABLE_MULTI_FETCH" yaml:"enable_multi_fetch"`
// EnableScheduleFetches replaces the legacy wave-based fetch organizers with the
// dependency-aware fetch scheduler (component-split, chain-inlined execution trees).
EnableScheduleFetches bool `envDefault:"false" env:"ENGINE_ENABLE_SCHEDULE_FETCHES" yaml:"enable_schedule_fetches"`

// Server-side WebSocket handler options (router accepting client connections)
WebSocketServerReadTimeout time.Duration `envDefault:"5s" env:"ENGINE_WEBSOCKET_SERVER_READ_TIMEOUT" yaml:"websocket_server_read_timeout,omitempty"`
WebSocketServerWriteTimeout time.Duration `envDefault:"10s" env:"ENGINE_WEBSOCKET_SERVER_WRITE_TIMEOUT" yaml:"websocket_server_write_timeout,omitempty"`
Expand Down
10 changes: 10 additions & 0 deletions router/pkg/config/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3973,6 +3973,16 @@
"description": "Enables the more efficient poll implementation for all WebSocket implementations (client, server) of the router. This is only available on Linux and MacOS. On Windows or when the host system is limited, the default synchronous implementation is used.",
"default": true
},
"enable_multi_fetch": {
"type": "boolean",
"description": "Enables merging entity fetches to the same subgraph that execute in the same wave into a single batched request with aliased _entities fields. Merging reduces the number of subgraph requests.",
"default": false
},
"enable_schedule_fetches": {
"type": "boolean",
"description": "Enables the dependency-aware fetch scheduler, which organizes fetches into component-split, chain-inlined execution trees instead of the legacy wave-based structure.",
"default": false
},
"websocket_client_write_timeout": {
"type": "string",
"format": "go-duration",
Expand Down
2 changes: 2 additions & 0 deletions router/pkg/config/testdata/config_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,8 @@
"EnableRequireFetchReasons": false,
"SubscriptionFetchTimeout": 30000000000,
"EnableDefer": false,
"EnableMultiFetch": false,
"EnableScheduleFetches": false,
"WebSocketServerReadTimeout": 5000000000,
"WebSocketServerWriteTimeout": 10000000000,
"WebSocketServerPollTimeout": 1000000000,
Expand Down
2 changes: 2 additions & 0 deletions router/pkg/config/testdata/config_full.json
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,8 @@
"EnableRequireFetchReasons": false,
"SubscriptionFetchTimeout": 30000000000,
"EnableDefer": false,
"EnableMultiFetch": false,
"EnableScheduleFetches": false,
"WebSocketServerReadTimeout": 5000000000,
"WebSocketServerWriteTimeout": 10000000000,
"WebSocketServerPollTimeout": 1000000000,
Expand Down
Loading