Skip to content

Commit 6566e02

Browse files
jensneusedevsergiy
andauthored
feat: improve resolve performance by solving merge abstract nodes in postprocessing (#826)
Previously, we've generated too many fields for abstract nodes. This meant that after resolving, we had to deduplicate and merge some fields. This new approach solves the problem in a post processing step as part of the planning. This means that the runtime does less work during resolving, and a little bit more work during planning, which gets cached anyways. --------- Co-authored-by: Sergiy 🇺🇦 <818351+devsergiy@users.noreply.github.com>
1 parent 3ec0ebe commit 6566e02

29 files changed

Lines changed: 4746 additions & 1506 deletions

execution/engine/execution_engine_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,18 @@ func newFederationEngineStaticConfig(ctx context.Context, setup *federationtesti
18111811
TypeName: "WalletType2",
18121812
FieldNames: []string{"currency", "amount", "specialField2"},
18131813
},
1814+
{
1815+
TypeName: "Namer",
1816+
FieldNames: []string{"name"},
1817+
},
1818+
{
1819+
TypeName: "A",
1820+
FieldNames: []string{"name"},
1821+
},
1822+
{
1823+
TypeName: "B",
1824+
FieldNames: []string{"name"},
1825+
},
18141826
},
18151827
FederationMetaData: plan.FederationMetaData{
18161828
Keys: plan.FederationFieldConfigurations{

execution/engine/federation_integration_test.go

Lines changed: 145 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,33 @@ func TestFederationIntegrationTestWithArt(t *testing.T) {
8080
// This tests produces data races in the generated gql code. Disable it when the race
8181
// detector is enabled.
8282
func TestFederationIntegrationTest(t *testing.T) {
83-
ctx, cancel := context.WithCancel(context.Background())
84-
defer cancel()
85-
86-
setup := federationtesting.NewFederationSetup(addGateway(false))
87-
defer setup.Close()
88-
89-
gqlClient := NewGraphqlClient(http.DefaultClient)
9083

9184
t.Run("single upstream query operation", func(t *testing.T) {
85+
setup := federationtesting.NewFederationSetup(addGateway(false))
86+
t.Cleanup(setup.Close)
87+
gqlClient := NewGraphqlClient(http.DefaultClient)
88+
ctx, cancel := context.WithCancel(context.Background())
89+
t.Cleanup(cancel)
9290
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/single_upstream.query"), nil, t)
9391
assert.Equal(t, `{"data":{"me":{"id":"1234","username":"Me"}}}`, string(resp))
9492
})
9593

9694
t.Run("query spans multiple federated servers", func(t *testing.T) {
95+
setup := federationtesting.NewFederationSetup(addGateway(false))
96+
t.Cleanup(setup.Close)
97+
gqlClient := NewGraphqlClient(http.DefaultClient)
98+
ctx, cancel := context.WithCancel(context.Background())
99+
t.Cleanup(cancel)
97100
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/multiple_upstream.query"), nil, t)
98101
assert.Equal(t, `{"data":{"topProducts":[{"name":"Trilby","reviews":[{"body":"A highly effective form of birth control.","author":{"username":"Me"}}]},{"name":"Fedora","reviews":[{"body":"Fedoras are one of the most fashionable hats around and can look great with a variety of outfits.","author":{"username":"Me"}}]}]}}`, string(resp))
99102
})
100103

101104
t.Run("mutation operation with variables", func(t *testing.T) {
105+
setup := federationtesting.NewFederationSetup(addGateway(false))
106+
t.Cleanup(setup.Close)
107+
gqlClient := NewGraphqlClient(http.DefaultClient)
108+
ctx, cancel := context.WithCancel(context.Background())
109+
t.Cleanup(cancel)
102110
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("mutations/mutation_with_variables.query"), queryVariables{
103111
"authorID": "3210",
104112
"upc": "top-1",
@@ -108,18 +116,31 @@ func TestFederationIntegrationTest(t *testing.T) {
108116
})
109117

110118
t.Run("union query", func(t *testing.T) {
119+
setup := federationtesting.NewFederationSetup(addGateway(false))
120+
t.Cleanup(setup.Close)
121+
gqlClient := NewGraphqlClient(http.DefaultClient)
122+
ctx, cancel := context.WithCancel(context.Background())
123+
t.Cleanup(cancel)
111124
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/union.query"), nil, t)
112125
assert.Equal(t, `{"data":{"me":{"username":"Me","history":[{"__typename":"Purchase","wallet":{"amount":123}},{"__typename":"Sale","rating":5},{"__typename":"Purchase","wallet":{"amount":123}}]}}}`, string(resp))
113126
})
114127

115128
t.Run("interface query", func(t *testing.T) {
129+
setup := federationtesting.NewFederationSetup(addGateway(false))
130+
t.Cleanup(setup.Close)
131+
gqlClient := NewGraphqlClient(http.DefaultClient)
132+
ctx, cancel := context.WithCancel(context.Background())
133+
t.Cleanup(cancel)
116134
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/interface.query"), nil, t)
117135
assert.Equal(t, `{"data":{"me":{"username":"Me","history":[{"wallet":{"amount":123,"specialField1":"some special value 1"}},{"rating":5},{"wallet":{"amount":123,"specialField2":"some special value 2"}}]}}}`, string(resp))
118136
})
119137

120138
t.Run("subscription query through WebSocket transport", func(t *testing.T) {
121-
ctx, cancel := context.WithCancel(ctx)
122-
defer cancel()
139+
setup := federationtesting.NewFederationSetup(addGateway(false))
140+
t.Cleanup(setup.Close)
141+
gqlClient := NewGraphqlClient(http.DefaultClient)
142+
ctx, cancel := context.WithCancel(context.Background())
143+
t.Cleanup(cancel)
123144
// Reset the products slice to the original state
124145
defer products.Reset()
125146

@@ -134,8 +155,11 @@ func TestFederationIntegrationTest(t *testing.T) {
134155
})
135156

136157
t.Run("Multiple queries and nested fragments", func(t *testing.T) {
137-
ctx, cancel := context.WithCancel(ctx)
138-
defer cancel()
158+
setup := federationtesting.NewFederationSetup(addGateway(false))
159+
t.Cleanup(setup.Close)
160+
gqlClient := NewGraphqlClient(http.DefaultClient)
161+
ctx, cancel := context.WithCancel(context.Background())
162+
t.Cleanup(cancel)
139163
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/multiple_queries_with_nested_fragments.query"), nil, t)
140164
expected := `
141165
{
@@ -181,8 +205,11 @@ func TestFederationIntegrationTest(t *testing.T) {
181205
})
182206

183207
t.Run("Multiple queries with __typename", func(t *testing.T) {
184-
ctx, cancel := context.WithCancel(ctx)
185-
defer cancel()
208+
setup := federationtesting.NewFederationSetup(addGateway(false))
209+
t.Cleanup(setup.Close)
210+
gqlClient := NewGraphqlClient(http.DefaultClient)
211+
ctx, cancel := context.WithCancel(context.Background())
212+
t.Cleanup(cancel)
186213
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/multiple_queries.query"), nil, t)
187214
expected := `
188215
{
@@ -210,8 +237,11 @@ func TestFederationIntegrationTest(t *testing.T) {
210237
})
211238

212239
t.Run("Query that returns union", func(t *testing.T) {
213-
ctx, cancel := context.WithCancel(ctx)
214-
defer cancel()
240+
setup := federationtesting.NewFederationSetup(addGateway(false))
241+
t.Cleanup(setup.Close)
242+
gqlClient := NewGraphqlClient(http.DefaultClient)
243+
ctx, cancel := context.WithCancel(context.Background())
244+
t.Cleanup(cancel)
215245
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/multiple_queries_with_union_return.query"), nil, t)
216246
expected := `
217247
{
@@ -286,8 +316,11 @@ func TestFederationIntegrationTest(t *testing.T) {
286316
})
287317

288318
t.Run("Object response type with interface and object fragment", func(t *testing.T) {
289-
ctx, cancel := context.WithCancel(ctx)
290-
defer cancel()
319+
setup := federationtesting.NewFederationSetup(addGateway(false))
320+
t.Cleanup(setup.Close)
321+
gqlClient := NewGraphqlClient(http.DefaultClient)
322+
ctx, cancel := context.WithCancel(context.Background())
323+
t.Cleanup(cancel)
291324
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/interface_fragment_on_object.graphql"), nil, t)
292325
expected := `
293326
{
@@ -302,8 +335,11 @@ func TestFederationIntegrationTest(t *testing.T) {
302335
})
303336

304337
t.Run("Interface response type with object fragment", func(t *testing.T) {
305-
ctx, cancel := context.WithCancel(ctx)
306-
defer cancel()
338+
setup := federationtesting.NewFederationSetup(addGateway(false))
339+
t.Cleanup(setup.Close)
340+
gqlClient := NewGraphqlClient(http.DefaultClient)
341+
ctx, cancel := context.WithCancel(context.Background())
342+
t.Cleanup(cancel)
307343
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/object_fragment_on_interface.graphql"), nil, t)
308344
expected := `
309345
{
@@ -319,8 +355,11 @@ func TestFederationIntegrationTest(t *testing.T) {
319355
})
320356

321357
t.Run("Union response type with interface fragments", func(t *testing.T) {
322-
ctx, cancel := context.WithCancel(ctx)
323-
defer cancel()
358+
setup := federationtesting.NewFederationSetup(addGateway(false))
359+
t.Cleanup(setup.Close)
360+
gqlClient := NewGraphqlClient(http.DefaultClient)
361+
ctx, cancel := context.WithCancel(context.Background())
362+
t.Cleanup(cancel)
324363
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/interface_fragments_on_union.graphql"), nil, t)
325364
expected := `
326365
{
@@ -360,98 +399,97 @@ func TestFederationIntegrationTest(t *testing.T) {
360399
// Duplicated properties (and therefore invalid JSON) are usually removed during normalization processes.
361400
// It is not yet decided whether this should be addressed before these normalization processes.
362401
t.Run("Complex nesting", func(t *testing.T) {
363-
ctx, cancel := context.WithCancel(ctx)
364-
defer cancel()
402+
setup := federationtesting.NewFederationSetup(addGateway(false))
403+
defer setup.Close()
404+
405+
gqlClient := NewGraphqlClient(http.DefaultClient)
406+
ctx, cancel := context.WithCancel(context.Background())
407+
t.Cleanup(cancel)
365408
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/complex_nesting.graphql"), nil, t)
366-
expected := `
367-
{
368-
"data": {
369-
"me": {
370-
"id": "1234",
371-
"username": "Me",
372-
"history": [
373-
{
374-
"wallet": {
375-
"currency": "USD"
376-
}
377-
},
378-
{
379-
"location": "Germany",
380-
"product": {
381-
"upc": "top-2",
382-
"name": "Fedora"
383-
}
384-
},
385-
{
386-
"wallet": {
387-
"currency": "USD"
388-
}
389-
}
390-
],
391-
"reviews": [
392-
{
393-
"__typename": "Review",
394-
"attachments": [
395-
{
396-
"upc": "top-1",
397-
"body": "How do I turn it on?"
398-
}
399-
]
400-
},
401-
{
402-
"__typename": "Review",
403-
"attachments": [
404-
{
405-
"upc": "top-2",
406-
"body": "The best hat I have ever bought in my life."
407-
},
408-
{
409-
"upc": "top-2",
410-
"size": 13.37
411-
}
412-
]
413-
}
414-
]
415-
}
416-
}
417-
}
418-
`
409+
expected := `{"data":{"me":{"id":"1234","username":"Me","history":[{"wallet":{"currency":"USD"}},{"location":"Germany","product":{"upc":"top-2","name":"Fedora"}},{"wallet":{"currency":"USD"}}],"reviews":[{"__typename":"Review","attachments":[{"__typename":"Question","upc":"top-1","body":"How do I turn it on?"}]},{"__typename":"Review","attachments":[{"__typename":"Rating","upc":"top-2","body":"The best hat I have ever bought in my life."},{"__typename":"Video","upc":"top-2","size":13.37}]}]}}}`
419410
assert.Equal(t, compact(expected), string(resp))
420411
})
421412

422-
t.Run("Merged fields are still resolved", func(t *testing.T) {
413+
t.Run("More complex nesting", func(t *testing.T) {
414+
setup := federationtesting.NewFederationSetup(addGateway(false))
415+
defer setup.Close()
423416

424-
ctx, cancel := context.WithCancel(ctx)
425-
defer cancel()
417+
gqlClient := NewGraphqlClient(http.DefaultClient)
418+
ctx, cancel := context.WithCancel(context.Background())
419+
t.Cleanup(cancel)
420+
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/more_complex_nesting.graphql"), nil, t)
421+
expected := `{"data":{"me":{"id":"1234","username":"Me","history":[{"wallet":{"currency":"USD"}},{"location":"Germany","product":{"name":"Fedora","upc":"top-2"}},{"wallet":{"currency":"USD"}}],"reviews":[{"__typename":"Review","attachments":[{"__typename":"Question","upc":"top-1","body":"How do I turn it on?"}],"comment":{"__typename":"Question","subject":"Life"}},{"__typename":"Review","attachments":[{"__typename":"Rating","upc":"top-2","body":"The best hat I have ever bought in my life."},{"__typename":"Video","upc":"top-2","size":13.37}],"comment":{"__typename":"Question","subject":"Life"}}]}}}`
422+
assert.Equal(t, compact(expected), string(resp))
423+
})
424+
425+
t.Run("Multiple nested interfaces", func(t *testing.T) {
426+
setup := federationtesting.NewFederationSetup(addGateway(false))
427+
defer setup.Close()
428+
429+
gqlClient := NewGraphqlClient(http.DefaultClient)
430+
ctx, cancel := context.WithCancel(context.Background())
431+
t.Cleanup(cancel)
432+
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/titlename.graphql"), nil, t)
433+
expected := `{"data":{"titleName":{"__typename":"TitleName","title":"Title","a":"A","b":"B","name":"Name","c":"C"}}}`
434+
assert.Equal(t, compact(expected), string(resp))
435+
})
436+
437+
t.Run("Multiple nested unions", func(t *testing.T) {
438+
setup := federationtesting.NewFederationSetup(addGateway(false))
439+
defer setup.Close()
440+
441+
gqlClient := NewGraphqlClient(http.DefaultClient)
442+
ctx, cancel := context.WithCancel(context.Background())
443+
t.Cleanup(cancel)
444+
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/cd.graphql"), nil, t)
445+
expected := `{"data":{"cds":[{"name":{"first":"Leonie","middle":"Johanna"}},{"name":{"first":"Jannik","last":"Neuse"}}]}}`
446+
assert.Equal(t, compact(expected), string(resp))
447+
})
448+
449+
t.Run("More complex nesting typename variant", func(t *testing.T) {
450+
setup := federationtesting.NewFederationSetup(addGateway(false))
451+
defer setup.Close()
452+
453+
gqlClient := NewGraphqlClient(http.DefaultClient)
454+
ctx, cancel := context.WithCancel(context.Background())
455+
t.Cleanup(cancel)
456+
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/more_complex_nesting_typename_variant.graphql"), nil, t)
457+
expected := `{"data":{"me":{"id":"1234","username":"Me","history":[{"wallet":{"currency":"USD"}},{"location":"Germany","product":{"name":"Fedora","upc":"top-2"}},{"wallet":{"currency":"USD"}}],"reviews":[{"__typename":"Review","attachments":[{"__typename":"Question","upc":"top-1","body":"How do I turn it on?"}]},{"__typename":"Review","attachments":[{"__typename":"Rating","upc":"top-2","body":"The best hat I have ever bought in my life."},{"__typename":"Video","upc":"top-2","size":13.37}]}]}}}`
458+
assert.Equal(t, compact(expected), string(resp))
459+
})
460+
461+
t.Run("Abstract object", func(t *testing.T) {
462+
setup := federationtesting.NewFederationSetup(addGateway(false))
463+
defer setup.Close()
464+
465+
gqlClient := NewGraphqlClient(http.DefaultClient)
466+
ctx, cancel := context.WithCancel(context.Background())
467+
t.Cleanup(cancel)
468+
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/abstract_object.graphql"), nil, t)
469+
expected := `{"data":{"abstractList":[{"__typename":"ConcreteListItem1","obj":{"__typename":"SomeType1","name":"1","age":1}},{"__typename":"ConcreteListItem2","obj":{"__typename":"SomeType2","name":"2","height":2}}]}}`
470+
assert.Equal(t, compact(expected), string(resp))
471+
})
472+
473+
t.Run("Abstract interface field", func(t *testing.T) {
474+
setup := federationtesting.NewFederationSetup(addGateway(false))
475+
defer setup.Close()
476+
477+
gqlClient := NewGraphqlClient(http.DefaultClient)
478+
ctx, cancel := context.WithCancel(context.Background())
479+
t.Cleanup(cancel)
480+
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/ab.graphql"), nil, t)
481+
expected := `{"data":{"interfaceUnion":{"__typename":"A","name":"A"}}}`
482+
assert.Equal(t, compact(expected), string(resp))
483+
})
484+
485+
t.Run("Merged fields are still resolved", func(t *testing.T) {
486+
setup := federationtesting.NewFederationSetup(addGateway(false))
487+
t.Cleanup(setup.Close)
488+
gqlClient := NewGraphqlClient(http.DefaultClient)
489+
ctx, cancel := context.WithCancel(context.Background())
490+
t.Cleanup(cancel)
426491
resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/merged_field.graphql"), nil, t)
427-
expected := `
428-
{
429-
"data": {
430-
"cat": {
431-
"name": "Pepper"
432-
},
433-
"me": {
434-
"id": "1234",
435-
"username": "Me",
436-
"realName": "User Usington",
437-
"reviews": [
438-
{
439-
"body": "A highly effective form of birth control."
440-
},
441-
{
442-
"body": "Fedoras are one of the most fashionable hats around and can look great with a variety of outfits."
443-
}
444-
],
445-
"history": [
446-
{},
447-
{
448-
"rating": 5
449-
},
450-
{}
451-
]
452-
}
453-
}
454-
}`
492+
expected := `{"data":{"cat":{"name":"Pepper"},"me":{"id":"1234","username":"Me","realName":"User Usington","reviews":[{"body":"A highly effective form of birth control."},{"body":"Fedoras are one of the most fashionable hats around and can look great with a variety of outfits."}],"history":[{},{"rating":5},{}]}}}`
455493
assert.Equal(t, compact(expected), string(resp))
456494
})
457495
}

execution/engine/testdata/complex_nesting_query_with_art.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"__typename": "Review",
2828
"attachments": [
2929
{
30+
"__typename": "Question",
3031
"upc": "top-1",
3132
"body": "How do I turn it on?"
3233
}
@@ -36,10 +37,12 @@
3637
"__typename": "Review",
3738
"attachments": [
3839
{
40+
"__typename": "Rating",
3941
"upc": "top-2",
4042
"body": "The best hat I have ever bought in my life."
4143
},
4244
{
45+
"__typename": "Video",
4346
"upc": "top-2",
4447
"size": 13.37
4548
}

0 commit comments

Comments
 (0)