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:
- Share the granular token boundary cache with
Authz::Boundary— the request-store cache of boundary records was private to the GraphQL preloader, soProjectBoundary#namespacereloaded the project namespace when a boundary was built from a separately loaded project instance in the same request. - Reuse the access token validated during request authentication —
Gitlab::Auth::RequestAuthenticatornow captures the token returned byvalidate_and_save_access_token!. The GraphQL context, DPoP detection (previously afind_by_tokendigest lookup per request), and granular token authorization (previously afind_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). - Preserve pipeline/project associations across retry resets —
Ci::RetryJobServiceresets the retried and new jobs to pick up fresh column values, butresetis 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. - Share project and namespace in atomic pipeline processing —
AtomicProcessingService#load_jobspreloadsproject: :namespacewithavailable_records, sharing the service's in-memory project and namespace instead of reloading them per batch viawith_project_preload. - Reuse loaded records when expiring pipeline ETag caches —
Ci::ExpirePipelineCacheServiceruns synchronously during atomic processing and reloaded the pipeline's project, namespace, and both routes from scratch for theupstream_and_all_downstreamspath building; it now preloads withavailable_recordsso the in-memory records are shared. - 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#enqueuereads the job definition on the atomic-processing job instance (one more load of the same row); fixing it means preloadingjob_definitioninload_jobs, which costs two batched queries and only wins for multi-job stages.- Three route loads remain from
full_pathcalls on distinct project/namespace instances (Gitlab::UserAccessprotected-branch check,Ci::PersistentRef, andCommitStatus#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(flagci_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