Skip to content

A runtime-precondition miss vocabulary, distinct from the AppliesTo capability axes #2546

Description

@erikdarlingdata

Split out of #2545 so it is tracked as its own work rather than sitting in a comment. This is the shared foundation every extension-backed collector would need, and it is useful on its own.

The distinction

We have two capability axes, both derived from the collectors' own AppliesTo gates:

Both answer "this collector can never run here", and both are properties of the target, fixed for the life of a connection. That is what makes the sweep-and-derive machinery (#2518) sound: every fact a gate reads is a scalar, and the derivation walks a finite cross-product of them.

A missing extension is a different kind of fact, and it does not belong on that axis:

  1. It is mutable at runtime. Engine kind, edition and version cannot change under a live connection. Someone can CREATE EXTENSION pg_stat_kcache while the service is running. A collector gated off at connect time would stay off until a reconnect — silently, and with no way for the operator to tell why installing the extension we recommended changed nothing.
  2. It is set-valued. CollectorTargetInfo carries nine facts today, all bool or int, none set-valued. "Which of these N extensions are installed" is a set, which is 2^N combinations if swept naively. (There is a way out — the IL decoder in CollectorEngineCapabilityDerivationTests already knows which facts each gate reads, so only extensions something genuinely gates on need sweeping — but it is still surgery on the sweep model, and point 1 makes it the wrong surgery.)
  3. The answer is actionable, and the others are not. "Your engine cannot do this" is final. "pg_stat_kcache is not installed — add it to shared_preload_libraries and it will start collecting" is a setup step. Those deserve different words.

What to build

A third miss shape alongside empty / not_collected / unavailable, evaluated at read time rather than at gate time: the read attempts, finds the surface absent, and answers naming the precondition and how to satisfy it.

Because it is re-evaluated per read, an extension installed five minutes ago is picked up on the next cycle — which is exactly what someone following our own advice expects to happen, and is the property an AppliesTo gate could not give.

Why it is worth having independently of extensions

The same shape covers preconditions we already handle inconsistently:

  • Query Store not enabled on a monitored database — a setup step, currently indistinguishable from "no data".
  • The system_health XE session stopped on a SQL Server that supports it — genuinely fixable, and today it lands in the same unavailable bucket as engines that can never have it, which On Azure SQL DB, twelve reads tell you to go start a capture that cannot exist there #2511 deliberately separated at the engine level but not at the session level.
  • HasMsdbAccess — already an AppliesTo fact, and arguably in the wrong place for the same reason: it describes a grant, which someone can change without a restart.

That last one is worth checking early. If msdb access belongs in this vocabulary rather than on the capability axis, this issue is not new machinery for PostgreSQL — it is the right home for something SQL Server already gets slightly wrong.

Relationship to #2545

#2545 (extension-aware PostgreSQL collection) is the umbrella and a stated stretch goal. This is the piece worth doing first and independently: no sweep changes, no IL-guard changes, and it makes each extension-backed collector a self-contained addition afterwards.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions