fix(server): cache strict-mode VALIDATION_ERROR to break retry-storm loop (#758)#798
Merged
Conversation
…loop (#758) Under the strict response-validation default, a drifted handler used to release its idempotency claim on the way out, letting a retrying buyer re-enter the handler indefinitely with the same drift. Dispatcher now caches the VALIDATION_ERROR envelope under (principal, key, payloadHash) for 10s so same-key retries short-circuit instead of re-executing. IdempotencyStore.saveTransientError is optional — custom stores fall back to release() when absent, preserving the pre-758 behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
VALIDATION_ERRORand released its idempotency claim — letting a retrying buyer re-enter the handler indefinitely with the same drift. This PR caches theVALIDATION_ERRORenvelope under the existing(principal, key, payloadHash)tuple for 10 seconds so same-key retries short-circuit to the cached error instead of re-running side effects.IdempotencyStore.saveTransientError(...)is optional on the interface — custom store implementations that omit it fall back torelease()(pre-idempotency: strict-mode VALIDATION_ERROR replay loop (unbounded re-execution) #758 behavior). Stores built viacreateIdempotencyStorepick it up automatically, so existing users get the fix for free.injectReplayed(true)on cached error envelopes —replayedis a success-replay field.IDEMPOTENCY_CONFLICT, and buyers generating fresh keys per retry are not short-circuited (backoff is the correct control point there).Why this shape
See
gh issue view 758for the option matrix. Option 2 (short-TTL error cache) won over Option 1 (circuit breaker) because (a) same-key retry storms are the dominant failure mode under strict mode, (b) it reuses existing scope infrastructure, and (c) it avoids a new per-tenant/per-handler counter abstraction.Code-reviewer and security-reviewer passes were run on this diff and their
Should Fixitems are addressed in this PR:patchsemver honest.SERVICE_UNAVAILABLE(in-flight), not re-execution (code review).VALIDATION_ERRORrates per principal" — the drifted-handler-plus-hostile-buyer pair is a cache-fill vector that warrants monitoring (security review).Test plan
npm test— 5245 pass / 0 failtest/server-idempotency.test.js:IDEMPOTENCY_CONFLICTSERVICE_UNAVAILABLE(in-flight)patch(.changeset/idempotency-transient-error-cache.md)🤖 Generated with Claude Code