Add a Redis state provider, the last item on the roadmap - #39
Merged
Conversation
State is written on every tick of every checker. A relational provider does a round trip to a disk-backed engine for each of those; this does one to memory. That is the whole reason to pick it, and it is why the roadmap asked for it. One hash per checker, holding the state, the type it was written as, and a version. A hash rather than a plain string so the version can be compared and swapped in the same command as the write: TrySetStateAsync is a Lua script, and Redis runs one to completion without interleaving anything else. WATCH/MULTI/ EXEC would also work, but it needs its own retry loop around the optimistic failure and it holds state on the connection; a script needs neither. Creating is the same shape against EXISTS, so two writers that both find nothing cannot both create. That last property is asserted against a real Redis rather than argued for: twelve writers read one version, race to write it, and exactly one wins. A fake would only have restated what the provider already believes, so the tests run against redis:7-alpine in a container through Testcontainers, and skip rather than fail where there is no container runtime -- the same bargain the CosmosDB combinations strike in the E2E project. GetStatesAsync issues every read before awaiting any. StackExchange.Redis pipelines on one connection, so the dashboard listing every checker costs one round trip rather than one per checker. Durability is whatever the Redis is configured for -- snapshots, AOF, or a managed offering's own promises -- and the README says that rather than implying the provider can offer more than the server does. The roadmap is now empty. What remains in its place is written as what it is: two decisions, not two features.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The last item on the README roadmap: "A Redis state provider — the fastest option for state written on every tick."
State is written on every tick of every checker. A relational provider does a round trip to a disk-backed engine for each of those; this does one to memory. That is the whole reason to pick it.
Optimistic concurrency, in one command
SupportsOptimisticConcurrencyistrue, and it earns it. One hash per checker holds the state, the type it was written as, and a version — a hash rather than a plain string so the compare and the write can be a single Lua script, which Redis runs to completion without interleaving anything else:WATCH/MULTI/EXECwould also work, but it needs its own retry loop around the optimistic failure and it holds state on the connection. A script needs neither. Creating is the same shape againstEXISTS, so two writers that both find nothing cannot both create.Tested against a real Redis, not a fake
The guarantees here are Redis's own, so a fake would only restate what the provider already believes. The tests run against
redis:7-alpinein a container via Testcontainers, and skip rather than fail where there is no container runtime — the same bargain the CosmosDB combinations strike in the E2E project.The property that matters is asserted rather than argued: twelve writers read one version, race to write it, and exactly one wins.
15 Redis tests, 463 total green on both net8.0 and net10.0, zero build warnings.
Other details
GetStatesAsyncissues every read before awaiting any. StackExchange.Redis pipelines on one connection, so the dashboard listing every checker costs one round trip rather than one per checker.healthie:state:by default) so the provider stays clear of whatever else is on that server.AddHealthieRedis()with no configuration string reuses anIConnectionMultiplexerthe application already registers, rather than opening a second connection to the same server.Durability is Redis's, not this package's
Redis is in memory. Whether state survives a restart is your Redis configuration, not something this provider can promise, and the package README says so instead of implying otherwise.
The roadmap is now empty
Every item on it has shipped. What replaces it is written as what it actually is — two decisions, not two features: the
Healthie.Api/ dashboard authorization defaults, and restore not being hash-pinned. Both are deliberate as they stand and both are yours to make.No release — version stays at 3.1.4.