summaryrefslogtreecommitdiff
path: root/lib/gitlab/graphql/calls_gitaly/instrumentation.rb
blob: fbd5e348c7d67616d15652bfe05ffc780232e0a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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