graphql-metrics 5.0.2 → 5.0.4

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 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