Add Ci::Partitionable::AssociationFinder concern

What does this MR do and why?

This MR introduces a Ci::Partitionable::AssociationFinder concern that prunes ci_pipelines partition scans when a non-CI-database model loads a belongs_to :pipeline association. First consumer: MergeTrains::Car.

Problem

Ci::Pipeline is partitioned. Models in the CI database carry partition_id and prune automatically. Models outside the CI database (Main DB, Sec DB) do not, so Rails' default reader issues SELECT * FROM p_ci_pipelines WHERE id = ? and scans every partition.

There are 18 such unpruned belongs_to :pipeline associations across 17 models in Main DB and Sec DB. The pattern was first addressed with a one-off inline override in !230857 (merged) for MergeRequest#head_pipeline, which is live in production with a measured call-rate reduction.

This MR generalizes that override into a reusable concern and applies it to the first additional consumer.

Solution

Adds Ci::Partitionable::AssociationFinder, a concern with an override_association_loader :assoc_name DSL that redefines the reader of an existing belongs_to association to:

  1. Skip if the feature flag is off (Rails' default reader runs)
  2. Skip if the association is already loaded (preserves caching)
  3. Skip if the foreign key is nil (no spurious query)
  4. Route the load through Ci::Pipeline.find_by_id, which Ci::PartitionableFinder overrides to apply partition pruning

The result is cached on the association proxy via association(name).target = ..., so subsequent reads hit the cache exactly like the default Rails reader.

MergeTrains::Car#pipeline is the first consumer:

include Ci::Partitionable::AssociationFinder

belongs_to :pipeline, class_name: 'Ci::Pipeline'
override_association_loader :pipeline

Behavior is gated behind the partitioned_pipeline_association_finder feature flag (gitlab_com_derisk type).

Eager loading (includes / preload / joins) is not covered. Stale reloads after FK mutation in the same record instance fall back to Rails' default find_target. Both are acceptable: production cost is dominated by single-record lazy loads, not bulk preloads.

Performance impact

  • Happy path (pipeline in current partition): 1 query with partition pruning instead of a full cross-partition scan
  • Fallback path (pipeline in older partition): up to 2 queries, same as Ci::PartitionableFinder's existing miss behavior, but with an optimistic pruned first attempt
  • FF off: zero behavior change

How to validate locally

bundle exec rspec spec/models/concerns/ci/partitionable/association_finder_spec.rb
bundle exec rspec ee/spec/models/merge_trains/car_spec.rb -e '#pipeline'

References

  • Broader parent issue: #593701 (broader unpruned single-id query problem)
  • Issue: #599815 (this work)
  • Pattern source: !230857 (merged) (live MergeRequest#head_pipeline inline override)

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 Madhusudan Vaishnao

Merge request reports

Loading