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:
- Skip if the feature flag is off (Rails' default reader runs)
- Skip if the association is already loaded (preserves caching)
- Skip if the foreign key is nil (no spurious query)
- Route the load through
Ci::Pipeline.find_by_id, whichCi::PartitionableFinderoverrides 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 :pipelineBehavior 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_pipelineinline 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.