Skip to content

Fix scheduler crash on restart with queued or running Edge tasks - #73832

Merged
shahar1 merged 2 commits into
apache:mainfrom
FrankYang0529:airflow-edge3-adopt-scheduler-session
Oct 2, 2026
Merged

shahar1 merged 2 commits into
apache:mainfrom
FrankYang0529:airflow-edge3-adopt-scheduler-session

Conversation

@FrankYang0529

@FrankYang0529 FrankYang0529 commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Why

  • Make EdgeExecutor respect [core] parallelism #72048 added @provide_session to EdgeExecutor.try_adopt_task_instances() so that the method could read the edge_job table.
  • The scheduler calls this method from adopt_or_reset_orphaned_tasks() without passing a session. At that point, the scheduler's session still holds the orphaned task instances, which the scheduler loaded with load_only(...). @provide_session does not open a new session in this case. It calls create_session(), which returns the thread-scoped session. In the scheduler's thread, that is the session the scheduler is already using. When try_adopt_task_instances() returns, create_session() commits and closes the scheduler's session in the middle of the adoption.
  • The scheduler then reads ti.last_heartbeat_at on a detached task instance and exits with DetachedInstanceError.

How

  • Read the edge_job rows through create_session(scoped=False), which opens a separate session and leaves the scheduler's session and the orphaned task instances alone.

Verification

  • Unit test: uv run --frozen --project providers/edge3 pytest providers/edge3/tests/unit/edge3/executors/test_edge_executor.py
  • Integration test:
export AIRFLOW_HOME="${TMPDIR:-/tmp}/edge-adopt-demo"
export AIRFLOW__CORE__DAGS_FOLDER="$AIRFLOW_HOME/dags"
export AIRFLOW__CORE__LOAD_EXAMPLES=False
export AIRFLOW__CORE__PARALLELISM=3
export AIRFLOW__CORE__EXECUTOR=airflow.providers.edge3.executors.EdgeExecutor
export AIRFLOW__DATABASE__EXTERNAL_DB_MANAGERS=airflow.providers.edge3.models.db.EdgeDBManager

rm -rf "$AIRFLOW_HOME" && mkdir -p "$AIRFLOW__CORE__DAGS_FOLDER"
cat > "$AIRFLOW__CORE__DAGS_FOLDER/edge_adopt_demo.py" <<'EOF'
import pendulum

from airflow.providers.standard.operators.bash import BashOperator
from airflow.sdk import DAG

with DAG(
    dag_id="edge_adopt_demo",
    schedule=None,
    start_date=pendulum.datetime(2026, 1, 1, tz="UTC"),
    is_paused_upon_creation=False,
):
    for i in range(1, 7):
        BashOperator(task_id=f"task_{i}", bash_command="true")
EOF
uv run --frozen --project providers/edge3 airflow db migrate > /dev/null 2>&1
uv run --frozen --project providers/edge3 airflow dags reserialize > /dev/null 2>&1
uv run --frozen --project providers/edge3 airflow dags trigger edge_adopt_demo --run-id demo > /dev/null 2>&1

uv run --frozen --project providers/edge3 airflow scheduler --num-runs 5 > "$AIRFLOW_HOME/scheduler-1.log" 2>&1
uv run --frozen --project providers/edge3 airflow tasks states-for-dag-run edge_adopt_demo demo -o plain 2>/dev/null | grep -v FileType

uv run --frozen --project providers/edge3 airflow scheduler --num-runs 5 > "$AIRFLOW_HOME/scheduler-2.log" 2>&1; echo "exit code: $?"
echo "DetachedInstanceError: $(grep -c "DetachedInstanceError:" "$AIRFLOW_HOME/scheduler-2.log")"
echo "parallelism limit reached: $(grep -c "parallelism limit reached" "$AIRFLOW_HOME/scheduler-2.log")"

The first airflow scheduler --num-runs 5 queues three of the six tasks, because [core] parallelism is 3, and then exits. The second one is the restart. It should adopt the three task instances that the first scheduler left queued.

On this branch, the restart adopts them. The adopted task instances keep their slots, so the scheduler logs Executor parallelism limit reached in each of its five loops and the other three tasks stay scheduled:

dag_id           logical_date    task_id    state      start_date    end_date
edge_adopt_demo                  task_1     queued
edge_adopt_demo                  task_2     queued
edge_adopt_demo                  task_3     queued
edge_adopt_demo                  task_4     scheduled
edge_adopt_demo                  task_5     scheduled
edge_adopt_demo                  task_6     scheduled
exit code: 0
DetachedInstanceError: 0
parallelism limit reached: 5

On main, the task states are the same, but the restart crashes before it adopts anything:

exit code: 1
DetachedInstanceError: 2
parallelism limit reached: 0

Was generative AI tooling used to co-author this PR?
  • Yes - Claude Code

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

@boring-cyborg boring-cyborg Bot added area:providers provider:edge Edge Executor / Worker (AIP-69) / edge3 labels Sep 28, 2026
@FrankYang0529
FrankYang0529 force-pushed the airflow-edge3-adopt-scheduler-session branch 2 times, most recently from 92b108e to ce51b71 Compare October 2, 2026 05:43
@FrankYang0529
FrankYang0529 marked this pull request as ready for review October 2, 2026 11:53
@shahar1

shahar1 commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

I've accidentally ommitted this PR from #74097, I'm adding the changelog as part of this one.

@shahar1
shahar1 force-pushed the airflow-edge3-adopt-scheduler-session branch from ce51b71 to b407544 Compare October 2, 2026 17:17
@shahar1
shahar1 merged commit 7800fc4 into apache:main Oct 2, 2026
6 checks passed
@FrankYang0529
FrankYang0529 deleted the airflow-edge3-adopt-scheduler-session branch October 2, 2026 23:55
shahar1 added a commit to shahar1/airflow that referenced this pull request Oct 3, 2026
A PR that only changes a provider runs that provider's tests, with its
direct dependents, in a pytest process of its own. On canary, the same
tests run inside the shared Providers[-amazon,celery,google,standard]
process, after every provider that sorts before them. cncf.kubernetes
tests reload airflow.executors.executor_loader and leave its state
behind, so a test that mocks ExecutorLoader through the module path can
pass in its PR and fail on every canary build and release-candidate
check afterwards. PR apache#73832 did exactly that with an edge3 test.

Selective checks now add the providers listed in
PROVIDERS_WITH_PROCESS_GLOBAL_TEST_SIDE_EFFECTS to the Providers[...]
test type whenever a provider whose own files changed sorts after them,
so the PR runs those tests in the order canary does. Dependents pulled
in only for coverage do not widen the selection, and the CI job
description keeps naming the selected providers.
shahar1 added a commit that referenced this pull request Oct 3, 2026
…74123)

A PR that only changes a provider runs that provider's tests, with its
direct dependents, in a pytest process of its own. On canary, the same
tests run inside the shared Providers[-amazon,celery,google,standard]
process, after every provider that sorts before them. cncf.kubernetes
tests reload airflow.executors.executor_loader and leave its state
behind, so a test that mocks ExecutorLoader through the module path can
pass in its PR and fail on every canary build and release-candidate
check afterwards. PR #73832 did exactly that with an edge3 test.

Selective checks now add the providers listed in
PROVIDERS_WITH_PROCESS_GLOBAL_TEST_SIDE_EFFECTS to the Providers[...]
test type whenever a provider whose own files changed sorts after them,
so the PR runs those tests in the order canary does. Dependents pulled
in only for coverage do not widen the selection, and the CI job
description keeps naming the selected providers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providers provider:edge Edge Executor / Worker (AIP-69) / edge3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants