@@ -109,7 +109,7 @@ def perform
109109 end
110110 end
111111
112- it 'keeps the polling interval within the bounds' do
112+ it 'keeps the polling interval within the default bounds' do
113113 job = FakeJob . new
114114 job . polling_interval_seconds = 5
115115 expect ( job . polling_interval_seconds ) . to eq ( 60 )
@@ -118,11 +118,25 @@ def perform
118118 expect ( job . polling_interval_seconds ) . to eq ( 24 . hours )
119119 end
120120
121- context 'exponential backoff rate' do
122- context 'updates the polling interval' do
123- it 'when changing exponential backoff rate only' do
121+ context 'when maximum polling interval is configured' do
122+ before do
123+ TestConfig . config [ :broker_client_max_async_poll_interval_seconds ] = 1800
124+ end
125+
126+ it 'limits the polling interval to the configured maximum' do
127+ job = FakeJob . new
128+ job . polling_interval_seconds = 10 . days
129+ expect ( job . polling_interval_seconds ) . to eq ( 1800 )
130+ end
131+ end
132+
133+ describe 'exponential backoff rate' do
134+ context 'when changing exponential backoff rate only' do
135+ before do
124136 TestConfig . config [ :broker_client_async_poll_exponential_backoff_rate ] = 2.0
137+ end
125138
139+ it 'updates the polling interval' do
126140 enqueued_time = 0
127141
128142 Timecop . freeze do
@@ -141,11 +155,15 @@ def perform
141155 end
142156 end
143157 end
158+ end
144159
145- it 'when changing exponential backoff rate and default polling interval' do
160+ context 'when changing exponential backoff rate and default polling interval' do
161+ before do
146162 TestConfig . config [ :broker_client_async_poll_exponential_backoff_rate ] = 1.3
147163 TestConfig . config [ :broker_client_default_async_poll_interval_seconds ] = 10
164+ end
148165
166+ it 'updates the polling interval' do
149167 enqueued_time = 0
150168
151169 Timecop . freeze do
@@ -164,36 +182,175 @@ def perform
164182 end
165183 end
166184 end
185+ end
167186
168- it 'when changing exponential backoff rate and retry_after from the job' do
169- TestConfig . config [ :broker_client_async_poll_exponential_backoff_rate ] = 1.3
170- TestConfig . config [ :broker_client_default_async_poll_interval_seconds ] = 10
187+ describe ' changing exponential backoff rate and retry_after from the job' do
188+ context 'when retry-after is larger than calculated backoff' do
189+ let ( :fake_job ) { FakeJob . new ( retry_after : [ 20 , 30 ] ) }
171190
172- enqueued_time = 0
191+ before do
192+ TestConfig . config [ :broker_client_async_poll_exponential_backoff_rate ] = 1.3
193+ TestConfig . config [ :broker_client_default_async_poll_interval_seconds ] = 10
194+ end
173195
174- Timecop . freeze do
175- Jobs ::Enqueuer . new ( queue : Jobs ::Queues . generic ) . enqueue_pollable ( FakeJob . new ( retry_after : [ 20 , 30 ] ) )
176- execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
177- enqueued_time = Time . now
196+ it 'uses retry-after interval' do
197+ enqueued_time = 0
198+
199+ Timecop . freeze do
200+ Jobs ::Enqueuer . new ( queue : Jobs ::Queues . generic ) . enqueue_pollable ( fake_job )
201+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
202+ enqueued_time = Time . now
203+ end
204+
205+ # the job should run after 20s (20s > 10 * 1.3^0)
206+ Timecop . freeze ( 19 . seconds . after ( enqueued_time ) ) do
207+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
208+ end
209+
210+ Timecop . freeze ( 21 . seconds . after ( enqueued_time ) ) do
211+ enqueued_time = Time . now
212+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
213+ end
214+
215+ # the job should run after 30s (30s > 10 * 1.3^1)
216+ Timecop . freeze ( 29 . seconds . after ( enqueued_time ) ) do
217+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
218+ end
219+
220+ Timecop . freeze ( 31 . seconds . after ( enqueued_time ) ) do
221+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
222+ end
178223 end
224+ end
179225
180- # the job should run after 20s * 1.3^0 = 20 seconds
181- Timecop . freeze ( 19 . seconds . after ( enqueued_time ) ) do
182- execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
226+ context 'when retry-after is smaller than calculated backoff' do
227+ let ( :fake_job ) { FakeJob . new ( retry_after : [ 10 , 20 ] ) }
228+
229+ before do
230+ TestConfig . config [ :broker_client_async_poll_exponential_backoff_rate ] = 1.3
231+ TestConfig . config [ :broker_client_default_async_poll_interval_seconds ] = 30
183232 end
184233
185- Timecop . freeze ( 21 . seconds . after ( enqueued_time ) ) do
186- enqueued_time = Time . now
187- execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
234+ it 'uses calculated interval' do
235+ enqueued_time = 0
236+
237+ Timecop . freeze do
238+ Jobs ::Enqueuer . new ( queue : Jobs ::Queues . generic ) . enqueue_pollable ( fake_job )
239+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
240+ enqueued_time = Time . now
241+ end
242+
243+ # the job should run after 30s (30s > 10s)
244+ Timecop . freeze ( 29 . seconds . after ( enqueued_time ) ) do
245+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
246+ end
247+
248+ Timecop . freeze ( 31 . seconds . after ( enqueued_time ) ) do
249+ enqueued_time = Time . now
250+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
251+ end
252+
253+ # the job should run after 30s (30s * 1.3^1 = 39 > 20s)
254+ Timecop . freeze ( 38 . seconds . after ( enqueued_time ) ) do
255+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
256+ end
257+
258+ Timecop . freeze ( 40 . seconds . after ( enqueued_time ) ) do
259+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
260+ end
188261 end
262+ end
263+
264+ context 'when calculated backoff gets larger than retry-after' do
265+ let ( :fake_job ) { FakeJob . new ( retry_after : [ 15 , 15 , 15 ] ) }
189266
190- # the job should run after 30s * 1.3^1 = 39 seconds
191- Timecop . freeze ( 38 . seconds . after ( enqueued_time ) ) do
192- execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
267+ before do
268+ TestConfig . config [ :broker_client_async_poll_exponential_backoff_rate ] = 2
269+ TestConfig . config [ :broker_client_default_async_poll_interval_seconds ] = 5
193270 end
194271
195- Timecop . freeze ( 40 . seconds . after ( enqueued_time ) ) do
196- execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
272+ it 'uses retry-after until calculated backoff is larger' do
273+ enqueued_time = 0
274+
275+ Timecop . freeze do
276+ Jobs ::Enqueuer . new ( queue : Jobs ::Queues . generic ) . enqueue_pollable ( fake_job )
277+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
278+ enqueued_time = Time . now
279+ end
280+
281+ # the job should run after 15s (15s > 5s (5 * 2^0))
282+ Timecop . freeze ( 14 . seconds . after ( enqueued_time ) ) do
283+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
284+ end
285+
286+ Timecop . freeze ( 16 . seconds . after ( enqueued_time ) ) do
287+ enqueued_time = Time . now
288+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
289+ end
290+
291+ # the job should run after 15s (15s > 10s (5 * 2^1))
292+ Timecop . freeze ( 14 . seconds . after ( enqueued_time ) ) do
293+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
294+ end
295+
296+ Timecop . freeze ( 16 . seconds . after ( enqueued_time ) ) do
297+ enqueued_time = Time . now
298+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
299+ end
300+
301+ # the job should run after 20s (20s > 15s (5 * 2^2))
302+ Timecop . freeze ( 19 . seconds . after ( enqueued_time ) ) do
303+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
304+ end
305+
306+ Timecop . freeze ( 21 . seconds . after ( enqueued_time ) ) do
307+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
308+ end
309+ end
310+
311+ context 'when maximum polling interval is configured' do
312+ before do
313+ TestConfig . config [ :broker_client_max_async_poll_interval_seconds ] = 18
314+ end
315+
316+ it 'limits the polling interval to the configured maximum' do
317+ enqueued_time = 0
318+
319+ Timecop . freeze do
320+ Jobs ::Enqueuer . new ( queue : Jobs ::Queues . generic ) . enqueue_pollable ( fake_job )
321+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
322+ enqueued_time = Time . now
323+ end
324+
325+ # the job should run after 15s (15s > 5s (5 * 2^0))
326+ Timecop . freeze ( 14 . seconds . after ( enqueued_time ) ) do
327+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
328+ end
329+
330+ Timecop . freeze ( 16 . seconds . after ( enqueued_time ) ) do
331+ enqueued_time = Time . now
332+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
333+ end
334+
335+ # the job should run after 15s (15s > 10s (5 * 2^1))
336+ Timecop . freeze ( 14 . seconds . after ( enqueued_time ) ) do
337+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
338+ end
339+
340+ Timecop . freeze ( 16 . seconds . after ( enqueued_time ) ) do
341+ enqueued_time = Time . now
342+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
343+ end
344+
345+ # the job should run after 18s (capped at )
346+ Timecop . freeze ( 17 . seconds . after ( enqueued_time ) ) do
347+ execute_all_jobs ( expected_successes : 0 , expected_failures : 0 )
348+ end
349+
350+ Timecop . freeze ( 19 . seconds . after ( enqueued_time ) ) do
351+ execute_all_jobs ( expected_successes : 1 , expected_failures : 0 )
352+ end
353+ end
197354 end
198355 end
199356 end
0 commit comments