Skip to content

Commit 160d44e

Browse files
johnfinnerty-nzaduh95
authored andcommitted
doc: clarify QUIC async write backpressure
Signed-off-by: John Finnerty <297514060+johnfinnerty-nz@users.noreply.github.com> PR-URL: #65947 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
1 parent bfa329d commit 160d44e

1 file changed

Lines changed: 32 additions & 8 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,17 @@ There are two ways to write data to a stream:
321321
up front or can be expressed as an iterable.
322322
* **Writer** β€” access [`stream.writer`][] to push data incrementally. The
323323
writer exposes synchronous methods (`writeSync()`, `writevSync()`,
324-
`endSync()`) that return immediately, as well as async equivalents
325-
(`write()`, `writev()`, `end()`) that wait for drain when backpressured.
324+
`endSync()`) that return immediately, as well as asynchronous counterparts
325+
(`write()`, `writev()`, `end()`). The asynchronous `write()` and `writev()`
326+
methods use the stream/iter strict backpressure policy: when the write buffer
327+
is full, they reject with `ERR_INVALID_STATE` instead of waiting for capacity.
328+
If a drain is already pending, `end()` waits for it before closing. Check
329+
`writer.canWrite` before writing. To wait for capacity, use `ondrain()` from
330+
`node:stream/iter`, then retry the write. The stream's `onblocked` callback
331+
reports that transport flow control has blocked progress, but does not
332+
signal that writer capacity is available again.
326333
`writeSync()` returns `false` when the write buffer is full; the caller
327-
should wait for drain before retrying.
334+
should wait with `ondrain()` before retrying.
328335

329336
These two approaches are mutually exclusive for a given stream.
330337

@@ -2322,12 +2329,16 @@ The Writer has the following methods:
23222329

23232330
* `writeSync(chunk)` β€” Synchronous write. Returns `true` if accepted,
23242331
`false` if flow-controlled. Data is NOT accepted on `false`.
2325-
* `write(chunk[, options])` β€” Async write with drain wait. `options.signal`
2326-
is checked at entry but not observed during the write.
2332+
* `write(chunk[, options])` β€” Async write. Rejects with `ERR_INVALID_STATE`
2333+
when the stream is flow-controlled rather than waiting for capacity.
2334+
`options.signal` is checked at entry but not observed during the write.
23272335
* `writevSync(chunks)` β€” Synchronous vectored write. All-or-nothing.
2328-
* `writev(chunks[, options])` β€” Async vectored write.
2336+
* `writev(chunks[, options])` β€” Async vectored write. Rejects with
2337+
`ERR_INVALID_STATE` when the stream is flow-controlled rather than waiting
2338+
for capacity.
23292339
* `endSync()` β€” Synchronous close. Returns total bytes or `-1`.
2330-
* `end([options])` β€” Async close.
2340+
* `end([options])` β€” Async close. If a drain is already pending, waits for it
2341+
before closing.
23312342
* `fail(reason)` β€” Errors the stream (sends `RESET_STREAM` to peer).
23322343
When `reason` is a [`QuicError`][], its [`error.errorCode`][] is used
23332344
as the wire code on the resulting `RESET_STREAM` frame; otherwise
@@ -2337,7 +2348,20 @@ The Writer has the following methods:
23372348
See [`stream.destroy()`][] for a full-stream abort that also resets
23382349
the readable side via `STOP_SENDING`.
23392350
* `canWrite` β€” `true` if writes will be accepted, `false` if at capacity,
2340-
or `null` if closed/errored.
2351+
or `null` if closed/errored. When `writeSync()` returns `false`, use
2352+
`ondrain()` from `node:stream/iter` to wait before retrying. If `ondrain()`
2353+
returns `null`, no drain wait is available and the write should not be
2354+
retried.
2355+
2356+
```mjs
2357+
import { ondrain } from 'node:stream/iter';
2358+
2359+
while (!writer.writeSync(chunk)) {
2360+
const drain = ondrain(writer);
2361+
if (drain === null) break;
2362+
await drain;
2363+
}
2364+
```
23412365

23422366
The bytes from each `writeSync()` / `writevSync()` / `write()` / `writev()`
23432367
input chunk are copied into an internal buffer, so the caller's source

0 commit comments

Comments
Β (0)