boqiang commented on issue #59120:
URL: https://github.com/apache/airflow/issues/59120#issuecomment-6158568975

   Hi maintainers — is anyone currently working on a fix for this issue, or is 
there an active PR I should coordinate with? I would be happy to contribute a 
MySQL deadlock regression test and help with a fix. I noticed the prior 
session-isolation/savepoint proposals and would like to avoid duplicating work.
   
   Below is a sanitized technical analysis, without organization-specific data.
   
   ### Conclusion
   A MySQL deadlock during DAG metadata autoflush can become a fatal scheduler 
error when the original database exception is caught and execution continues 
with a failed SQLAlchemy Session. The next ORM operation raises 
`PendingRollbackError`, which can escape the scheduling loop.
   
   ### Failure mechanism
   The relevant path is `_create_dag_runs` and its enclosing transaction/retry 
boundary. Concurrent updates to parsing fields (`last_parsed_time`, 
`last_parse_duration`) and scheduling fields (`next_dagrun*`) can contend on 
`dag` rows. The deadlock is the trigger; reusing the failed Session turns it 
into a scheduler failure. The DAG named in a creation-error message need not be 
the DAG whose pending UPDATE was autoflushed.
   
   ```mermaid
   sequenceDiagram
       participant S as Scheduler
       participant O as SQLAlchemy Session
       participant D as MySQL
       S->>O: Create DagRun / autoflush metadata
       O->>D: UPDATE dag amid concurrent metadata writes
       D-->>O: Deadlock (1213)
       O-->>S: Database exception
       Note over S,O: Broad handler catches error and continues
       S->>O: Next ORM operation on failed Session
       O-->>S: PendingRollbackError
       Note over S: Exception escapes scheduling loop
   ```
   
   ### Supporting code evidence
   * In the official 3.1.8 source, `_create_dag_runs` catches `Exception` 
around DagRun creation and continues.
   * `retry_db_transaction` / `run_with_db_retries` in 
`airflow/utils/retries.py` handles `DBAPIError`, including rollback before 
retry. Swallowing the original error prevents that path; the later 
`PendingRollbackError` is not the configured retry category.
   * #59121 adds a TODO describing this problem, rather than implementing 
recovery.
   
   ### Proposed direction — not implemented or validated
   Could database exceptions be propagated to the existing owning 
transaction/retry boundary instead of being swallowed by per-DAG handlers? For 
example, explicitly re-raising `DBAPIError` before a generic handler is one 
candidate to evaluate, including other broad handlers on the same path. The 
owner would roll back the failed unit, discard/reload ORM state and retry with 
bounded backoff, re-querying candidates and reacquiring required HA locks.
   
   ```mermaid
   sequenceDiagram
       participant S as Scheduler
       participant T as Owning transaction/retry boundary
       participant O as SQLAlchemy Session
       participant D as MySQL
       S->>T: Schedule a batch
       T->>O: Query / flush
       O->>D: Metadata transaction
       D-->>O: Deadlock (1213)
       O-->>T: Propagate original DBAPIError
       T->>O: Roll back failed unit and reload state
       Note over T: Bounded backoff, re-query and reacquire HA locks
       T->>O: Retry the transaction
       O->>D: Retry metadata work
       D-->>O: Success
       T-->>S: Continue scheduling
   ```
   
   This is a proposal for discussion, not a claim that adding one exception 
clause is sufficient. It needs real MySQL testing for full-transaction deadlock 
rollback, HA lock correctness, duplicate-run prevention, retry exhaustion and 
continued scheduler heartbeats. Simply increasing retry counts or adding 
`PendingRollbackError` to retry eligibility would not address the swallowed 
original error. Savepoint approaches also need to account for `prohibit_commit` 
and MySQL deadlock semantics.
   
   Would this recovery boundary be an acceptable direction, or is there a 
preferred approach / existing PR I could help validate?


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