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

   <!-- SPDX-License-Identifier: Apache-2.0
         https://www.apache.org/licenses/LICENSE-2.0 -->
   
   **Depends on #74279.** This branch is stacked on #74279's branch, so until 
that PR is merged the diff here also shows its commits. The only new commit is 
e869a14f24, which changes `SqliteHook`, its tests and the provider's operator 
guide. I'll rebase onto main once #74279 is merged.
   
   This is the follow-up promised in #74279, which added 
`DbApiHook.cancel_query()` and `SQLExecuteQueryOperator.on_kill` / 
`cancel_on_kill`. As shown there, it changes nothing for `SqliteHook`:
   
   * `sqlite3` doesn't return to Python until a statement completes, so Python 
can't run the `SIGTERM` handler (`on_kill`) or the `SIGALRM` handler of 
`execution_timeout` during the statement.
   * A `sqlite3` connection has no DB-API `cancel()` for `cancel_query()` to 
call.
   * On a real Airflow, a 15 s `execution_timeout` on a long statement fired 
after 5 min 23 s, when the statement finished.
   
   **Change:**
   
   * While a statement runs in the main thread, `SqliteHook` registers a 
progress handler (every `progress_handler_interval` = 100,000 SQLite 
instructions). Calling it into Python lets pending signal handlers run.
   * `SqliteHook.cancel_query()` interrupts the statement with 
`sqlite3.Connection.interrupt()`.
   * An exception raised by a signal handler inside the progress handler 
(`AirflowTaskTimeout`) aborts the statement, but `sqlite3` only reports it 
through `sys.unraisablehook` and raises `OperationalError("interrupted")`. 
While the statement runs, the hook recognises its own progress handler in the 
exception's traceback and raises the original exception instead, so 
`execution_timeout` still fails the task with `AirflowTaskTimeout`. I check the 
traceback because `unraisable.object` is the callback on 3.11–3.13 but `None` 
on 3.14. Other unraisable exceptions go on to the previous hook.
   * The handler isn't registered with `cancel_on_interrupt=False` / 
`cancel_on_kill=False`, or outside the main thread.
   
   **Side effect:** reporting the exception requires 
`sqlite3.enable_callback_tracebacks(True)` while the statement runs, and 
`sqlite3` has no getter for that flag, so it's switched back off (its default) 
afterwards. During such a statement, an error raised by a user-defined function 
registered on the connection is now reported through `sys.unraisablehook` 
instead of being discarded. The statement still fails with the same 
`OperationalError`. The progress handler costs about 0.15 % on a 20 M-row 
recursive CTE (median of 5 runs).
   
   **Verified on a real Airflow 3.3.2** (LocalExecutor, SQLite 3.50.4, the same 
harness as in #74279):
   
   | kill | before (#74279 only) | with this PR |
   |---|---|---|
   | mark failed, `cancel_on_kill=True` | `on_kill` never runs; the statement 
stops only because the task is SIGKILLed 5 s later | interrupted through 
`connection.interrupt()` 0.2 s after SIGTERM |
   | `execution_timeout` (15 s), `cancel_on_kill=True` | fired after 5 min 23 
s, when the statement completed | interrupted 0.1 s after the timeout; task 
fails with `AirflowTaskTimeout` |
   | either, `cancel_on_kill=False` | unchanged | unchanged, as designed |
   
   **Tests:**
   
   * Real in-memory SQLite tests run a recursive CTE that would take well over 
10 s, and interrupt it from a signal handler after 0.5 s: once raising 
`AirflowTaskTimeout`, once calling `cancel_query()`. Without this change both 
run to completion (20 s) and fail.
   * A user-defined function error still fails the statement and reaches the 
previous `sys.unraisablehook`.
   * Mocked tests check when the progress handler is installed and removed.
   * `providers/sqlite/tests/unit/sqlite`: 28 passed on Python 3.11, 3.13 and 
3.14.
   
   The sqlite operator guide gets a short section on this. The provider's 
`common-sql` lower bound doesn't need to change for it to install: the 
overrides only run when the base hook calls them, which needs the `common-sql` 
release that ships #74279.
   
   related: #74279, #27314
   


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