kaxil opened a new pull request, #73508:
URL: https://github.com/apache/airflow/pull/73508

   Follow-up to AIP-105 (#65474). A task's `retry_policy` can now be a sequence 
of policies.
   
   ## Summary
   
   `ChainRetryPolicy([...])` consults policies in order with the original task 
exception. The first RETRY or FAIL wins. A DEFAULT means the policy has nothing 
to add and the next one is asked; when every policy returns DEFAULT the task's 
own `retries` and `retry_delay` apply, as they do today.
   
   Two orders become expressible that were not:
   
   - **Rules first.** Known failures are settled by an `ExceptionRetryPolicy` 
before a slower or costlier policy (one that calls a model, for instance) is 
consulted, so it never sees them.
   - **A floor behind a fallible policy.** A policy that can fail on its own 
gets deterministic rules after it, without carrying its own copy of a fallback 
mechanism.
   
   ```python
   from airflow.sdk import ChainRetryPolicy, ExceptionRetryPolicy, RetryAction, 
RetryRule
   
   retry_policy = ChainRetryPolicy([
       ExceptionRetryPolicy(rules=[RetryRule(exception=PermissionError, 
action=RetryAction.FAIL)]),
       HTTPStatusRetryPolicy(),  # any RetryPolicy, from any package
       ExceptionRetryPolicy(rules=[RetryRule(exception=ConnectionError, 
retry_delay=timedelta(seconds=30))]),
   ])
   ```
   
   ## Design rationale
   
   **DEFAULT means "next policy", not "stop".** The SDK documents 
`RetryAction.DEFAULT` as "fall through to standard retry logic". Inside a chain 
that is what you get once the chain is exhausted, so DEFAULT is the natural 
abstention signal and no new enum value or wrapper type is needed. Two 
consequences are documented: a `RetryRule` with `action=DEFAULT` passes control 
on rather than ending the chain, and an `ExceptionRetryPolicy` with 
`default=RetryAction.FAIL` ends the chain wherever it sits.
   
   **A broken policy does not take the floor down with it.** An ordinary 
exception from a policy, or a return value that is not a `RetryDecision`, is 
logged and treated as DEFAULT, so the rules after it still run. `BaseException` 
propagates, so a cancelled or terminated task behaves as it does now.
   
   **The reason survives truncation.** The winning decision's reason names the 
deciding policy first and the earlier verdicts after it: 
`HTTPStatusRetryPolicy: HTTP 404 (after ExceptionRetryPolicy: no decision)`. 
The worker stores `retry_reason` truncated to 500 characters, so the verdict 
comes first and the trail is what gets cut. The worker stores the reason only 
on a RETRY; on FAIL and DEFAULT it appears in the task log, and the docs say so.
   
   **Why the SDK.** The class composes `RetryPolicy`, `RetryDecision` and 
`RetryAction`, all SDK types, and a chain of two rule policies or a user's own 
policy followed by rules is a use with no provider involved. Third-party 
`RetryPolicy` subclasses compose without depending on anything else. The 
constructor takes a sequence like `ExceptionRetryPolicy(rules=[...])` rather 
than `*policies`, so a chain built from configuration is a plain list. Members 
are validated as `RetryPolicy` instances at construction, which is stricter 
than the operator's duck-typed `retry_policy` argument, so a typo fails at 
parse time rather than at the first task failure.
   
   **No serialization or migration.** `retry_policy` is already a flag-only 
serialized field and the worker re-parses the Dag, so a chain rides along like 
any other policy object. `ChainRetryPolicy` does not implement `serialize()`; 
nothing in Airflow calls it, and the base docstring now says so.
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to