@@ -22,6 +22,8 @@ defmodule Oban.Queue.Producer do
2222 @ enforce_keys [ :conf , :foreman , :limit , :nonce , :queue ]
2323 defstruct [
2424 :conf ,
25+ :cooldown_ref ,
26+ :dispatched_at ,
2527 :foreman ,
2628 :limit ,
2729 :name ,
@@ -102,29 +104,14 @@ defmodule Oban.Queue.Producer do
102104 end
103105
104106 @ impl GenServer
105- def handle_info ( :poll , % State { conf: conf } = state ) do
106- conf . repo . checkout ( fn ->
107- state
108- |> deschedule ( )
109- |> pulse ( )
110- |> send_poll_after ( )
111- |> dispatch ( )
112- end )
113- end
114-
115- def handle_info ( :rescue , % State { conf: conf } = state ) do
116- conf . repo . checkout ( fn ->
117- state
118- |> rescue_orphans ( )
119- |> send_rescue_after ( )
120- |> dispatch ( )
121- end )
122- end
123-
124107 def handle_info ( { :DOWN , ref , :process , _pid , _reason } , % State { running: running } = state ) do
125108 dispatch ( % { state | running: Map . delete ( running , ref ) } )
126109 end
127110
111+ def handle_info ( :dispatch , % State { } = state ) do
112+ dispatch ( state )
113+ end
114+
128115 def handle_info ( { :notification , insert ( ) , payload } , % State { queue: queue } = state ) do
129116 case payload do
130117 % { "queue" => ^ queue } -> dispatch ( state )
@@ -162,6 +149,25 @@ defmodule Oban.Queue.Producer do
162149 dispatch ( state )
163150 end
164151
152+ def handle_info ( :poll , % State { conf: conf } = state ) do
153+ conf . repo . checkout ( fn ->
154+ state
155+ |> deschedule ( )
156+ |> pulse ( )
157+ |> send_poll_after ( )
158+ |> dispatch ( )
159+ end )
160+ end
161+
162+ def handle_info ( :rescue , % State { conf: conf } = state ) do
163+ conf . repo . checkout ( fn ->
164+ state
165+ |> rescue_orphans ( )
166+ |> send_rescue_after ( )
167+ |> dispatch ( )
168+ end )
169+ end
170+
165171 def handle_info ( :reset_circuit , state ) do
166172 { :noreply , open_circuit ( state ) }
167173 end
@@ -230,12 +236,14 @@ defmodule Oban.Queue.Producer do
230236 state
231237 end
232238
233- defp pulse ( % State { conf: conf } = state ) do
239+ defp pulse ( % State { conf: conf , running: running } = state ) do
240+ running_ids = for { _ref , { % _ { id: id } , _pid } } <- running , do: id
241+
234242 args =
235243 state
236244 |> Map . take ( [ :limit , :nonce , :paused , :queue , :started_at ] )
237245 |> Map . put ( :node , conf . node )
238- |> Map . put ( :running , running_job_ids ( state ) )
246+ |> Map . put ( :running , running_ids )
239247
240248 Query . insert_beat ( conf , args )
241249
@@ -256,29 +264,56 @@ defmodule Oban.Queue.Producer do
256264 { :noreply , state }
257265 end
258266
259- defp dispatch ( % State { conf: conf , foreman: foreman } = state ) do
260- % State { limit: limit , nonce: nonce , queue: queue , running: running } = state
267+ defp dispatch ( % State { } = state ) do
268+ cond do
269+ dispatch_now? ( state ) ->
270+ % State { conf: conf , limit: limit , nonce: nonce , queue: queue , running: running } = state
261271
262- started_jobs =
263- for job <- fetch_jobs ( conf , queue , nonce , limit - map_size ( running ) ) , into: % { } do
264- { :ok , pid } = DynamicSupervisor . start_child ( foreman , Executor . child_spec ( job , conf ) )
272+ running =
273+ conf
274+ |> fetch_jobs ( queue , nonce , limit - map_size ( running ) )
275+ |> start_jobs ( state )
276+ |> Map . merge ( running )
265277
266- { Process . monitor ( pid ) , { job , pid } }
267- end
278+ { :noreply , % { state | cooldown_ref: nil , dispatched_at: system_now ( ) , running: running } }
279+
280+ cooldown_available? ( state ) ->
281+ % State { conf: conf , dispatched_at: dispatched_at } = state
282+
283+ dispatch_after = system_now ( ) - dispatched_at + conf . dispatch_cooldown
284+ cooldown_ref = Process . send_after ( self ( ) , :dispatch , dispatch_after )
268285
269- { :noreply , % { state | running: Map . merge ( running , started_jobs ) } }
286+ { :noreply , % { state | cooldown_ref: cooldown_ref } }
287+
288+ true ->
289+ { :noreply , state }
290+ end
270291 rescue
271292 exception in trip_errors ( ) -> { :noreply , trip_circuit ( exception , state ) }
272293 end
273294
295+ defp dispatch_now? ( % State { dispatched_at: nil } ) , do: true
296+
297+ defp dispatch_now? ( % State { conf: conf , dispatched_at: dispatched_at } ) do
298+ system_now ( ) > dispatched_at + conf . dispatch_cooldown
299+ end
300+
301+ defp cooldown_available? ( % State { cooldown_ref: ref } ) , do: is_nil ( ref )
302+
274303 defp fetch_jobs ( conf , queue , nonce , count ) do
275304 case Query . fetch_available_jobs ( conf , queue , nonce , count ) do
276305 { 0 , nil } -> [ ]
277306 { _count , jobs } -> jobs
278307 end
279308 end
280309
281- defp running_job_ids ( % State { running: running } ) do
282- for { _ref , { % _ { id: id } , _pid } } <- running , do: id
310+ defp start_jobs ( jobs , % State { conf: conf , foreman: foreman } ) do
311+ for job <- jobs , into: % { } do
312+ { :ok , pid } = DynamicSupervisor . start_child ( foreman , Executor . child_spec ( job , conf ) )
313+
314+ { Process . monitor ( pid ) , { job , pid } }
315+ end
283316 end
317+
318+ defp system_now , do: System . monotonic_time ( :millisecond )
284319end
0 commit comments