Skip to content

ApplyToConfig drops ComposeStatementTimeoutSeconds — the held config goes permanently stale against the store #2917

Closed

Description

@erikdarlingdata

StoreConfigProvider.ApplyToConfig assigns every scalar in StoreConfigView that has a same-named DarlingConfig property — except ComposeStatementTimeoutSeconds. Verified:

awk '/public static void ApplyToConfig/,/^    }/' StoreConfigProvider.cs | grep -c ComposeStatementTimeoutSeconds
0

while QueryStoreTextBudgetMb, PlanContentRetentionDays, MaxConcurrentSweeps, PlanXmlCompression, QueryStoreBackfillEnabled and CapturePlans — the other six — are all assigned on the same code path.

The knob is otherwise fully plumbed: seeded to config_service (StoreConfigProvider.cs ~930), selected by the view read (~1217), clamped to [5,600] and mapped into StoreConfigView (~1233/1155), and StoreConfigView.ComposeStatementTimeoutSeconds exists (~1689). Only the last hop is missing.

What this does and does not break

It does not change the effective timeout. Tracing the consumer: nothing reads DarlingConfig.ComposeStatementTimeoutSeconds except the seed. DarlingManagedRoles.ReadComposeStatementTimeoutAsync reads config.config_service.compose_statement_timeout_seconds straight out of the store with its own SELECT, and BuildProvisioningSql bakes it into ALTER ROLE viewer/mcp SET statement_timeout. So an operator's change is delivered by the next managed start, which is #2357's documented design (see the class summary on ComposeStatementTimeoutStoreTests and the doc comment on StoreConfigView.ComposeStatementTimeoutSeconds, both of which say "next restart" and are correct).

What it does break is the held config's truthfulness. config.ComposeStatementTimeoutSeconds reports the darling.json value for the life of the process while config_service says something else, and the doc comment on DarlingConfig.ComposeStatementTimeoutSeconds actively invites a reader to trust it — "the store is authoritative afterwards, like every other value here". Any future consumer that reads the held value silently gets the file value. That is a landmine, not a live defect.

It is also an inconsistency in the seam's contract rather than an instance of a design. #2862's plan-capture cadence deliberately relies on "a property ABSENT from StoreConfigView survives a reload untouched"; this one is present in the view and dropped anyway, which is neither of the two intended behaviours.

Fix

One assignment in ApplyToConfig, plus a correction to the DarlingConfig doc comment so it states the restart-scoped delivery the view's comment already states.

The pin worth having

Every existing test on this path names its own knob, which is why the seventh was dropped and why the eighth would be. A family-derived pin over the real types — every StoreConfigView scalar with a same-named, same-typed, settable DarlingConfig property must round-trip through ApplyToConfig, with the deliberately-unmatched scalars (ConfigVersion, Paused, the four Mcp*/Web*) pinned as a set with written reasons — covers knobs that do not exist yet. ApplyToConfig takes no I/O, so it is a pure test.

Follow-up on the delivery scope itself is separate.

Activity

  1. added a commit that references this issue on Sep 4, 2026
  2. erikdarlingdata commented on Sep 4, 2026

    @erikdarlingdata
    OwnerAuthor

    Fixed by #2919, merged to dev as 7333ccb. (Auto-close didn't fire because this repo's default branch is main and the PR merged to dev.)

    The propagation fix and the family pin are on dev. The delivery question — a reload observes the new value without re-asserting it onto the roles — is #2918 and stays open.

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