Add worker to self-heal stuck pipelines

What does this MR do and why?

Adds a cron worker (Ci::StuckPipelines::ProcessWorker) to self-heal pipelines stuck in running state despite having no pending or running builds. This occurs rarely when PipelineProcessWorker is never queued after all builds finish, leaving pipelines appearing to run indefinitely (e.g., during incidents). Re-triggering PipelineProcessWorker is safe since it is idempotent.

The worker runs every 15 minutes and:

  • Finds running pipelines not updated in the last 5 minutes (within a 1-hour lookback window) that have no entries in ci_pending_builds or ci_running_builds
  • Enqueues PipelineProcessWorker for each stuck pipeline, capped at 2,000 per run
  • Uses an exclusive lease (30-minute timeout) to prevent concurrent execution

Gated behind the ci_process_stuck_pipelines feature flag (disabled by default). Expanding to other stuck states (created, pending) is planned as a follow-up.

References

#582085

Database review

The worker executes two new queries. Note that in these examples the search window is 24 hours instead of the 1 hour used in the worker, so that records are returned (DB Lab snapshots can be >1h out of date):

How to set up and validate locally

  1. Enable the feature flag: Feature.enable(:ci_process_stuck_pipelines)
  2. Create a pipeline with some jobs and wait for it to complete
  3. In the Rails console, simulate the pipeline getting stuck: pipeline.update_columns(status: 'running', updated_at: 10.minutes.ago)
  4. Wait for the cron job, or manually execute Ci::StuckPipelines::ProcessService.new.execute and verify the pipeline is now marked as complete.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Tiger Watson

Merge request reports

Loading