Add postgres_index_bloat_estimate SQL function
What does this MR do and why?
The view postgres_index_bloat_estimates was producing underperforming plans on instances where catalog statistics have drifted, causing
single-index bloat lookups (the only way the application uses it) to timeout.
The new postgres_index_bloat_estimate(schema, idxname) function:
- Anchors on pg_class with the index name as a parameter, so the outer scan is a direct seek via pg_class_relname_nsp_index.
- Uses CROSS JOIN LATERAL for the bloat math instead of nested subqueries with a ProjectSet barrier, so predicates can be pushed all the way through.
- Splits the pg_stats join into two pushdown-friendly lookups (one against the table for regular columns, one against the index for expression columns) so pg_stats's internal joins can use index seeks instead of scanning thousands of pg_class rows.
PostgresIndex#bloat_size now calls the function directly, removing the PostgresIndexBloatEstimate model entirely. The view is left in place; dropping it is a separate MR / post-deploy migration.
- Here is the execution plan when calling the function - https://console.postgres.ai/gitlab/gitlab-production-main/sessions/52120/commands/153534
- And here is the plan for the underlying query - https://console.postgres.ai/gitlab/gitlab-production-main/sessions/52120/commands/153535
For some reason the first one reports more buffers used (~12 MiB vs ~1.40 MiB).
References
How to set up and validate locally
- Create the function on DBLab clone
- Verify it returns same results as the existing `` view
select *, postgres_index_bloat_estimate('public', split_part(identifier, '.', 2)), bloat_size_bytes = postgres_index_bloat_estimate('public', split_part(identifier, '.', 2)) as equal from postgres_index_bloat_estimates order by bloat_size_bytes desc limit 2
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.