Add timeouts to read-only phases of CI job assignment

What does this MR do and why?

In Ci::RegisterJobService, slow CI/CD variable evaluation can eat a job request's entire 60-second Rack::Timeout. Because that timeout raises an Exception subclass rather than StandardError, it slips past the service's rescue: the build is never dropped, so it either blocks the head of the queue or, more often, is left running with no runner attached once response rendering dies mid-flight. The stuck build then only surfaces much later via the stuck-build sweep.

This MR gives each request a 55-second budget (REQUEST_TIME_BUDGET). The two read-only phases that evaluate variables — pre-assign runner checks and response rendering — run under Timeout.timeout for whatever remains of the budget, raising immediately once it's exhausted. The resulting PhaseTimeoutError is a StandardError, so the existing rescue catches it, drops the build with scheduler_failure, and lets the queue move on. State transitions stay outside the timed blocks so a timeout can never interrupt one. Guarded by the ci_register_job_phase_timeouts feature flag (request scoped actor, disabled by default).

References

Screenshots or screen recordings

Smoke test on GDK — the same job picked up and succeeding in both flag states:

Flag disabled (default) Flag enabled
smoke-flag-off smoke-flag-on

How to set up and validate locally

  1. In a project with a runner, add a minimal CI config: smoke: { script: ['echo ok'] }.
  2. With the flag disabled (default), push a commit and confirm the job runs and succeeds.
  3. Enable the flag with Feature.enable(:ci_register_job_phase_timeouts), push again, and confirm the job still runs and succeeds.

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 Hordur Freyr Yngvason

Merge request reports

Loading
Loading