11# frozen_string_literal: true
2+
3+
4+ def ts_puts ( str )
5+ puts "[#{ Time . now . to_f . to_s . ljust ( 18 ) } | #{ Async ::Task . current . object_id } | #{ Fiber . current . object_id } ] #{ str } "
6+ end
27module GraphQL
38 class Dataloader
49 class AsyncDataloader < Dataloader
510 def yield ( source = Fiber [ :__graphql_current_dataloader_source ] )
611 trace = Fiber [ :__graphql_current_multiplex ] &.current_trace
712 trace &.dataloader_fiber_yield ( source )
13+ task = Async ::Task . current
14+ working_jobs = Fiber [ :graphql_dataloader_working_jobs ]
15+ waiting_jobs = Fiber [ :graphql_dataloader_waiting_jobs ]
16+ working_jobs . delete ( task )
17+ waiting_jobs << task
18+ ts_puts "Snoozing #{ working_jobs . size } / #{ @pending_jobs . size } "
19+ if working_jobs . empty?
20+ ts_puts "Yield signalling manager condition"
21+ # TODO This won't properly wait for next_tick
22+ Fiber [ :graphql_dataloader_manager ] . signal
23+ end
24+ ts_puts "Waiting for jobs condition (#{ Fiber [ :graphql_dataloader_next_tick ] . object_id } )"
825 Fiber [ :graphql_dataloader_next_tick ] . wait
26+ ts_puts "Resuming"
927 trace &.dataloader_fiber_resume ( source )
28+ waiting_jobs . delete ( task )
29+ working_jobs << task
1030 nil
1131 end
1232
@@ -18,41 +38,59 @@ def run(trace_query_lazy: nil)
1838 source_tasks = [ ]
1939 next_source_tasks = [ ]
2040 first_pass = true
21-
41+ parent_condition = Async :: Condition . new
2242 sources_condition = Async ::Condition . new
2343 jobs_condition = Async ::Condition . new
2444 trace &.begin_dataloader ( self )
2545 fiber_vars = get_fiber_variables
2646 raised_error = nil
47+ loops = 0
2748 Sync do |root_task |
28- while first_pass || !job_fibers . empty?
49+ while first_pass || !job_fibers . empty? || !next_job_fibers . empty?
50+ ts_puts "WHILE #{ @pending_jobs . size } / #{ job_fibers . map ( &:object_id ) } / #{ next_job_fibers . map ( &:object_id ) } / #{ source_tasks . map ( &:object_id ) } / #{ next_source_tasks . map ( &:object_id ) } "
51+ loops += 1
2952 first_pass = false
3053 set_fiber_variables ( fiber_vars )
31- run_pending_steps ( job_fibers , next_job_fibers , source_tasks , jobs_fiber_limit , trace , root_task , jobs_condition )
32-
33- while !source_tasks . empty? || @source_cache . each_value . any? { |group_sources | group_sources . each_value . any? ( &:pending? ) }
34- while ( task = ( source_tasks . shift || ( ( ( job_fibers . size + next_job_fibers . size + source_tasks . size + next_source_tasks . size ) < total_fiber_limit ) && spawn_source_task ( root_task , sources_condition , trace ) ) ) )
35- if task . alive?
36- root_task . yield
37- next_source_tasks << task
38- else
39- task . wait # re-raise errors
40- end
54+ run_pending_steps ( job_fibers , next_job_fibers , source_tasks , jobs_fiber_limit , trace , root_task , jobs_condition , parent_condition )
55+
56+ if !source_tasks . empty? || @source_cache . each_value . any? { |group_sources | group_sources . each_value . any? ( &:pending? ) }
57+ while ( ( job_fibers . size + next_job_fibers . size + source_tasks . size + next_source_tasks . size ) < total_fiber_limit ) &&
58+ @source_cache . each_value . any? { |group_sources | group_sources . each_value . any? ( &:pending? ) }
59+ ts_puts "source while"
60+ spawn_source_task ( root_task , sources_condition , trace , source_tasks , next_source_tasks , parent_condition )
4161 end
4262
63+ if source_tasks . any?
64+ ts_puts "WAIT for manager"
65+ parent_condition . wait
66+ end
67+ ts_puts "SIGNAL to sources condition"
4368 sources_condition . signal
44- source_tasks . concat ( next_source_tasks )
45- next_source_tasks . clear
69+ ts_puts "END Sources run"
70+ end
71+ if jobs_condition . waiting?
72+ ts_puts "SIGNAL to jobs condition (#{ jobs_condition . object_id } , #{ jobs_condition . waiting? } )"
73+ jobs_condition . signal
74+ ts_puts "WAIT for parent_condition in main loop"
75+ possible_error = parent_condition . wait
76+ if possible_error
77+ raise possible_error
78+ end
4679 end
47- jobs_condition . signal
4880
4981 if !@lazies_at_depth . empty?
5082 with_trace_query_lazy ( trace_query_lazy ) do
51- run_next_pending_lazies ( job_fibers , trace , root_task , jobs_condition )
52- run_pending_steps ( job_fibers , next_job_fibers , source_tasks , jobs_fiber_limit , trace , root_task , jobs_condition )
83+ run_next_pending_lazies ( job_fibers , trace , root_task , jobs_condition , next_job_fibers , parent_condition )
84+ run_pending_steps ( job_fibers , next_job_fibers , source_tasks , jobs_fiber_limit , trace , root_task , jobs_condition , parent_condition )
5385 end
5486 end
87+
88+ ts_puts [ loops :] . inspect
89+ if loops > 10
90+ root_task . cancel
91+ end
5592 end
93+ ts_puts "END Sync { ... }"
5694 rescue StandardError => err
5795 raised_error = err
5896 end
@@ -68,37 +106,64 @@ def run(trace_query_lazy: nil)
68106
69107 private
70108
71- def run_pending_steps ( job_fibers , next_job_fibers , source_tasks , jobs_fiber_limit , trace , parent_task , condition )
72- while ( f = ( job_fibers . shift || ( ( ( job_fibers . size + next_job_fibers . size + source_tasks . size ) < jobs_fiber_limit ) && spawn_job_task ( trace , parent_task , condition ) ) ) )
73- if f . alive?
74- parent_task . yield
75- next_job_fibers << f
76- else
77- f . wait # re-raise errors
109+ def run_pending_steps ( job_fibers , next_job_fibers , source_tasks , jobs_fiber_limit , trace , parent_task , condition , parent_condition )
110+ ts_puts "Run pending steps task"
111+ # while (f = (job_fibers.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size) < jobs_fiber_limit) && spawn_job_task(trace, parent_task, condition, job_fibers, next_job_fibers))))
112+ while ( ( job_fibers . size + next_job_fibers . size + source_tasks . size ) < jobs_fiber_limit ) && !@pending_jobs . empty?
113+ ts_puts "WHILE Spawning job tasks (#{ @pending_jobs . size } jobs)"
114+ spawn_job_task ( trace , parent_task , condition , job_fibers , next_job_fibers , parent_condition )
115+ end
116+ if !job_fibers . empty?
117+ ts_puts "Waiting for manager condition"
118+ possible_err = parent_condition . wait
119+ ts_puts "possible_err: #{ possible_err } "
120+ if possible_err
121+ raise possible_err
78122 end
79123 end
80- job_fibers . concat ( next_job_fibers )
81- next_job_fibers . clear
124+ ts_puts "Finished run_pending_steps task"
82125 end
83126
84- def spawn_job_task ( trace , parent_task , condition )
127+ def spawn_job_task ( trace , parent_task , condition , job_fibers , next_job_fibers , parent_condition , prepend = false )
85128 if !@pending_jobs . empty?
86129 fiber_vars = get_fiber_variables
87- parent_task . async do
130+ new_task = parent_task . async do |task |
131+ ts_puts "New jobs task"
88132 trace &.dataloader_spawn_execution_fiber ( @pending_jobs )
133+ Fiber [ :graphql_dataloader_working_jobs ] = job_fibers
134+ Fiber [ :graphql_dataloader_waiting_jobs ] = next_job_fibers
89135 Fiber [ :graphql_dataloader_next_tick ] = condition
136+ Fiber [ :graphql_dataloader_manager ] = parent_condition
90137 set_fiber_variables ( fiber_vars )
138+ if prepend
139+ job_fibers . unshift ( task )
140+ else
141+ job_fibers . push ( task )
142+ end
91143 while job = @pending_jobs . shift
144+ ts_puts "Dequeued #{ job . class } ##{ job . object_id } "
92145 job . call
146+ ts_puts "Finished job #{ job . class } ##{ job . object_id } "
93147 end
148+ ensure
94149 cleanup_fiber
150+ ts_puts "END JOBS TASK, #{ $!} "
151+ job_fibers . delete ( task )
152+ if job_fibers . empty? && @pending_jobs . empty?
153+ ts_puts "Signal parent condition from spawn_job_task"
154+ parent_condition . signal ( $!)
155+ end
95156 trace &.dataloader_fiber_exit
96157 end
158+ if !new_task . alive?
159+ new_task . wait # raise the error
160+ end
97161 end
98162 end
99163
100164 #### TODO DRY Had to duplicate to remove spawn_job_fiber
101- def run_next_pending_lazies ( job_fibers , trace , parent_task , condition )
165+ def run_next_pending_lazies ( job_fibers , trace , parent_task , condition , next_job_fibers , parent_condition )
166+ ts_puts "run_next_pending_lazies"
102167 smallest_depth = nil
103168 @lazies_at_depth . each_key do |depth_key |
104169 smallest_depth ||= depth_key
@@ -113,12 +178,12 @@ def run_next_pending_lazies(job_fibers, trace, parent_task, condition)
113178 lazies . each_with_index do |l , idx |
114179 append_job { l . value }
115180 end
116- job_fibers . unshift ( spawn_job_task ( trace , parent_task , condition ) )
181+ spawn_job_task ( trace , parent_task , condition , job_fibers , next_job_fibers , parent_condition , true )
117182 end
118183 end
119184 end
120185
121- def spawn_source_task ( parent_task , condition , trace )
186+ def spawn_source_task ( parent_task , condition , trace , source_tasks , next_source_tasks , parent_condition )
122187 pending_sources = nil
123188 @source_cache . each_value do |source_by_batch_params |
124189 source_by_batch_params . each_value do |source |
@@ -131,22 +196,36 @@ def spawn_source_task(parent_task, condition, trace)
131196
132197 if pending_sources
133198 fiber_vars = get_fiber_variables
134- parent_task . async do
199+ new_task = parent_task . async do |task |
200+ ts_puts "Starting sources task"
201+ source_tasks << task
202+ Fiber [ :graphql_dataloader_working_jobs ] = source_tasks
203+ Fiber [ :graphql_dataloader_waiting_jobs ] = next_source_tasks
135204 trace &.dataloader_spawn_source_fiber ( pending_sources )
136205 set_fiber_variables ( fiber_vars )
137206 Fiber [ :graphql_dataloader_next_tick ] = condition
207+ Fiber [ :graphql_dataloader_manager ] = parent_condition
138208 pending_sources . each do |s |
209+ ts_puts "Running #{ s . class } "
139210 trace &.begin_dataloader_source ( s )
140211 s . run_pending_keys
141212 trace &.end_dataloader_source ( s )
213+ ts_puts "Finished #{ s . class } "
142214 end
143215 nil
144- rescue StandardError => err
145- err
146216 ensure
217+ ts_puts "Ending sources task"
218+ source_tasks . delete ( task )
219+ if source_tasks . empty?
220+ ts_puts "Signaling parent condition from spawn_source_task"
221+ parent_condition . signal
222+ end
147223 cleanup_fiber
148224 trace &.dataloader_fiber_exit
149225 end
226+ if !new_task . alive?
227+ new_task . wait # raise the error
228+ end
150229 end
151230 end
152231 end
0 commit comments