Skip to content

clients: add opt-in windowed delivery and examples - #529

Draft
Connor Peet (connor4312) wants to merge 8 commits into
mainfrom
connor/client-windowing
Draft

Connor Peet (connor4312) wants to merge 8 commits into
mainfrom
connor/client-windowing

Conversation

@connor4312

Copy link
Copy Markdown
Member

Summary

Adds opt-in windowed delivery to the TypeScript, Rust, Go, Swift and .NET clients, a shared conformance corpus that all five run, and runnable examples for each.

The protocol, the TCP channel and the window-exempt annotations merged in #510 and its stack. This PR adds the client implementations on top of that.

What changed

Shared conformance corpus (types/test-cases/windowed-delivery/): 24 scripted scenarios between one client and a fake host, plus a table of exact UTF-16 sizes. They cover accepting and declining windowing, automatic and manual release, fragments larger than the window, unknown methods being counted, coalescing (enabled, disabled, never across action origins), sends blocking on a full window, outbound fragments, re-subscribing, and protocol violations. Every client runs the same files through a small driver.

Client support (one commit per client): windowing is off by default and opted into per subscription. Each client:

  • offers limits on subscribe, falls back to ordinary delivery when the host declines or the transport cannot provide raw text, and reports the reason;
  • measures each message as its raw text in UTF-16 bytes and returns credit when the consumer releases a delivery, either automatically on advancing to the next one or manually;
  • paces sends to the host window, splits large messages into fragments, schedules subscriptions fairly, and limits how much it queues (waitWritable and the language equivalents);
  • coalesces adjacent queued terminal/data, chat/delta and outgoing tcp/data, and accepts custom coalescers;
  • exposes flow-control status, a status-change stream, and an observe-only event stream;
  • resets counters on reconnect, and connects windowing to the multi-host and managed-subscription layers.

Each client has a changelog fragment.

Generator fix: the Go, Kotlin and .NET generators treated any snapshot with a lifecycle member as SessionState, but TerminalState has one too. They now also require provider, and a terminal snapshot round-trip fixture covers it.

Examples: each client has a basic and an advanced example. They run against an in-memory demo host that follows the window rules, so they need no server.

  • Basic: a terminal subscription opts into windowing. The host lowers the offered 4096-byte window to 1024, pauses each time it fills, and resumes as credit returns.
  • Advanced: a slow renderer lets messages queue. The built-in coalescer joins output lines, and a custom coalescer keeps only the last of several adjacent terminal/resized actions, so 17 host messages arrive as 6–7 renders. Then a paste into a slow terminal waits for the subscription to become writable before each chunk.
Client Run
TypeScript npx tsx examples/windowing-basic.ts
Rust cargo run -p ahp --example windowing_basic
Go go run ./examples/windowing_basic
Swift swift run --package-path clients/swift/Examples WindowingBasic
.NET dotnet run --project examples/WindowingBasic

Each advanced example has the matching backpressure name. Swift's examples are a separate local package, so the published Package.swift is unchanged.

Notes for reviewers

  • Kotlin is not included. It has no windowing support yet.
  • The clients differ in a few places. The first two are worth aligning, here or in a follow-up:
    • When the send queue is full, TypeScript, Go and Swift fail the send with a backpressure error. Rust and .NET wait for room instead.
    • .NET's automatic-release reader does not expose each delivery's size, so its examples use manual release and release each delivery when the loop moves on.
    • Every client except TypeScript reports a negotiated event. Rust also reports a blocked/unblocked pair during negotiation, before any data is sent.
    • Swift escapes / as \/ in outgoing JSON, and .NET's default serializer escapes more characters. Their outgoing messages are slightly larger; the window accounting stays consistent.

Validation

  • npm run generate: no changes. Root npm test: 594 passed.
  • TypeScript: typecheck, typecheck:examples, and npm test (159 passed).
  • Rust: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, and cargo test --workspace (112 passed).
  • Go: gofmt, go vet ./..., go test ./..., and both examples under -race.
  • Swift: root package build and swift test (134 passed). The examples package builds in Swift 6 mode with no warnings.
  • .NET (SDK 10 container): solution build with 0 warnings; dotnet test passed 708 of 709. The remaining test, RealSocketTypeScriptConformanceTests, needs Node.js, which isn't in the .NET SDK container.
  • All ten examples were run, and the advanced examples were checked for the coalesced 132x40 resize (1192 bytes, the sum of the four resizes) and an intact paste.

Go, Kotlin, and .NET chose SessionState for any snapshot with a lifecycle
member, but TerminalState has one too. Require provider as well, and add a
terminal snapshot round-trip fixture.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scripted host conversations and UTF-16 size cases that every client library
runs, so buffering, credit, coalescing, and sending behave the same way in
each language.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Subscriptions can opt into windowed delivery. Credit follows the consumer,
sends wait for the host window, queued data is coalesced, and managed hosts
keep windowed subscriptions across reconnects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Subscriptions can opt into windowed delivery. Credit follows the consumer,
sends wait for the host window, queued data is coalesced, and managed hosts
keep windowed subscriptions across reconnects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Subscriptions can opt into windowed delivery. Credit follows the consumer,
sends wait for the host window, queued data is coalesced, and managed hosts
keep windowed subscriptions across reconnects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Subscriptions can opt into windowed delivery. Credit follows the consumer,
sends wait for the host window, queued data is coalesced, and managed hosts
keep windowed subscriptions across reconnects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Subscriptions can opt into windowed delivery. Credit follows the consumer,
sends wait for the host window, queued data is coalesced, and managed hosts
keep windowed subscriptions across reconnects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a basic and an advanced windowing example to the TypeScript, Rust, Go, Swift, and .NET clients. Each example runs against an in-memory demo host that follows the window rules, so it needs no server. The basic example shows the host pausing at a full window and resuming on credit. The advanced example shows a slow renderer, built-in coalescing, a custom coalescer that keeps the latest terminal resize, and a paste that waits for the subscription to become writable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

1 participant