hkc-8010 opened a new issue, #74678:
URL: https://github.com/apache/airflow/issues/74678

   ### Under which category would you file this issue?
   
   Providers
   
   ### Apache Airflow version
   
   3.1.x (also reproduced on main at eee6e2a4a5)
   
   ### What happened and how to reproduce it?
   
   `DbtCloudHook(timeout_seconds=...)` has no effect on the sync API calls. The 
request goes out with `timeout=None`, so if dbt Cloud never answers, the task 
blocks forever on the socket read. Since nothing raises, `retry_limit` never 
kicks in either. We hit this in production with `DbtCloudRunJobOperator`: a 
task sat for about 9 hours on a single `GET /api/v2/accounts/<id>/runs/<id>/` 
before deferring, even though `hook_params={"timeout_seconds": 60, 
"retry_limit": 3}` was set. A py-spy dump of the stuck process showed the value 
being lost between the two `HttpHook` methods:
   
   ```
   run (http/hooks/http.py:419)            extra_options: {"timeout": 60}
   run_and_check (http/hooks/http.py:471)  extra_options: {}
                                           send_kwargs: {"timeout": None, ...}
   _make_request (urllib3)                 read_timeout: None
   ```
   
   Proxies from the dbt Cloud connection extra (`{"proxies": {...}}`) are 
dropped the same way.
   
   Cause: since #51893 (http 5.3.2), `HttpHook.run()` passes 
`self.merged_extra` to `run_and_check()`, and `run_and_check()` reads proxies 
from `session.proxies`. Both are only populated by `HttpHook.get_conn()` 
(through `_configure_session_from_extra`). `DbtCloudHook.get_conn()` overrides 
it without calling super, so `merged_extra` stays `{}` and the session has no 
proxies. `DbtCloudHook` still builds `extra_options={"timeout": ..., "proxies": 
...}` correctly; it is just never used.
   
   The existing tests (`test_timeout_passed_to_run_and_get_response`, 
`test_timeout_passed_to_paginate`, `test_timeout_with_proxies`) mock 
`run_with_advanced_retry`, so they stop before `HttpHook.run` and could not 
catch this.
   
   To reproduce:
   
   ```python
   import json
   from unittest import mock
   
   import requests
   
   from airflow.models.connection import Connection
   from airflow.providers.dbt.cloud.hooks.dbt import DbtCloudHook
   
   conn = Connection(
       conn_id="dbt", conn_type="dbt_cloud", login="1", password="tok",
       extra=json.dumps({"proxies": {"https": "http://proxy.example:3128"}}),
   )
   hook = DbtCloudHook("dbt", timeout_seconds=60)
   hook.__dict__["connection"] = conn
   
   
   def fake_send(self, request, **kwargs):
       print("timeout =", kwargs.get("timeout"), "proxies =", 
kwargs.get("proxies"))
       resp = requests.Response()
       resp.status_code = 200
       resp._content = b'{"data": {}}'
       return resp
   
   
   with mock.patch.object(requests.Session, "send", fake_send):
       hook.get_job_run(run_id=1, account_id=1)
   ```
   
   http 5.3.1: `timeout = 60 proxies = {'https': 'http://proxy.example:3128'}`
   http 5.3.2+ and main: `timeout = None proxies = {}`
   
   Against a TCP server that accepts and never replies, `timeout_seconds=2, 
retry_limit=3` raises `ReadTimeout` after about 9s on http 5.3.1 and hangs 
indefinitely on 5.3.2 / 6.2.0.
   
   ### What you think should happen instead?
   
   `timeout_seconds` and connection `proxies` should reach 
`requests.Session.send`, as they did with http 5.3.1. A timed-out request 
should raise `requests.exceptions.ReadTimeout` and be retried per `retry_limit` 
(`_retryable_error` already treats `Timeout` as retryable).
   
   ### Operating System
   
   Linux (Debian-based Astro Runtime image); repro also runs on macOS
   
   ### Deployment
   
   Astronomer
   
   ### Apache Airflow Provider(s)
   
   dbt-cloud, http
   
   ### Versions of Apache Airflow Providers
   
   apache-airflow-providers-dbt-cloud 4.9.3 (also 4.10.0 and main)
   apache-airflow-providers-http 6.2.0 (any version from 5.3.2 on)
   
   ### Anything else?
   
   The deferrable trigger path uses aiohttp with its own `ClientTimeout` and is 
not affected. Only the sync calls are: trigger, the status check before 
deferring, `wait_for_job_run_status`, and cancel.
   
   I'd like to fix this in `DbtCloudHook.get_conn()` (set `merged_extra` and 
`session.proxies` from the passed `extra_options`) rather than in `HttpHook`, 
since other subclasses such as `DingdingHook` deliberately ignore 
`extra_options`.
   
   ### Are you willing to submit PR?
   
   - [x] Yes I am willing to submit a PR!
   
   ### Code of Conduct
   
   - [x] I agree to follow this project's Code of Conduct
   


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