fix(processing): retry publish with exponential backoff, dead-letter on exhaustion - #476
Merged
Xhristin3 merged 2 commits intoJul 29, 2026
Conversation
…on exhaustion (XStreamRollz#343) Previously, the first publish() failure called fail() which dropped the entire queue and errored the session. This caused permanent data loss for all queued events on any transient API error. Changes: - Rewrite StreamSession.pump() to retry each event with exponential backoff (100ms * 2^attempt, capped at 5s) up to maxPublishRetries times. - After exhausting the retry budget, dead-letter the individual event (emit 'dead-letter', log at ERROR level) and continue processing the rest of the queue — the session stays running. - The session only transitions to 'errored' via the explicit fail() path, which is now reserved for coordinator-level failures (e.g. lock errors). - Add maxPublishRetries constructor param to StreamSession (default 3). - Add maxPublishRetries option to SessionRegistryOptions and thread it through spawn() so the worker can configure it via env var. - Add PROCESSING_PUBLISH_MAX_RETRIES env var (default 3) to config.ts zod schema and document in .env.example. - Wire MAX_PUBLISH_RETRIES into the SessionRegistry constructor in worker.ts. - Update session.test.ts: replace the old 'errors on first failure' test with dead-letter + fail-fast variants; add 4 new tests covering: retry-then-success, dead-letter-and-continue, dead-letter event shape, and session remaining healthy after dead-lettering. Closes XStreamRollz#343
5 tasks
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
Closes #343
StreamSession.pump()previously calledthis.fail(error)on the very firstpublish()failure, which cleared the entire queue and moved the session toerrored. A single transient API hiccup caused permanent data loss for every event behind the failed one.Root Cause
Fix
Each event publish is now wrapped in an exponential-backoff retry loop. After exhausting the retry budget the individual event is dead-lettered and the loop continues — the session stays running and the rest of the queue is processed normally.
The backoff is capped at 5 s per attempt.
fail()is still available for coordinator-level failures (e.g. lock errors) but is no longer called from inside the publish loop.Changes
xstreamroll-processing/src/session.tsmaxPublishRetriesconstructor param (default3)pump()inner loop replaced with backoff-retry logic'dead-letter'(event: StreamEvent, err: Error)on exhaustionrunningstate after dead-letteringxstreamroll-processing/src/session-registry.tsmaxPublishRetries?: numbertoSessionRegistryOptionsoptions.maxPublishRetriestonew StreamSession()inspawn()xstreamroll-processing/src/config.tsAdded
PROCESSING_PUBLISH_MAX_RETRIESto the zod env schema (default'3', min0).xstreamroll-processing/src/worker.tsReads
env.PROCESSING_PUBLISH_MAX_RETRIESand passes it asmaxPublishRetriesto theSessionRegistry.xstreamroll-processing/.env.exampleDocuments
PROCESSING_PUBLISH_MAX_RETRIESwith notes on the fail-fast mode (=0).xstreamroll-processing/__tests__/session.test.tsmaxPublishRetries=0dead-letter test and afail()-is-still-wired testdescribeblock:dead-letterevent carries the originalStreamEventand theErrorTest Results