Repository navigation
Serve replicator-mode flag checks only once the cache is ready - #71
Open
ryanechternacht wants to merge 1 commit into
Open
ryanechternacht wants to merge 1 commit into
ryanechternacht wants to merge 1 commit into
Conversation
This was referenced Sep 30, 2026
Add DataStream::Client#cache_ready?, which in replicator mode reports the replicator's `ready` field and is always true outside replicator mode. check_flag_with_entitlement and check_flags now share one gate, use_datastream_cache?, so single and bulk checks can't drift. The replicator health poll now parses the JSON body whatever the HTTP status, so a 503 from /ready still records its cache_version. It only records a non-empty cache_version, and a failed poll (connection error, timeout, unparseable body) sets not ready and keeps the last version. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Member
Author
|
Rebased onto main after #65 (credit leases). The only conflict was two sets of new methods added at the same spot in lib/schematic/datastream/client.rb; both are kept. Note that #65's client-lease check path in check() reads the DataStream cache without the cache-ready gate, the same as Go's checkWithClientLease and Node's lease path. Whether lease checks should also gate on cache readiness is an open question across SDKs (Python #135 gates it), so this PR leaves it as is. Tests: 812 runs and 201 custom runs pass, rubocop clean. |
ryanechternacht
force-pushed
the
ryan/replicator-cache-ready-gate
branch
from
September 30, 2026 17:50
d32efd8 to
28ce9e4
Compare
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.
Depends on SchematicHQ/schematic-replicator#143. Do not merge before it. That PR changes
/readysoready: true(HTTP 200) means the cache is complete for itscache_version, and a replicator that is still loading answers 503 withready: falseand acache_versionin the body. This gate relies on that meaning.Problem
Ruby already routed replicator-mode flag checks through a readiness check, but only indirectly, and the health poll didn't match the new
/readycontract:check_flag_with_entitlementandcheck_flags(with keys) each gated the cache path on@datastream_client&.connected?, which in replicator mode happens to return replicator readiness. Single and bulk behaved the same (not ready: API path; ready: cache, with API fallback when evaluation errors), but the gate lived in two places and depended onconnected?meaning something other than its name.cache_version.cache_versioneven when the body had none, which replaced the known version with an empty string.Change
DataStream::Client#cache_ready?is new. In replicator mode it returns thereadyfield from the last health poll. Outside replicator mode it returns true, as in Go.SchematicClient#use_datastream_cache?is the one gate that bothcheck_flag_with_entitlement(and socheck_flag) andcheck_flagscall. It isconnected? && cache_ready?. In replicator modeconnected?andcache_ready?report the same readiness. In websocket modecache_ready?is true, so the gate isconnected?, exactly as before.ready, and recordscache_versiononly when it is non-empty. A failed poll (connection error, timeout, or a body that isn't a JSON object) sets not ready and keeps the last knowncache_version. Fail-closed on an unreachable replicator is unchanged; it is still an open question in #143.connected?behaves the same. Its doc comment now says that in replicator mode it reports replicator readiness, and points tocache_ready?.track's local metric update is untouched. It still gates onconnected?. A separate PR (ryan/replicator-track-offline) removes that gate, matching SchematicHQ/schematic-go#239. It edits only thetrackmethod, which this PR does not touch, so the two should merge cleanly.Ruby has no credit-lease prewarm or other non-flag-check cache read besides
track, so nothing else needed a decision here.Parity with Go
This matches SchematicHQ/schematic-go#240:
cache_ready?mirrorsIsCacheReady()(true outside replicator mode), anduse_datastream_cache?mirrorsuseDataStreamCache()at both the single and bulk branches. One difference: Go's websocket mode doesn't gate flag checks on the connection, while Ruby's always has. The Ruby gate keepsconnected?so websocket mode is unchanged.Tests
New
Replicator mode - cache readiness gateblock intest/custom.test.rb. It runs a real replicator-modeSchematicClientover a Redis stand-in seeded the way the replicator writes (flags underflags:<version>:<key>, the company undercompany:<version>:<id>plus a key lookup), with WebMock serving/readyand the API. The cache and the API disagree on both flags, so each result shows which one answered.{"ready": false, "cache_version": "v1"}):check_flagandcheck_flagsboth call the API and return its values. With the API failing, both return flag defaults.{"ready": false, "cache_version": "vX"}sets not ready and records vX, and a later 200 with ready sets ready. An unreachable URL and an unparseable body each set not ready and keep the previouscache_version. A 200 with nocache_versionkeeps the previous one.cache_ready?is true outside replicator mode.Existing mocks of the DataStream client now also define
cache_ready?.Locally, in a
ruby:3.3container to match CI:bundle exec rubocop(1140 files, no offenses) andbundle exec ruby -I lib -I test test/custom.test.rb(200 runs, 423 assertions, 0 failures, 0 errors) pass.🤖 Generated with Claude Code