Preserve unread events when NewEvents iteration stops early#549
Open
PratikDhanave wants to merge 1 commit into
Open
Preserve unread events when NewEvents iteration stops early#549PratikDhanave wants to merge 1 commit into
PratikDhanave wants to merge 1 commit into
Conversation
Run.NewEvents advanced the read bookmark to the end of the event buffer at iterator start, before yielding any event. A consumer that stopped iterating early (a break in the range loop) therefore permanently lost every event it had not yet seen: NewEventCount dropped to zero and a later NewEvents call yielded nothing. Advance the bookmark as each event is delivered instead, so an early break leaves the remaining events available to the next NewEvents call. Full-drain callers are unaffected.
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes workflow/inproc.Run.NewEvents() so that an early stop (e.g., break from for evt := range run.NewEvents()) no longer advances the read bookmark past unread buffered events, preventing silent event loss. This aligns the bookmark with what has actually been yielded and adds a regression test to lock in the behavior.
Changes:
- Update
Run.NewEvents()to advancelastBookmarkincrementally per yielded event rather than jumping to the end up front. - Add
TestRun_NewEventsEarlyBreakPreservesUnreadEventsto verify unread events remain available after an early break.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| workflow/inproc/run.go | Adjusts NewEvents() bookmark advancement to prevent dropping unread events on early iteration termination. |
| workflow/inproc/events_test.go | Adds a regression test ensuring unread events remain countable and iterable after an early break from NewEvents(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Run.NewEvents advanced the read bookmark to the end of the event buffer at iterator start, before yielding any event:
A consumer that stops iterating early — an ordinary
breakin afor evt := range run.NewEvents()loop, e.g. stopping once it sees the output event — therefore permanently loses every event it had not yet seen.NewEventCount()drops to0and a laterNewEvents()call yields nothing, even though those events were never delivered.The method already guards early termination with
if !yield(evt) { return }, so early break is an anticipated usage; only the bookmark accounting was wrong.Fix
Advance the bookmark as each event is delivered, so the cursor tracks what the consumer has actually seen:
This yields exactly-once delivery: no drops on early break, no re-delivery on full drain. Every in-tree caller drains fully today, so their behavior is unchanged.
Test
TestRun_NewEventsEarlyBreakPreservesUnreadEventsruns a workflow (6 buffered events), consumes exactly one via an earlybreak, then asserts the remaining events are still reported byNewEventCount()and re-iterable viaNewEvents(). It fails on the old code (remaining reported as0, want5) and passes with the fix.