Skip to content

Decode deadline callback kwargs where the callback runs - #74830

Merged
potiuk merged 5 commits into
apache:mainfrom
potiuk:deadline-callback-kwargs-decode-at-use
Oct 19, 2026
Merged

potiuk merged 5 commits into
apache:mainfrom
potiuk:deadline-callback-kwargs-decode-at-use

Conversation

@potiuk

@potiuk potiuk commented Oct 15, 2026

Copy link
Copy Markdown
Member

Deadline Alert callback kwargs were decoded in the scheduler and the API server whenever a
serialized Dag's deadline alerts were loaded. Neither component calls the callback: the decoded
callback is only used to pick the callback model by type and to copy serialize() back into
Callback.data, which is an identity round-trip. Decoding there means constructing user objects
in components that never use them, and it is also lossy for sync callbacks, whose kwargs went
through BaseSerialization and a JSON workload before reaching the worker.

This keeps the kwargs in their serde-encoded form on the server and decodes them at the point
of use, on both consumption paths:

  • Server: _decode_deadline_callback keeps kwargs encoded (only a legacy
    {"__type": "dict", "__var": ...} container is unwrapped). Re-encoding a decoded
    SerializedDeadlineAlert writes them back unchanged instead of serializing them again.
  • AsyncCallback (triggerer): TriggererCallback.queue stores the encoded kwargs on the
    trigger row as-is via a new Trigger.from_serialized_kwargs, giving the same stored form as
    before. The triggerer decodes them with its existing pipeline when it creates the
    CallbackTrigger, so the triggerer itself is unchanged.
  • SyncCallback (worker): execute_callback decodes the kwargs in the callback subprocess
    right before calling the user callable. The scheduler-provided context is passed through.
  • SyncCallback version note: the executor callback data now carries
    "kwargs_encoding": "serde". BaseExecutor.run_workload passes it to supervise_callback only
    when set, and the Task SDK decodes the kwargs only when it is present, so callbacks created
    before upgrade (no marker, already-decoded kwargs) run unchanged. A worker with an upgraded
    apache-airflow and an older Task SDK fails such callbacks with an error instead of running
    them with encoded arguments. Workers should upgrade apache-airflow and
    apache-airflow-task-sdk together; see the significant newsfragment.

Both the current serde encoding and the legacy {"__type", "__var"} encoding are handled, and
Callback rows created before upgrade (holding already-decoded kwargs) keep working on both paths.

Tests cover:

  • kwargs naming a class are not constructed during server-side decode, and round-trip
    byte-identically;
  • AsyncCallback kwargs arrive decoded in the CallbackTrigger built the way the triggerer
    builds it;
  • SyncCallback kwargs arrive decoded in the callable after a full
    store -> workload JSON -> execute_callback round-trip;
  • each of the above for current and legacy encodings;
  • with the encoding marker the worker decodes, without it the kwargs are passed as-is, and an
    unknown marker fails the callback;
  • the async path is unchanged.

The test that asserted the previous decode-on-load behaviour is replaced.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 5 following the guidelines


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {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.

Deadline Alert callback kwargs were decoded in the scheduler and the API
server whenever deadline alerts were loaded, although neither component
calls the callback. Keep the kwargs serde-encoded on the server and
decode them where they are used: the triggerer decodes AsyncCallback
kwargs with its existing pipeline, and the callback subprocess decodes
SyncCallback kwargs right before calling the user callable.

Executor callback data now carries a "kwargs_encoding" marker so the
worker only decodes encoded kwargs, callbacks created before upgrade run
unchanged, and an older Task SDK fails such callbacks instead of passing
encoded arguments.

The runtime-provided callback context is serialized strictly, as before;
only the stored callback kwargs keep their encoded form.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
@potiuk
potiuk force-pushed the deadline-callback-kwargs-decode-at-use branch from bd82fc1 to 1f44532 Compare October 15, 2026 23:08
Deadline callback kwargs are now kept in their encoded form, so metric tags and the callback repr showed the raw encoding instead of the values. Show them in a readable form again, without importing or building anything they name.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
A timedelta payload holding NaN made the display of callback kwargs raise, which would escape from metric emission and the callback repr. Show such values, and any other interval that cannot be built, in the generic form instead.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
… it is refused

The triggerer-path deadline test pinned the AttributeError raised by
strict serialization. Assert the outcome instead (no trigger is created
and nothing in the context is constructed), so the test holds whether
the context is refused by raising or by failing the callback.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
@potiuk

potiuk commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

@ferruzzi @ramitkataria Could you take a look at this when you have a moment? It's targeted at 3.4.0 and needs a review to make the cut on 2026-10-19. CI is green.

@potiuk
potiuk requested a review from dstandish as a code owner October 17, 2026 00:00

@vatsrahul1001 vatsrahul1001 left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Callback kwargs stay encoded on the server and are decoded where the callback runs, the encoding marker keeps callbacks created before the upgrade working, and the triggerer now fails a single undecodable trigger instead of stopping the runner. Could use reviews from @ferruzzi @ramitkataria before merging.

@potiuk
potiuk merged commit 233f9c3 into apache:main Oct 19, 2026
108 checks passed
@potiuk
potiuk deleted the deadline-callback-kwargs-decode-at-use branch October 19, 2026 14:56
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-4-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
✅ v3-4-test PR Link

potiuk added a commit that referenced this pull request Oct 19, 2026
…74830) (#75114)

* Decode deadline callback kwargs where the callback runs

Deadline Alert callback kwargs were decoded in the scheduler and the API
server whenever deadline alerts were loaded, although neither component
calls the callback. Keep the kwargs serde-encoded on the server and
decode them where they are used: the triggerer decodes AsyncCallback
kwargs with its existing pipeline, and the callback subprocess decodes
SyncCallback kwargs right before calling the user callable.

Executor callback data now carries a "kwargs_encoding" marker so the
worker only decodes encoded kwargs, callbacks created before upgrade run
unchanged, and an older Task SDK fails such callbacks instead of passing
encoded arguments.

The runtime-provided callback context is serialized strictly, as before;
only the stored callback kwargs keep their encoded form.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Render deadline callback kwargs readably in metrics and repr

Deadline callback kwargs are now kept in their encoded form, so metric tags and the callback repr showed the raw encoding instead of the values. Show them in a readable form again, without importing or building anything they name.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Keep deadline callback kwargs display total for non-finite intervals

A timedelta payload holding NaN made the display of callback kwargs raise, which would escape from metric emission and the callback repr. Show such values, and any other interval that cannot be built, in the generic form instead.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Assert that the deadline context never reaches the triggerer, not how it is refused

The triggerer-path deadline test pinned the AttributeError raised by
strict serialization. Assert the outcome instead (no trigger is created
and nothing in the context is constructed), so the test holds whether
the context is refused by raising or by failing the callback.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Fail only the affected trigger when its kwargs cannot be decoded
(cherry picked from commit 233f9c3)


Generated-by: Claude Opus 5

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
vatsrahul1001 pushed a commit that referenced this pull request Oct 20, 2026
…74830) (#75114)

* Decode deadline callback kwargs where the callback runs

Deadline Alert callback kwargs were decoded in the scheduler and the API
server whenever deadline alerts were loaded, although neither component
calls the callback. Keep the kwargs serde-encoded on the server and
decode them where they are used: the triggerer decodes AsyncCallback
kwargs with its existing pipeline, and the callback subprocess decodes
SyncCallback kwargs right before calling the user callable.

Executor callback data now carries a "kwargs_encoding" marker so the
worker only decodes encoded kwargs, callbacks created before upgrade run
unchanged, and an older Task SDK fails such callbacks instead of passing
encoded arguments.

The runtime-provided callback context is serialized strictly, as before;
only the stored callback kwargs keep their encoded form.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Render deadline callback kwargs readably in metrics and repr

Deadline callback kwargs are now kept in their encoded form, so metric tags and the callback repr showed the raw encoding instead of the values. Show them in a readable form again, without importing or building anything they name.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Keep deadline callback kwargs display total for non-finite intervals

A timedelta payload holding NaN made the display of callback kwargs raise, which would escape from metric emission and the callback repr. Show such values, and any other interval that cannot be built, in the generic form instead.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Assert that the deadline context never reaches the triggerer, not how it is refused

The triggerer-path deadline test pinned the AttributeError raised by
strict serialization. Assert the outcome instead (no trigger is created
and nothing in the context is constructed), so the test holds whether
the context is refused by raising or by failing the callback.

Generated-by: Claude Opus 5 following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

* Fail only the affected trigger when its kwargs cannot be decoded
(cherry picked from commit 233f9c3)


Generated-by: Claude Opus 5

Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
@dondaum

dondaum commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

@potiuk I’m actually not sure whether this change might lead to any issues:
https://github.com/apache/airflow/blob/main/airflow-core/src/airflow/jobs/scheduler_job_runner.py#L1436 in _enqueue_executor_callbacks
As far as I understand, the scheduler uses the decoded callback.data; therefore, they must be decoded for everything to function correctly. Or am I missing something important here @ferruzzi ?

(Apologies for the tag – I just saw this change and realized that it is part of 3.4.0.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants