graphql 2.6.5 → 2.6.7
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 +4 -4
- data/lib/graphql/dataloader/async_dataloader.rb +159 -170
- data/lib/graphql/dataloader/source.rb +14 -9
- data/lib/graphql/dataloader.rb +51 -35
- data/lib/graphql/execution/interpreter/runtime.rb +1 -2
- data/lib/graphql/execution/runner.rb +2 -2
- data/lib/graphql/execution/selections_step.rb +4 -4
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/schema/build_from_definition.rb +11 -3
- data/lib/graphql/schema/directive.rb +3 -0
- data/lib/graphql/schema/resolver.rb +1 -1
- data/lib/graphql/schema/wrapper.rb +4 -0
- data/lib/graphql/schema.rb +3 -3
- data/lib/graphql/version.rb +1 -1
- metadata +2 -58
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 75192ceea307d33645d2d511db98530f8021c8af45af33fc75e86dcd69f53952
|
|
4
|
+
data.tar.gz: ff3ee9e77876ad45fb67491fb5076df4614ab086ecfa15e476b3d35bca2e3b22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50bc8591ece79fc8070b4c3a6a3611f29e9a4ad84a94e6f310a5fd2c00ac0d10b6db5ebe44474e1d5f002a76615d6a25dd4dbfc2a8f56ec1c2d0d88972ce95e2
|
|
7
|
+
data.tar.gz: b28b83923ac25bb605d7fe1e9b25a64e37aa8173a64c958849287cdd0c87cc419deea25f36b4d525a6ea17088b7a02cb0a51011750c568337a6274b0373ff43b
|
|
@@ -4,6 +4,11 @@ module GraphQL
|
|
|
4
4
|
class Dataloader
|
|
5
5
|
class AsyncDataloader < Dataloader
|
|
6
6
|
def self.use(...)
|
|
7
|
+
install_graphql_methods
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.install_graphql_methods
|
|
7
12
|
if !Async::Task.method_defined?(:cancel)
|
|
8
13
|
Async::Task.alias_method(:cancel, :stop)
|
|
9
14
|
end
|
|
@@ -11,7 +16,6 @@ module GraphQL
|
|
|
11
16
|
Async::Task.attr_accessor(:graphql_async_dataloader_run)
|
|
12
17
|
Async::Task.attr_accessor(:graphql_async_dataloader_condition)
|
|
13
18
|
end
|
|
14
|
-
super
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def initialize(...)
|
|
@@ -19,9 +23,12 @@ module GraphQL
|
|
|
19
23
|
create_pending_run
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
# @api private
|
|
27
|
+
attr_reader :pending_sources
|
|
28
|
+
|
|
22
29
|
def create_pending_run
|
|
23
30
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
24
|
-
@pending_run = Run.new(total_fiber_limit, jobs_fiber_limit)
|
|
31
|
+
@pending_run = Run.new(self, total_fiber_limit, jobs_fiber_limit)
|
|
25
32
|
end
|
|
26
33
|
|
|
27
34
|
def yield(source = Fiber[:__graphql_current_dataloader_source])
|
|
@@ -29,16 +36,17 @@ module GraphQL
|
|
|
29
36
|
run = task.graphql_async_dataloader_run
|
|
30
37
|
trace = run.trace
|
|
31
38
|
trace&.dataloader_fiber_yield(source)
|
|
32
|
-
run.
|
|
39
|
+
run.tasks_channel.push([:paused_task, task])
|
|
33
40
|
condition = task.graphql_async_dataloader_condition
|
|
34
41
|
condition.wait
|
|
35
|
-
run.
|
|
42
|
+
run.tasks_channel.push([:resumed_task, task])
|
|
36
43
|
trace&.dataloader_fiber_resume(source)
|
|
37
44
|
nil
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
class Run
|
|
41
|
-
def initialize(total_fiber_limit, jobs_fiber_limit)
|
|
48
|
+
def initialize(dataloader, total_fiber_limit, jobs_fiber_limit)
|
|
49
|
+
@dataloader = dataloader
|
|
42
50
|
@root_task = nil
|
|
43
51
|
@trace = nil
|
|
44
52
|
@jobs = []
|
|
@@ -47,12 +55,13 @@ module GraphQL
|
|
|
47
55
|
@jobs_fiber_limit = jobs_fiber_limit
|
|
48
56
|
@lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
49
57
|
|
|
50
|
-
@
|
|
51
|
-
@
|
|
52
|
-
@
|
|
53
|
-
@
|
|
54
|
-
@
|
|
55
|
-
@
|
|
58
|
+
@running_tasks = nil
|
|
59
|
+
@tasks_channel = nil
|
|
60
|
+
@tasks_channel_task = nil
|
|
61
|
+
@activity = nil
|
|
62
|
+
@task_error = nil
|
|
63
|
+
@expected_resumes = 0
|
|
64
|
+
@mode = nil
|
|
56
65
|
|
|
57
66
|
@snoozed_jobs_condition = Async::Condition.new
|
|
58
67
|
@snoozed_sources_condition = Async::Condition.new
|
|
@@ -60,74 +69,76 @@ module GraphQL
|
|
|
60
69
|
|
|
61
70
|
attr_accessor :trace, :root_task
|
|
62
71
|
|
|
63
|
-
attr_reader :jobs, :lazies_at_depth, :jobs_fiber_limit,
|
|
72
|
+
attr_reader :jobs, :lazies_at_depth, :jobs_fiber_limit, :snoozed_jobs_condition, :snoozed_sources_condition, :tasks_channel
|
|
64
73
|
|
|
65
74
|
def jobs_bandwidth?
|
|
66
|
-
running_count < jobs_fiber_limit
|
|
75
|
+
running_count < @jobs_fiber_limit
|
|
67
76
|
end
|
|
68
77
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
if within_limit < 1
|
|
72
|
-
1
|
|
73
|
-
else
|
|
74
|
-
within_limit
|
|
75
|
-
end
|
|
78
|
+
def sources_bandwidth?
|
|
79
|
+
running_count < current_sources_fiber_limit
|
|
76
80
|
end
|
|
77
81
|
|
|
78
82
|
def close_queues
|
|
79
|
-
@
|
|
80
|
-
@
|
|
83
|
+
@tasks_channel.close
|
|
84
|
+
@tasks_channel_task.cancel
|
|
85
|
+
end
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
@
|
|
87
|
+
def wait_for_activity
|
|
88
|
+
@activity.wait
|
|
84
89
|
end
|
|
85
90
|
|
|
86
|
-
def
|
|
87
|
-
@
|
|
88
|
-
@snoozed_sources_condition.instance_variable_get(:@ready).num_waiting +
|
|
89
|
-
@started_count +
|
|
90
|
-
@started_tasks.size -
|
|
91
|
-
@finished_count
|
|
91
|
+
def quiesced?
|
|
92
|
+
@running_tasks.empty? && @tasks_channel.empty? && @expected_resumes == 0
|
|
92
93
|
end
|
|
93
94
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
end
|
|
95
|
+
def has_pending_work?
|
|
96
|
+
@mode == :jobs ? @jobs.any? : @dataloader.pending_sources.any?(&:pending?) # rubocop:disable Development/NoneWithoutBlockCop
|
|
97
|
+
end
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
@
|
|
99
|
+
def has_bandwidth?
|
|
100
|
+
@mode == :jobs ? jobs_bandwidth? : sources_bandwidth?
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
while task = @started_tasks.wait
|
|
114
|
-
@started_count += 1
|
|
115
|
-
if task.status == :initialized # could also be resumed after waiting
|
|
116
|
-
task.run
|
|
117
|
-
end
|
|
118
|
-
end
|
|
103
|
+
# Signalled tasks don't appear in any accounting until their first slice
|
|
104
|
+
# pushes `:resumed_task`, so they have to be counted at signal time:
|
|
105
|
+
def expect_resumes(count)
|
|
106
|
+
@expected_resumes = count
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def check_error!
|
|
110
|
+
if (err = @task_error)
|
|
111
|
+
@task_error = nil
|
|
112
|
+
raise err
|
|
119
113
|
end
|
|
114
|
+
end
|
|
120
115
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
116
|
+
def new_queues(mode)
|
|
117
|
+
@mode = mode
|
|
118
|
+
@tasks_channel = Async::Queue.new(parent: @root_task)
|
|
119
|
+
@activity = Async::Condition.new
|
|
120
|
+
@task_error = nil
|
|
121
|
+
@expected_resumes = 0
|
|
122
|
+
@running_tasks = []
|
|
123
|
+
@tasks_channel_task = @root_task.async do |_t|
|
|
124
|
+
while ((msg, data) = @tasks_channel.wait)
|
|
125
|
+
case msg
|
|
126
|
+
when :started_task
|
|
127
|
+
@running_tasks.push(data)
|
|
128
|
+
data.run
|
|
129
|
+
when :resumed_task
|
|
130
|
+
if @expected_resumes > 0
|
|
131
|
+
@expected_resumes -= 1
|
|
129
132
|
end
|
|
133
|
+
@running_tasks.push(data)
|
|
134
|
+
when :finished_task, :paused_task
|
|
135
|
+
@running_tasks.delete(data)
|
|
136
|
+
when :task_error
|
|
137
|
+
@task_error ||= data
|
|
138
|
+
else
|
|
139
|
+
raise ArgumentError, "Unknown tasks_channel action: #{msg.inspect}"
|
|
130
140
|
end
|
|
141
|
+
@activity.signal
|
|
131
142
|
end
|
|
132
143
|
end
|
|
133
144
|
end
|
|
@@ -135,6 +146,23 @@ module GraphQL
|
|
|
135
146
|
def running?
|
|
136
147
|
@snoozed_jobs_condition.waiting? || @snoozed_sources_condition.waiting?
|
|
137
148
|
end
|
|
149
|
+
|
|
150
|
+
def current_sources_fiber_limit
|
|
151
|
+
within_limit = @total_fiber_limit - running_count
|
|
152
|
+
if within_limit < 1
|
|
153
|
+
1
|
|
154
|
+
else
|
|
155
|
+
within_limit
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def running_count
|
|
162
|
+
@snoozed_jobs_condition.instance_variable_get(:@ready).num_waiting +
|
|
163
|
+
@snoozed_sources_condition.instance_variable_get(:@ready).num_waiting +
|
|
164
|
+
(@running_tasks&.size || 0)
|
|
165
|
+
end
|
|
138
166
|
end
|
|
139
167
|
|
|
140
168
|
def append_job(callable = nil, &block)
|
|
@@ -177,9 +205,10 @@ module GraphQL
|
|
|
177
205
|
end
|
|
178
206
|
prev_pending_keys.each do |source_instance, pending|
|
|
179
207
|
pending.each do |key, value|
|
|
180
|
-
if
|
|
181
|
-
|
|
182
|
-
|
|
208
|
+
next if source_instance.results.key?(key)
|
|
209
|
+
|
|
210
|
+
queue_pending_source(source_instance) if source_instance.pending.empty?
|
|
211
|
+
source_instance.pending[key] = value
|
|
183
212
|
end
|
|
184
213
|
end
|
|
185
214
|
end
|
|
@@ -203,13 +232,14 @@ module GraphQL
|
|
|
203
232
|
|
|
204
233
|
while first_pass || run.running? || !jobs.empty?
|
|
205
234
|
first_pass = false
|
|
206
|
-
|
|
207
|
-
|
|
235
|
+
run_queue(run, run.snoozed_jobs_condition, :jobs)
|
|
236
|
+
run_queue(run, run.snoozed_sources_condition, :sources)
|
|
208
237
|
|
|
209
238
|
if !run.lazies_at_depth.empty?
|
|
210
239
|
with_trace_query_lazy(trace_query_lazy) do
|
|
211
|
-
|
|
212
|
-
|
|
240
|
+
if enqueue_next_pending_lazies(run.lazies_at_depth)
|
|
241
|
+
run_queue(run, run.snoozed_jobs_condition, :jobs)
|
|
242
|
+
end
|
|
213
243
|
end
|
|
214
244
|
end
|
|
215
245
|
end
|
|
@@ -232,129 +262,88 @@ module GraphQL
|
|
|
232
262
|
|
|
233
263
|
private
|
|
234
264
|
|
|
235
|
-
def
|
|
236
|
-
|
|
265
|
+
def run_queue(run, condition, mode)
|
|
266
|
+
opened_queues = false
|
|
237
267
|
|
|
238
|
-
if
|
|
239
|
-
|
|
268
|
+
if condition.waiting?
|
|
269
|
+
opened_queues = true
|
|
270
|
+
run.new_queues(mode)
|
|
271
|
+
run.expect_resumes(condition.instance_variable_get(:@ready).num_waiting)
|
|
272
|
+
condition.signal
|
|
240
273
|
end
|
|
241
|
-
pending_jobs = run.jobs
|
|
242
|
-
while (!pending_jobs.empty? && (has_limit = run.jobs_bandwidth?)) || (unsnoozed)
|
|
243
|
-
unsnoozed = false
|
|
244
|
-
if has_limit
|
|
245
|
-
spawn_job_task(run)
|
|
246
|
-
end
|
|
247
|
-
run.wait_for_queues
|
|
248
|
-
end
|
|
249
|
-
ensure
|
|
250
|
-
run.close_queues
|
|
251
|
-
end
|
|
252
274
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
task.graphql_async_dataloader_run = run
|
|
260
|
-
task.graphql_async_dataloader_condition = run.snoozed_jobs_condition
|
|
261
|
-
set_fiber_variables(fiber_vars)
|
|
262
|
-
while job = pending_jobs.shift
|
|
263
|
-
job.call
|
|
275
|
+
loop do
|
|
276
|
+
pending_work = (mode == :jobs) ? (!run.jobs.empty? && run.jobs_bandwidth? ? run.jobs : nil) : (drain_pending_sources)
|
|
277
|
+
if pending_work
|
|
278
|
+
if opened_queues == false
|
|
279
|
+
opened_queues = true
|
|
280
|
+
run.new_queues(mode)
|
|
264
281
|
end
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
282
|
+
num_tasks = mode == :sources ? run.current_sources_fiber_limit : 1
|
|
283
|
+
if num_tasks > pending_work.size
|
|
284
|
+
num_tasks = pending_work.size
|
|
285
|
+
end
|
|
286
|
+
spawn_tasks(run, mode, condition, pending_work, num_tasks)
|
|
269
287
|
end
|
|
270
|
-
run.started_tasks.push(new_task)
|
|
271
|
-
new_task
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def run_sources(run)
|
|
276
|
-
run.new_queues
|
|
277
288
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
allowed_tasks = run.allowed_sources_tasks
|
|
283
|
-
while (has_pending = @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) } ) || unsnoozed
|
|
284
|
-
unsnoozed = false
|
|
285
|
-
if has_pending
|
|
286
|
-
spawn_source_task(run, allowed_tasks)
|
|
289
|
+
if !opened_queues
|
|
290
|
+
break
|
|
287
291
|
end
|
|
288
|
-
run.wait_for_queues
|
|
289
|
-
end
|
|
290
|
-
ensure
|
|
291
|
-
run.close_queues
|
|
292
|
-
end
|
|
293
292
|
|
|
294
|
-
|
|
295
|
-
def run_next_pending_lazies(run)
|
|
296
|
-
smallest_depth = nil
|
|
297
|
-
run.lazies_at_depth.each_key do |depth_key|
|
|
298
|
-
smallest_depth ||= depth_key
|
|
299
|
-
if depth_key < smallest_depth
|
|
300
|
-
smallest_depth = depth_key
|
|
301
|
-
end
|
|
302
|
-
end
|
|
293
|
+
run.check_error!
|
|
303
294
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
begin
|
|
308
|
-
run.new_queues
|
|
309
|
-
lazies.each_with_index do |l, idx|
|
|
310
|
-
append_job { l.value }
|
|
311
|
-
end
|
|
312
|
-
spawn_job_task(run) # Todo what was the last `true` condition?
|
|
313
|
-
run.wait_for_queues
|
|
314
|
-
ensure
|
|
315
|
-
run.close_queues
|
|
295
|
+
if run.quiesced?
|
|
296
|
+
if !run.has_pending_work? || !run.has_bandwidth?
|
|
297
|
+
break
|
|
316
298
|
end
|
|
299
|
+
# Quiesced, but more work appeared - loop around to drain it.
|
|
300
|
+
else
|
|
301
|
+
run.wait_for_activity
|
|
317
302
|
end
|
|
318
303
|
end
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
pending_sources = nil
|
|
323
|
-
@source_cache.each_value do |source_by_batch_params|
|
|
324
|
-
source_by_batch_params.each_value do |source|
|
|
325
|
-
if source.pending?
|
|
326
|
-
pending_sources ||= []
|
|
327
|
-
pending_sources << source
|
|
328
|
-
end
|
|
329
|
-
end
|
|
304
|
+
ensure
|
|
305
|
+
if opened_queues
|
|
306
|
+
run.close_queues
|
|
330
307
|
end
|
|
308
|
+
end
|
|
331
309
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
310
|
+
# Use a separate method for this so that the outer loop's reassignment of `pending_work`
|
|
311
|
+
# doesn't affect already-running tasks which (would) close over that variable
|
|
312
|
+
def spawn_tasks(run, mode, condition, pending_work, num_tasks)
|
|
313
|
+
fiber_vars = get_fiber_variables
|
|
314
|
+
trace = run.trace
|
|
315
|
+
num_tasks.times do
|
|
316
|
+
new_task = Async::Task.new(run.root_task) do |task|
|
|
317
|
+
task.graphql_async_dataloader_run = run
|
|
318
|
+
task.graphql_async_dataloader_condition = condition
|
|
319
|
+
set_fiber_variables(fiber_vars)
|
|
320
|
+
case mode
|
|
321
|
+
when :jobs
|
|
322
|
+
trace&.dataloader_spawn_execution_fiber(pending_work)
|
|
323
|
+
while job = pending_work.shift
|
|
324
|
+
job.call
|
|
325
|
+
end
|
|
326
|
+
when :sources
|
|
327
|
+
trace&.dataloader_spawn_source_fiber(pending_work)
|
|
328
|
+
while (source = pending_work.shift)
|
|
329
|
+
Fiber[:__graphql_current_dataloader_source] = source
|
|
345
330
|
trace&.begin_dataloader_source(source)
|
|
346
331
|
source.run_pending_keys
|
|
347
332
|
trace&.end_dataloader_source(source)
|
|
348
333
|
end
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
run.finished_tasks.push($! || task)
|
|
352
|
-
cleanup_fiber
|
|
353
|
-
trace&.dataloader_fiber_exit
|
|
334
|
+
else
|
|
335
|
+
raise ArgumentError, "Unknown mode: #{mode.inspect}"
|
|
354
336
|
end
|
|
355
|
-
|
|
356
|
-
|
|
337
|
+
nil
|
|
338
|
+
rescue StandardError => err
|
|
339
|
+
run.tasks_channel.push([:task_error, err])
|
|
340
|
+
else
|
|
341
|
+
run.tasks_channel.push([:finished_task, task])
|
|
342
|
+
ensure
|
|
343
|
+
cleanup_fiber
|
|
344
|
+
trace&.dataloader_fiber_exit
|
|
357
345
|
end
|
|
346
|
+
run.tasks_channel.push([:started_task, new_task])
|
|
358
347
|
end
|
|
359
348
|
end
|
|
360
349
|
end
|
|
@@ -20,9 +20,7 @@ module GraphQL
|
|
|
20
20
|
# @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result.
|
|
21
21
|
def request(value)
|
|
22
22
|
res_key = result_key_for(value)
|
|
23
|
-
|
|
24
|
-
@pending[res_key] ||= normalize_fetch_key(value)
|
|
25
|
-
end
|
|
23
|
+
add_pending_key(res_key, value)
|
|
26
24
|
Dataloader::Request.new(self, value)
|
|
27
25
|
end
|
|
28
26
|
|
|
@@ -51,9 +49,7 @@ module GraphQL
|
|
|
51
49
|
def request_all(values)
|
|
52
50
|
values.each do |v|
|
|
53
51
|
res_key = result_key_for(v)
|
|
54
|
-
|
|
55
|
-
@pending[res_key] ||= normalize_fetch_key(v)
|
|
56
|
-
end
|
|
52
|
+
add_pending_key(res_key, v)
|
|
57
53
|
end
|
|
58
54
|
Dataloader::RequestAll.new(self, values)
|
|
59
55
|
end
|
|
@@ -65,7 +61,7 @@ module GraphQL
|
|
|
65
61
|
if @results.key?(result_key)
|
|
66
62
|
result_for(result_key)
|
|
67
63
|
else
|
|
68
|
-
|
|
64
|
+
add_pending_key(result_key, value)
|
|
69
65
|
sync([result_key])
|
|
70
66
|
result_for(result_key)
|
|
71
67
|
end
|
|
@@ -79,8 +75,7 @@ module GraphQL
|
|
|
79
75
|
values.each { |v|
|
|
80
76
|
k = result_key_for(v)
|
|
81
77
|
result_keys << k
|
|
82
|
-
if
|
|
83
|
-
@pending[k] ||= normalize_fetch_key(v)
|
|
78
|
+
if add_pending_key(k, v)
|
|
84
79
|
pending_keys << k
|
|
85
80
|
end
|
|
86
81
|
}
|
|
@@ -106,6 +101,7 @@ module GraphQL
|
|
|
106
101
|
# Then run the batch and update the cache.
|
|
107
102
|
# @return [void]
|
|
108
103
|
def sync(pending_result_keys)
|
|
104
|
+
@dataloader.queue_pending_source(self) if pending?
|
|
109
105
|
@dataloader.yield(self)
|
|
110
106
|
iterations = 0
|
|
111
107
|
while pending_result_keys.any? { |key| !@results.key?(key) }
|
|
@@ -193,6 +189,15 @@ module GraphQL
|
|
|
193
189
|
|
|
194
190
|
private
|
|
195
191
|
|
|
192
|
+
def add_pending_key(result_key, value)
|
|
193
|
+
return false if @results.key?(result_key)
|
|
194
|
+
|
|
195
|
+
was_empty = @pending.empty?
|
|
196
|
+
@pending[result_key] ||= normalize_fetch_key(value)
|
|
197
|
+
@dataloader.queue_pending_source(self) if was_empty
|
|
198
|
+
true
|
|
199
|
+
end
|
|
200
|
+
|
|
196
201
|
# Reads and returns the result for the key from the internal cache, or raises an error if the result was an error
|
|
197
202
|
# @param key [Object] key passed to {#load} or {#load_all}
|
|
198
203
|
# @return [Object] The result from {#fetch} for `key`.
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -59,6 +59,8 @@ module GraphQL
|
|
|
59
59
|
|
|
60
60
|
def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
|
|
61
61
|
@source_cache = Hash.new { |h, k| h[k] = {} }.compare_by_identity
|
|
62
|
+
@pending_source_set = Set.new.compare_by_identity
|
|
63
|
+
@pending_sources = []
|
|
62
64
|
@pending_jobs = []
|
|
63
65
|
if !nonblocking.nil?
|
|
64
66
|
@nonblocking = nonblocking
|
|
@@ -108,7 +110,7 @@ module GraphQL
|
|
|
108
110
|
# @param batch_parameters [Array<Object>]
|
|
109
111
|
# @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
|
|
110
112
|
# and cached for the lifetime of this {Multiplex}.
|
|
111
|
-
if (RUBY_ENGINE == "ruby" &&
|
|
113
|
+
if (RUBY_ENGINE == "ruby" && RUBY_VERSION < "3") || RUBY_ENGINE == "truffleruby" # truffle-ruby wasn't doing well with the implementation below
|
|
112
114
|
def with(source_class, *batch_args)
|
|
113
115
|
batch_key = source_class.batch_key_for(*batch_args)
|
|
114
116
|
@source_cache[source_class][batch_key] ||= begin
|
|
@@ -148,6 +150,14 @@ module GraphQL
|
|
|
148
150
|
nil
|
|
149
151
|
end
|
|
150
152
|
|
|
153
|
+
# @api private
|
|
154
|
+
def queue_pending_source(source)
|
|
155
|
+
if @pending_source_set.add?(source)
|
|
156
|
+
@pending_sources << source
|
|
157
|
+
end
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
160
|
+
|
|
151
161
|
# Clear any already-loaded objects from {Source} caches
|
|
152
162
|
# @return [void]
|
|
153
163
|
def clear_cache
|
|
@@ -187,9 +197,10 @@ module GraphQL
|
|
|
187
197
|
@lazies_at_depth = prev_lazies_at_depth
|
|
188
198
|
prev_pending_keys.each do |source_instance, pending|
|
|
189
199
|
pending.each do |key, value|
|
|
190
|
-
if
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
next if source_instance.results.key?(key)
|
|
201
|
+
|
|
202
|
+
queue_pending_source(source_instance) if source_instance.pending.empty?
|
|
203
|
+
source_instance.pending[key] = value
|
|
193
204
|
end
|
|
194
205
|
end
|
|
195
206
|
end
|
|
@@ -212,8 +223,10 @@ module GraphQL
|
|
|
212
223
|
|
|
213
224
|
if !@lazies_at_depth.empty?
|
|
214
225
|
with_trace_query_lazy(trace_query_lazy) do
|
|
215
|
-
|
|
216
|
-
|
|
226
|
+
if enqueue_next_pending_lazies(@lazies_at_depth)
|
|
227
|
+
job_fibers.unshift(spawn_job_fiber(trace))
|
|
228
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
229
|
+
end
|
|
217
230
|
end
|
|
218
231
|
end
|
|
219
232
|
end
|
|
@@ -274,24 +287,19 @@ module GraphQL
|
|
|
274
287
|
|
|
275
288
|
private
|
|
276
289
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if depth_key < smallest_depth
|
|
282
|
-
smallest_depth = depth_key
|
|
283
|
-
end
|
|
284
|
-
end
|
|
290
|
+
# Returns true if anything was actually enqueued
|
|
291
|
+
def enqueue_next_pending_lazies(lazies_at_depth)
|
|
292
|
+
smallest_depth = lazies_at_depth.each_key.min
|
|
293
|
+
return false if smallest_depth.nil?
|
|
285
294
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
end
|
|
292
|
-
job_fibers.unshift(spawn_job_fiber(trace))
|
|
293
|
-
end
|
|
295
|
+
lazies = lazies_at_depth.delete(smallest_depth)
|
|
296
|
+
return false if lazies.empty?
|
|
297
|
+
|
|
298
|
+
lazies.each do |lazy|
|
|
299
|
+
append_job { lazy.value }
|
|
294
300
|
end
|
|
301
|
+
|
|
302
|
+
true
|
|
295
303
|
end
|
|
296
304
|
|
|
297
305
|
def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
@@ -305,7 +313,7 @@ module GraphQL
|
|
|
305
313
|
end
|
|
306
314
|
join_queues(job_fibers, next_job_fibers)
|
|
307
315
|
|
|
308
|
-
while (!source_fibers.empty? ||
|
|
316
|
+
while (!source_fibers.empty? || !@pending_sources.empty?)
|
|
309
317
|
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
|
|
310
318
|
if f.alive?
|
|
311
319
|
finished = run_fiber(f)
|
|
@@ -356,21 +364,29 @@ module GraphQL
|
|
|
356
364
|
end
|
|
357
365
|
end
|
|
358
366
|
|
|
359
|
-
def
|
|
360
|
-
pending_sources =
|
|
361
|
-
@
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
367
|
+
def drain_pending_sources
|
|
368
|
+
pending_sources = @pending_sources
|
|
369
|
+
@pending_sources = []
|
|
370
|
+
@pending_source_set.clear
|
|
371
|
+
|
|
372
|
+
pending_sources.select!(&:pending?)
|
|
373
|
+
pending_sources.empty? ? nil : pending_sources
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def dequeue_pending_source
|
|
377
|
+
while (source = @pending_sources.shift)
|
|
378
|
+
@pending_source_set.delete(source)
|
|
379
|
+
return source if source.pending?
|
|
368
380
|
end
|
|
381
|
+
end
|
|
369
382
|
|
|
370
|
-
|
|
383
|
+
def spawn_source_fiber(trace)
|
|
384
|
+
if !@pending_sources.empty?
|
|
371
385
|
spawn_fiber do
|
|
372
|
-
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
373
|
-
|
|
386
|
+
trace&.dataloader_spawn_source_fiber(@pending_sources)
|
|
387
|
+
# This will find sources which were enqueued during `#fetch`:
|
|
388
|
+
while (source = dequeue_pending_source)
|
|
389
|
+
next if !source.pending?
|
|
374
390
|
Fiber[:__graphql_current_dataloader_source] = source
|
|
375
391
|
trace&.begin_dataloader_source(source)
|
|
376
392
|
source.run_pending_keys
|
|
@@ -765,8 +765,7 @@ module GraphQL
|
|
|
765
765
|
|
|
766
766
|
response_list
|
|
767
767
|
rescue NoMethodError => err
|
|
768
|
-
|
|
769
|
-
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
|
768
|
+
if err.name == :each && err.receiver == value
|
|
770
769
|
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
|
771
770
|
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
|
772
771
|
else
|
|
@@ -281,8 +281,8 @@ module GraphQL
|
|
|
281
281
|
end
|
|
282
282
|
result = dir_defn.resolve_operation(selected_operation, query, objects, dir_args, query.context)
|
|
283
283
|
if result.is_a?(Finalizer)
|
|
284
|
-
result.path =
|
|
285
|
-
add_finalizer(query,
|
|
284
|
+
result.path = beginning_path
|
|
285
|
+
add_finalizer(query, data, nil, result)
|
|
286
286
|
if result.is_a?(HaltExecution)
|
|
287
287
|
continue_execution = false
|
|
288
288
|
break
|
|
@@ -19,7 +19,7 @@ module GraphQL
|
|
|
19
19
|
@storage = ast_variables.each_with_object({}) do |ast_variable, memo|
|
|
20
20
|
if schema.validate_max_errors && schema.validate_max_errors <= @errors.count
|
|
21
21
|
add_max_errors_reached_message
|
|
22
|
-
break
|
|
22
|
+
break memo
|
|
23
23
|
end
|
|
24
24
|
# Find the right value for this variable:
|
|
25
25
|
# - First, use the value provided at runtime
|
|
@@ -82,6 +82,7 @@ module GraphQL
|
|
|
82
82
|
replace_late_bound_types_with_built_in(types)
|
|
83
83
|
|
|
84
84
|
schema_extensions = nil
|
|
85
|
+
definitions_by_name = nil
|
|
85
86
|
document.definitions.each do |definition|
|
|
86
87
|
case definition
|
|
87
88
|
when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
|
|
@@ -101,9 +102,16 @@ module GraphQL
|
|
|
101
102
|
if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
|
|
102
103
|
if definition.is_a?(GraphQL::Language::Nodes::ObjectTypeDefinition) || definition.is_a?(Language::Nodes::InterfaceTypeDefinition)
|
|
103
104
|
interface_names = definition.interfaces.map(&:name)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
if !interface_names.empty?
|
|
106
|
+
definitions_by_name ||= document.definitions.each_with_object({}) do |d, by_name|
|
|
107
|
+
by_name[d.name] ||= d if d.respond_to?(:name)
|
|
108
|
+
end
|
|
109
|
+
transitive_names = interface_names.map { |n| definitions_by_name[n]&.interfaces&.map(&:name) }
|
|
110
|
+
transitive_names.flatten!
|
|
111
|
+
transitive_names.compact!
|
|
112
|
+
else
|
|
113
|
+
transitive_names = interface_names
|
|
114
|
+
end
|
|
107
115
|
if !(missing_transitive_interfaces = transitive_names - interface_names).empty?
|
|
108
116
|
raise GraphQL::Schema::InvalidDocumentError, "type #{definition.name} is missing one or more transitive interface names: #{missing_transitive_interfaces.join(", ")}. Add them to the type's `implements` list and try again."
|
|
109
117
|
end
|
|
@@ -168,6 +168,9 @@ module GraphQL
|
|
|
168
168
|
# Let validation handle this
|
|
169
169
|
value
|
|
170
170
|
end
|
|
171
|
+
elsif arg_defn.default_value?
|
|
172
|
+
value = arg_defn.default_value
|
|
173
|
+
graphql_value = arg_type.coerce_isolated_result(value) unless value.nil?
|
|
171
174
|
else
|
|
172
175
|
value = graphql_value = nil
|
|
173
176
|
end
|
|
@@ -83,7 +83,7 @@ module GraphQL
|
|
|
83
83
|
is_authed, new_return_value = authorized?(**@prepared_arguments)
|
|
84
84
|
rescue GraphQL::UnauthorizedError => err
|
|
85
85
|
new_return_value = q.schema.unauthorized_object(err)
|
|
86
|
-
is_authed =
|
|
86
|
+
is_authed = false
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
data/lib/graphql/schema.rb
CHANGED
|
@@ -665,8 +665,8 @@ module GraphQL
|
|
|
665
665
|
inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY)
|
|
666
666
|
own_um + inherited_um
|
|
667
667
|
else
|
|
668
|
-
joined_um = own_union_memberships.dup
|
|
669
|
-
find_inherited_value(:
|
|
668
|
+
joined_um = own_union_memberships.transform_values(&:dup)
|
|
669
|
+
find_inherited_value(:union_memberships, EMPTY_HASH).each do |k, v|
|
|
670
670
|
um = joined_um[k] ||= []
|
|
671
671
|
um.concat(v)
|
|
672
672
|
end
|
|
@@ -860,7 +860,7 @@ module GraphQL
|
|
|
860
860
|
# @return [Array<GraphQL::StaticValidation::Error >]
|
|
861
861
|
def validate(string_or_document, rules: nil, context: nil)
|
|
862
862
|
doc = if string_or_document.is_a?(String)
|
|
863
|
-
GraphQL.parse(string_or_document)
|
|
863
|
+
GraphQL.parse(string_or_document, max_tokens: max_query_string_tokens)
|
|
864
864
|
else
|
|
865
865
|
string_or_document
|
|
866
866
|
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.6.
|
|
4
|
+
version: 2.6.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Mosolgo
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-07-
|
|
10
|
+
date: 2026-07-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -261,34 +261,6 @@ dependencies:
|
|
|
261
261
|
- - ">="
|
|
262
262
|
- !ruby/object:Gem::Version
|
|
263
263
|
version: '0'
|
|
264
|
-
- !ruby/object:Gem::Dependency
|
|
265
|
-
name: jekyll
|
|
266
|
-
requirement: !ruby/object:Gem::Requirement
|
|
267
|
-
requirements:
|
|
268
|
-
- - ">="
|
|
269
|
-
- !ruby/object:Gem::Version
|
|
270
|
-
version: '0'
|
|
271
|
-
type: :development
|
|
272
|
-
prerelease: false
|
|
273
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
274
|
-
requirements:
|
|
275
|
-
- - ">="
|
|
276
|
-
- !ruby/object:Gem::Version
|
|
277
|
-
version: '0'
|
|
278
|
-
- !ruby/object:Gem::Dependency
|
|
279
|
-
name: jekyll-sass-converter
|
|
280
|
-
requirement: !ruby/object:Gem::Requirement
|
|
281
|
-
requirements:
|
|
282
|
-
- - "~>"
|
|
283
|
-
- !ruby/object:Gem::Version
|
|
284
|
-
version: '2.2'
|
|
285
|
-
type: :development
|
|
286
|
-
prerelease: false
|
|
287
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
288
|
-
requirements:
|
|
289
|
-
- - "~>"
|
|
290
|
-
- !ruby/object:Gem::Version
|
|
291
|
-
version: '2.2'
|
|
292
264
|
- !ruby/object:Gem::Dependency
|
|
293
265
|
name: yard
|
|
294
266
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -303,34 +275,6 @@ dependencies:
|
|
|
303
275
|
- - ">="
|
|
304
276
|
- !ruby/object:Gem::Version
|
|
305
277
|
version: '0'
|
|
306
|
-
- !ruby/object:Gem::Dependency
|
|
307
|
-
name: jekyll-algolia
|
|
308
|
-
requirement: !ruby/object:Gem::Requirement
|
|
309
|
-
requirements:
|
|
310
|
-
- - ">="
|
|
311
|
-
- !ruby/object:Gem::Version
|
|
312
|
-
version: '0'
|
|
313
|
-
type: :development
|
|
314
|
-
prerelease: false
|
|
315
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
316
|
-
requirements:
|
|
317
|
-
- - ">="
|
|
318
|
-
- !ruby/object:Gem::Version
|
|
319
|
-
version: '0'
|
|
320
|
-
- !ruby/object:Gem::Dependency
|
|
321
|
-
name: jekyll-redirect-from
|
|
322
|
-
requirement: !ruby/object:Gem::Requirement
|
|
323
|
-
requirements:
|
|
324
|
-
- - ">="
|
|
325
|
-
- !ruby/object:Gem::Version
|
|
326
|
-
version: '0'
|
|
327
|
-
type: :development
|
|
328
|
-
prerelease: false
|
|
329
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
330
|
-
requirements:
|
|
331
|
-
- - ">="
|
|
332
|
-
- !ruby/object:Gem::Version
|
|
333
|
-
version: '0'
|
|
334
278
|
- !ruby/object:Gem::Dependency
|
|
335
279
|
name: m
|
|
336
280
|
requirement: !ruby/object:Gem::Requirement
|