Analyze newly created partitions bypassing the analyze interval
What does this MR do and why?
The partition manager only ANALYZEs a partitioned table on a throttled interval, and that throttle checks the oldest partition's last-analyze timestamp. A brand-new partition can therefore serve queries with zero planner statistics until the interval elapses, letting the planner pick pathological plans — this is what caused INC-13566, where a fresh security_findings partition fell back to a massive parallel sequential scan.
The fix runs the throttled whole-table ANALYZE first (its recurse already covers every partition), and when the throttle skips it, runs ANALYZE (SKIP_LOCKED) directly on each newly created partition after the create/attach transaction commits — so the planner has real statistics from the partition's first query either way.
Resolves #627248 (closed).
How to set up and validate locally
bin/rspec spec/lib/gitlab/database/partitioning/partition_manager_spec.rb- All examples pass; the context "when a partition is created within the analyze interval" shows the newly created partition gets analyzed directly while the whole-table ANALYZE stays throttled.
Supporting evidence
- Scope: the per-partition ANALYZE only runs for models whose partitioning strategy sets
analyze_interval— same opt-in as the existing whole-table ANALYZE. #624175 and !252300 add a defaultanalyze_intervalper strategy; once both land, every partitioned model is covered. - Flag coordination: gated behind the same ops kill-switch
analyze_partitioned_tables_on_rotation(default enabled) as !252300. The flag YAML here is scoped to what this MR guards; whichever MR merges second rebases, drops the duplicate file, and broadens the description once both paths share the gate. - Lock/cost: the ANALYZE runs after the
with_lock_retriescreate/attach transaction commits, so it doesn't extend the SHARE UPDATE EXCLUSIVE lock window. The partition is empty at this point, so the ANALYZE costs milliseconds.SKIP_LOCKEDskips it if another process holds a conflicting lock — now detected viapg_class.reltuplesand logged as a warning. - Failure isolation: ANALYZE errors are rescued per path and logged as "Failed to run ANALYZE on created partitions" / "Failed to run ANALYZE on partitioned table", instead of surfacing under the pre-existing (and by then misleading) "Failed to create / detach partition(s)" message, since creation has already committed. A whole-table ANALYZE failure still falls back to the per-partition pass.
- Verification: RuboCop clean on both changed files.
bin/rspec spec/lib/gitlab/database/partitioning/partition_manager_spec.rb→ 55 examples, 0 failures, 1 pending (pre-existing environment-conditional skip).bin/rspec spec/lib/gitlab/database/partitioning_spec.rb→ 24 examples, 0 failures, 1 pending. The new spec contexts create a real partition while the throttle is satisfied and assert it receivesANALYZE (SKIP_LOCKED)and real statistics (pg_class.reltuples >= 0;-1means never analyzed), plus coverage for the no-duplicate ordering, the whole-table-failure fallback, and the SKIP_LOCKED warning. - Related: production incident INC-13566. An earlier mitigation, !252297, set
analyze_interval: 1.dayonSecurity::Finding— that shrinks the stats-less window but doesn't close it.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.