graphql-metrics 5.0.2 → 5.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 193863b65e11d92f46e0e88e31d3d07bf7bfc3b8f3f78566ab1ea03f0480a027
4
- data.tar.gz: '029dacd1491b30aaaed8f1f29f08de8df61621690e68c437c493acd19be48c58'
3
+ metadata.gz: 7422838e228e2f0dd82382ee2b165b7ab7dfee2b5430044d5b8e2e3f83da173a
4
+ data.tar.gz: 934f63e77e19731fa2f675185d92d81c30b9087a747cfddc7798986327b85a8c
5
5
  SHA512:
6
- metadata.gz: b785685753bf27bd368fcca80a9054dad9ad7d829d1a6931eb3c9ddc9090e33e7fe48a6a4d8d3305ae753078b8b47e9b6df9e65f54b497c05f15f1861ce3029e
7
- data.tar.gz: 4bb9ab1cc43808134b16a071b4d3292c7416614f1d9d59e398c62f5843d9b53175f569385b5f4a74013a5b7817b73487f08a48736aba7228a543596a3f6271d8
6
+ metadata.gz: 16f4f5ed3f00779216232748c5ad45e248b90b8827568d6f323e3a9477e61e273f67e66893bb439494d3e88526b8aec84a7fe1d35691b283235687cecacdbf58
7
+ data.tar.gz: c69155471bc5c372903af17d8311b0a6f95c55df7f8e490436217ffbfb0c4472401567375ed097bc11b93aa4cd1276f3d64a7d09e6d3e41254efc0fbed0a1156
@@ -7,7 +7,7 @@ jobs:
7
7
  matrix:
8
8
  os: [ubuntu-latest]
9
9
  ruby: ['2.7', '3.0', '3.1', '3.2']
10
- gemfile: ['graphql_1.13', 'graphql_2.0']
10
+ gemfile: ['graphql_1.13', 'graphql_2.0', 'graphql_head']
11
11
  runs-on: ${{ matrix.os }}
12
12
  env:
13
13
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ 5.0.4
2
+ -----
3
+ - [71](https://github.com/Shopify/graphql-metrics/pull/71) Fix handling of inline fragment without a parent type.
4
+
5
+ 5.0.3
6
+ -----
7
+ - [69](https://github.com/Shopify/graphql-metrics/pull/69) Loosen concurrent-ruby dependency
8
+
1
9
  5.0.2
2
10
  -----
3
11
  - [63](https://github.com/Shopify/graphql-metrics/pull/67) Reset `lex` pre-context metrics on `analyze_multiplex`.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ eval_gemfile("../Gemfile")
4
+
5
+ gem("graphql", github: "rmosolgo/graphql-ruby", branch: "master")
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.require_paths = ["lib"]
32
32
 
33
33
  spec.add_runtime_dependency "graphql", ">= 1.12.10"
34
- spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.0"
34
+ spec.add_runtime_dependency "concurrent-ruby", ">= 1.1.0"
35
35
  spec.add_development_dependency "rake", "~> 10.0"
36
36
  spec.add_development_dependency "minitest", "~> 5.0"
37
37
  spec.add_development_dependency 'graphql-batch'
@@ -175,7 +175,7 @@ module GraphQL
175
175
  when GraphQL::Language::Nodes::OperationDefinition
176
176
  parent.operation_type
177
177
  when GraphQL::Language::Nodes::InlineFragment
178
- parent.type.name
178
+ parent&.type&.name
179
179
  else
180
180
  parent.name
181
181
  end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Metrics
5
+ module Trace
6
+ def initialize(**_rest)
7
+ super
8
+ context = (@multiplex || @query).context
9
+ @skip_tracing = context&.fetch(GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS, false)
10
+ end
11
+
12
+ # NOTE: These methods come from the graphql ruby gem and are in "chronological" order based on the phases
13
+ # of execution of the graphql-ruby gem, though old versions of the gem aren't always consistent about this (see
14
+ # https://github.com/rmosolgo/graphql-ruby/issues/3393). Most of them can be run multiple times when
15
+ # multiplexing multiple queries.
16
+
17
+ # wraps everything below this line; only run once
18
+ def execute_multiplex(multiplex:, &block)
19
+ return yield if @skip_tracing
20
+ capture_multiplex_start_time(&block)
21
+
22
+ end
23
+
24
+ # may not trigger if the query is passed in pre-parsed
25
+ def lex(query_string:, &block)
26
+ return yield if @skip_tracing
27
+ capture_lexing_time(&block)
28
+ end
29
+
30
+ # may not trigger if the query is passed in pre-parsed
31
+ def parse(query_string:, &block)
32
+ return yield if @skip_tracing
33
+ capture_parsing_time(&block)
34
+ end
35
+
36
+ def validate(query:, validate:, &block)
37
+ return yield if @skip_tracing
38
+ capture_validation_time(query.context, &block)
39
+ end
40
+
41
+ # wraps all `analyze_query`s; only run once
42
+ def analyze_multiplex(multiplex:, &block)
43
+ return yield if @skip_tracing
44
+ # Ensures that we reset potentially long-lived PreContext objects between multiplexs. We reset at this point
45
+ # since all parsing and validation will be done by this point, and a GraphQL::Query::Context will exist.
46
+ pre_context.reset
47
+ yield
48
+ end
49
+
50
+ def analyze_query(query:, &block)
51
+ return yield if @skip_tracing
52
+ capture_analysis_time(query.context, &block)
53
+ end
54
+
55
+ def execute_query(query:, &block)
56
+ return yield if @skip_tracing
57
+ capture_query_start_time(query.context, &block)
58
+ end
59
+
60
+ def execute_field(field:, query:, ast_node:, arguments:, object:, &block)
61
+ return yield if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
62
+ return yield unless GraphQL::Metrics.timings_capture_enabled?(query.context)
63
+ trace_field(GraphQL::Metrics::INLINE_FIELD_TIMINGS, query, &block)
64
+ end
65
+
66
+ def execute_field_lazy(field:, query:, ast_node:, arguments:, object:, &block)
67
+ return yield if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
68
+ return yield unless GraphQL::Metrics.timings_capture_enabled?(query.context)
69
+ trace_field(GraphQL::Metrics::LAZY_FIELD_TIMINGS, query, &block)
70
+ end
71
+
72
+ private
73
+
74
+ PreContext = Struct.new(
75
+ :multiplex_start_time,
76
+ :multiplex_start_time_monotonic,
77
+ :parsing_start_time_offset,
78
+ :parsing_duration,
79
+ :lexing_start_time_offset,
80
+ :lexing_duration
81
+ ) do
82
+ def reset
83
+ self[:multiplex_start_time] = nil
84
+ self[:multiplex_start_time_monotonic] = nil
85
+ self[:parsing_start_time_offset] = nil
86
+ self[:parsing_duration] = nil
87
+ self[:lexing_start_time_offset] = nil
88
+ self[:lexing_duration] = nil
89
+ end
90
+ end
91
+
92
+ def pre_context
93
+ # NOTE: This is used to store timings from lexing, parsing, validation, before we have a context to store
94
+ # values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread.
95
+ @pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new)
96
+ @pre_context.value
97
+ end
98
+
99
+ def capture_multiplex_start_time
100
+ pre_context.multiplex_start_time = GraphQL::Metrics.current_time
101
+ pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic
102
+
103
+ yield
104
+ end
105
+
106
+ def capture_lexing_time
107
+ timed_result = GraphQL::Metrics.time { yield }
108
+
109
+ pre_context.lexing_start_time_offset = timed_result.start_time
110
+ pre_context.lexing_duration = timed_result.duration
111
+
112
+ timed_result.result
113
+ end
114
+
115
+ def capture_parsing_time
116
+ timed_result = GraphQL::Metrics.time { yield }
117
+
118
+ pre_context.parsing_start_time_offset = timed_result.start_time
119
+ pre_context.parsing_duration = timed_result.duration
120
+
121
+ timed_result.result
122
+ end
123
+
124
+ # Also consolidates parsing timings (if any) from the `pre_context`
125
+ def capture_validation_time(context)
126
+ # Queries may already be lexed and parsed before execution (whether a single query or multiplex).
127
+ # If we don't have those values, use some sane defaults.
128
+ if pre_context.lexing_duration.nil?
129
+ pre_context.lexing_start_time_offset = pre_context.multiplex_start_time
130
+ pre_context.lexing_duration = 0.0
131
+ end
132
+ if pre_context.parsing_duration.nil?
133
+ pre_context.parsing_start_time_offset = pre_context.multiplex_start_time
134
+ pre_context.parsing_duration = 0.0
135
+ end
136
+
137
+ timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield }
138
+
139
+ ns = context.namespace(CONTEXT_NAMESPACE)
140
+
141
+ ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time
142
+ ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic
143
+ ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset
144
+ ns[LEXING_DURATION] = pre_context.lexing_duration
145
+ ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset
146
+ ns[PARSING_DURATION] = pre_context.parsing_duration
147
+ ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset
148
+ ns[VALIDATION_DURATION] = timed_result.duration
149
+
150
+ timed_result.result
151
+ end
152
+
153
+ def capture_analysis_time(context)
154
+ ns = context.namespace(CONTEXT_NAMESPACE)
155
+
156
+ timed_result = GraphQL::Metrics.time(ns[MULTIPLEX_START_TIME_MONOTONIC]) { yield }
157
+
158
+ ns[ANALYSIS_START_TIME_OFFSET] = timed_result.time_since_offset
159
+ ns[ANALYSIS_DURATION] = timed_result.duration
160
+
161
+ timed_result.result
162
+ end
163
+
164
+ def capture_query_start_time(context)
165
+ ns = context.namespace(CONTEXT_NAMESPACE)
166
+ ns[QUERY_START_TIME] = GraphQL::Metrics.current_time
167
+ ns[QUERY_START_TIME_MONOTONIC] = GraphQL::Metrics.current_time_monotonic
168
+
169
+ yield
170
+ end
171
+
172
+ def trace_field(context_key, query)
173
+ ns = query.context.namespace(CONTEXT_NAMESPACE)
174
+ offset_time = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
175
+ start_time = GraphQL::Metrics.current_time_monotonic
176
+
177
+ result = yield
178
+
179
+ duration = GraphQL::Metrics.current_time_monotonic - start_time
180
+ time_since_offset = start_time - offset_time if offset_time
181
+
182
+ path_excluding_numeric_indicies = query.context[:current_path].select { |p| p.is_a?(String) }
183
+ ns[context_key][path_excluding_numeric_indicies] ||= []
184
+ ns[context_key][path_excluding_numeric_indicies] << {
185
+ start_time_offset: time_since_offset, duration: duration
186
+ }
187
+
188
+ result
189
+ end
190
+ end
191
+ end
192
+ end
@@ -19,6 +19,12 @@ module GraphQL
19
19
  GRAPHQL_GEM_TRACING_LAZY_FIELD_KEY = 'execute_field_lazy'
20
20
  ]
21
21
 
22
+ include GraphQL::Metrics::Trace
23
+
24
+ def initialize
25
+ # no-op, but don't want the behavior from GraphQL::Metrics::Trace
26
+ end
27
+
22
28
  def trace(key, data, &block)
23
29
  # NOTE: Context doesn't exist yet during lexing, parsing.
24
30
  context = data[:query]&.context
@@ -54,130 +60,11 @@ module GraphQL
54
60
  GraphQL::Metrics::LAZY_FIELD_TIMINGS
55
61
  end
56
62
 
57
- trace_field(context_key, data, &block)
63
+ trace_field(context_key, data[:query], &block)
58
64
  else
59
65
  return yield
60
66
  end
61
67
  end
62
-
63
- private
64
-
65
- PreContext = Struct.new(
66
- :multiplex_start_time,
67
- :multiplex_start_time_monotonic,
68
- :parsing_start_time_offset,
69
- :parsing_duration,
70
- :lexing_start_time_offset,
71
- :lexing_duration
72
- ) do
73
- def reset
74
- self[:multiplex_start_time] = nil
75
- self[:multiplex_start_time_monotonic] = nil
76
- self[:parsing_start_time_offset] = nil
77
- self[:parsing_duration] = nil
78
- self[:lexing_start_time_offset] = nil
79
- self[:lexing_duration] = nil
80
- end
81
- end
82
-
83
- def pre_context
84
- # NOTE: This is used to store timings from lexing, parsing, validation, before we have a context to store
85
- # values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread.
86
- @pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new)
87
- @pre_context.value
88
- end
89
-
90
- def capture_multiplex_start_time
91
- pre_context.multiplex_start_time = GraphQL::Metrics.current_time
92
- pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic
93
-
94
- yield
95
- end
96
-
97
- def capture_lexing_time
98
- timed_result = GraphQL::Metrics.time { yield }
99
-
100
- pre_context.lexing_start_time_offset = timed_result.start_time
101
- pre_context.lexing_duration = timed_result.duration
102
-
103
- timed_result.result
104
- end
105
-
106
- def capture_parsing_time
107
- timed_result = GraphQL::Metrics.time { yield }
108
-
109
- pre_context.parsing_start_time_offset = timed_result.start_time
110
- pre_context.parsing_duration = timed_result.duration
111
-
112
- timed_result.result
113
- end
114
-
115
- # Also consolidates parsing timings (if any) from the `pre_context`
116
- def capture_validation_time(context)
117
- # Queries may already be lexed and parsed before execution (whether a single query or multiplex).
118
- # If we don't have those values, use some sane defaults.
119
- if pre_context.lexing_duration.nil?
120
- pre_context.lexing_start_time_offset = pre_context.multiplex_start_time
121
- pre_context.lexing_duration = 0.0
122
- end
123
- if pre_context.parsing_duration.nil?
124
- pre_context.parsing_start_time_offset = pre_context.multiplex_start_time
125
- pre_context.parsing_duration = 0.0
126
- end
127
-
128
- timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield }
129
-
130
- ns = context.namespace(CONTEXT_NAMESPACE)
131
-
132
- ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time
133
- ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic
134
- ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset
135
- ns[LEXING_DURATION] = pre_context.lexing_duration
136
- ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset
137
- ns[PARSING_DURATION] = pre_context.parsing_duration
138
- ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset
139
- ns[VALIDATION_DURATION] = timed_result.duration
140
-
141
- timed_result.result
142
- end
143
-
144
- def capture_analysis_time(context)
145
- ns = context.namespace(CONTEXT_NAMESPACE)
146
-
147
- timed_result = GraphQL::Metrics.time(ns[MULTIPLEX_START_TIME_MONOTONIC]) { yield }
148
-
149
- ns[ANALYSIS_START_TIME_OFFSET] = timed_result.time_since_offset
150
- ns[ANALYSIS_DURATION] = timed_result.duration
151
-
152
- timed_result.result
153
- end
154
-
155
- def capture_query_start_time(context)
156
- ns = context.namespace(CONTEXT_NAMESPACE)
157
- ns[QUERY_START_TIME] = GraphQL::Metrics.current_time
158
- ns[QUERY_START_TIME_MONOTONIC] = GraphQL::Metrics.current_time_monotonic
159
-
160
- yield
161
- end
162
-
163
- def trace_field(context_key, data)
164
- ns = data[:query].context.namespace(CONTEXT_NAMESPACE)
165
- offset_time = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
166
- start_time = GraphQL::Metrics.current_time_monotonic
167
-
168
- result = yield
169
-
170
- duration = GraphQL::Metrics.current_time_monotonic - start_time
171
- time_since_offset = start_time - offset_time if offset_time
172
-
173
- path_excluding_numeric_indicies = data[:path].select { |p| p.is_a?(String) }
174
- ns[context_key][path_excluding_numeric_indicies] ||= []
175
- ns[context_key][path_excluding_numeric_indicies] << {
176
- start_time_offset: time_since_offset, duration: duration
177
- }
178
-
179
- result
180
- end
181
68
  end
182
69
  end
183
70
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Metrics
5
- VERSION = "5.0.2"
5
+ VERSION = "5.0.4"
6
6
  end
7
7
  end
@@ -3,6 +3,7 @@
3
3
  require "concurrent"
4
4
  require "graphql/metrics/version"
5
5
  require "graphql/metrics/instrumentation"
6
+ require "graphql/metrics/trace"
6
7
  require "graphql/metrics/tracer"
7
8
  require "graphql/metrics/analyzer"
8
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.2
4
+ version: 5.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Butcher
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-06 00:00:00.000000000 Z
11
+ date: 2023-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -28,14 +28,14 @@ dependencies:
28
28
  name: concurrent-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.1.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.1.0
41
41
  - !ruby/object:Gem::Dependency
@@ -218,10 +218,12 @@ files:
218
218
  - bin/setup
219
219
  - gemfiles/graphql_1.13.gemfile
220
220
  - gemfiles/graphql_2.0.gemfile
221
+ - gemfiles/graphql_head.gemfile
221
222
  - graphql_metrics.gemspec
222
223
  - lib/graphql/metrics.rb
223
224
  - lib/graphql/metrics/analyzer.rb
224
225
  - lib/graphql/metrics/instrumentation.rb
226
+ - lib/graphql/metrics/trace.rb
225
227
  - lib/graphql/metrics/tracer.rb
226
228
  - lib/graphql/metrics/version.rb
227
229
  homepage: https://github.com/Shopify/graphql-metrics