Add prioritized transport, bounded reconnect, and configurable keepalive

Carry numeric priorities through encrypted RPC and adapter writers with bounded admission. Correct push semantics and socket lifecycle cleanup, expose dispatch gating, and add opt-in connection health.

Preserve the WebSocket setup API while allowing applications to override keepalive settings. Enable Android socket publication and document the 0.7.2-SNAPSHOT release workflow.
This commit is contained in:
sergeych committed 2026-10-15 08:36:34 +04:00
1 parent 0a52bb5c03
commit 617f22dda1
27 files changed
+1154 -295

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
# Numeric transport priority — 0.7.2-SNAPSHOT
```kotlin
import net.sergeych.kiloparsec.call
import net.sergeych.kiloparsec.push
remote.call(command, arguments, priority = 100)
remote.push(notification, arguments, priority = 0)
```
Larger signed `Int` values go first. Equal priorities preserve FIFO. Existing
call/push entry points remain unchanged and use zero unless invoked inside an
explicit priority call context. The overloads are extension functions, preserving
existing RemoteInterface implementers. A coroutine context carries the selected
value through the encrypted RPC wrappers; it becomes serialized outer-block data.
The adapter owns one outgoing writer. Waiting for a response does not occupy the
writer: calls retain independent request IDs and response waiters. Both transport
responses and errors copy the request's priority, including encrypted RPC errors
returned inside an outer response. Pushes have no normal response.
## Wire compatibility
The legacy block tag and body are unchanged. Nonzero priority is appended as four
big-endian two's-complement bytes. Zero omits the trailer entirely. The new reader
defaults to zero only when the trailer is absent; a partial trailer is malformed.
This relies on each Transport.Block having its own finite byte-array frame. TCP's
existing length prefix, WebSocket messages and UDP data blocks provide that
boundary. The old decoder consumes the legacy body and ignores trailing bytes.
Do not use this EOF-terminated extension to concatenate unframed Block objects or
embed them directly in a positional collection; wrap each block in its own bytes.
The handshake remains version 1 because the original wire format is retained.
Old peers interoperate but do not honor/inherit priority. New peers can decode
both formats. BiPack @Extendable is deliberately not added: it would insert a new
field-count prefix before existing fields and break old readers.
## Adapter behavior
TCP, WebSocket client/server, and UDP use a per-connection priority writer.
The writer reads the legacy header lengths and skips payloads to locate priority;
it does not deserialize/copy/decrypt the payload or guess from the last four bytes.
`Transport.Device.send(block)` defaults to the existing output channel for custom
or legacy devices. Built-in ProxyDevice-based adapters override it to enter their
writer directly. The old output channel remains available, bridged into that writer;
new transports should call send so pending senders are visible to priority ordering.
Custom adapters retain their existing FIFO behavior until they implement priority.
Queue admission is bounded to 128 waiting blocks and 32 MiB (plus the block
currently being written). Overflow fails explicitly rather than allocating an
unbounded backlog. Send awaits its adapter write callback, not a remote RPC reply.
Queued cancellation removes the block; cancellation cannot recall a write already
started. Closing the writer releases pending senders. There is no rate limiter or
batch-collection delay. WebSocket writes flush after each block to avoid excessive
look-ahead in the engine's FIFO. Socket/TLS buffers and network loss can still delay
control behind bytes already submitted; priority is not a preemption guarantee.
## Verification
Focused JVM tests cover signed numeric ordering, stable ties, queued cancellation,
bounded admission, close, absent/partial trailers, unchanged legacy bodies, and
priority inheritance through encrypted concurrent calls and encrypted errors.
An encrypted simulated 16 KiB/s link saturated in both directions maintains control
round trips below 400 ms and aggregate utilization above 80% of full-duplex capacity.
This simulation does not model kernel/TLS buffering or packet loss.
JVM, Android and JS compilation and socket/resource lifecycle tests pass.
Caravan also checked call/error/response fixtures with the actual packaged
0.7.1-SNAPSHOT binary and the new 0.7.2-SNAPSHOT binary in separate JVM processes.