Skip to content

Return each attempt's own URL from operator extra links - #71522

Closed
1fanwang wants to merge 1 commit into
apache:mainfrom
1fanwang:oss/links-c-state-store
Closed

1fanwang wants to merge 1 commit into
apache:mainfrom
1fanwang:oss/links-c-state-store

Conversation

@1fanwang

@1fanwang 1fanwang commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Operator extra links keep only the latest attempt's URL. After a retry, attempt 1 can open attempt 2's logs.

Closes: #71471

Why

The link reader ignores the key's attempt number. The execution API clears task XComs before each attempt, so the row belongs to the latest attempt.

What

The task runner writes each rendered link to the task state store under an attempt key. The reader checks it first and falls back to XCom for older links.

Tests

This SQLite probe stores attempt 1 in the state store and attempt 2 in XCom:

Probe and output
T=airflow-core/tests/test_operator_link_attempt_probe.py
cat >"$T" <<'PY'
import json, pytest
from airflow._shared.state import TaskScope
from airflow.models.xcom import XComModel
from airflow.serialization.definitions.operatorlink import XComOperatorLink
from airflow.state import get_state_backend
@pytest.mark.db_test
def test_probe(session, create_task_instance):
    t = create_task_instance(task_id="link")
    ids = dict(dag_id=t.dag_id, run_id=t.run_id, task_id=t.task_id, map_index=t.map_index)
    get_state_backend().set(TaskScope(**ids), "_link_attempt_1__link_MyLink", json.dumps("https://logs/attempt-1"), session=session)
    XComModel.set(key="_link_MyLink", value="https://logs/attempt-2", session=session, **ids)
    session.commit()
    link = XComOperatorLink(name="My Link", xcom_key="_link_MyLink")
    for n in (1, 2):
        print(f"try {n} -> {link.get_link(None, ti_key=t.key._replace(try_number=n))}")
PY

git worktree add --detach /tmp/airflow-71522-main upstream/main
cp "$T" "/tmp/airflow-71522-main/$T"
(cd /tmp/airflow-71522-main && AIRFLOW_HOME=/tmp/af-main uv run --project airflow-core pytest "$T" -s -q --no-cov)
AIRFLOW_HOME=/tmp/af-head uv run --project airflow-core pytest "$T" -s -q --no-cov
upstream/main:
try 1 -> https://logs/attempt-2
try 2 -> https://logs/attempt-2

this PR:
try 1 -> https://logs/attempt-1
try 2 -> https://logs/attempt-2

Risk

The task state scope has no attempt field, so the key includes the attempt number. Existing XCom writes and fallback behavior remain unchanged.

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

Generated-by: GitHub Copilot CLI (GPT-6 Astra) following the guidelines

@1fanwang
1fanwang force-pushed the oss/links-c-state-store branch 2 times, most recently from 1e4e436 to e2148a5 Compare August 13, 2026 00:15
@1fanwang 1fanwang changed the title Operator extra links per attempt (draft C): use the task state store Return each attempt's own URL from operator extra links Aug 13, 2026
@1fanwang
1fanwang marked this pull request as ready for review August 13, 2026 00:17
@1fanwang
1fanwang force-pushed the oss/links-c-state-store branch from e2148a5 to 2283d2b Compare August 13, 2026 05:00
@1fanwang
1fanwang requested a review from potiuk as a code owner August 13, 2026 05:00
@1fanwang
1fanwang force-pushed the oss/links-c-state-store branch from 2283d2b to a7fb9b7 Compare August 13, 2026 05:01
@potiuk potiuk added the ready for maintainer review Set after triaging when all criteria pass. label Aug 13, 2026
@eladkal eladkal added this to the Airflow 3.3.2 milestone Aug 14, 2026
@eladkal eladkal added type:bug-fix Changelog: Bug Fixes backport-to-v3-3-test Backport to v3-3-test labels Aug 14, 2026
@ashb

ashb commented Sep 25, 2026

Copy link
Copy Markdown
Member

@amoghrajesh Can you take a look at this, it is like trying to fix the same issue as #73315

An operator extra link is cached as a single XCom row under the link's xcom_key,
and XComOperatorLink.get_link ignores the try_number on the TaskInstanceKey it is
given. A task's XComs are also cleared before every attempt, so that row only ever
holds whichever attempt ran last. Asking for an earlier attempt's link therefore
returns the latest attempt's URL, and the original attempt's logs cannot be reached
from the UI at all.

The worker now also writes each attempt's rendered link to the task state store,
which a retry does not clear, and the reader prefers that row. The XCom row is left
in place and is still the answer for links written before this change.

Reading goes through get_state_backend(), the same resolver the execution API uses,
so a deployment pointing [state_store] backend elsewhere keeps working.

Closes: apache#71471
Signed-off-by: 1fanwang <1fannnw@gmail.com>
@1fanwang
1fanwang force-pushed the oss/links-c-state-store branch from 0093e77 to 424d516 Compare September 25, 2026 20:54
@1fanwang

Copy link
Copy Markdown
Contributor Author

Closing in favor of #73315 — @amoghrajesh's fix solves the same problem (operator extra links surviving across retries) with a simpler, self-contained approach: per-try-suffixed XCom keys plus skipping those keys during XCom clearing. Mine additionally depended on the task-state-store abstraction from #71307/#68936, which is still pending consensus on the mailing list, so 73315 is the better fix to land.

@1fanwang 1fanwang closed this Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:dag-processor area:task-sdk backport-to-v3-3-test Backport to v3-3-test ready for maintainer review Set after triaging when all criteria pass. type:bug-fix Changelog: Bug Fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Operator extra links don't work per attempt

5 participants