CONCEPT Cited by 1 source
Plan cost to wallclock constant k¶
Definition¶
The k constant is a per-(query-pattern × host) time-varying calibration factor that translates Postgres's dimensionless planner cost estimate into an estimated wall-clock execution time:
The name is deliberately ironic — "the magical not-quite-constant k" — because k is a constant only within a narrow (pattern × host × short time window) scope. It varies:
- Per query pattern. Two queries of equal plan cost but different shape (e.g. index lookup vs. aggregation) have systematically different
k. - Per host. CPU speed, page-cache state, disk IOPS, concurrent load all vary the wall-clock impact of a given cost unit.
- Over time. Tables grow, workload mix shifts, cache temperature changes.
Why k is needed¶
Postgres's query planner emits plan cost in dimensionless units — the integer output of a weighted sum over the planner's cost constants (seq_page_cost, random_page_cost, cpu_tuple_cost, etc.). The planner uses these costs only to pick the cheapest plan relative to the other candidates for the same query; it does not guarantee calibration across queries or across hosts. "The cost is measured in dimensionless units and is based on configurable weights assigned to each step the plan will take." (Source: sources/2026-04-21-planetscale-behind-the-scenes-how-database-traffic-control-works.)
For admission control the user needs a wall-clock estimate, which requires multiplying cost by a real-world-seconds-per-cost-unit factor. That factor is k.
Why k is not literally constant¶
From the source:
"Plan costs are roughly linear (a plan with double the cost should take something like double the time and resources to execute), and the relationship between plan costs and real-world resources is heavily dependent on what query you're running, what server you run it on, and what else is happening on that server at the moment … We assume that there is an unknown constant k that we can multiply the plan cost by, to get the actual wall-clock time it will take to execute that query. But that constant is different for each query pattern and for each host. The constant may also change over time as the workload mix on the server changes and as tables grow and change. So it's not exactly a constant!"
Reynolds's answer is learn k per-pattern from observation, not estimate it from first principles.
Implementation: EMA of observed cost and observed time¶
"Traffic Control implements a hash table on each host, mapping query patterns to two averages: CPU time and planner cost estimates. Both are exponential moving averages, heavily weighting recent queries. Every time a query completes, we update both of those averages. The magical not-quite-constant k is the ratio of the two."
So k is not stored directly. Instead:
per-pattern state:
EMA_cost = exponential moving average of observed planner cost
EMA_wallclock = exponential moving average of observed wall-clock CPU time
derived:
k = EMA_wallclock / EMA_cost
On every query completion both EMAs update, and k is recomputed (or computed lazily at the next pre-execution check). Both the numerator and denominator are observed quantities — the planner cost is the same dimensionless number the planner emitted pre-execution, and the wall-clock is measured by the ExecutorRun hook entry/exit timestamps (see concepts/postgres-hook).
Pattern grouping¶
The hash-table key is a query-pattern fingerprint — an AST-normalised form of the SQL that groups semantically equivalent queries together regardless of literal values. Two SELECT * FROM users WHERE id = 42 and SELECT * FROM users WHERE id = 99 share one pattern and one pair of EMAs.
Use in admission control¶
In Traffic Control's four-check pre-execution sequence, k feeds two of the checks:
- Per-query limit check — is
plan_cost(q) × k > budget.per_query_limit? - Cumulative-cost check — would
plan_cost(q) × koverflow the budget's reverse leaky bucket?
Caveats¶
- Cold-start for new patterns. The first query with a never-seen pattern has no EMA; either a default
kis used or the query is admitted unconditionally. The source doesn't specify. - Drift for evolving patterns. A pattern whose table grows 10× sees its wall-clock EMA lag the new reality until enough samples accumulate. Short EMA half-life fixes drift faster but amplifies noise.
- Mis-admitted queries. If
kunder-estimates wall-clock for a given pattern, queries admitted under the cumulative-cost check can overshoot their budget in aggregate. Mitigated but not prevented by the EMA update. - Plan-cost units are not time.
kis the conversion; don't expectplan_cost × fixed_constantto be meaningful across patterns.
Seen in¶
- sources/2026-04-21-planetscale-behind-the-scenes-how-database-traffic-control-works — canonical wiki source. Reynolds frames the
kestimator as the load-bearing piece making pre-execution admission control viable inside Postgres: without it, the four-check sequence can verify concurrency but not cost.