1class DeferJob(Exception):
2 """Signal that a job should be deferred and re-tried later.
3
4 Unlike regular exceptions that indicate errors, DeferJob is used for expected
5 delays like:
6 - Waiting for external resources (API rate limits, data not ready)
7 - Polling for status changes
8 - Temporary unavailability
9
10 Example:
11 # Finite retries - will fail if data never becomes ready
12 if not data.is_ready():
13 raise DeferJob(delay=60, increment_retries=True)
14
15 # Infinite retries - safe for rate limits
16 if rate_limited():
17 raise DeferJob(delay=300, increment_retries=False)
18 """
19
20 def __init__(self, *, delay: int, increment_retries: bool = False):
21 self.delay = delay
22 self.increment_retries = increment_retries
23 super().__init__(f"Job deferred for {delay} seconds")
24
25
26class DeferError(Exception):
27 """Raised when a deferred job cannot be re-enqueued.
28
29 This typically happens when concurrency limits prevent the job from being
30 re-queued. The transaction will be rolled back and the job will remain
31 in its current state, then be converted to ERRORED status for retry.
32 """
33
34 pass