From 15aa9c0a8f04cb170c7497f1b9ecb823d61ec871 Mon Sep 17 00:00:00 2001 From: OldBigBuddha <13755428+OldBigBuddha@users.noreply.github.com> Date: Sun, 17 Nov 2024 19:38:37 +0900 Subject: [PATCH 1/6] add property in exec --- codegen/config/exec.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codegen/config/exec.go b/codegen/config/exec.go index 838e17b2c22..cc19bcc6718 100644 --- a/codegen/config/exec.go +++ b/codegen/config/exec.go @@ -20,6 +20,8 @@ type ExecConfig struct { // Only for follow-schema layout: FilenameTemplate string `yaml:"filename_template,omitempty"` // String template with {name} as placeholder for base name. DirName string `yaml:"dir"` + + WorkerLimit uint `yaml: "worker_limit"` } type ExecLayout string From 1c3cb2b9a797ca3ad5107b230581c7ebc7be7b26 Mon Sep 17 00:00:00 2001 From: OldBigBuddha Date: Mon, 18 Nov 2024 11:42:11 +0900 Subject: [PATCH 2/6] add semaphore for goroutine limitation in template --- codegen/config/exec.go | 2 +- codegen/generated!.gotpl | 1 + codegen/type.gotpl | 22 ++++++++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/codegen/config/exec.go b/codegen/config/exec.go index cc19bcc6718..c9a5b6bc291 100644 --- a/codegen/config/exec.go +++ b/codegen/config/exec.go @@ -21,7 +21,7 @@ type ExecConfig struct { FilenameTemplate string `yaml:"filename_template,omitempty"` // String template with {name} as placeholder for base name. DirName string `yaml:"dir"` - WorkerLimit uint `yaml: "worker_limit"` + WorkerLimit uint `yaml:"worker_limit"` } type ExecLayout string diff --git a/codegen/generated!.gotpl b/codegen/generated!.gotpl index 7f526a2174b..8638b1cf9a9 100644 --- a/codegen/generated!.gotpl +++ b/codegen/generated!.gotpl @@ -10,6 +10,7 @@ {{ reserveImport "bytes" }} {{ reserveImport "embed" }} +{{ reserveImport "golang.org/x/sync/semaphore"}} {{ reserveImport "github.com/vektah/gqlparser/v2" "gqlparser" }} {{ reserveImport "github.com/vektah/gqlparser/v2/ast" }} {{ reserveImport "github.com/99designs/gqlgen/graphql" }} diff --git a/codegen/type.gotpl b/codegen/type.gotpl index 1898d44460f..aa143b1f22e 100644 --- a/codegen/type.gotpl +++ b/codegen/type.gotpl @@ -101,6 +101,9 @@ ret := make(graphql.Array, len(v)) {{- if not $type.IsScalar }} var wg sync.WaitGroup + {{- if gt $.Config.Exec.WorkerLimit 0 }} + sm := semaphore.NewWeighted({{ $.Config.Exec.WorkerLimit }}) + {{- end }} isLen1 := len(v) == 1 if !isLen1 { wg.Add(len(v)) @@ -124,14 +127,29 @@ }() {{- end }} if !isLen1 { - defer wg.Done() + {{- if gt $.Config.Exec.WorkerLimit 0 }} + defer func(){ + sm.Release(1) + wg.Done() + }() + {{- else }} + defer wg.Done() + {{- end }} } ret[i] = ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, v[i]) } if isLen1 { f(i) } else { - go f(i) + {{- if gt $.Config.Exec.WorkerLimit 0 }} + if err := sm.Acquire(ctx, 1); err != nil { + ec.Error(ctx, ctx.Err()) + } else { + go f(i) + } + {{- else }} + go f(i) + {{- end }} } {{ else }} ret[i] = ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, v[i]) From 90378e61a7ab9b191992d63ac550bd09434cada4 Mon Sep 17 00:00:00 2001 From: OldBigBuddha Date: Mon, 18 Nov 2024 11:46:07 +0900 Subject: [PATCH 3/6] generation test with worker_limit option --- api/generate_test.go | 4 ++ api/testdata/workerlimit/gqlgen.yml | 70 +++++++++++++++++++ api/testdata/workerlimit/graph/model/doc.go | 1 + .../workerlimit/graph/schema.graphqls | 28 ++++++++ 4 files changed, 103 insertions(+) create mode 100644 api/testdata/workerlimit/gqlgen.yml create mode 100644 api/testdata/workerlimit/graph/model/doc.go create mode 100644 api/testdata/workerlimit/graph/schema.graphqls diff --git a/api/generate_test.go b/api/generate_test.go index 0572042ef2d..c98f91e27b4 100644 --- a/api/generate_test.go +++ b/api/generate_test.go @@ -34,6 +34,10 @@ func TestGenerate(t *testing.T) { name: "federation2", workDir: filepath.Join(wd, "testdata", "federation2"), }, + { + name: "worker_limit", + workDir: filepath.Join(wd, "testdata", "workerlimit"), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/testdata/workerlimit/gqlgen.yml b/api/testdata/workerlimit/gqlgen.yml new file mode 100644 index 00000000000..0f0ff69c536 --- /dev/null +++ b/api/testdata/workerlimit/gqlgen.yml @@ -0,0 +1,70 @@ +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - graph/*.graphqls + +# Where should the generated server code go? +exec: + filename: graph/generated.go + package: graph + worker_limit: 1 + +# Uncomment to enable federation +# federation: +# filename: graph/federation.go +# package: graph + +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: graph + package: graph + +# Optional: turn on use `gqlgen:"fieldName"` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +# omit_slice_element_pointers: false + +# Optional: turn off to make struct-type struct fields not use pointers +# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing } +# struct_fields_always_pointers: true + +# Optional: turn off to make resolvers return values instead of pointers for structs +# resolvers_always_return_pointers: true + +# Optional: turn on to return pointers instead of values in unmarshalInput +# return_pointers_in_unmarshalinput: false + +# Optional: wrap nullable input fields with Omittable +# nullable_input_omittable: true + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +autobind: + - "github.com/99designs/gqlgen/api/testdata/default/graph/model" + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 diff --git a/api/testdata/workerlimit/graph/model/doc.go b/api/testdata/workerlimit/graph/model/doc.go new file mode 100644 index 00000000000..8b537907051 --- /dev/null +++ b/api/testdata/workerlimit/graph/model/doc.go @@ -0,0 +1 @@ +package model diff --git a/api/testdata/workerlimit/graph/schema.graphqls b/api/testdata/workerlimit/graph/schema.graphqls new file mode 100644 index 00000000000..c6a91bb4808 --- /dev/null +++ b/api/testdata/workerlimit/graph/schema.graphqls @@ -0,0 +1,28 @@ +# GraphQL schema example +# +# https://gqlgen.com/getting-started/ + +type Todo { + id: ID! + text: String! + done: Boolean! + user: User! +} + +type User { + id: ID! + name: String! +} + +type Query { + todos: [Todo!]! +} + +input NewTodo { + text: String! + userId: String! +} + +type Mutation { + createTodo(input: NewTodo!): Todo! +} From 076ef6a8b365d2642162fb55cec9727007fe7097 Mon Sep 17 00:00:00 2001 From: OldBigBuddha Date: Mon, 18 Nov 2024 15:23:56 +0900 Subject: [PATCH 4/6] add comment --- codegen/config/exec.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/codegen/config/exec.go b/codegen/config/exec.go index c9a5b6bc291..fe3cb3e7113 100644 --- a/codegen/config/exec.go +++ b/codegen/config/exec.go @@ -21,6 +21,10 @@ type ExecConfig struct { FilenameTemplate string `yaml:"filename_template,omitempty"` // String template with {name} as placeholder for base name. DirName string `yaml:"dir"` + // Maximum number of goroutines in parallel to use when running multiple child resolvers + // Suppressing the number of goroutines generated can reduce memory consumption per request, + // but processing time may increase due to the reduced number of concurrences + // Default: 0 (unlimited) WorkerLimit uint `yaml:"worker_limit"` } From 6bf4b836da5f47bb0f6d87d424350081f659b79d Mon Sep 17 00:00:00 2001 From: OldBigBuddha Date: Mon, 18 Nov 2024 15:33:35 +0900 Subject: [PATCH 5/6] update init-template --- init-templates/gqlgen.yml.gotmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/init-templates/gqlgen.yml.gotmpl b/init-templates/gqlgen.yml.gotmpl index 648ec2b4430..6429c93a942 100644 --- a/init-templates/gqlgen.yml.gotmpl +++ b/init-templates/gqlgen.yml.gotmpl @@ -6,6 +6,8 @@ schema: exec: filename: graph/generated.go package: graph + # Optional: Maximum number of goroutines in concurrency to use per child resolvers(default: unlimited) + # worker_limit: 1000 # Uncomment to enable federation # federation: From 35e6a648cffbddcc65631f1607013445c0840ec6 Mon Sep 17 00:00:00 2001 From: OldBigBuddha Date: Mon, 18 Nov 2024 15:36:33 +0900 Subject: [PATCH 6/6] misc --- codegen/config/exec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/config/exec.go b/codegen/config/exec.go index fe3cb3e7113..37ec2c30b56 100644 --- a/codegen/config/exec.go +++ b/codegen/config/exec.go @@ -21,7 +21,7 @@ type ExecConfig struct { FilenameTemplate string `yaml:"filename_template,omitempty"` // String template with {name} as placeholder for base name. DirName string `yaml:"dir"` - // Maximum number of goroutines in parallel to use when running multiple child resolvers + // Maximum number of goroutines in concurrency to use when running multiple child resolvers // Suppressing the number of goroutines generated can reduce memory consumption per request, // but processing time may increase due to the reduced number of concurrences // Default: 0 (unlimited)