Skip to content

bug: recurring git-sync stalls permanently after a worker dies mid-submission (leaked deployment concurrency slot) #9925

Description

@rvandepu

Current Behavior

The recurring git_repositories_sync Prefect deployment (entrypoint backend/infrahub/git/tasks:sync_remote_repositories, cron * * * * *, global concurrency limit = 1) stops running permanently after a task-worker is interrupted while one of its runs is in the PENDING / "Submitting" phase.

When a worker acquires the deployment's single concurrency slot and then dies (or the container is restarted) before the run transitions to Running, the flow run is orphaned in PENDING / "Submitting" (run_count=0, start_time=None) and its concurrency slot is never released (active_slots stays at 1/1). Because the deployment concurrency limit is 1, every subsequent per-minute scheduled run is created but sits in Scheduled forever and never executes. The git fetch never runs again — CoreRepository.commit freezes and no new "Sync git repo with origin" tasks appear — even though the schedule is active (status: READY, polled recently) and the worker is otherwise healthy (on-demand / event-driven flows keep completing normally).

Restarting the stack does not recover it: the orphaned run and the leaked slot persist in the Prefect PostgreSQL backend across restarts. The clean-up-deadlocks recurring flow is subject to the exact same mechanism (it also has a concurrency limit of 1) and was itself stuck "Submitting", so it could not clean this up.

Expected Behavior

  • A flow run orphaned in PENDING / "Submitting" that never reached Running should eventually be marked crashed/cancelled and its deployment concurrency slot released, so the recurring schedule resumes on its own.
  • Alternatively, the deployment concurrency slot lease should carry a TTL that expires when the holding worker stops renewing it, so a dead worker cannot hold the slot indefinitely.
  • clean-up-deadlocks should be resilient to this failure mode (not blockable by its own single-slot limit) so it can actually recover leaked slots.

Steps to Reproduce

  1. Configure a git repository so the recurring git_repositories_sync flow runs regularly.
  2. While a "Sync Git Repositories" run is in the PENDING / "Submitting" phase (slot acquired, not yet Running), kill/restart the task-worker container.
  3. Observe the run stays PENDING / "Submitting" (run_count=0, start_time=None), and the deployment concurrency limit reports active_slots=1 of limit=1.
  4. Observe subsequent scheduled runs accumulate in Scheduled and never start; the repository commit stops advancing.
  5. Restart the whole stack — the stall persists.

Additional Information

Diagnostics (Prefect 3.7.5 API):

  • git_repositories_sync deployment: schedule active, paused: false, status: READY, last_polled recent.
  • GET /v2/concurrency_limits/filter -> deployment:<id> shows limit=1, active_slots=1.
  • POST /flow_runs/filter with state=PENDING -> the orphaned "Sync Git Repositories" (and "Clean up deadlocks") runs, state_name="Submitting", run_count=0, start_time=None, stuck for hours.

Manual recovery that worked:

  1. Cancel the orphaned run: POST /flow_runs/{id}/set_state {state:{type:CANCELLED}, force:true} — this alone did not release the slot (the concurrency lease was orphaned from the run).
  2. Reset the leaked slot: PATCH /v2/concurrency_limits/deployment:<id> with body {"active_slots":0}. The next scheduled run then acquired the slot and the fetch resumed normally.

Environment: Infrahub 1.10.5 (Docker Compose), Prefect 3.7.5, task-manager backed by PostgreSQL, host OS Linux x86_64.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions