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

  1. bin/rspec spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
  2. 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 default analyze_interval per 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_retries create/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_LOCKED skips it if another process holds a conflicting lock — now detected via pg_class.reltuples and 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 receives ANALYZE (SKIP_LOCKED) and real statistics (pg_class.reltuples >= 0; -1 means 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.day on Security::Finding — that shrinks the stats-less window but doesn't close it.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Edited by Amandeep Singh

Merge request reports

Loading
Loading