Skip to content

feat: restart poisoned streams instead of leaving corrupted state#262

Open
sighphyre wants to merge 1 commit intov6from
feat/restart-poisoned-streams
Open

feat: restart poisoned streams instead of leaving corrupted state#262
sighphyre wants to merge 1 commit intov6from
feat/restart-poisoned-streams

Conversation

@sighphyre
Copy link
Copy Markdown
Member

This terminates a stream and restarts it in the case of poisoned events. In theory this should never happen, however, if for some reason the SDK receives a domain event it cannot process then continuing on is not safe - a rehydration from scratch is necessary to maintain data integrity

hydrated atomic.Bool
recordFailEvent func(failEvent)
subscribe func(*http.Request, ...eventsource.StreamOption) (*eventsource.Stream, error)
restartStreamFn func()
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't see a way to test this behavior nicely without introducing this seam. Not a huge fan but I also don't think it's a disaster

cancel context.CancelFunc

streamMu sync.Mutex
streamCancel context.CancelFunc
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have stream level cancellation, which allows us to yeet the stream and start a new one without breaking the struct's guarantees

}
}
case <-sc.ctx.Done():
case <-streamCtx.Done():
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run stream now binds to a scoped context. If that context dies the reader dies. Before this was struct level, meaning this could only be done once

Copy link
Copy Markdown
Contributor

@gastonfournier gastonfournier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one relatively minor comment (it's an edge case)

Comment on lines +234 to +238
sc.streamMu.Lock()
if sc.streamCancel != nil {
sc.streamCancel()
}
sc.streamMu.Unlock()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be the common practice. It should be fine, but I'm thinking... what if streamCancel() fails... will it remain locked?

Suggested change
sc.streamMu.Lock()
if sc.streamCancel != nil {
sc.streamCancel()
}
sc.streamMu.Unlock()
sc.streamMu.Lock()
defer sc.streamMu.Unlock()
if sc.streamCancel != nil {
sc.streamCancel()
}

@github-project-automation github-project-automation bot moved this from New to Approved PRs in Issues and PRs Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Approved PRs

Development

Successfully merging this pull request may close these issues.

3 participants