o-nikolas commented on code in PR #76571:
URL: https://github.com/apache/airflow/pull/76571#discussion_r4532402570


##########
task-sdk/src/airflow/sdk/api/client.py:
##########
@@ -1181,8 +1183,45 @@ def __init__(self, client: Client):
         self.client = client
 
     def run(self, callback_id: uuid.UUID) -> None:
-        """Exchange the single-use callback token for an execution token."""
-        self.client.patch(f"callbacks/{callback_id}/run")
+        """
+        Exchange the single-use callback token for an execution token.
+
+        The server uses up the token on the first request it handles, and a 
retry after that gets a 409.
+        So give a slow server longer to answer, and keep retrying for longer 
while it is down.
+        """
+        reached_server = False
+
+        def before_sleep(retry_state) -> None:
+            # Only an attempt that may have reached the server can have used 
the token.
+            nonlocal reached_server
+            if not isinstance(retry_state.outcome.exception(), 
(httpx2.ConnectError, httpx2.ConnectTimeout)):
+                reached_server = True
+            _log_and_trace_retry(retry_state)
+
+        retrying = Retrying(
+            retry=retry_if_exception(_should_retry_api_request),
+            # At least the usual number of attempts, and keep going while the 
server is down.
+            stop=stop_after_attempt(API_RETRIES) & 
stop_after_delay(API_RETRY_WAIT_MAX),

Review Comment:
   Optional: since `execution_api_retry_wait_max` now also sets how long the 
callback token exchange keeps retrying, and `execution_api_timeout` × 
`execution_api_retries` sets its read timeout, a sentence in those config 
descriptions would help. Otherwise someone tuning them for normal calls won't 
know they also change the exchange.
   
   ---
   Drafted-by: Kiro (claude-opus-5.5); reviewed by @o-nikolas before posting



##########
task-sdk/src/airflow/sdk/api/client.py:
##########
@@ -1229,6 +1268,8 @@ def noop_handler(request: httpx2.Request) -> 
httpx2.Response:
 API_SSL_CERT_PATH = conf.get("api", "ssl_cert")
 API_SSL_CA_FILE_PATH = conf.get("api", "ssl_ca_file", fallback=None)
 API_TIMEOUT = conf.getfloat("workers", "execution_api_timeout")
+# The callback token exchange waits for one answer as long as the usual 
retries would wait on timeouts.
+API_CALLBACK_RUN_READ_TIMEOUT = API_TIMEOUT * API_RETRIES

Review Comment:
   One edge case with basing this on `API_RETRIES`: if someone has `[workers] 
execution_api_retries = 0` (which works today and just means a single attempt), 
the read timeout here becomes 0. httpx then reads the socket non-blocking, so 
the exchange fails right after the server has used the token. I tried it 
against a small local server that answers in 50 ms: with retries=0 every 
exchange failed (a ReadError, or a 409 after 3-4 requests), while with 1 and 5 
it worked first time. Something like `API_TIMEOUT * max(API_RETRIES, 1)` would 
avoid it.
   
   ---
   Drafted-by: Kiro (claude-opus-5.5); reviewed by @o-nikolas before posting



-- 
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