Repository navigation
Performance: only plan the dynamic severity branches when they are used - #3081
TheBeast85 wants to merge 1 commit into
Conversation
|
I reviewed the latest iteration at exact head VerdictI found no P0-P2 correctness or architectural issue. The split is sound, is located at the correct PostgreSQL-function boundary, preserves the existing cache/fallback contract, and demonstrably avoids planning the expensive dynamic body on cached hits. I would treat the implementation as mergeable after rebasing onto current The main weakness is test coverage: the PR adds no automated regression for this central report-severity path. I also found one non-blocking wording nuance around inlining. Correctness and architecture
Goal validationI reproduced the old and new SQL shapes on PostgreSQL 17 and compared nine cases: valid cached value, user-specific cache selection, expired cache, null cached aggregate, uncached static severity, dynamic severity, static override, dynamic override, null result type, and no results. Every old/new result was identical, including null behavior. With
Non-blocking wording nuance: Test coverageThe PR adds no tests. The existing C suite does not create these PostgreSQL functions or exercise cached/dynamic report severity, function volatility, or planning behavior. Recommended additions:
Independent verification
Agent: GEA |
report_severity was one function body, and that body was planned again on every call. Measured on the severity aggregate with pg_stat_statements.track_planning on, that was 1479 ms of planning across 5047 calls against 30 ms of execution. Marking it STABLE and taking out its CTE did not help: task_severity calls report_severity from its own SQL body and task_severity is itself not inlinable, its body has a subquery with FROM, so every row creates a fresh call context. What costs the planning is not the common case. Almost every call is answered by the cached value in report_counts, a single index lookup, while the four dynamic branches underneath scan the results table and are hardly ever evaluated. A function body is only planned when the function is invoked, and coalesce stops at the first non null argument at execution time. Split into report_severity_cached, report_severity_dynamic and a report_severity that is a coalesce of the two, the expensive body is no longer planned at all; it stops appearing in pg_stat_statements. This does not depend on report_severity being inlined into the caller. The severity functions are marked STABLE, as they only read. The severity aggregate goes from 0.351 s to 0.098 s. Severity values and the aggregate are identical character for character with the reference taken before the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Head branch was pushed to by a user without write access
9121cef to
d12266a
Compare
|
Rebased onto current main, no conflicts; the extended |
|
@TheBeast85 if that is OK with you, I'll merge #3206 into this branch? |
|
@daniel-grnbn Yes, please go ahead and merge #3206 into this branch. Thanks for the tests! |
What
report_severityintoreport_severity_cached,report_severity_dynamicand areport_severitythat is acoalesceof the two.STABLE.Why
report_severitywas one function body, and that body was planned again on every call: 1479 ms of planning across 5047 calls against 30 ms of execution, measured on the severity aggregate withpg_stat_statements.track_planningon.Almost every call is answered by the cached value in
report_counts, a single index lookup. The four dynamic branches underneath scan theresultstable and are hardly ever evaluated, yet every call paid to plan them. A function body is only planned when the function is invoked, andcoalescestops at the first non null argument at execution time, so moving the expensive branches into their own function means they are only planned when there is no cached value. They stop appearing inpg_stat_statementsaltogether.STABLEalone did not help, and neither did removing the CTE:task_severitycallsreport_severityfrom its own SQL body and is itself not inlinable, so every row creates a fresh call context.The severity aggregate goes from 0.351 s to 0.098 s. Severity values and the aggregate are identical character for character with the reference taken before the change.
References
Split out of #3067 as requested.
Checklist