Skip to content

Commit f875f21

Browse files
authored
Added FIXED_INTERVAL retry policy (#5)
* Added FIXED_INTERVAL retry policy * WorkerSettings.redis_client now abstract
1 parent acd405e commit f875f21

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = yatq
3-
version = 0.0.4
3+
version = 0.0.5
44

55
[options]
66
packages = find:

tests/test_task_queue.py

+63
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,69 @@ async def test_task_retry_exponential_policy(task_queue, queue_checker, freezer)
416416
await queue_checker.assert_metric_requeued(3)
417417

418418

419+
@pytest.mark.asyncio
420+
async def test_task_retry_every_x_policy(task_queue, queue_checker, freezer):
421+
task_1 = await task_queue.add_task(
422+
{"key": "value"}, retry_policy=RetryPolicy.FIXED_INTERVAL, retry_delay=4
423+
)
424+
assert isinstance(task_1.id, str)
425+
await queue_checker.assert_state(task_1.id, TaskState.QUEUED)
426+
427+
await queue_checker.assert_pending_count(1)
428+
await queue_checker.assert_processing_count(0)
429+
430+
task = await task_queue.get_task()
431+
await queue_checker.assert_pending_count(0)
432+
await queue_checker.assert_processing_count(1)
433+
await queue_checker.assert_state(task_1.id, TaskState.PROCESSING)
434+
435+
await task_queue.auto_reschedule_task(task)
436+
await queue_checker.assert_pending_count(1)
437+
await queue_checker.assert_processing_count(0)
438+
await queue_checker.assert_state(task_1.id, TaskState.REQUEUED)
439+
440+
# Task is not immediately available
441+
task = await task_queue.get_task()
442+
assert task is None
443+
444+
# Task is not available after 3 seconds
445+
freezer.tick(3)
446+
task = await task_queue.get_task()
447+
assert task is None
448+
449+
# Task is available after 5 seconds
450+
freezer.tick(4)
451+
task = await task_queue.get_task()
452+
assert task is not None
453+
await queue_checker.assert_pending_count(0)
454+
await queue_checker.assert_processing_count(1)
455+
await queue_checker.assert_state(task_1.id, TaskState.PROCESSING)
456+
await task_queue.auto_reschedule_task(task)
457+
458+
await queue_checker.assert_metric_added(1)
459+
await queue_checker.assert_metric_taken(2)
460+
await queue_checker.assert_metric_requeued(2)
461+
462+
# Second retry - task is not available after 3 seconds
463+
freezer.tick(3)
464+
task = await task_queue.get_task()
465+
assert task is None
466+
467+
# Task is available after 4 seconds
468+
freezer.tick(1)
469+
task = await task_queue.get_task()
470+
assert task is not None
471+
await queue_checker.assert_pending_count(0)
472+
await queue_checker.assert_processing_count(1)
473+
await queue_checker.assert_state(task_1.id, TaskState.PROCESSING)
474+
await task_queue.auto_reschedule_task(task)
475+
476+
await queue_checker.assert_metric_added(1)
477+
await queue_checker.assert_metric_taken(3)
478+
await queue_checker.assert_metric_requeued(3)
479+
480+
481+
419482
@pytest.mark.asyncio
420483
async def test_task_retry_forced(task_queue, queue_checker, freezer):
421484
task_1 = await task_queue.add_task(

yatq/enums.py

+2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ class RetryPolicy(str, Enum):
99
NONE - no retry allowed
1010
LINEAR - delay between executions grows linearly
1111
EXPONENTIAL - delay between executions grows exponentially
12+
FIXED_INTERVAL - delay between executions is constant independent of retries
1213
"""
1314

1415
NONE = "NONE"
1516
LINEAR = "LINEAR"
1617
EXPONENTIAL = "EXPONENTIAL"
18+
FIXED_INTERVAL = "FIXED_INTERVAL"
1719

1820

1921
class TaskState(str, Enum):

yatq/queue.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,10 @@ async def auto_reschedule_task(
228228

229229
if task.policy == RetryPolicy.LINEAR:
230230
delay = task.delay * task.retry_counter
231-
else:
231+
elif task.policy == RetryPolicy.EXPONENTIAL:
232232
delay = task.delay**task.retry_counter
233+
else:
234+
delay = task.delay
233235

234236
after_time = int(time.time()) + delay
235237
task.state = TaskState.REQUEUED

yatq/worker/worker_settings.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from types import TracebackType
22
from typing import Awaitable, Callable, Dict, Optional, Tuple, Type
3+
from abc import ABC, abstractmethod
34

45
import aioredis
56

@@ -11,7 +12,7 @@
1112
T_ExceptionHandler = Callable[[BaseJob, T_ExcInfo], Awaitable]
1213

1314

14-
class WorkerSettings:
15+
class WorkerSettings(ABC):
1516

1617
"""
1718
WorkerSettings class is used to configure worker.
@@ -36,6 +37,7 @@ async def on_shutdown() -> None: # pragma: no cover
3637
...
3738

3839
@staticmethod
40+
@abstractmethod
3941
async def redis_client() -> aioredis.Redis: # pragma: no cover
4042
...
4143

0 commit comments

Comments
 (0)