Repository navigation
fix: flush handling (port of nodejs/node#66473) - #259
Merged
Merged
Conversation
- flush() with minLength 0 now waits for in-flight writes before calling the callback. - end() waits for pending flush callbacks before closing. - Concurrent flush() calls are tracked with counters. - flush() no longer fsyncs stdout/stderr and ignores EBADF. - Periodic flushes do not stack while a flush is pending. - end()/reopen()/destroy() while opening no longer double-close or throw; pending flushes fail with an error when the stream is destroyed. The flushSync() change from the upstream PR (throwing while a write is in progress) is intentionally left out as it is breaking.
mcollina
force-pushed
the
fix/flush-handling
branch
from
October 4, 2026 17:47
d9ad48b to
3026b23
Compare
jsumners
approved these changes
Oct 4, 2026
- flushSync() preserves ordering where it can: it cancels a pending EAGAIN/EBUSY retry and writes everything in order, writes the remainder of a partial write first when called from a 'write' listener, and writes to the previous file while reopening. With an async write in flight, only the queued data is written. - 'write' is emitted after the written bytes are released. - flushBufferSync() keeps _lens in sync when re-queuing _writingBuf. - flush() fsyncs stdout/stderr again (useful when redirected to a file) and ignores EBADF/EINVAL/ENOTSUP/EOPNOTSUPP/EROFS from fsync.
Align with nodejs/node#66473: when destroy() is called while a write is in flight, a pending flush() now succeeds if all the data was written, and fails only if data is left. A pending EAGAIN/EBUSY retry is cancelled on destroy instead of writing to the closed fd.
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.
Ports the fixes from nodejs/node#66473 (
Utf8Streamis a port of sonic-boom, and the same bugs exist here).cc @jasnell
flush()flush(cb)withminLength: 0calledcbsynchronously while a write was still in flight. It now waits for pending writes.flush(cb)followed byend()(withminLength > 0) never calledcb.end()now waits for pending flushes before closing.flush()calls are tracked with counters instead of a shared boolean.EBADF,EINVAL,ENOTSUP,EOPNOTSUPP,EROFS, e.g. stdout on a pipe or TTY) no longer fail the flush. stdout/stderr redirected to a regular file are still fsynced.flushSync()No longer throws or changes behaviour in the common case; it preserves ordering where possible:
EAGAIN/EBUSYwrite, it cancels the retry and writes everything synchronously, in order.'write'listener, it writes the unwritten remainder of a partial write before newly queued data ('write'is now emitted after the written bytes are released).flushBufferSync()now keeps_lensin sync when re-queuing the current write buffer.Lifecycle
end()+destroy()while the file is opening crashed with an uncaughtEBADF(double close);destroy()+end()threw from thereadylistener. Both now close cleanly.destroy()with a write in flight lets it complete: a pendingflush(cb)succeeds if all the data was written, and receives aSonicBoom destroyederror otherwise. With nothing in flight, it fails immediately.destroy()no longer continue writing to the closed fd, and a pending EAGAIN/EBUSY retry is cancelled.Differences from upstream
destroy(), upstream emits a public'error'; here only the flush callback receives the error. The destroy-while-opening case is aligned with upstream in a follow-up PR.Tests
New cases in
test/flush.test.jsandtest/flush-sync.test.js(sync and async, utf8 and buffer modes) fail without the fix and pass with it. READMEflush()/flushSync()docs updated.