provider-usage 0.6.0: disclose a reading served through an ongoing failure - #12
Merged
Conversation
A preserved last-known-good entry is currently byte-identical to a fresh one apart from fetchedAt, so a consumer cannot separate "this figure is old because the producer cannot reach the provider" from "this figure is old because nothing polled recently". Those have opposite remedies, and a consumer with only a timestamp has to guess with a wall-clock threshold -- which denies fresh-enough data in order to catch stale data. One consumer built exactly that and had a dispatch blocked on a 58-minute snapshot, which was genuinely stale but indistinguishable from a slow poll at the moment it mattered. `since` is deliberately not fetchedAt: the reading was taken when it was taken, and the failure began afterwards. The gap between them is how long the producer has been blind, which is what a staleness policy wants. Additive and absent on a fresh entry, so today's shape is byte-identical and a consumer predating the field decodes unchanged -- both pinned.
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.
Additive field on
ProviderUsage, requested by a consumer that built compensating machinery in its absence.The gap
A preserved last-known-good entry — one a producer keeps serving through a transient upstream failure rather than blanking the window — is currently byte-identical to a fresh entry apart from
fetchedAt. It carries noerrorand noerrorClass, because it is a clone of the last healthy read.So a consumer cannot separate:
Those have opposite remedies. The first is a reason to stop acting on the number; the second is not. With only a timestamp, the only available tool is a wall-clock threshold — which denies fresh-enough data in order to catch stale data.
That is not hypothetical. A consumer of
usage.get(insula#5) reports two pieces of machinery built solely to compensate: a 5-minute TTL on lane verdicts invalidated on wall clock because nothing else can invalidate it, and a fail-closed gate denying dispatches on snapshots older than 10 minutes. Both have fired in production; the sharpest instance blocked a dispatch on a 58-minute-old snapshot. That snapshot was genuinely stale — the producer's refresh interval is 60s, so 58 minutes means an hour of failing fetches — but it was indistinguishable from a slow poll at the moment the decision was made.The shape
Absent on a fresh entry. Present when the entry is a preserved reading served through an ongoing failure.
sinceis deliberately notfetchedAt. The reading was taken when it was taken; the failure began afterwards. The gap between the two is how long the producer has been blind, which is the quantity a staleness policy actually wants — an entry can be minutes old with the producer perfectly healthy, or seconds old with the producer failing since just after the read.classis optional so a producer that cannot classify a failure still discloses the state. Disclosing "stale, cause unstated" beats looking fresh.Compatibility
skip_serializing_if+default, so a fresh entry emits no key and today's shape is byte-identical. Two tests pin both directions: a fresh entry's JSON contains nostalekey at all, and an entry from a producer predating the field decodes unchanged.Four unit tests total,
cargo testgreen, clippy 0, fmt clean.Note on what this is not
A producer serving preserved readings is behaving correctly — a brief upstream failure should not blank a window. This field discloses that it is happening; it does not report a fault. Consumers should read it as "the producer's confidence in this number has a known age", not as an error.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Discloses when a
ProviderUsagereading is being preserved through an ongoing failure. Previously a preserved reading was indistinguishable from a fresh one except forfetchedAt; now astalefield conveys failure start time and optional class so consumers can act correctly.stale: { since: RFC3339, class?: String }onProviderUsageincortexkit-provider-usage0.6.0; absent on fresh entries and only serialized when present.classuses the same vocabulary aserror_class.sinceis when the producer first failed to refresh, notfetchedAt.skip_serializing_ifanddefault; existing producers/consumers continue to interoperate. Tests pin absence/presence and round-tripping.stalewhen serving preserved readings; consumers can gate decisions onstale.sinceinstead of wall-clock TTLs. No required migration.Written for commit 97a7eae. Summary will update on new commits.