Skip to content

PlanRegressionSql is ~90% of store statement cancellations: 25-90s dedup sort, no CommandTimeout, and a faster identical-result formulation #2827

Description

@erikdarlingdata

Summary

PgFactCollector.PlanRegressionSql is ~90% of all statement cancellations on the use1 Darling store: 325 of 371 for 2026-09-03, and 31 of 34 in the two hours to 08:50Z. It has no explicit CommandTimeout, so it inherits Npgsql's undocumented 30 s default, and it measures 25–90 s on the busier servers. Every cancellation is then silently swallowed (see #2826).

Grouping every canceling statement due to user request in the store's PG log by the statement that follows it (with multi-line STATEMENT: continuation capture):

statement all day last 2 h
WITH deduped AS (… — this query 325 31
SELECT DISTINCT database_name FROM query_store_stats … 27 2
WITH snaps AS (SELECT DISTINCT collection_time FROM v_index_object_stats … 8 0
WITH per_collection AS (… 4 1
SELECT MAX(last_execution_time) FROM query_store_stats … (#2796's, now bounded) 3 0
four others 1 each 0

The plan is healthy — this is not #2796's or #2820's shape

EXPLAIN (ANALYZE, BUFFERS), server 1671144557, work_mem=31MB:

work_mem is not the constraint, and raising it makes things worse — measured, not assumed:

work_mem Execution Time
31 MB (default) 25,617 ms
512 MB 59,323 ms

Not a one-server outlier: the 3rd-busiest server measures 25,141 ms on the same plan shape. Several servers sit just under the 30 s cliff, which is why cancellation is intermittent (325/day) rather than total.

The dedup is the cost, and there is a faster formulation

Isolating the deduped CTE alone (same filters, same server, count(*) of the result):

formulation time rows
current — ROW_NUMBER() OVER (…) … WHERE rn = 1 23,374 ms 1,724,487
DISTINCT ON (…) 49,807 ms 1,724,681
GROUP BY … + ordered array_agg(…)[1] 9,608 ms 1,724,681

(row-count differences are live-data growth between runs, not semantics — DISTINCT ON and array_agg agree exactly)

The window-function form forces a global sort of ~4M rows on 8 keys. The GROUP BY form lets the planner hash-aggregate at the interval grain and sort only within each small group.

Results are byte-identical. md5 over the full ordered result set, both arms, 6 servers:

server rows ORIG md5 NEW md5 match
1671144557 20 4fef20b1… 4fef20b1… ✓
406978503 20 e9b9ee07… e9b9ee07… ✓
-1782126272 20 97e1b994… 97e1b994… ✓
-1201003152 20 419a73fa… 419a73fa… ✓
1137744987 0 (empty) (empty) ✓
1137774987 20 99aeb51c… 99aeb51c… ✓

Paired timings in that run (ORIG → NEW): 27.8→25.7, 25.5→24.0, 46.1→31.6, 43.6→39.0, 91.0→41.3 s.

Honest limits

Correction to a prior finding

PlanRegressionSql was reported earlier as having "dropped off the cancellation list entirely". It did not — WITH deduped AS ( is PlanRegressionSql's leading CTE. The earlier grouping labelled it by method name and this one by query text; they are the same statement. Its rate fell from ~93/hr at 01:00Z to ~17/hr, tracking the overall decline after #2796 and the retention arming, but it has been the dominant source throughout.

Activity

  1. added 2 commits that reference this issue on Sep 3, 2026
  2. erikdarlingdata commented on Sep 3, 2026

    @erikdarlingdata
    OwnerAuthor

    Resolved by #2830, merged to dev. Closing manually: Fixes #N only fires on the dev->main release merge, so completed work otherwise sits open until release.

  3. added a commit that references this issue on Sep 10, 2026
    f0c659e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions