graphql-metrics 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|