Handle concurrent XCom writes with savepoint and UPDATE fallback to avoid 409 - #69959
Handle concurrent XCom writes with savepoint and UPDATE fallback to avoid 409#69959nagasrisai wants to merge 15 commits into
Conversation
|
@nagasrisai This PR has a few issues that need to be addressed before it can be reviewed — please see our Pull Request quality criteria. Issues found:
What to do next:
There is no rush — take your time and work at your own pace. We appreciate your contribution and are happy to wait for updates. If you have questions, feel free to ask on the Airflow Slack. Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you. |
Catching all IntegrityError was too broad: a FK violation (e.g. the task_instance cascade-deleted between the dag_run_id lookup and the flush) would also land here, causing the fallback UPDATE to match zero rows while still returning 200 — silently losing the write. Narrow the guard to unique-constraint messages per dialect (matching the pattern in _UniqueConstraintErrorHandler) and re-raise everything else.
|
@kaxil - Thanks for the pointers! I've pushed two follow-up commits:
Will also look into getting the static checks clean. |
…m_set Adds two tests to TestXComsSetEndpoint: * test_xcom_set_concurrent_write_uses_update_fallback — pre-seeds the row, patches XComModel.set to raise a unique-constraint IntegrityError once, and asserts the endpoint returns 201 with the latest value stored via the UPDATE fallback. * test_xcom_set_fk_integrity_error_propagates — patches XComModel.set to raise an FK IntegrityError and asserts it propagates as a 500 rather than being silently swallowed.
…ASGI EndOfStream crash
Closes #69956
Summary
When two task runners (concurrent execution or task retry) write the same XCom key at roughly the same time, both call
XComModel.setwhich does a DELETE followed by an INSERT. Both DELETEs succeed (they each see the record that was just written by the peer or see nothing), then both try to INSERT the same primary key. The second INSERT hits the unique constraint and the Execution API converts theIntegrityErrorto an HTTP 409, causing the task to fail.Fix
Wrap the
XComModel.setcall in a savepoint (session.begin_nested()) so that a constraint violation only rolls back the XCom write and not any earlier work in the same request (e.g. thetask_mapmerge). TheIntegrityErroris caught and handled with an explicit UPDATE that overwrites the existing entry with the latest value, which is the correct semantic for a retry.Changes
airflow-core/src/airflow/api_fastapi/execution_api/routes/xcoms.pyupdateto thesqlalchemyimportIntegrityErrorimport fromsqlalchemy.excXComModel.setinsession.begin_nested()and catchIntegrityErrorwith an UPDATE fallbackImportant
🛠️ Maintainer triage note for @nagasrisai · by
@potiuk· 2026-07-28 16:11 UTCHelpful heads-up from the maintainers — please address before this PR can be reviewed:
The ball is in your court — you've been assigned to this PR. Fix the above, then mark it Ready for review.
See the Pull Request quality criteria for how to fix each item. There is no rush.
Note: your branch is 270 commits behind
main— please rebase and push again to get up-to-date CI results.Automated triage — may be imperfect; a maintainer takes the next look. We use this two-stage triage process so maintainers' limited time goes to the conversation with you.