Uh oh!
There was an error while loading. Please reload this page.
AIP-103: Adding ability for per task state key retention from operators - #66699
Conversation
…4c-per-key-expiry-task-sdk
Co-authored-by: Wei Lee <weilee.rx@gmail.com>
…4c-per-key-expiry-task-sdk
…4c-per-key-expiry-task-sdk
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ashb
commented
May 14, 2026
I think we should change the Exec API interface -- rather than sending retention_days to the server, lets make the Exec API endpoint(s?) take an I'm wondering if instead of retention_days being a) days, b) an integer, it should be a timeinterval? WDYT? It could be |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
amoghrajesh
commented
May 19, 2026
Thanks for review folks, merging this one. |
Uh oh!
There was an error while loading. Please reload this page.
closes: #66461
Why?
The global
[state_store] default_retention_daysconfig applies one retention window to every task state key. Some keys have meaningfully different lifetimes — a submitted job ID is useful for the life of a run, while a short-lived lock key might need only hours. This adds the ability to express per-key retention without changing the global default for everything.Current behaviour
All task state keys written via
PUT /state/ti/{id}/{key}receiveexpires_at = now + default_retention_days, regardless of the individual key's intended lifetime (as established in #66463).Proposed change
Adds an optional
retention: timedelta | Noneparameter toTaskStateAccessor.set(), along with a publicNEVER_EXPIREconstant.timedelta(...)— expire this key after the given duration, overriding the global default.NEVER_EXPIRE— key never expires, regardless of global config. Stored asNULLin the DB — the GC pass skipsNULLrows.None/ omitted — use[state_store] default_retention_daysas before.The worker computes an absolute
expires_atUTC timestamp before sending to the server — the server stores it directly with no date arithmetic.Changes span the full stack:
TaskStateAccessor.set(),SetTaskStatecomms message,TaskStatePutBody,BaseStateBackend.set()/aset(),MetastoreStateBackend._set_task_state()/_aset_task_state(), and the Execution API route.User implications / backcompat
None.
retentiondefaults toNone.BaseStateBackend.set()andaset()gain a newexpires_at: datetime | None = Nonekeyword argument replacingretention_days: int | None = None.expires_at=Nonemeans never expire (stored asNULL).Usage:
Testing
expires_at = NULL[state_store] default_retention_daysWas generative AI tooling used to co-author this PR?
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.