Count tracked usage locally when DataStream is not connected - #134
Open
ryanechternacht wants to merge 1 commit into
Open
ryanechternacht wants to merge 1 commit into
ryanechternacht wants to merge 1 commit into
Conversation
track bumps the cached company metric so the next flag check sees the usage before the server pushes the real figure. The bump was gated on is_connected(), which in replicator mode is the replicator's ready flag. A replicator that reports ready: false (account closed, Schematic unreachable) keeps its cache and flag checks keep evaluating from it, so with the gate the cached metric froze and numeric limits stopped tripping for as long as the replicator stayed not ready. Drop the gate in both replicator and WebSocket mode. The bump only touches a company already in the cache, and the server's next push for that company replaces the metric outright, so it cannot double count. The track event is still sent to the API as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4 tasks
bpapillon
approved these changes
Sep 29, 2026
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.
Problem
In replicator mode the SDK polls the replicator's health endpoint and treats its
readyfield as "connected". When a customer's Schematic account is closed or Schematic is unreachable, the replicator stays up and keeps its Redis cache, but reportsready: false.AsyncSchematic.track(andtrack_with_reservation) bump the cached company metric so the next flag check sees the usage before the server pushes the real figure. That bump in_update_company_metricswas gated onds.is_connected(). Flag checks keep evaluating from the cache while the replicator is not ready, so with the gate the cached metric froze: tracked usage was never reflected locally and numeric limits stopped tripping for as long as the replicator stayed not ready.Findings
Why the gate exists. It was ported from the Go SDK in the original DataStream port (#58), with no discussion of it in that PR's review. In Go it was added with the optimistic metric updates in SchematicHQ/schematic-go#73 (
body.Company != nil && c.datastreamConnected), before replicator mode existed, and #82 later rewrote it asIsConnected(). Neither PR gives a reason for the check. It reads as a copy of the "is the stream up" guard used elsewhere, not a correctness requirement.What the update does when not connected.
DataStreamClient.update_company_metricsreads the company from the cache, adds the quantity to the metric whoseevent_subtypematches, and writes it back under the per-company lock. It never touches the network.track_with_reservationwhose event the server drops on its idempotency key, is handled separately bysettled_locallyand is unchanged.WebSocket mode. The gate is removed there too. A dropped socket does not clear the cache, and
check_flagstill answers from cached entities while disconnected, so the same frozen-metric problem applied. When the socket comes back, pushed company data replaces the bumped value.Change
_update_company_metricsno longer checksis_connected(). It runs whenever DataStream is configured and the event names a company.Test
tests/custom/test_replicator_track.pybuilds anAsyncSchematicin replicator mode on a fake Redis, drives one health poll that returns{"ready": false}, and seeds a company at 95 of a 100 unit limit.check_flagallows,track(quantity=10)enqueues the event and moves the cached metric to 105, and the nextcheck_flagdenies without calling the API. A second test tracks for an uncached company and checks that nothing is written. The first test fails on main.test_track_updates_company_metrics_when_datastream_not_connectedcovers a DataStream client that reports not connected (the WebSocket case) with a mock.poetry run pytest -n auto .: 758 passed, 3 skipped.poetry run mypy .: clean.ruff checkon the changed files: clean.Related: #133 removes the same gate from
check_flags. The two PRs are independent.🤖 Generated with Claude Code