Draft: PoC: Reduce SQL queries in the job retry request path

What does this MR do and why?

Proof of concept: removes duplicated SQL queries from the job retry request path (100 → ~78 on the GraphQL jobRetry mutation with CI variables) by reusing records the request has already loaded, with no behavior changes. It is intended as a persisted artifact to be split into focused MRs; see the splitting plan below.

Six independent mechanisms, one commit each:

  1. Share the granular token boundary cache with Authz::Boundary — the request-store cache of boundary records was private to the GraphQL preloader, so ProjectBoundary#namespace reloaded the project namespace when a boundary was built from a separately loaded project instance in the same request.
  2. Reuse the access token validated during request authenticationGitlab::Auth::RequestAuthenticator now captures the token returned by validate_and_save_access_token!. The GraphQL context, DPoP detection (previously a find_by_token digest lookup per request), and granular token authorization (previously a find_by_id) reuse it instead of re-finding the token. Granular authorization also checks the format-level permission before the token lookup, skipping it entirely for formats with no format-level permission (such as GraphQL).
  3. Preserve pipeline/project associations across retry resetsCi::RetryJobService resets the retried and new jobs to pick up fresh column values, but reset is an eager reload that drops the association cache; the service now captures the pipeline up front and re-attaches the known pipeline/project instances after each reset.
  4. Share project and namespace in atomic pipeline processingAtomicProcessingService#load_jobs preloads project: :namespace with available_records, sharing the service's in-memory project and namespace instead of reloading them per batch via with_project_preload.
  5. Reuse loaded records when expiring pipeline ETag cachesCi::ExpirePipelineCacheService runs synchronously during atomic processing and reloaded the pipeline's project, namespace, and both routes from scratch for the upstream_and_all_downstreams path building; it now preloads with available_records so the in-memory records are shared.
  6. Share the job definition record across job retry clones — the clone points at the same immutable, checksum-addressed definition row as the source job, but loaded it three more times (instance presence validation during save, attribute reads on the unsaved clone, and on the post-reset instance); the loaded record is now passed through and re-attached instead.

A further commit removes the now-redundant query threshold override from the REST job retry request spec.

Measured progression (GraphQL job retry with CI variables)

Change Queries
Baseline 101
Boundary cache fix 100
Reuse authenticated token 93
Preserve retry pipeline/project associations 90
Reuse project/namespace during atomic processing 84
Reuse records in ETag cache expiry 80
Share job definition across clones 78

The first five rows were measured with Gitlab::QueryLimiting; the last two were measured with ActiveRecord::QueryRecorder deltas (4 and 2 fewer SELECTs) on the same example and are stated in the same units.

Known remaining duplication (not addressed here)

  • Ci::ProcessBuildService#enqueue reads the job definition on the atomic-processing job instance (one more load of the same row); fixing it means preloading job_definition in load_jobs, which costs two batched queries and only wins for multi-job stages.
  • Three route loads remain from full_path calls on distinct project/namespace instances (Gitlab::UserAccess protected-branch check, Ci::PersistentRef, and CommitStatus#expire_etag_cache!).
  • The ETag path building also queries merge requests and fork networks per status change regardless of whether the pipeline has merge requests.

Split status

  • CI-side MRs (this section is now split out):
    • !247529 (merged) — preserve retry pipeline/project associations (no feature flag)
    • !247531 (merged) — atomic processing available_records (flag ci_atomic_processing_preload_available_records)
    • !247533 — ETag cache expiry record reuse (flag ci_expire_pipeline_cache_record_reuse)
    • !247537 — job definition sharing (flag ci_retry_shared_job_definition, stacked on the first MR)
  • Auth-side MR (groupauthentication / groupauthorization) for the token reuse, DPoP detection, granular authorization, and boundary cache commits: not yet opened. Security-sensitive surface; should carry security and an AppSec ping when opened for review.
  • The REST spec threshold override removal stays here until both sides land.

References

MR acceptance checklist

Evaluated against the MR acceptance checklist.

Edited by Hordur Freyr Yngvason

Merge request reports

Loading