Skip to content

Clarify pipeToSync() byte accounting when SyncWriter.write() returns false #16

@trivikr

Description

@trivikr

Summary

API.md documents Stream.pipeToSync(source, ...transforms?, writer, options?) as piping a synchronous source to a SyncWriter.

It also documents synchronous writer methods returning boolean, but the required pipeToSync() behavior when a sync write returns false is not explicit.

Should Stream.pipeToSync() treat a false return from writer.write() or writer.writev() as a failed write, and avoid counting those bytes as written?

Current ambiguity

API.md documents sync writer behavior in two related places:

  • Writer.writeSync() / writevSync() can return false as a failure/backpressure indicator.
  • SyncWriter.write() / writev() return boolean.
  • Stream.pipeToSync() returns number, described as the total bytes written.

However, pipeToSync() does not currently specify whether a false return means:

  1. the data was not accepted and pipeToSync() should throw;
  2. the data was accepted but backpressure was signaled, so bytes should still be counted;
  3. the data was not accepted, but pipeToSync() should stop and return the bytes accepted so far.

This matters for byte accounting.

Example

const writer = {
  desiredSize: 1,
  chunks: [],

  write(chunk) {
    if (this.chunks.length >= 1) {
      return false;
    }
    this.chunks.push(chunk);
    return true;
  },

  writev(chunks) {
    return this.write(chunks[0]);
  },

  end() {
    return this.chunks.reduce((n, chunk) => n + chunk.byteLength, 0);
  },

  fail() {},
};

const total = Stream.pipeToSync(['a', 'b'], writer, { preventClose: true });

If the second write returns false and does not accept the data, should total be 1, or should pipeToSync() throw?

It seems incorrect for pipeToSync() to return 2, because only one byte was accepted by the writer.

Possible behavior

Stream.pipeToSync() could specify:

  • If writer.write() or writer.writev() returns true, the batch was accepted and its bytes are counted.
  • If it returns false and the writer did not accept the data, pipeToSync() must not count those bytes.
  • Since pipeToSync() has no async fallback path, a failed sync write should probably cause pipeToSync() to throw.
  • If any policy allows false while still accepting data, the docs should explicitly say that pipeToSync() must count those bytes.

Why

Without this clarification, implementations can silently drop data while still reporting it as written. That makes the number returned by pipeToSync() unreliable.

This also makes it unclear how pipeToSync() should interact with strict backpressure, where a synchronous writer may be unable to accept more data.

Questions

  • Should API.md specify that pipeToSync() throws when a sync write returns false for data that was not accepted?
  • Should SyncWriter.write() / writev() distinguish “not accepted” from “accepted but backpressured”, or should false have one consistent meaning for pipeToSync() byte accounting?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions