protocol_v2: the v2 message schema over BSATN #83
No reviewers
Labels
No labels
area:defold
area:docs
area:infra
area:protocol
area:testing
kind:bug
kind:chore
phase:breadth
phase:core
phase:hardening
phase:slice
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
yuris/spacetimedb-defold!83
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bsatn-phase2-protocol"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 2 of #9. Stacked on #82. Still inert — no connection speaks it yet.
Encodes the five ClientMessage variants, decodes all eight ServerMessage ones, and adds the typed row decode that is the binary twin of
protocol.decode_typed. Rows leave aBsatnRowListstill encoded so the cache decodes them, which keeps row slicing and the frame budget untouched.Two places the binary codec cannot mirror the JSON one, both documented at the site:
crates/lib/src/identity.rsstores it little-endian whileto_hexencodes the big-endian array.Verified: 10 passed, 0 failed on this branch alone. The differential test decodes the same
all_typesrow through both codecs and deep-equals — that is the value-surface proof, and it is non-vacuous (20 scalar columns differ between the two rows exercised).Review round: one BLOCKER, fixed, plus six smaller findings
BLOCKER — unvalidated
RowOffsetsgave quadratic memory amplificationBsatnRowListinRowOffsetsmode carried a server-suppliedarray<u64>of row starts that thedecoder used without checking. The offsets were trusted to be ordered and in bounds; they are neither
guaranteed nor were they verified.
Measured, not theorised: 232 KB of frame produced 388 MB of Lua heap. The mechanism is that
overlapping offsets make every row a near-copy of the whole buffer rather than a slice of it, so cost
grows as frame²/64 — which extrapolates to roughly 15 GB at a 1 MB frame. A single hostile or
merely buggy frame was an OOM.
Fixed by enforcing the offsets as a non-descending, in-bounds partition of
rows_data. That isthe same validation that closes a second, quieter hole: an offset past 2^53 reaches
string.subas adouble that cannot be represented as a
ptrdiff_t, and that cast is undefined. It was notuniformly wrong — on x86-64 it returned the whole buffer, while arm64 and wasm32 saturate the other
way. Identical bytes would have decoded to different values per platform, which on an HTML5 target is
not a hypothetical tier.
The rest
vecof a zero-width type turned 4 wirebytes into 2,000,000 Lua tables — the count was read and trusted with nothing to spend it against.
offers; a frame that decoded and left bytes behind used to pass silently.
nilOption argument now encodes. It was being lost to Lua array-length semantics,so the last argument of a reducer call could vanish.
they live on this branch).
type it cannot handle rather than killing the connection.
M.encode_valuedeleted — unused, and an exported encoder with no caller is a maintenanceliability, not an API.
Benchmark correction (applies to the numbers this stack published): the 10k apply-latency pair of
20.0ms / 10.5ms and its ratio of 0.53 were measured with the machine's load unrecorded. A re-run of the
same commit measured every native absolute 2-3x higher (v1 median 61.7ms, v2 44.6ms over 5 runs) at
loadavg11.06 on 16 cores, with the harness flagging 47% spread on two sweeps, and put the ratio at0.59-0.65, not 0.53. Direction holds, the value does not — #9 and the wiki now state the conditions
instead of the bare digits, and a clean re-measure is an open follow-up. The wire-byte tables are
unaffected and stand as published (v2 ~46% of v1 on row payloads, 31% on client frames): they are byte
counts, not timings.
Verification state at the stack tip (
bsatn-phase5-docs): unit suite 11 passed, 0 failed;all four conformance suites PASS end to end — v1 native, v2 native, v1 HTML5, v2 HTML5 — under the
new protocol-specific gates, whose negative controls were proven non-vacuous;
stylua --check stdb dev/testclean.
bd2643c57e841dd7ed45