dabla commented on code in PR #75774:
URL: https://github.com/apache/airflow/pull/75774#discussion_r4390371361


##########
providers/microsoft/azure/src/airflow/providers/microsoft/azure/sensors/msgraph.py:
##########
@@ -119,26 +134,30 @@ def execute(self, context: Context):
                 headers=self.headers,
                 data=self.data,
                 conn_id=self.conn_id,
-                timeout=self.timeout,
                 proxies=self.proxies,
                 scopes=self.scopes,
                 api_version=self.api_version,
                 serializer=type(self.serializer),
             ),
             method_name=self.execute_complete.__name__,
+            kwargs={"sensor_started_at": started_at},
+            timeout=timedelta(seconds=remaining) if remaining is not None else 
None,
         )
 
-    def retry_execute(
-        self,
-        context: Context,
-        **kwargs,
-    ) -> Any:
-        self.execute(context=context)
+    @staticmethod
+    def _anchor(sensor_started_at: float | None) -> float:

Review Comment:
   **[nit]** One concept, three names: `_anchor`, `started_at`, 
`sensor_started_at` (and "anchor" again in the test names).
   
   They all mean the moment the try started. Picking one (`started_at` reads 
best next to `BaseSensorOperator.execute`, which uses the same word) and naming 
the method after what it returns, e.g. `_started_at(sensor_started_at)`, keeps 
the code and the tests in one vocabulary ([Fowler, 
*UbiquitousLanguage*](https://martinfowler.com/bliki/UbiquitousLanguage.html)).



##########
providers/microsoft/azure/src/airflow/providers/microsoft/azure/sensors/msgraph.py:
##########
@@ -183,11 +202,22 @@ def execute_complete(
 
                     return result
 
-                # Re-defer with timeout so Airflow enforces the sensor timeout 
natively
+                started_at = self._anchor(sensor_started_at)
+                remaining = self._remaining_timeout(started_at)
+                if remaining is not None and remaining <= 0:
+                    raise AirflowSensorTimeout(
+                        f"Sensor has timed out; run duration of {self.timeout 
- remaining} seconds "
+                        f"exceeds the specified timeout of {self.timeout}."
+                    )
+                delay = (
+                    self.retry_delay
+                    if remaining is None
+                    else min(self.retry_delay, timedelta(seconds=remaining))
+                )
                 self.defer(
-                    trigger=TimeDeltaTrigger(self.retry_delay),
+                    trigger=TimeDeltaTrigger(delay),

Review Comment:
   **[warning]** The sleep defer carries no `timeout`, so the deadline is 
cleared for the whole sleep.
   
   A defer without `timeout` stores `trigger_timeout=None` (execution API 
`ti_update_state`, and `TaskInstance._defer_task` on 2.x), which is the same 
clearing this PR removes from the request defer. While the task sleeps on the 
`TimeDeltaTrigger`, core has no deadline for it: a trigger that never fires 
(triggerer stuck, trigger lost) leaves the task deferred until 
`execution_timeout`, if any. #62241 had put a timeout on this defer; the fix 
here drops it instead of making it stop sliding.
   
   Suggest keeping a timeout on the sleep defer that lies above the sleep 
itself so the "one last poll" path is never raced by it, for example:
   
   ```python
   self.defer(
       trigger=TimeDeltaTrigger(delay),
       method_name=self.retry_execute.__name__,
       kwargs={"sensor_started_at": started_at},
       timeout=None if remaining is None else timedelta(seconds=remaining) + 
self.retry_delay,
   )
   ```
   
   A lost sleep trigger then ends through `TaskDeferralTimeout`, which 
`BaseSensorOperator.resume_execution` turns into `AirflowSensorTimeout` with 
`soft_fail`/`never_fail` applied, the same path the request defer already 
relies on. One test for the sleep defer's timeout next to 
`test_execute_complete_redefers_with_capped_sleep_and_anchor` would pin it.



##########
providers/microsoft/azure/src/airflow/providers/microsoft/azure/sensors/msgraph.py:
##########
@@ -119,26 +134,30 @@ def execute(self, context: Context):
                 headers=self.headers,
                 data=self.data,
                 conn_id=self.conn_id,
-                timeout=self.timeout,
                 proxies=self.proxies,
                 scopes=self.scopes,
                 api_version=self.api_version,
                 serializer=type(self.serializer),
             ),
             method_name=self.execute_complete.__name__,
+            kwargs={"sensor_started_at": started_at},
+            timeout=timedelta(seconds=remaining) if remaining is not None else 
None,
         )
 
-    def retry_execute(
-        self,
-        context: Context,
-        **kwargs,
-    ) -> Any:
-        self.execute(context=context)
+    @staticmethod
+    def _anchor(sensor_started_at: float | None) -> float:
+        return sensor_started_at if sensor_started_at is not None else 
timezone.utcnow().timestamp()
+
+    def _remaining_timeout(self, started_at: float) -> float | None:
+        if self.timeout is None:

Review Comment:
   **[nit]** `self.timeout is None` cannot happen; the three `None` branches 
and `test_timeout_attribute_none_never_times_out` cover a state the base class 
rejects.
   
   `BaseSensorOperator.__init__` coerces `timeout` to a number 
(`self._coerce_timeout(timeout).total_seconds()`) and raises on anything else, 
on the Task SDK and on 2.11 alike, with a 7-day default. The test only reaches 
the branch by assigning `sensor.timeout = None` after construction. Typing 
`_remaining_timeout` as `-> float`, dropping the `None` checks in 
`retry_execute`, `_defer_request` and `execute_complete`, and removing that 
test leaves less to read for the same behaviour ([Fowler, 
*Yagni*](https://martinfowler.com/bliki/Yagni.html)).



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