Skip to content

Stoic 0.8.1

Pre-release
Pre-release

Choose a tag to compare

@rvaccone rvaccone released this 10 Nov 21:32
Immutable release. Only release title and notes can be modified.

Stoic 0.8.1 makes the HTTP stream buffer an exact bound on Apple platforms, and classifies deep error chains without overflowing the stack.

Streaming: the buffer is a real bound (design/19)

URLSessionTransport.stream now pulls the body from URLSession.bytes(for:) into a ring of streamBufferBytes, and hands the caller chunks of up to 64 KiB.

  • What the transport holds never exceeds streamBufferBytes, whatever the load. When the ring is full, nothing more is taken from the system, which stops reading from the connection at its own window (11 to 13 MB, measured on macOS 27, set by no API), so the server waits.
  • Why it changed. The previous design suspended the data task from its delegate. The system reads ahead of a delegate queue that is not running, and suspending cannot recall what is already queued. Once, on a loaded CI machine, a 512 KiB buffer held 266 MB of a 256 MiB body. With the delegate queue blocked for a second, a probe saw the whole 268 MB read into the process.
  • A trickle is not held for a chunk. A reader that finds less than a chunk waits for one, or for a millisecond, whichever comes first. Events sent one at a time arrive within about 2 ms.
  • The cost is CPU on every byte: about 370 MB/s idle and 290 MB/s loaded over loopback in an optimised build, against 2,800 MB/s before. That is well above what a network delivers.
  • URL loading holds the first 512 bytes of a body that has no Content-Type, to guess one. A server that streams events should name its type.
  • Linux has no bytes(for:). It keeps the delegate path, whose buffer is not bounded there, as before.

Errors: deep chains (design/05)

containsCancellation, containsDefect and firstError recursed once per layer. They overflowed the stack at about 200 layers of an error wrapped in a loop, on every path that classifies an error: retries, scopes, supervisors, presented(). They are loops now, with the same order of checks. Releasing such a chain is Swift's own recursion, which overflows between 2,000 and 5,000 layers in a debug build; Stoic cannot change that.

Breaking

None: no public API changed.

Still owed

0.8.0's compatibility fixtures (design/32), including Presented, new in 0.8.0, are recorded at the next feature release.