diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/graphql/authorize.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/graphql/calls_gitaly.rb | 15 | ||||
-rw-r--r-- | lib/gitlab/graphql/calls_gitaly/instrumentation.rb | 40 | ||||
-rw-r--r-- | lib/gitlab/graphql/mount_mutation.rb | 5 |
4 files changed, 59 insertions, 3 deletions
diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb index f8d0208e275..e83b567308b 100644 --- a/lib/gitlab/graphql/authorize.rb +++ b/lib/gitlab/graphql/authorize.rb @@ -8,7 +8,7 @@ module Gitlab extend ActiveSupport::Concern def self.use(schema_definition) - schema_definition.instrument(:field, Instrumentation.new, after_built_ins: true) + schema_definition.instrument(:field, Gitlab::Graphql::Authorize::Instrumentation.new, after_built_ins: true) end end end diff --git a/lib/gitlab/graphql/calls_gitaly.rb b/lib/gitlab/graphql/calls_gitaly.rb new file mode 100644 index 00000000000..40cd74a34f2 --- /dev/null +++ b/lib/gitlab/graphql/calls_gitaly.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + # Wraps the field resolution to count Gitaly calls before and after. + # Raises an error if the field calls Gitaly but hadn't declared such. + module CallsGitaly + extend ActiveSupport::Concern + + def self.use(schema_definition) + schema_definition.instrument(:field, Gitlab::Graphql::CallsGitaly::Instrumentation.new, after_built_ins: true) + end + end + end +end diff --git a/lib/gitlab/graphql/calls_gitaly/instrumentation.rb b/lib/gitlab/graphql/calls_gitaly/instrumentation.rb new file mode 100644 index 00000000000..fbd5e348c7d --- /dev/null +++ b/lib/gitlab/graphql/calls_gitaly/instrumentation.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + module CallsGitaly + class Instrumentation + # Check if any `calls_gitaly: true` declarations need to be added + # Do nothing if a constant complexity was provided + def instrument(_type, field) + type_object = field.metadata[:type_class] + return field unless type_object.respond_to?(:calls_gitaly?) + return field if type_object.constant_complexity? || type_object.calls_gitaly? + + old_resolver_proc = field.resolve_proc + + gitaly_wrapped_resolve = -> (typed_object, args, ctx) do + previous_gitaly_call_count = Gitlab::GitalyClient.get_request_count + result = old_resolver_proc.call(typed_object, args, ctx) + current_gitaly_call_count = Gitlab::GitalyClient.get_request_count + calls_gitaly_check(type_object, current_gitaly_call_count - previous_gitaly_call_count) + result + end + + field.redefine do + resolve(gitaly_wrapped_resolve) + end + end + + def calls_gitaly_check(type_object, calls) + return if calls < 1 + + # Will inform you if there needs to be `calls_gitaly: true` as a kwarg in the field declaration + # if there is at least 1 Gitaly call involved with the field resolution. + error = RuntimeError.new("Gitaly is called for field '#{type_object.name}' on #{type_object.owner.try(:name)} - please either specify a constant complexity or add `calls_gitaly: true` to the field declaration") + Gitlab::Sentry.track_exception(error) + end + end + end + end +end diff --git a/lib/gitlab/graphql/mount_mutation.rb b/lib/gitlab/graphql/mount_mutation.rb index 9048967d4e1..b10e963170a 100644 --- a/lib/gitlab/graphql/mount_mutation.rb +++ b/lib/gitlab/graphql/mount_mutation.rb @@ -6,11 +6,12 @@ module Gitlab extend ActiveSupport::Concern class_methods do - def mount_mutation(mutation_class) + def mount_mutation(mutation_class, **custom_kwargs) # Using an underscored field name symbol will make `graphql-ruby` # standardize the field name field mutation_class.graphql_name.underscore.to_sym, - mutation: mutation_class + mutation: mutation_class, + **custom_kwargs end end end |