Skip to content

Add AddMartenHighWaterHealthCheck<TStore>() for stores registered with AddMartenStore - #5716

Merged
jeremydmiller merged 1 commit into
masterfrom
claude/5705-ancillary-high-water-health-check
Oct 19, 2026
Merged

jeremydmiller merged 1 commit into
masterfrom
claude/5705-ancillary-high-water-health-check

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #5705.

The bug

AddMartenHighWaterHealthCheck() only works with the default store from AddMarten():

  • HighWaterHealthCheck takes IDocumentStore, which isn't registered when an application uses only AddMartenStore<TStore>(). The first probe throws Unable to resolve service for type 'Marten.IDocumentStore'.
  • autoRestart only ever resolved the unmarked IProjectionCoordinator, never IProjectionCoordinator<TStore>.

The fix

Two generic overloads, mirroring the existing two (plain filter, and the provider-aware filter from #5061):

Services.AddHealthChecks()
    .AddMartenHighWaterHealthCheck<IInvoicingStore>(staleThreshold: TimeSpan.FromSeconds(30), autoRestart: true);
  • Each call registers a check named HighWaterHealthCheck:<store type>, tagged with the store's name. HighWaterHealthCheckExtensions.HighWaterHealthCheckNameFor<TStore>() returns that name.
  • The check is the new HighWaterHealthCheck<TStore>, a subclass that resolves TStore and overrides a new protected resolveCoordinator to use IProjectionCoordinator<TStore>.
  • Its settings travel with its own registration. The non-generic overloads Replace the HighWaterHealthCheckSettings singleton, so a generic overload doing the same would silently re-configure the default store's check.
  • The staleness tracker stays shared. Its keys start with the database identifier, which is the store's StoreName, so two stores never share a key even when they share a database.

Binary compatibility

No existing signature changes. Everything here is new members: two overloads, one helper, one subclass, and a protected virtual. The generic overloads can't steal existing calls, because TStore can't be inferred, so a call has to name it.

Tests

Marten.AspNetCore.Testing/high_water_health_check_for_ancillary_stores.cs. The hosts are built and never started, so a seeded high-water row stays put, which is the stalled agent the check reports.

Test Pins
a_store_registered_only_through_AddMartenStore_can_be_probed the issue's repro
the_default_check_still_needs_the_default_store the store-less overload still means the default store
a_stalled_mark_on_the_ancillary_store_is_reported detection works on the ancillary store
auto_restart_goes_through_the_ancillary_stores_own_coordinator a recording IProjectionCoordinator<TStore> is the one asked
each_store_keeps_its_own_settings_and_tracking two stores in one database, stalled at different marks, each reported on its own
registering_an_ancillary_check_does_not_reconfigure_the_default_one 1h default threshold stays 1h beside a 200ms ancillary one

Two mutations confirm the tests have teeth:

  • Replacing the shared settings singleton fails registering_an_ancillary_check_does_not_reconfigure_the_default_one.
  • Dropping the coordinator override fails auto_restart_goes_through_the_ancillary_stores_own_coordinator.

These tests live in Marten.AspNetCore.Testing, which CI runs. The existing HighWaterHealthCheckTests live in DaemonTests.ManualOnly, which has no target in build/build.cs and no CI job. They still pass locally (23/23) against this change.

Local, net10.0:

  • Marten.AspNetCore.Testing: 122/122.
  • markdownlint and cspell are clean on the docs page.

Docs

docs/events/projections/healthchecks.md gains a "Separate stores" section. It also notes that replacing HighWaterHealthCheckSettings only affects the default store's check.


Generated by Claude Code

…h AddMartenStore

The high-water health check resolved IDocumentStore, which is not registered
when an application uses only AddMartenStore<TStore>(), so its first probe
threw. autoRestart also only ever asked the unmarked coordinator.

Two generic overloads mirror the existing two. Each registers a check named
HighWaterHealthCheck:<store> that resolves TStore and restarts through
IProjectionCoordinator<TStore>. Its settings travel with its own registration
instead of replacing the HighWaterHealthCheckSettings singleton, which belongs
to the default store's check. No existing signature changes.

Closes #5705.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017FSnat2TKnusp6MdEZgpHj
@jeremydmiller
jeremydmiller merged commit 91b0851 into master Oct 19, 2026
32 checks passed
@jeremydmiller
jeremydmiller deleted the claude/5705-ancillary-high-water-health-check branch October 19, 2026 20:12
This was referenced Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AddMartenHighWaterHealthCheck doesn't support stores registered with AddMartenStore<TStore>()

2 participants