graphql-metrics 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +20 -0
- data/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile.lock +23 -20
- data/README.md +179 -107
- data/Rakefile +6 -0
- data/bin/console +4 -6
- data/graphql_metrics.gemspec +7 -7
- data/lib/graphql/metrics.rb +80 -0
- data/lib/graphql/metrics/analyzer.rb +129 -0
- data/lib/graphql/metrics/instrumentation.rb +54 -0
- data/lib/graphql/metrics/tracer.rb +109 -0
- data/lib/graphql/metrics/version.rb +7 -0
- metadata +55 -24
- data/.travis.yml +0 -5
- data/lib/graphql_metrics.rb +0 -6
- data/lib/graphql_metrics/extractor.rb +0 -277
- data/lib/graphql_metrics/instrumentation.rb +0 -107
- data/lib/graphql_metrics/timed_batch_executor.rb +0 -80
- data/lib/graphql_metrics/version.rb +0 -5
@@ -1,107 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQLMetrics
|
4
|
-
class Instrumentation
|
5
|
-
extend Forwardable
|
6
|
-
|
7
|
-
CONTEXT_NAMESPACE = :extracted_metrics
|
8
|
-
TIMING_CACHE_KEY = :timing_cache
|
9
|
-
START_TIME_KEY = :query_start_time
|
10
|
-
|
11
|
-
attr_reader :ctx_namespace, :query
|
12
|
-
def_delegators :extractor, :extractor_defines_any_visitors?
|
13
|
-
|
14
|
-
def self.use(schema_definition)
|
15
|
-
instrumentation = self.new
|
16
|
-
return unless instrumentation.extractor_defines_any_visitors?
|
17
|
-
|
18
|
-
instrumentation.setup_instrumentation(schema_definition)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.current_time
|
22
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
|
-
end
|
24
|
-
|
25
|
-
def use(schema_definition)
|
26
|
-
return unless extractor_defines_any_visitors?
|
27
|
-
|
28
|
-
setup_instrumentation(schema_definition)
|
29
|
-
end
|
30
|
-
|
31
|
-
def setup_instrumentation(schema_definition)
|
32
|
-
schema_definition.instrument(:query, self)
|
33
|
-
schema_definition.instrument(:field, self)
|
34
|
-
end
|
35
|
-
|
36
|
-
def extractor
|
37
|
-
@extractor ||= Extractor.new(self)
|
38
|
-
end
|
39
|
-
|
40
|
-
def before_query(query)
|
41
|
-
return unless extractor_defines_any_visitors?
|
42
|
-
|
43
|
-
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
44
|
-
ns[TIMING_CACHE_KEY] = {}
|
45
|
-
ns[START_TIME_KEY] = self.class.current_time
|
46
|
-
rescue StandardError => ex
|
47
|
-
extractor.handle_extraction_exception(ex)
|
48
|
-
end
|
49
|
-
|
50
|
-
def after_query(query)
|
51
|
-
@query = query
|
52
|
-
|
53
|
-
return unless extractor_defines_any_visitors?
|
54
|
-
return if respond_to?(:skip_extraction?) && skip_extraction?(query)
|
55
|
-
return unless @ctx_namespace = query.context.namespace(CONTEXT_NAMESPACE)
|
56
|
-
|
57
|
-
before_query_extracted(query, query.context) if respond_to?(:before_query_extracted)
|
58
|
-
|
59
|
-
extractor.extract!(query)
|
60
|
-
|
61
|
-
after_query_teardown(query) if respond_to?(:after_query_teardown)
|
62
|
-
rescue StandardError => ex
|
63
|
-
extractor.handle_extraction_exception(ex)
|
64
|
-
end
|
65
|
-
|
66
|
-
def instrument(type, field)
|
67
|
-
return field unless respond_to?(:field_extracted) || extractor.respond_to?(:field_extracted)
|
68
|
-
return field if type.introspection?
|
69
|
-
|
70
|
-
old_resolve_proc = field.resolve_proc
|
71
|
-
new_resolve_proc = ->(obj, args, ctx) do
|
72
|
-
start_time = self.class.current_time
|
73
|
-
result = old_resolve_proc.call(obj, args, ctx)
|
74
|
-
|
75
|
-
begin
|
76
|
-
next result if respond_to?(:skip_field_resolution_timing?) &&
|
77
|
-
skip_field_resolution_timing?(query, ctx)
|
78
|
-
|
79
|
-
end_time = self.class.current_time
|
80
|
-
|
81
|
-
ns = ctx.namespace(CONTEXT_NAMESPACE)
|
82
|
-
|
83
|
-
ns[TIMING_CACHE_KEY][ctx.ast_node] ||= []
|
84
|
-
ns[TIMING_CACHE_KEY][ctx.ast_node] << end_time - start_time
|
85
|
-
|
86
|
-
result
|
87
|
-
rescue StandardError => ex
|
88
|
-
extractor.handle_extraction_exception(ex)
|
89
|
-
result
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
field.redefine { resolve(new_resolve_proc) }
|
94
|
-
end
|
95
|
-
|
96
|
-
def after_query_resolver_times(ast_node)
|
97
|
-
ctx_namespace.dig(Instrumentation::TIMING_CACHE_KEY).fetch(ast_node, [])
|
98
|
-
end
|
99
|
-
|
100
|
-
def after_query_start_and_end_time
|
101
|
-
start_time = ctx_namespace[Instrumentation::START_TIME_KEY]
|
102
|
-
return unless start_time
|
103
|
-
|
104
|
-
[start_time, self.class.current_time]
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQLMetrics
|
4
|
-
BASE_CLASS = if defined?(GraphQL::Batch::Executor)
|
5
|
-
GraphQL::Batch::Executor
|
6
|
-
else
|
7
|
-
class NoExecutor
|
8
|
-
class << self
|
9
|
-
def resolve(_loader)
|
10
|
-
super
|
11
|
-
end
|
12
|
-
|
13
|
-
def around_promise_callbacks
|
14
|
-
super
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
NoExecutor
|
20
|
-
end
|
21
|
-
|
22
|
-
class TimedBatchExecutor < BASE_CLASS
|
23
|
-
TIMINGS = {}
|
24
|
-
private_constant :TIMINGS
|
25
|
-
|
26
|
-
class << self
|
27
|
-
def timings
|
28
|
-
TIMINGS
|
29
|
-
end
|
30
|
-
|
31
|
-
def clear_timings
|
32
|
-
TIMINGS.clear
|
33
|
-
end
|
34
|
-
|
35
|
-
def serialize_loader_key(loader_key)
|
36
|
-
identifiers = []
|
37
|
-
|
38
|
-
serialized = loader_key.map do |group_arg|
|
39
|
-
if [Class, Symbol, String].include?(group_arg.class)
|
40
|
-
group_arg
|
41
|
-
elsif group_arg.is_a?(Numeric)
|
42
|
-
identifiers << group_arg
|
43
|
-
'_'
|
44
|
-
elsif group_arg.respond_to?(:id)
|
45
|
-
identifiers << group_arg.id
|
46
|
-
"#{group_arg.class}/_"
|
47
|
-
else
|
48
|
-
'?'
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
[serialized.map(&:to_s).join('/'), identifiers.map(&:to_s)]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def resolve(loader)
|
57
|
-
@resolve_meta = {
|
58
|
-
start_time: Instrumentation.current_time,
|
59
|
-
current_loader: loader,
|
60
|
-
perform_queue_sizes: loader.send(:queue).size
|
61
|
-
}
|
62
|
-
|
63
|
-
super
|
64
|
-
end
|
65
|
-
|
66
|
-
def around_promise_callbacks
|
67
|
-
return super unless @resolve_meta
|
68
|
-
|
69
|
-
end_time = Instrumentation.current_time
|
70
|
-
|
71
|
-
TIMINGS[@resolve_meta[:current_loader].loader_key] ||= { times: [], perform_queue_sizes: [] }
|
72
|
-
TIMINGS[@resolve_meta[:current_loader].loader_key][:times] << end_time - @resolve_meta[:start_time]
|
73
|
-
TIMINGS[@resolve_meta[:current_loader].loader_key][:perform_queue_sizes] << @resolve_meta[:perform_queue_sizes]
|
74
|
-
|
75
|
-
@resolve_meta = nil
|
76
|
-
|
77
|
-
super
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|