graphql 2.6.5 → 2.6.6
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 +126 -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/query/variables.rb +1 -1
- 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: 3366525e188ac8b81b4dc7bdc8e34bb1839153fbec23d8e2a5faf07c02b56aa6
|
|
4
|
+
data.tar.gz: 414d8f17cee8d40adf8c5923427e83d187efdbd08871c7b9d8bbd4fa13b0e550
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4423d53187dcf97e67ae1ba93cc9b5135c7db35cab04abc5533b96e97e3abd5d2c4bbbe683a64abc0117a022743a53df804d170e5fbd0fd7430a3987466531b
|
|
7
|
+
data.tar.gz: c6093a9a53e554aed992da96b80007bbe10df7ac9552c4a1dc0cce19ae8a3f33fd3e2cb0858c117c1e061c4b996a4af8a7b161b31b4f3806ff3a30a84dfaddb2
|
|
@@ -19,9 +19,12 @@ module GraphQL
|
|
|
19
19
|
create_pending_run
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# @api private
|
|
23
|
+
attr_reader :pending_sources
|
|
24
|
+
|
|
22
25
|
def create_pending_run
|
|
23
26
|
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
24
|
-
@pending_run = Run.new(total_fiber_limit, jobs_fiber_limit)
|
|
27
|
+
@pending_run = Run.new(self, total_fiber_limit, jobs_fiber_limit)
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
def yield(source = Fiber[:__graphql_current_dataloader_source])
|
|
@@ -29,16 +32,17 @@ module GraphQL
|
|
|
29
32
|
run = task.graphql_async_dataloader_run
|
|
30
33
|
trace = run.trace
|
|
31
34
|
trace&.dataloader_fiber_yield(source)
|
|
32
|
-
run.
|
|
35
|
+
run.tasks_channel.push([:paused_task, task])
|
|
33
36
|
condition = task.graphql_async_dataloader_condition
|
|
34
37
|
condition.wait
|
|
35
|
-
run.
|
|
38
|
+
run.tasks_channel.push([:resumed_task, task])
|
|
36
39
|
trace&.dataloader_fiber_resume(source)
|
|
37
40
|
nil
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
class Run
|
|
41
|
-
def initialize(total_fiber_limit, jobs_fiber_limit)
|
|
44
|
+
def initialize(dataloader, total_fiber_limit, jobs_fiber_limit)
|
|
45
|
+
@dataloader = dataloader
|
|
42
46
|
@root_task = nil
|
|
43
47
|
@trace = nil
|
|
44
48
|
@jobs = []
|
|
@@ -47,12 +51,10 @@ module GraphQL
|
|
|
47
51
|
@jobs_fiber_limit = jobs_fiber_limit
|
|
48
52
|
@lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
49
53
|
|
|
50
|
-
@
|
|
51
|
-
@
|
|
52
|
-
@
|
|
53
|
-
@finished_count_task = nil
|
|
54
|
+
@running_tasks = nil
|
|
55
|
+
@tasks_channel = nil
|
|
56
|
+
@tasks_channel_task = nil
|
|
54
57
|
@finished_all_tasks = nil
|
|
55
|
-
@finished_first_pass = nil
|
|
56
58
|
|
|
57
59
|
@snoozed_jobs_condition = Async::Condition.new
|
|
58
60
|
@snoozed_sources_condition = Async::Condition.new
|
|
@@ -60,73 +62,59 @@ module GraphQL
|
|
|
60
62
|
|
|
61
63
|
attr_accessor :trace, :root_task
|
|
62
64
|
|
|
63
|
-
attr_reader :jobs, :lazies_at_depth, :jobs_fiber_limit,
|
|
65
|
+
attr_reader :jobs, :lazies_at_depth, :jobs_fiber_limit, :snoozed_jobs_condition, :snoozed_sources_condition, :tasks_channel
|
|
64
66
|
|
|
65
67
|
def jobs_bandwidth?
|
|
66
|
-
running_count < jobs_fiber_limit
|
|
68
|
+
running_count < @jobs_fiber_limit
|
|
67
69
|
end
|
|
68
70
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
if within_limit < 1
|
|
72
|
-
1
|
|
73
|
-
else
|
|
74
|
-
within_limit
|
|
75
|
-
end
|
|
71
|
+
def sources_bandwidth?
|
|
72
|
+
running_count < current_sources_fiber_limit
|
|
76
73
|
end
|
|
77
74
|
|
|
78
75
|
def close_queues
|
|
79
|
-
@
|
|
80
|
-
@
|
|
81
|
-
|
|
82
|
-
@started_tasks.close
|
|
83
|
-
@started_count_task.cancel
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def running_count
|
|
87
|
-
@snoozed_jobs_condition.instance_variable_get(:@ready).num_waiting +
|
|
88
|
-
@snoozed_sources_condition.instance_variable_get(:@ready).num_waiting +
|
|
89
|
-
@started_count +
|
|
90
|
-
@started_tasks.size -
|
|
91
|
-
@finished_count
|
|
76
|
+
@tasks_channel.close
|
|
77
|
+
@tasks_channel_task.cancel
|
|
92
78
|
end
|
|
93
79
|
|
|
94
80
|
def wait_for_queues
|
|
95
|
-
if !@finished_first_pass.resolved?
|
|
96
|
-
@finished_first_pass.resolve(true)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
81
|
@finished_all_tasks.wait
|
|
100
82
|
@finished_all_tasks = Async::Promise.new
|
|
101
83
|
end
|
|
102
84
|
|
|
103
|
-
def
|
|
104
|
-
@
|
|
105
|
-
@
|
|
106
|
-
|
|
107
|
-
@started_count = 0
|
|
108
|
-
@finished_first_pass = Async::Promise.new
|
|
109
|
-
@finished_all_tasks = Async::Promise.new
|
|
110
|
-
|
|
111
|
-
@started_count_task = @root_task.async do |task|
|
|
112
|
-
@finished_first_pass.wait
|
|
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
|
|
119
|
-
end
|
|
85
|
+
def wait_for_no_running_tasks
|
|
86
|
+
@no_running_tasks.wait
|
|
87
|
+
@no_running_tasks = Async::Promise.new
|
|
88
|
+
end
|
|
120
89
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
90
|
+
def new_queues(mode)
|
|
91
|
+
@tasks_channel = Async::Queue.new(parent: @root_task)
|
|
92
|
+
@no_running_tasks = Async::Promise.new
|
|
93
|
+
@finished_all_tasks = Async::Promise.new
|
|
94
|
+
@running_tasks = []
|
|
95
|
+
@tasks_channel_task = @root_task.async do |_t|
|
|
96
|
+
while ((msg, data) = @tasks_channel.wait)
|
|
97
|
+
case msg
|
|
98
|
+
when :started_task
|
|
99
|
+
@running_tasks.push(data)
|
|
100
|
+
data.run
|
|
101
|
+
when :resumed_task
|
|
102
|
+
@running_tasks.push(data)
|
|
103
|
+
when :finished_task, :paused_task
|
|
104
|
+
@running_tasks.delete(data)
|
|
105
|
+
has_pending_work = mode == :jobs ? @jobs.any? : @dataloader.pending_sources.any?(&:pending?) # rubocop:disable Development/NoneWithoutBlockCop
|
|
106
|
+
if @running_tasks.empty?
|
|
107
|
+
@no_running_tasks.resolve(true)
|
|
108
|
+
has_bandwidth = mode == :jobs ? jobs_bandwidth? : sources_bandwidth?
|
|
109
|
+
if (!has_pending_work) || (!has_bandwidth)
|
|
110
|
+
@finished_all_tasks.resolve(true)
|
|
111
|
+
end
|
|
129
112
|
end
|
|
113
|
+
when :task_error
|
|
114
|
+
@no_running_tasks.resolve(true)
|
|
115
|
+
@finished_all_tasks.reject(data)
|
|
116
|
+
else
|
|
117
|
+
raise ArgumentError, "Unknown tasks_channel action: #{msg.inspect}"
|
|
130
118
|
end
|
|
131
119
|
end
|
|
132
120
|
end
|
|
@@ -135,6 +123,23 @@ module GraphQL
|
|
|
135
123
|
def running?
|
|
136
124
|
@snoozed_jobs_condition.waiting? || @snoozed_sources_condition.waiting?
|
|
137
125
|
end
|
|
126
|
+
|
|
127
|
+
def current_sources_fiber_limit
|
|
128
|
+
within_limit = @total_fiber_limit - running_count
|
|
129
|
+
if within_limit < 1
|
|
130
|
+
1
|
|
131
|
+
else
|
|
132
|
+
within_limit
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def running_count
|
|
139
|
+
@snoozed_jobs_condition.instance_variable_get(:@ready).num_waiting +
|
|
140
|
+
@snoozed_sources_condition.instance_variable_get(:@ready).num_waiting +
|
|
141
|
+
(@running_tasks&.size || 0)
|
|
142
|
+
end
|
|
138
143
|
end
|
|
139
144
|
|
|
140
145
|
def append_job(callable = nil, &block)
|
|
@@ -177,9 +182,10 @@ module GraphQL
|
|
|
177
182
|
end
|
|
178
183
|
prev_pending_keys.each do |source_instance, pending|
|
|
179
184
|
pending.each do |key, value|
|
|
180
|
-
if
|
|
181
|
-
|
|
182
|
-
|
|
185
|
+
next if source_instance.results.key?(key)
|
|
186
|
+
|
|
187
|
+
queue_pending_source(source_instance) if source_instance.pending.empty?
|
|
188
|
+
source_instance.pending[key] = value
|
|
183
189
|
end
|
|
184
190
|
end
|
|
185
191
|
end
|
|
@@ -203,13 +209,14 @@ module GraphQL
|
|
|
203
209
|
|
|
204
210
|
while first_pass || run.running? || !jobs.empty?
|
|
205
211
|
first_pass = false
|
|
206
|
-
|
|
207
|
-
|
|
212
|
+
run_queue(run, run.snoozed_jobs_condition, :jobs)
|
|
213
|
+
run_queue(run, run.snoozed_sources_condition, :sources)
|
|
208
214
|
|
|
209
215
|
if !run.lazies_at_depth.empty?
|
|
210
216
|
with_trace_query_lazy(trace_query_lazy) do
|
|
211
|
-
|
|
212
|
-
|
|
217
|
+
if enqueue_next_pending_lazies(run.lazies_at_depth)
|
|
218
|
+
run_queue(run, run.snoozed_jobs_condition, :jobs)
|
|
219
|
+
end
|
|
213
220
|
end
|
|
214
221
|
end
|
|
215
222
|
end
|
|
@@ -232,129 +239,78 @@ module GraphQL
|
|
|
232
239
|
|
|
233
240
|
private
|
|
234
241
|
|
|
235
|
-
def
|
|
236
|
-
|
|
242
|
+
def run_queue(run, condition, mode)
|
|
243
|
+
should_wait_for_all_tasks = false
|
|
237
244
|
|
|
238
|
-
if (unsnoozed =
|
|
239
|
-
|
|
245
|
+
if (unsnoozed = condition.waiting?)
|
|
246
|
+
should_wait_for_all_tasks = true
|
|
247
|
+
run.new_queues(mode)
|
|
248
|
+
condition.signal
|
|
240
249
|
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
250
|
|
|
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
|
|
251
|
+
while (pending_work = (mode == :jobs) ? (!run.jobs.empty? && run.jobs_bandwidth? ? run.jobs : nil) : (drain_pending_sources)) || unsnoozed
|
|
252
|
+
unsnoozed = false
|
|
253
|
+
if pending_work
|
|
254
|
+
if should_wait_for_all_tasks == false
|
|
255
|
+
should_wait_for_all_tasks = true
|
|
256
|
+
run.new_queues(mode)
|
|
264
257
|
end
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
258
|
+
num_tasks = mode == :sources ? run.current_sources_fiber_limit : 1
|
|
259
|
+
if num_tasks > pending_work.size
|
|
260
|
+
num_tasks = pending_work.size
|
|
261
|
+
end
|
|
262
|
+
spawn_tasks(run, mode, condition, pending_work, num_tasks)
|
|
269
263
|
end
|
|
270
|
-
run.started_tasks.push(new_task)
|
|
271
|
-
new_task
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
264
|
|
|
275
|
-
|
|
276
|
-
run.new_queues
|
|
277
|
-
|
|
278
|
-
if (unsnoozed = run.snoozed_sources_condition.waiting?)
|
|
279
|
-
run.snoozed_sources_condition.signal
|
|
265
|
+
run.wait_for_no_running_tasks
|
|
280
266
|
end
|
|
281
267
|
|
|
282
|
-
|
|
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)
|
|
287
|
-
end
|
|
268
|
+
if should_wait_for_all_tasks
|
|
288
269
|
run.wait_for_queues
|
|
289
270
|
end
|
|
290
271
|
ensure
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
#### TODO DRY Had to duplicate to remove spawn_job_fiber
|
|
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
|
|
303
|
-
|
|
304
|
-
if smallest_depth
|
|
305
|
-
lazies = run.lazies_at_depth.delete(smallest_depth)
|
|
306
|
-
if !lazies.empty?
|
|
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
|
|
316
|
-
end
|
|
317
|
-
end
|
|
272
|
+
if should_wait_for_all_tasks
|
|
273
|
+
run.close_queues
|
|
318
274
|
end
|
|
319
275
|
end
|
|
320
276
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
task.graphql_async_dataloader_condition = run.snoozed_sources_condition
|
|
342
|
-
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
343
|
-
set_fiber_variables(fiber_vars)
|
|
344
|
-
while (source = pending_sources.shift)
|
|
277
|
+
# Use a separate method for this so that the outer loop's reassignment of `pending_work`
|
|
278
|
+
# doesn't affect already-running tasks which (would) close over that variable
|
|
279
|
+
def spawn_tasks(run, mode, condition, pending_work, num_tasks)
|
|
280
|
+
fiber_vars = get_fiber_variables
|
|
281
|
+
trace = run.trace
|
|
282
|
+
num_tasks.times do
|
|
283
|
+
new_task = Async::Task.new(run.root_task) do |task|
|
|
284
|
+
task.graphql_async_dataloader_run = run
|
|
285
|
+
task.graphql_async_dataloader_condition = condition
|
|
286
|
+
set_fiber_variables(fiber_vars)
|
|
287
|
+
case mode
|
|
288
|
+
when :jobs
|
|
289
|
+
trace&.dataloader_spawn_execution_fiber(pending_work)
|
|
290
|
+
while job = pending_work.shift
|
|
291
|
+
job.call
|
|
292
|
+
end
|
|
293
|
+
when :sources
|
|
294
|
+
trace&.dataloader_spawn_source_fiber(pending_work)
|
|
295
|
+
while (source = pending_work.shift)
|
|
296
|
+
Fiber[:__graphql_current_dataloader_source] = source
|
|
345
297
|
trace&.begin_dataloader_source(source)
|
|
346
298
|
source.run_pending_keys
|
|
347
299
|
trace&.end_dataloader_source(source)
|
|
348
300
|
end
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
run.finished_tasks.push($! || task)
|
|
352
|
-
cleanup_fiber
|
|
353
|
-
trace&.dataloader_fiber_exit
|
|
301
|
+
else
|
|
302
|
+
raise ArgumentError, "Unknown mode: #{mode.inspect}"
|
|
354
303
|
end
|
|
355
|
-
|
|
356
|
-
|
|
304
|
+
nil
|
|
305
|
+
rescue StandardError => err
|
|
306
|
+
run.tasks_channel.push([:task_error, err])
|
|
307
|
+
else
|
|
308
|
+
run.tasks_channel.push([:finished_task, task])
|
|
309
|
+
ensure
|
|
310
|
+
cleanup_fiber
|
|
311
|
+
trace&.dataloader_fiber_exit
|
|
357
312
|
end
|
|
313
|
+
run.tasks_channel.push([:started_task, new_task])
|
|
358
314
|
end
|
|
359
315
|
end
|
|
360
316
|
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
|
|
@@ -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.6
|
|
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-21 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
|