Skip to content

fix(app): swap packet_id parity to match Go (server=Even, client=Odd)#30

Merged
singchia merged 1 commit into
mainfrom
fix/packet-id-parity
May 2, 2026
Merged

fix(app): swap packet_id parity to match Go (server=Even, client=Odd)#30
singchia merged 1 commit into
mainfrom
fix/packet-id-parity

Conversation

@singchia

@singchia singchia commented May 2, 2026

Copy link
Copy Markdown
Owner

Summary

D2 (#25) introduced the per-end packet_id allocator with parity reversed from the Go reference:

Go (v1.3.0-rc.2) Rust (D2) Now
Server id.NewIDCounter(id.Even)2, 4, 6, … Odd → 1, 3, 5, … Even → 2, 4, 6, …
Client id.NewIDCounter(id.Odd)1, 3, 5, … Even → 2, 4, 6, … Odd → 1, 3, 5, …

(server/end.go:136 and client/end_options.go:127 are the upstream call sites.)

Why interop still passed despite the bug

Go's actual packet_id is (time.Now().Unix() << 32) | counter — values always sit in the high half of the u64 range. Our Rust allocator uses the small-counter form (1, 3, 5…). So Go's IDs and Rust's IDs are in different magnitude ranges and never collided on the wire even with the parity flipped. The 3 cross-language interop tests passed by accident, not by design.

Aligning now removes a latent footgun — it keeps the protocol matrix boring ("Rust says X iff Go says X") and avoids a real collision the day someone tightens the allocator (e.g. switches to (timestamp << 32) | counter to match Go's spec exactly).

Changes

  • crates/app/src/end_ctx.rs::new — server seed 1 → 2, client seed 2 → 1. Still increments by 2.
  • Doc comments in end_ctx.rs corrected to say "Server: even, Client: odd".
  • Unit test renamed server_side_packet_id_is_odd_client_is_even…_is_even_client_is_odd, assertions flipped.

Test plan

  • cargo test --workspace --all-targets — 145 pass
  • cargo test -p geminio --features interop --test interop — 3/3 pass (Go server + Rust client RPC, Go server + Rust client publish, Rust server + Go client RPC)
  • cargo clippy --all-targets -- -D warnings clean
  • cargo fmt --all -- --check clean

🤖 Generated with Claude Code

D2 introduced the per-end packet_id allocator with the parity
reversed from the Go reference: server walked Odd, client walked
Even. Go does the opposite — `server/end.go` builds the factory
with `id.NewIDCounter(id.Even)`, `client/end_options.go` uses
`id.Odd`.

In practice the reversal didn't break interop because Go's
`packet_id` is `(timestamp << 32) | counter`, so its values are
always in a different magnitude range from our small-counter
allocations and the two never collided on a wire. But the
convention divergence is not load-bearing — there's no reason to
deviate from the reference. Aligning now keeps the protocol matrix
boring ("Rust says X iff Go says X") and avoids a footgun for
future reviewers comparing the two impls side-by-side.

Changes:

  EndCtx::new — server seed 1→2, client seed 2→1 (still ±2)
  doc comments in end_ctx.rs reflect the corrected mapping
  test rename: server_side_packet_id_is_odd_client_is_even →
                _is_even_client_is_odd, with assertions flipped

3/3 interop tests + 145 workspace tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@singchia
singchia merged commit 8a2b455 into main May 2, 2026
8 checks passed
@singchia
singchia deleted the fix/packet-id-parity branch May 2, 2026 07:44
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