From 5c19c84141c643fa8e3ef59a63d9788ebc966c43 Mon Sep 17 00:00:00 2001 From: Rumen Nikiforov Date: Tue, 30 May 2023 23:28:26 +0300 Subject: [PATCH] Addressing few issues in defer feature (#2656) * Using @fiatja atomic approach And fixed hasNext to only appear in the payload when there is deferred usage * Regenerate * Use go 1.18 compatible atomic operations * Regenerate --- _examples/chat/generated.go | 107 ++--- _examples/config/generated.go | 111 ++--- _examples/dataloader/generated.go | 111 ++--- .../embedding/subdir/entity.generated.go | 6 +- .../embedding/subdir/gendir/generated.go | 96 +++-- .../embedding/subdir/prelude.generated.go | 31 +- _examples/embedding/subdir/root.generated.go | 5 +- _examples/embedding/subdir/root_.generated.go | 58 +-- .../federation/accounts/graph/generated.go | 111 ++--- .../federation/products/graph/generated.go | 111 ++--- .../federation/reviews/graph/generated.go | 126 +++--- _examples/fileupload/generated.go | 101 +++-- _examples/scalars/generated.go | 101 +++-- _examples/selection/generated.go | 102 +++-- _examples/starwars/generated/exec.go | 131 +++--- _examples/todo/generated.go | 101 +++-- _examples/type-system-extension/generated.go | 101 +++-- api/testdata/default/graph/generated.go | 106 ++--- api/testdata/federation2/graph/generated.go | 111 ++--- codegen/generated!.gotpl | 56 +-- codegen/object.gotpl | 11 +- codegen/root_.gotpl | 57 +-- .../followschema/builtinscalar.generated.go | 6 +- .../followschema/complexity.generated.go | 5 +- .../followschema/defaults.generated.go | 11 +- .../followschema/directive.generated.go | 11 +- .../followschema/embedded.generated.go | 16 +- .../followschema/fields_order.generated.go | 6 +- .../followschema/interfaces.generated.go | 45 +- .../followschema/issue896.generated.go | 6 +- .../followschema/loops.generated.go | 11 +- .../testserver/followschema/maps.generated.go | 6 +- .../followschema/nulls.generated.go | 10 +- .../followschema/panics.generated.go | 5 +- .../followschema/prelude.generated.go | 31 +- .../primitive_objects.generated.go | 10 +- .../followschema/ptr_to_any.generated.go | 6 +- .../ptr_to_ptr_input.generated.go | 11 +- .../followschema/ptr_to_slice.generated.go | 6 +- .../followschema/root_.generated.go | 58 +-- .../followschema/scalar_default.generated.go | 6 +- .../followschema/schema.generated.go | 55 ++- .../followschema/slices.generated.go | 6 +- .../followschema/useptr.generated.go | 11 +- .../testserver/followschema/v-ok.generated.go | 11 +- .../followschema/validtypes.generated.go | 16 +- .../followschema/variadic.generated.go | 6 +- .../weird_type_cases.generated.go | 31 +- .../followschema/wrapped_type.generated.go | 15 +- codegen/testserver/singlefile/generated.go | 396 ++++++++++-------- graphql/response.go | 2 +- integration/generated.go | 111 ++--- .../testdata/entityresolver/generated/exec.go | 171 ++++---- 53 files changed, 1656 insertions(+), 1292 deletions(-) diff --git a/_examples/chat/generated.go b/_examples/chat/generated.go index 80a84becf6e..a4b35f545bb 100644 --- a/_examples/chat/generated.go +++ b/_examples/chat/generated.go @@ -11,6 +11,7 @@ import ( "io" "strconv" "sync" + "sync/atomic" "time" "github.com/99designs/gqlgen/graphql" @@ -93,7 +94,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -181,7 +182,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -195,9 +196,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -209,28 +210,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -278,11 +260,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2901,9 +2902,10 @@ func (ec *executionContext) _Chatroom(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2954,9 +2956,10 @@ func (ec *executionContext) _Message(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3002,9 +3005,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3070,9 +3074,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3145,9 +3150,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3192,9 +3198,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3249,9 +3256,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3296,9 +3304,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3350,9 +3359,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3406,9 +3416,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/config/generated.go b/_examples/config/generated.go index d504aa7d739..ab591bd868c 100644 --- a/_examples/config/generated.go +++ b/_examples/config/generated.go @@ -97,7 +97,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -189,7 +189,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputNewTodo, ) @@ -205,9 +205,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -219,28 +219,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -269,11 +250,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2892,9 +2892,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2963,9 +2964,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3052,9 +3054,10 @@ func (ec *executionContext) _Todo(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3100,9 +3103,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3155,9 +3159,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3202,9 +3207,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3259,9 +3265,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3306,9 +3313,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3360,9 +3368,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3416,9 +3425,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3485,9 +3495,10 @@ func (ec *executionContext) _role(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/dataloader/generated.go b/_examples/dataloader/generated.go index e4ae333f502..f5df1ea4266 100644 --- a/_examples/dataloader/generated.go +++ b/_examples/dataloader/generated.go @@ -101,7 +101,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -226,7 +226,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -240,9 +240,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -254,28 +254,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -289,11 +270,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -3072,9 +3072,10 @@ func (ec *executionContext) _Address(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3181,9 +3182,10 @@ func (ec *executionContext) _Customer(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3219,9 +3221,10 @@ func (ec *executionContext) _Item(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3300,9 +3303,10 @@ func (ec *executionContext) _Order(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3406,9 +3410,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3461,9 +3466,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3508,9 +3514,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3565,9 +3572,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3612,9 +3620,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3666,9 +3675,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3722,9 +3732,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/embedding/subdir/entity.generated.go b/_examples/embedding/subdir/entity.generated.go index 54e37c20228..4e16b925ae9 100644 --- a/_examples/embedding/subdir/entity.generated.go +++ b/_examples/embedding/subdir/entity.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/plugin/federation/fedruntime" @@ -101,9 +102,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/embedding/subdir/gendir/generated.go b/_examples/embedding/subdir/gendir/generated.go index ce7375701bd..c95a5103d1f 100644 --- a/_examples/embedding/subdir/gendir/generated.go +++ b/_examples/embedding/subdir/gendir/generated.go @@ -72,7 +72,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -117,7 +117,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -131,9 +131,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -145,28 +145,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -180,11 +161,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2545,9 +2545,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2580,9 +2581,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2635,9 +2637,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2682,9 +2685,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2739,9 +2743,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2786,9 +2791,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2840,9 +2846,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2896,9 +2903,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/embedding/subdir/prelude.generated.go b/_examples/embedding/subdir/prelude.generated.go index 903c663e070..1831b379ed1 100644 --- a/_examples/embedding/subdir/prelude.generated.go +++ b/_examples/embedding/subdir/prelude.generated.go @@ -8,6 +8,7 @@ import ( "fmt" "strconv" "sync" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -1905,9 +1906,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1952,9 +1954,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2009,9 +2012,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2056,9 +2060,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2110,9 +2115,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2166,9 +2172,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/embedding/subdir/root.generated.go b/_examples/embedding/subdir/root.generated.go index 997039927d8..b9c6784155f 100644 --- a/_examples/embedding/subdir/root.generated.go +++ b/_examples/embedding/subdir/root.generated.go @@ -495,9 +495,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/embedding/subdir/root_.generated.go b/_examples/embedding/subdir/root_.generated.go index b9654e9ab8b..03c8e18e5ab 100644 --- a/_examples/embedding/subdir/root_.generated.go +++ b/_examples/embedding/subdir/root_.generated.go @@ -8,6 +8,7 @@ import ( "embed" "errors" "fmt" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -61,7 +62,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -106,7 +107,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -120,9 +121,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -134,29 +135,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + return &response } @@ -168,11 +151,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") diff --git a/_examples/federation/accounts/graph/generated.go b/_examples/federation/accounts/graph/generated.go index dd68225a8f5..c9835d00421 100644 --- a/_examples/federation/accounts/graph/generated.go +++ b/_examples/federation/accounts/graph/generated.go @@ -93,7 +93,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -202,7 +202,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -216,9 +216,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -230,28 +230,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -265,11 +246,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2985,9 +2985,10 @@ func (ec *executionContext) _EmailHost(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3070,9 +3071,10 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3182,9 +3184,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3235,9 +3238,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3270,9 +3274,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3325,9 +3330,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3372,9 +3378,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3429,9 +3436,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3476,9 +3484,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3530,9 +3539,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3586,9 +3596,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/federation/products/graph/generated.go b/_examples/federation/products/graph/generated.go index c1df02cb36b..cf1682e41ca 100644 --- a/_examples/federation/products/graph/generated.go +++ b/_examples/federation/products/graph/generated.go @@ -96,7 +96,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -229,7 +229,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -243,9 +243,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -257,28 +257,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -292,11 +273,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -3242,9 +3242,10 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3285,9 +3286,10 @@ func (ec *executionContext) _Manufacturer(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3343,9 +3345,10 @@ func (ec *executionContext) _Product(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3455,9 +3458,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3490,9 +3494,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3545,9 +3550,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3592,9 +3598,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3649,9 +3656,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3696,9 +3704,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3750,9 +3759,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3806,9 +3816,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/federation/reviews/graph/generated.go b/_examples/federation/reviews/graph/generated.go index 361ac474f3b..e46bca46556 100644 --- a/_examples/federation/reviews/graph/generated.go +++ b/_examples/federation/reviews/graph/generated.go @@ -107,7 +107,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -251,7 +251,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -265,9 +265,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -279,28 +279,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -314,11 +295,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -3297,9 +3297,10 @@ func (ec *executionContext) _EmailHost(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3382,9 +3383,10 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3420,9 +3422,10 @@ func (ec *executionContext) _Manufacturer(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3465,9 +3468,10 @@ func (ec *executionContext) _Product(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3558,9 +3562,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3606,9 +3611,10 @@ func (ec *executionContext) _Review(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3687,9 +3693,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3722,9 +3729,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3777,9 +3785,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3824,9 +3833,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3881,9 +3891,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3928,9 +3939,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3982,9 +3994,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -4038,9 +4051,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/fileupload/generated.go b/_examples/fileupload/generated.go index 421c4d31c05..eaebc918e93 100644 --- a/_examples/fileupload/generated.go +++ b/_examples/fileupload/generated.go @@ -85,7 +85,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -178,7 +178,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputUploadFile, ) @@ -194,9 +194,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -208,28 +208,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -258,11 +239,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2878,9 +2878,10 @@ func (ec *executionContext) _File(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2947,9 +2948,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3018,9 +3020,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3073,9 +3076,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3120,9 +3124,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3177,9 +3182,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3224,9 +3230,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3278,9 +3285,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3334,9 +3342,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/scalars/generated.go b/_examples/scalars/generated.go index 46d0ab7ed08..8427f2e5753 100644 --- a/_examples/scalars/generated.go +++ b/_examples/scalars/generated.go @@ -94,7 +94,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -231,7 +231,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputSearchArgs, ) @@ -247,9 +247,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -261,28 +261,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -296,11 +277,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -3217,9 +3217,10 @@ func (ec *executionContext) _Address(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3329,9 +3330,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3461,9 +3463,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3516,9 +3519,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3563,9 +3567,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3620,9 +3625,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3667,9 +3673,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3721,9 +3728,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3777,9 +3785,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/selection/generated.go b/_examples/selection/generated.go index 00de3dfcbcd..3c1b2bf8092 100644 --- a/_examples/selection/generated.go +++ b/_examples/selection/generated.go @@ -10,6 +10,7 @@ import ( "fmt" "strconv" "sync" + "sync/atomic" "time" "github.com/99designs/gqlgen/graphql" @@ -77,7 +78,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -150,7 +151,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true @@ -164,9 +165,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -178,28 +179,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -213,11 +195,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2653,9 +2654,10 @@ func (ec *executionContext) _Like(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2700,9 +2702,10 @@ func (ec *executionContext) _Post(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2768,9 +2771,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2823,9 +2827,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2870,9 +2875,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2927,9 +2933,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2974,9 +2981,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3028,9 +3036,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3084,9 +3093,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/starwars/generated/exec.go b/_examples/starwars/generated/exec.go index 0a1b83927b8..a07c44e818f 100644 --- a/_examples/starwars/generated/exec.go +++ b/_examples/starwars/generated/exec.go @@ -156,7 +156,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -492,7 +492,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputReviewInput, ) @@ -508,9 +508,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -522,28 +522,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -572,11 +553,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -4974,9 +4974,10 @@ func (ec *executionContext) _Droid(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5083,9 +5084,10 @@ func (ec *executionContext) _FriendsConnection(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5123,9 +5125,10 @@ func (ec *executionContext) _FriendsEdge(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5280,9 +5283,10 @@ func (ec *executionContext) _Human(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5325,9 +5329,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5373,9 +5378,10 @@ func (ec *executionContext) _PageInfo(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5561,9 +5567,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5603,9 +5610,10 @@ func (ec *executionContext) _Review(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5687,9 +5695,10 @@ func (ec *executionContext) _Starship(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5742,9 +5751,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5789,9 +5799,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5846,9 +5857,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5893,9 +5905,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5947,9 +5960,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6003,9 +6017,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/todo/generated.go b/_examples/todo/generated.go index 10228716049..fda22c6b058 100644 --- a/_examples/todo/generated.go +++ b/_examples/todo/generated.go @@ -85,7 +85,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -166,7 +166,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputTodoInput, ) @@ -184,9 +184,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { return ec._MyQuery(ctx, rc.Operation.SelectionSet), nil }) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -198,28 +198,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -250,11 +231,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2812,9 +2812,10 @@ func (ec *executionContext) _MyMutation(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2921,9 +2922,10 @@ func (ec *executionContext) _MyQuery(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2969,9 +2971,10 @@ func (ec *executionContext) _Todo(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3024,9 +3027,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3071,9 +3075,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3128,9 +3133,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3175,9 +3181,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3229,9 +3236,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3285,9 +3293,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/_examples/type-system-extension/generated.go b/_examples/type-system-extension/generated.go index b3022a1f3d7..dc8a20eaf88 100644 --- a/_examples/type-system-extension/generated.go +++ b/_examples/type-system-extension/generated.go @@ -87,7 +87,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -156,7 +156,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputTodoInput, ) @@ -172,9 +172,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._MyQuery(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -186,28 +186,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -236,11 +217,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2822,9 +2822,10 @@ func (ec *executionContext) _MyMutation(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2912,9 +2913,10 @@ func (ec *executionContext) _MyQuery(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2965,9 +2967,10 @@ func (ec *executionContext) _Todo(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3020,9 +3023,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3067,9 +3071,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3124,9 +3129,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3171,9 +3177,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3225,9 +3232,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3281,9 +3289,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/api/testdata/default/graph/generated.go b/api/testdata/default/graph/generated.go index 4cd68e9ba77..8442226c52e 100644 --- a/api/testdata/default/graph/generated.go +++ b/api/testdata/default/graph/generated.go @@ -84,7 +84,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -155,7 +155,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputNewTodo, ) @@ -171,9 +171,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -185,28 +185,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -235,11 +216,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2714,9 +2714,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2785,9 +2786,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2838,9 +2840,10 @@ func (ec *executionContext) _Todo(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2881,9 +2884,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2936,9 +2940,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2983,9 +2988,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3040,9 +3046,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3087,9 +3094,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3141,9 +3149,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3197,9 +3206,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/api/testdata/federation2/graph/generated.go b/api/testdata/federation2/graph/generated.go index dabe1675c4e..d3fdee53b51 100644 --- a/api/testdata/federation2/graph/generated.go +++ b/api/testdata/federation2/graph/generated.go @@ -92,7 +92,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -177,7 +177,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputNewTodo, ) @@ -193,9 +193,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -207,28 +207,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -257,11 +238,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -2885,9 +2885,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2978,9 +2979,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3031,9 +3033,10 @@ func (ec *executionContext) _Todo(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3074,9 +3077,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3109,9 +3113,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3164,9 +3169,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3211,9 +3217,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3268,9 +3275,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3315,9 +3323,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3369,9 +3378,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3425,9 +3435,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/generated!.gotpl b/codegen/generated!.gotpl index 584036d43e9..42e84b65da9 100644 --- a/codegen/generated!.gotpl +++ b/codegen/generated!.gotpl @@ -103,7 +103,7 @@ } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec {{ if not .Config.OmitComplexity -}} switch typeName + "." + field { @@ -138,7 +138,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( {{- range $input := .Inputs -}} {{ if not $input.HasUnmarshal }} @@ -164,9 +164,9 @@ data = ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet) {{- end }} } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -178,28 +178,9 @@ var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func (deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -259,11 +240,30 @@ type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } + func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func () { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() + } + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") diff --git a/codegen/object.gotpl b/codegen/object.gotpl index 6e58f004e5f..adb1df4929c 100644 --- a/codegen/object.gotpl +++ b/codegen/object.gotpl @@ -129,13 +129,14 @@ func (ec *executionContext) _{{$object.Name}}(ctx context.Context, sel ast.Selec out.Dispatch(ctx) if out.Invalids > 0 { return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), FieldSet: dfs, - Context: ctx, + Context: ctx, }) } diff --git a/codegen/root_.gotpl b/codegen/root_.gotpl index cc50630ee22..dcfc84d0ed1 100644 --- a/codegen/root_.gotpl +++ b/codegen/root_.gotpl @@ -76,7 +76,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec {{- if not .Config.OmitComplexity }} switch typeName + "." + field { @@ -111,7 +111,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e,nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( {{- range $input := .Inputs -}} {{ if not $input.HasUnmarshal }} @@ -137,9 +137,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { data = ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet) {{- end }} } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -151,29 +151,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func (deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + return &response } {{ end }} @@ -231,11 +213,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func () { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") diff --git a/codegen/testserver/followschema/builtinscalar.generated.go b/codegen/testserver/followschema/builtinscalar.generated.go index 54a7d9a507e..35d69d9e3d1 100644 --- a/codegen/testserver/followschema/builtinscalar.generated.go +++ b/codegen/testserver/followschema/builtinscalar.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -103,9 +104,10 @@ func (ec *executionContext) _Map(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/complexity.generated.go b/codegen/testserver/followschema/complexity.generated.go index 46797f6b496..38502144be7 100644 --- a/codegen/testserver/followschema/complexity.generated.go +++ b/codegen/testserver/followschema/complexity.generated.go @@ -323,9 +323,10 @@ func (ec *executionContext) _OverlappingFields(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/defaults.generated.go b/codegen/testserver/followschema/defaults.generated.go index 7d031f09ec8..12b75f71cb3 100644 --- a/codegen/testserver/followschema/defaults.generated.go +++ b/codegen/testserver/followschema/defaults.generated.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -476,9 +477,10 @@ func (ec *executionContext) _DefaultParametersMirror(ctx context.Context, sel as return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -545,9 +547,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/directive.generated.go b/codegen/testserver/followschema/directive.generated.go index 0d99102c517..2692254a38d 100644 --- a/codegen/testserver/followschema/directive.generated.go +++ b/codegen/testserver/followschema/directive.generated.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -662,9 +663,10 @@ func (ec *executionContext) _ObjectDirectives(ctx context.Context, sel ast.Selec return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -697,9 +699,10 @@ func (ec *executionContext) _ObjectDirectivesWithCustomGoModel(ctx context.Conte return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/embedded.generated.go b/codegen/testserver/followschema/embedded.generated.go index 9e886061bd1..c01dcd2ebe7 100644 --- a/codegen/testserver/followschema/embedded.generated.go +++ b/codegen/testserver/followschema/embedded.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -185,9 +186,10 @@ func (ec *executionContext) _EmbeddedCase1(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -223,9 +225,10 @@ func (ec *executionContext) _EmbeddedCase2(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -261,9 +264,10 @@ func (ec *executionContext) _EmbeddedCase3(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/fields_order.generated.go b/codegen/testserver/followschema/fields_order.generated.go index 902597fe84b..ccaad875d89 100644 --- a/codegen/testserver/followschema/fields_order.generated.go +++ b/codegen/testserver/followschema/fields_order.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -141,9 +142,10 @@ func (ec *executionContext) _FieldsOrderPayload(ctx context.Context, sel ast.Sel return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/interfaces.generated.go b/codegen/testserver/followschema/interfaces.generated.go index 0e94a552641..1ac286bd2b9 100644 --- a/codegen/testserver/followschema/interfaces.generated.go +++ b/codegen/testserver/followschema/interfaces.generated.go @@ -1218,9 +1218,10 @@ func (ec *executionContext) _BackedByInterface(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1266,9 +1267,10 @@ func (ec *executionContext) _Cat(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1305,9 +1307,10 @@ func (ec *executionContext) _Circle(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1353,9 +1356,10 @@ func (ec *executionContext) _ConcreteNodeA(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1396,9 +1400,10 @@ func (ec *executionContext) _ConcreteNodeInterface(ctx context.Context, sel ast. return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1439,9 +1444,10 @@ func (ec *executionContext) _Coordinates(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1487,9 +1493,10 @@ func (ec *executionContext) _Dog(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1528,9 +1535,10 @@ func (ec *executionContext) _Rectangle(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1571,9 +1579,10 @@ func (ec *executionContext) _Size(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/issue896.generated.go b/codegen/testserver/followschema/issue896.generated.go index 017c62ca445..9af05bd13f1 100644 --- a/codegen/testserver/followschema/issue896.generated.go +++ b/codegen/testserver/followschema/issue896.generated.go @@ -7,6 +7,7 @@ import ( "errors" "strconv" "sync" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -98,9 +99,10 @@ func (ec *executionContext) _CheckIssue896(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/loops.generated.go b/codegen/testserver/followschema/loops.generated.go index 7429c218b36..5df3bea4d7e 100644 --- a/codegen/testserver/followschema/loops.generated.go +++ b/codegen/testserver/followschema/loops.generated.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -152,9 +153,10 @@ func (ec *executionContext) _LoopA(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -190,9 +192,10 @@ func (ec *executionContext) _LoopB(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/maps.generated.go b/codegen/testserver/followschema/maps.generated.go index 293a5219adf..7fcd91fc1f2 100644 --- a/codegen/testserver/followschema/maps.generated.go +++ b/codegen/testserver/followschema/maps.generated.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -185,9 +186,10 @@ func (ec *executionContext) _MapStringInterfaceType(ctx context.Context, sel ast return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/nulls.generated.go b/codegen/testserver/followschema/nulls.generated.go index ee8311b3f90..477f70560ea 100644 --- a/codegen/testserver/followschema/nulls.generated.go +++ b/codegen/testserver/followschema/nulls.generated.go @@ -501,9 +501,10 @@ func (ec *executionContext) _Error(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -714,9 +715,10 @@ func (ec *executionContext) _Errors(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/panics.generated.go b/codegen/testserver/followschema/panics.generated.go index 91bd82dd8fe..a4e2d2d0a5f 100644 --- a/codegen/testserver/followschema/panics.generated.go +++ b/codegen/testserver/followschema/panics.generated.go @@ -347,9 +347,10 @@ func (ec *executionContext) _Panics(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/prelude.generated.go b/codegen/testserver/followschema/prelude.generated.go index 3ba98c02269..b4941eabc8e 100644 --- a/codegen/testserver/followschema/prelude.generated.go +++ b/codegen/testserver/followschema/prelude.generated.go @@ -8,6 +8,7 @@ import ( "fmt" "strconv" "sync" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -1776,9 +1777,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1823,9 +1825,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1880,9 +1883,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1927,9 +1931,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -1981,9 +1986,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -2037,9 +2043,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/primitive_objects.generated.go b/codegen/testserver/followschema/primitive_objects.generated.go index 1c6fcc513c1..f0c518b93e8 100644 --- a/codegen/testserver/followschema/primitive_objects.generated.go +++ b/codegen/testserver/followschema/primitive_objects.generated.go @@ -314,9 +314,10 @@ func (ec *executionContext) _Primitive(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -424,9 +425,10 @@ func (ec *executionContext) _PrimitiveString(ctx context.Context, sel ast.Select return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/ptr_to_any.generated.go b/codegen/testserver/followschema/ptr_to_any.generated.go index 10674b344f4..9066693936a 100644 --- a/codegen/testserver/followschema/ptr_to_any.generated.go +++ b/codegen/testserver/followschema/ptr_to_any.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -137,9 +138,10 @@ func (ec *executionContext) _PtrToAnyContainer(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/ptr_to_ptr_input.generated.go b/codegen/testserver/followschema/ptr_to_ptr_input.generated.go index 0336295a023..ef0e33a8965 100644 --- a/codegen/testserver/followschema/ptr_to_ptr_input.generated.go +++ b/codegen/testserver/followschema/ptr_to_ptr_input.generated.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -364,9 +365,10 @@ func (ec *executionContext) _PtrToPtrInner(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -406,9 +408,10 @@ func (ec *executionContext) _PtrToPtrOuter(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/ptr_to_slice.generated.go b/codegen/testserver/followschema/ptr_to_slice.generated.go index 40b1cdfe075..63db6f615c8 100644 --- a/codegen/testserver/followschema/ptr_to_slice.generated.go +++ b/codegen/testserver/followschema/ptr_to_slice.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -97,9 +98,10 @@ func (ec *executionContext) _PtrToSliceContainer(ctx context.Context, sel ast.Se return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/root_.generated.go b/codegen/testserver/followschema/root_.generated.go index 8ed39c2e3c5..01def44a193 100644 --- a/codegen/testserver/followschema/root_.generated.go +++ b/codegen/testserver/followschema/root_.generated.go @@ -8,6 +8,7 @@ import ( "embed" "errors" "fmt" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -466,7 +467,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -2037,7 +2038,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputDefaultInput, ec.unmarshalInputFieldsOrderInput, @@ -2067,9 +2068,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -2081,29 +2082,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + return &response } case ast.Mutation: @@ -2147,11 +2130,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") diff --git a/codegen/testserver/followschema/scalar_default.generated.go b/codegen/testserver/followschema/scalar_default.generated.go index b749f6cbaeb..3c472a59bf7 100644 --- a/codegen/testserver/followschema/scalar_default.generated.go +++ b/codegen/testserver/followschema/scalar_default.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -97,9 +98,10 @@ func (ec *executionContext) _EmbeddedDefaultScalar(ctx context.Context, sel ast. return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/schema.generated.go b/codegen/testserver/followschema/schema.generated.go index d98ed39a919..1e8e110f6d2 100644 --- a/codegen/testserver/followschema/schema.generated.go +++ b/codegen/testserver/followschema/schema.generated.go @@ -5899,9 +5899,10 @@ func (ec *executionContext) _Autobind(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -5936,9 +5937,10 @@ func (ec *executionContext) _EmbeddedPointer(ctx context.Context, sel ast.Select return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6002,9 +6004,10 @@ func (ec *executionContext) _ForcedResolver(ctx context.Context, sel ast.Selecti return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6040,9 +6043,10 @@ func (ec *executionContext) _InnerObject(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6078,9 +6082,10 @@ func (ec *executionContext) _InvalidIdentifier(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6116,9 +6121,10 @@ func (ec *executionContext) _It(ctx context.Context, sel ast.SelectionSet, obj * return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6226,9 +6232,10 @@ func (ec *executionContext) _ModelMethods(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6264,9 +6271,10 @@ func (ec *executionContext) _OuterObject(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6335,9 +6343,10 @@ func (ec *executionContext) _Pet(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7773,9 +7782,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7921,9 +7931,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/slices.generated.go b/codegen/testserver/followschema/slices.generated.go index cabb35693de..a4448437ee6 100644 --- a/codegen/testserver/followschema/slices.generated.go +++ b/codegen/testserver/followschema/slices.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -229,9 +230,10 @@ func (ec *executionContext) _Slices(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/useptr.generated.go b/codegen/testserver/followschema/useptr.generated.go index d271457dead..36443a39f68 100644 --- a/codegen/testserver/followschema/useptr.generated.go +++ b/codegen/testserver/followschema/useptr.generated.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -168,9 +169,10 @@ func (ec *executionContext) _A(ctx context.Context, sel ast.SelectionSet, obj *A return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -206,9 +208,10 @@ func (ec *executionContext) _B(ctx context.Context, sel ast.SelectionSet, obj *B return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/v-ok.generated.go b/codegen/testserver/followschema/v-ok.generated.go index 5184dc5b724..d5d2630aab1 100644 --- a/codegen/testserver/followschema/v-ok.generated.go +++ b/codegen/testserver/followschema/v-ok.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -143,9 +144,10 @@ func (ec *executionContext) _VOkCaseNil(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -178,9 +180,10 @@ func (ec *executionContext) _VOkCaseValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/validtypes.generated.go b/codegen/testserver/followschema/validtypes.generated.go index 941b252c521..a8d7fc07fec 100644 --- a/codegen/testserver/followschema/validtypes.generated.go +++ b/codegen/testserver/followschema/validtypes.generated.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -854,9 +855,10 @@ func (ec *executionContext) _Content_Post(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -889,9 +891,10 @@ func (ec *executionContext) _Content_User(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -942,9 +945,10 @@ func (ec *executionContext) _ValidType(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/variadic.generated.go b/codegen/testserver/followschema/variadic.generated.go index 365691e1186..7b476d000c0 100644 --- a/codegen/testserver/followschema/variadic.generated.go +++ b/codegen/testserver/followschema/variadic.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -154,9 +155,10 @@ func (ec *executionContext) _VariadicModel(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/weird_type_cases.generated.go b/codegen/testserver/followschema/weird_type_cases.generated.go index d4df640b7ed..b02e8772aab 100644 --- a/codegen/testserver/followschema/weird_type_cases.generated.go +++ b/codegen/testserver/followschema/weird_type_cases.generated.go @@ -6,6 +6,7 @@ import ( "context" "errors" "strconv" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" @@ -308,9 +309,10 @@ func (ec *executionContext) _AIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -346,9 +348,10 @@ func (ec *executionContext) _AbIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -384,9 +387,10 @@ func (ec *executionContext) _XXIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -422,9 +426,10 @@ func (ec *executionContext) _XxIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -460,9 +465,10 @@ func (ec *executionContext) _asdfIt(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -498,9 +504,10 @@ func (ec *executionContext) _iIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/followschema/wrapped_type.generated.go b/codegen/testserver/followschema/wrapped_type.generated.go index f3923461ce5..f7885ebc3c2 100644 --- a/codegen/testserver/followschema/wrapped_type.generated.go +++ b/codegen/testserver/followschema/wrapped_type.generated.go @@ -315,9 +315,10 @@ func (ec *executionContext) _WrappedMap(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -384,9 +385,10 @@ func (ec *executionContext) _WrappedSlice(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -424,9 +426,10 @@ func (ec *executionContext) _WrappedStruct(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/codegen/testserver/singlefile/generated.go b/codegen/testserver/singlefile/generated.go index 0b093d2367b..f8f82075717 100644 --- a/codegen/testserver/singlefile/generated.go +++ b/codegen/testserver/singlefile/generated.go @@ -614,7 +614,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -2185,7 +2185,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputDefaultInput, ec.unmarshalInputFieldsOrderInput, @@ -2215,9 +2215,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -2229,28 +2229,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -2296,11 +2277,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -15614,9 +15614,10 @@ func (ec *executionContext) _A(ctx context.Context, sel ast.SelectionSet, obj *A return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15652,9 +15653,10 @@ func (ec *executionContext) _AIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15690,9 +15692,10 @@ func (ec *executionContext) _AbIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15748,9 +15751,10 @@ func (ec *executionContext) _Autobind(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15786,9 +15790,10 @@ func (ec *executionContext) _B(ctx context.Context, sel ast.SelectionSet, obj *B return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15865,9 +15870,10 @@ func (ec *executionContext) _BackedByInterface(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15913,9 +15919,10 @@ func (ec *executionContext) _Cat(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15948,9 +15955,10 @@ func (ec *executionContext) _CheckIssue896(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -15987,9 +15995,10 @@ func (ec *executionContext) _Circle(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16035,9 +16044,10 @@ func (ec *executionContext) _ConcreteNodeA(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16078,9 +16088,10 @@ func (ec *executionContext) _ConcreteNodeInterface(ctx context.Context, sel ast. return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16113,9 +16124,10 @@ func (ec *executionContext) _Content_Post(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16148,9 +16160,10 @@ func (ec *executionContext) _Content_User(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16191,9 +16204,10 @@ func (ec *executionContext) _Coordinates(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16228,9 +16242,10 @@ func (ec *executionContext) _DefaultParametersMirror(ctx context.Context, sel as return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16276,9 +16291,10 @@ func (ec *executionContext) _Dog(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16314,9 +16330,10 @@ func (ec *executionContext) _EmbeddedCase1(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16352,9 +16369,10 @@ func (ec *executionContext) _EmbeddedCase2(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16390,9 +16408,10 @@ func (ec *executionContext) _EmbeddedCase3(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16425,9 +16444,10 @@ func (ec *executionContext) _EmbeddedDefaultScalar(ctx context.Context, sel ast. return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16462,9 +16482,10 @@ func (ec *executionContext) _EmbeddedPointer(ctx context.Context, sel ast.Select return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16512,9 +16533,10 @@ func (ec *executionContext) _Error(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16725,9 +16747,10 @@ func (ec *executionContext) _Errors(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16760,9 +16783,10 @@ func (ec *executionContext) _FieldsOrderPayload(ctx context.Context, sel ast.Sel return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16826,9 +16850,10 @@ func (ec *executionContext) _ForcedResolver(ctx context.Context, sel ast.Selecti return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16864,9 +16889,10 @@ func (ec *executionContext) _InnerObject(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16902,9 +16928,10 @@ func (ec *executionContext) _InvalidIdentifier(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16940,9 +16967,10 @@ func (ec *executionContext) _It(ctx context.Context, sel ast.SelectionSet, obj * return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -16978,9 +17006,10 @@ func (ec *executionContext) _LoopA(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17016,9 +17045,10 @@ func (ec *executionContext) _LoopB(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17054,9 +17084,10 @@ func (ec *executionContext) _Map(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17091,9 +17122,10 @@ func (ec *executionContext) _MapStringInterfaceType(ctx context.Context, sel ast return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17201,9 +17233,10 @@ func (ec *executionContext) _ModelMethods(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17270,9 +17303,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17315,9 +17349,10 @@ func (ec *executionContext) _ObjectDirectives(ctx context.Context, sel ast.Selec return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17350,9 +17385,10 @@ func (ec *executionContext) _ObjectDirectivesWithCustomGoModel(ctx context.Conte return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17388,9 +17424,10 @@ func (ec *executionContext) _OuterObject(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17477,9 +17514,10 @@ func (ec *executionContext) _OverlappingFields(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17618,9 +17656,10 @@ func (ec *executionContext) _Panics(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17689,9 +17728,10 @@ func (ec *executionContext) _Pet(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17763,9 +17803,10 @@ func (ec *executionContext) _Primitive(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17873,9 +17914,10 @@ func (ec *executionContext) _PrimitiveString(ctx context.Context, sel ast.Select return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17910,9 +17952,10 @@ func (ec *executionContext) _PtrToAnyContainer(ctx context.Context, sel ast.Sele return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17953,9 +17996,10 @@ func (ec *executionContext) _PtrToPtrInner(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -17995,9 +18039,10 @@ func (ec *executionContext) _PtrToPtrOuter(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -18030,9 +18075,10 @@ func (ec *executionContext) _PtrToSliceContainer(ctx context.Context, sel ast.Se return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19468,9 +19514,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19509,9 +19556,10 @@ func (ec *executionContext) _Rectangle(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19552,9 +19600,10 @@ func (ec *executionContext) _Size(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19599,9 +19648,10 @@ func (ec *executionContext) _Slices(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19747,9 +19797,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19782,9 +19833,10 @@ func (ec *executionContext) _VOkCaseNil(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19817,9 +19869,10 @@ func (ec *executionContext) _VOkCaseValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19870,9 +19923,10 @@ func (ec *executionContext) _ValidType(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -19936,9 +19990,10 @@ func (ec *executionContext) _VariadicModel(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20005,9 +20060,10 @@ func (ec *executionContext) _WrappedMap(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20074,9 +20130,10 @@ func (ec *executionContext) _WrappedSlice(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20114,9 +20171,10 @@ func (ec *executionContext) _WrappedStruct(ctx context.Context, sel ast.Selectio return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20152,9 +20210,10 @@ func (ec *executionContext) _XXIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20190,9 +20249,10 @@ func (ec *executionContext) _XxIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20245,9 +20305,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20292,9 +20353,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20349,9 +20411,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20396,9 +20459,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20450,9 +20514,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20506,9 +20571,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20544,9 +20610,10 @@ func (ec *executionContext) _asdfIt(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -20582,9 +20649,10 @@ func (ec *executionContext) _iIt(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/graphql/response.go b/graphql/response.go index 8bce8e1bb8c..a82f27e27c8 100644 --- a/graphql/response.go +++ b/graphql/response.go @@ -17,7 +17,7 @@ type Response struct { Data json.RawMessage `json:"data"` Label string `json:"label,omitempty"` Path ast.Path `json:"path,omitempty"` - HasNext bool `json:"hasNext,omitempty"` + HasNext *bool `json:"hasNext,omitempty"` Extensions map[string]interface{} `json:"extensions,omitempty"` } diff --git a/integration/generated.go b/integration/generated.go index 6bfabd3b3b6..1889e323fd1 100644 --- a/integration/generated.go +++ b/integration/generated.go @@ -108,7 +108,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -236,7 +236,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputDateFilter, ec.unmarshalInputListCoercion, @@ -253,9 +253,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -267,28 +267,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -302,11 +283,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -3287,9 +3287,10 @@ func (ec *executionContext) _Element(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3484,9 +3485,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3519,9 +3521,10 @@ func (ec *executionContext) _RemoteModelWithOmitempty(ctx context.Context, sel a return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3593,9 +3596,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3628,9 +3632,10 @@ func (ec *executionContext) _Viewer(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3683,9 +3688,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3730,9 +3736,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3787,9 +3794,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3834,9 +3842,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3888,9 +3897,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -3944,9 +3954,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, diff --git a/plugin/federation/testdata/entityresolver/generated/exec.go b/plugin/federation/testdata/entityresolver/generated/exec.go index 4309a6dcd63..7cb4bde96d9 100644 --- a/plugin/federation/testdata/entityresolver/generated/exec.go +++ b/plugin/federation/testdata/entityresolver/generated/exec.go @@ -178,7 +178,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e, nil, 0, nil} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -632,7 +632,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, nil, 0, make(chan graphql.DeferredResult)} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputMultiHelloByNamesInput, ec.unmarshalInputMultiHelloMultipleRequiresByNamesInput, @@ -652,9 +652,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) data = ec._Query(ctx, rc.Operation.SelectionSet) } else { - if ec.pendingDeferred > 0 { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults - ec.pendingDeferred-- + atomic.AddInt32(&ec.pendingDeferred, -1) data = result.Result response.Path = result.Path response.Label = result.Label @@ -666,28 +666,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { var buf bytes.Buffer data.MarshalGQL(&buf) response.Data = buf.Bytes() - response.HasNext = ec.pendingDeferred+len(ec.deferredGroups) > 0 - - // dispatch deferred calls - dg := ec.deferredGroups - ec.deferredGroups = nil - for _, deferred := range dg { - ec.pendingDeferred++ - go func(deferred graphql.DeferredGroup) { - ctx = graphql.WithFreshResponseContext(deferred.Context) - deferred.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: deferred.Path, - Label: deferred.Label, - Result: deferred.FieldSet, - Errors: graphql.GetErrors(ctx), - } - // null fields should bubble up - if deferred.FieldSet.Invalids > 0 { - ds.Result = graphql.Null - } - ec.deferredResults <- ds - }(deferred) + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } return &response @@ -701,11 +682,30 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema - deferredGroups []graphql.DeferredGroup - pendingDeferred int + deferred int32 + pendingDeferred int32 deferredResults chan graphql.DeferredResult } +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") @@ -6404,9 +6404,10 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6447,9 +6448,10 @@ func (ec *executionContext) _Hello(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6490,9 +6492,10 @@ func (ec *executionContext) _HelloMultiSingleKeys(ctx context.Context, sel ast.S return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6528,9 +6531,10 @@ func (ec *executionContext) _HelloWithErrors(ctx context.Context, sel ast.Select return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6566,9 +6570,10 @@ func (ec *executionContext) _MultiHello(ctx context.Context, sel ast.SelectionSe return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6619,9 +6624,10 @@ func (ec *executionContext) _MultiHelloMultipleRequires(ctx context.Context, sel return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6667,9 +6673,10 @@ func (ec *executionContext) _MultiHelloRequires(ctx context.Context, sel ast.Sel return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6705,9 +6712,10 @@ func (ec *executionContext) _MultiHelloWithError(ctx context.Context, sel ast.Se return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6753,9 +6761,10 @@ func (ec *executionContext) _MultiPlanetRequiresNested(ctx context.Context, sel return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6806,9 +6815,10 @@ func (ec *executionContext) _PlanetMultipleRequires(ctx context.Context, sel ast return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6854,9 +6864,10 @@ func (ec *executionContext) _PlanetRequires(ctx context.Context, sel ast.Selecti return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6902,9 +6913,10 @@ func (ec *executionContext) _PlanetRequiresNested(ctx context.Context, sel ast.S return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -6995,9 +7007,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7040,9 +7053,10 @@ func (ec *executionContext) _World(ctx context.Context, sel ast.SelectionSet, ob return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7078,9 +7092,10 @@ func (ec *executionContext) _WorldName(ctx context.Context, sel ast.SelectionSet return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7123,9 +7138,10 @@ func (ec *executionContext) _WorldWithMultipleKeys(ctx context.Context, sel ast. return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7158,9 +7174,10 @@ func (ec *executionContext) __Service(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7213,9 +7230,10 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7260,9 +7278,10 @@ func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionS return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7317,9 +7336,10 @@ func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7364,9 +7384,10 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7418,9 +7439,10 @@ func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs, @@ -7474,9 +7496,10 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o return graphql.Null } - // assign deferred groups to main executionContext + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + for label, dfs := range deferred { - ec.deferredGroups = append(ec.deferredGroups, graphql.DeferredGroup{ + ec.processDeferredGroup(graphql.DeferredGroup{ Label: label, Path: graphql.GetPath(ctx), FieldSet: dfs,