Phase 6.1 Stream D follow-up - SealedBootstrap consumes ResilientConfigReader + StaleConfigFlag; /healthz surfaces flag #96

Merged
dohertj2 merged 1 commits from phase-6-1-stream-d-wiring-followup into v2 2026-04-19 11:16:59 -04:00
Owner

Closes release blocker #2 from docs/v2/v2-release-readiness.md.

Summary

  • SealedBootstrap — consumes ResilientConfigReader + GenerationSealedCache + StaleConfigFlag. Routes sp_GetCurrentGenerationForCluster through the timeout → retry → fallback pipeline. Seals a fresh snapshot on every central-DB success.
  • OpcUaApplicationHost gains optional staleConfigFlag ctor param. HealthEndpointsHost now consumes flag.IsStale via its existing usingStaleConfig hook, so /healthz body reflects reality once a fallback fires.
  • Ships alongside the original NodeBootstrap; Program.cs can switch between them once operators are ready for generation-sealed semantics.

Test plan

  • 4 new integration tests: central-DB success seals + flag fresh; central-DB failure falls back + flag stale; no-snapshot + central-down throws clear error; successful bootstrap after fallback clears flag.
  • Full solution dotnet test: 1168 passing (was 1164, +4).
  • Production follow-up: Program.cs switches NodeBootstrap → SealedBootstrap + a HostedService polls sp_GetCurrentGenerationForCluster so peer-published generations land in this node’s cache.

🤖 Generated with Claude Code

Closes release blocker #2 from `docs/v2/v2-release-readiness.md`. ## Summary - `SealedBootstrap` — consumes ResilientConfigReader + GenerationSealedCache + StaleConfigFlag. Routes `sp_GetCurrentGenerationForCluster` through the timeout → retry → fallback pipeline. Seals a fresh snapshot on every central-DB success. - `OpcUaApplicationHost` gains optional `staleConfigFlag` ctor param. HealthEndpointsHost now consumes `flag.IsStale` via its existing `usingStaleConfig` hook, so `/healthz` body reflects reality once a fallback fires. - Ships alongside the original NodeBootstrap; Program.cs can switch between them once operators are ready for generation-sealed semantics. ## Test plan - [x] 4 new integration tests: central-DB success seals + flag fresh; central-DB failure falls back + flag stale; no-snapshot + central-down throws clear error; successful bootstrap after fallback clears flag. - [x] Full solution `dotnet test`: 1168 passing (was 1164, +4). - [ ] Production follow-up: Program.cs switches NodeBootstrap → SealedBootstrap + a HostedService polls sp_GetCurrentGenerationForCluster so peer-published generations land in this node’s cache. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dohertj2 added 1 commit 2026-04-19 11:16:48 -04:00
Closes release blocker #2 from docs/v2/v2-release-readiness.md — the
generation-sealed cache + resilient reader + stale-config flag shipped as
unit-tested primitives in PR #81, but no production path consumed them until
now. This PR wires them end-to-end.

Server additions:
- SealedBootstrap — Phase 6.1 Stream D consumption hook. Resolves the node's
  current generation through ResilientConfigReader's timeout → retry →
  fallback-to-sealed pipeline. On every successful central-DB fetch it seals
  a fresh snapshot to <cache-root>/<cluster>/<generationId>.db so a future
  cache-miss has a known-good fallback. Alongside the original NodeBootstrap
  (which still uses the single-file ILocalConfigCache); Program.cs can
  switch between them once operators are ready for the generation-sealed
  semantics.
- OpcUaApplicationHost: new optional staleConfigFlag ctor parameter. When
  wired, HealthEndpointsHost consumes `flag.IsStale` via the existing
  usingStaleConfig Func<bool> hook. Means `/healthz` actually reports
  `usingStaleConfig: true` whenever a read fell back to the sealed cache —
  closes the loop between Stream D's flag + Stream C's /healthz body shape.

Tests (4 new SealedBootstrapIntegrationTests, all pass):
- Central-DB success path seals snapshot + flag stays fresh.
- Central-DB failure falls back to sealed snapshot + flag flips stale (the
  SQL-kill scenario from Phase 6.1 Stream D.4.a).
- No-snapshot + central-down throws GenerationCacheUnavailableException
  with a clear error (the first-boot scenario from D.4.c).
- Next successful bootstrap after a fallback clears the stale flag.

Full solution dotnet test: 1168 passing (was 1164, +4). Pre-existing
Client.CLI Subscribe flake unchanged.

Production activation: Program.cs wires SealedBootstrap (instead of
NodeBootstrap), constructs OpcUaApplicationHost with the staleConfigFlag,
and a HostedService polls sp_GetCurrentGenerationForCluster periodically so
peer-published generations land in this node's sealed cache. The poller
itself is Stream D.1.b follow-up.

The sp_PublishGeneration SQL-side hook (where the publish commit itself
could also write to a shared sealed cache) stays deferred — the per-node
seal pattern shipped here is the correct v2 GA model: each Server node
owns its own on-disk cache and refreshes from its own DB reads, matching
the Phase 6.1 scope-table description.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit 37ba9e8d14 into v2 2026-04-19 11:16:59 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#96