Skip to content

Always resolve while draining the stream#542

Open
peter-leonov-ch wants to merge 1 commit intomainfrom
always_drain_stream
Open

Always resolve while draining the stream#542
peter-leonov-ch wants to merge 1 commit intomainfrom
always_drain_stream

Conversation

@peter-leonov-ch
Copy link
Copy Markdown
Collaborator

@peter-leonov-ch peter-leonov-ch commented Feb 9, 2026

Summary

Make sure that an already ended stream does not hang waiting for the end event.

While writing tests for drainStream() it turned out that for await covers all the tests cases.

Checklist

Delete items not relevant to your PR:

  • Unit and integration tests covering the common scenarios were added

@peter-leonov-ch peter-leonov-ch changed the title hm? [DRAFT] Always drain the stream Feb 9, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@peter-leonov-ch peter-leonov-ch changed the title [DRAFT] Always drain the stream Always resolve while draining the stream Feb 19, 2026
@peter-leonov-ch peter-leonov-ch marked this pull request as ready for review February 20, 2026 09:10
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of stream) {
// consume the stream
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@slvrtrn this is too good to be true 😅 Is there anything I'm missing here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can it throw now? there used to be error handler as well

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes it can. I've only dared to touch this piece after adding a bunch of unit tests for drainStream(). On the way I've fixed a hanging state when drainStream() receives a closed stream which does not emit the end event. This case is covered by the iterator out of the box.

The very reason I've tried the iterator here is that the pattern "check the state => add listeners => listen => remove listeners => resolve / reject" reminded about the even stream => async iterator wrappers in general.

Stateful iterators handle both the streaming bit that we need here and the burdensome init / teardown stages.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Now if we wanted to silence the errors we'd just wrap the loop in a try/catch. It's also easier to measure time inside the function by wrapping the loop in a "span" or console.time()-like construct.

peter-leonov-ch added a commit that referenced this pull request Mar 2, 2026
## Summary

This is a more conservative version of:
* #542

Here we keep the iterator approach for future.

Also, splitting the public API `drainStream()` from the new
`drainStreamInternal()` internal one.

---------

Co-authored-by: Copilot <[email protected]>
Copilot AI review requested due to automatic review settings March 17, 2026 19:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Node client’s stream draining helper to avoid hanging when draining a stream that has already ended, by switching to async-iteration-based consumption.

Changes:

  • Replace manual event-listener-based draining in drainStream() with for await ... of consumption.
  • Rely on async iterator semantics to handle already-ended streams without waiting for an end event.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +21 to +23
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of stream) {
// consume the stream
Comment on lines +21 to +22
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of stream) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants