Skip to content

Stamp every inbound message when its last byte arrives - #179

Merged
kahrendt merged 3 commits into
mainfrom
receive-stamp-by-size
Oct 7, 2026
Merged

kahrendt merged 3 commits into
mainfrom
receive-stamp-by-size

Conversation

@kahrendt

@kahrendt kahrendt commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

A message's receive time is when its last byte arrived at the transport. Every inbound message now gets the completion stamp, which the transports pass to end_inbound_message() / end_inbound_fragment(); begin_inbound_message() no longer takes a stamp, and the ESP httpd handler-entry stamp is gone.

Measured on an ESP32 httpd connection: the completion stamp is ~140 us later than the old handler-entry stamp for a server/time reply (a fixed ~70 us offset shift, which GPS ground-truth runs show moves the observed bias in the right direction) and 1 to 5 ms later for an audio chunk, with rare stalls of tens of ms under load. Under ring pressure the httpd stamp also includes any wait for ring space.

  • No ring header change.
  • Host (IXWebSocket) delivers whole messages, so both stamps are the same there.

Testing: host ASan + TSan, esp32s3 build. New table test covers each route (ring, fallback, multi-frame into either). Not run on hardware.

Implements Sendspin/spec#298

A message up to SendspinConnection::SINGLE_SEGMENT_MESSAGE_BYTES keeps the
stamp from when the transport first saw it; a longer one gets the completion
stamp the transport now passes to end_inbound_message().
@kahrendt
kahrendt marked this pull request as ready for review October 7, 2026 13:16
@kahrendt
kahrendt requested a balanced review from Copilot October 7, 2026 13:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Fragmented messages at or below the cutoff incorrectly retain the first-fragment timestamp instead of the final-fragment completion time.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Updates inbound receive timestamps so large messages use completion time while preserving first-sight timestamps for single-segment messages.

Changes:

  • Pass completion timestamps through host and ESP transports.
  • Apply size-aware timestamps to ring and fallback routes.
  • Add boundary and routing coverage.
File Description
src/​connection.h Extends inbound completion APIs and defines the segment cutoff.
src/​connection.cpp Applies completion timestamps to large inbound messages.
src/​esp/​client_connection.cpp Passes ESP client event timestamps.
src/​esp/​server_connection.cpp Captures ESP server payload completion time.
src/​host/​client_connection.cpp Passes host client receive time.
src/​host/​server_connection.cpp Passes host server receive time.
tests/​loopback_connection.h Exposes the cutoff to tests.
tests/​test_noise_transport.cpp Adds boundary and route coverage.
tests/​test_client_lifecycle.cpp Updates the completion API call.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/connection.cpp
Its fragments may arrive in separate segments, so the first fragment's stamp
can be early; begin_inbound_fragment() no longer takes a stamp.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The size heuristic cannot guarantee single-segment arrival and can retain a pre-completion timestamp contrary to the referenced specification.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Always publish completion timestamp after final byte arrival

src/​connection.h:904

A payload being small enough to fit one TCP segment does not guarantee that it arrived in one segment: the sender can write the WebSocket header and payload separately, TCP can emit smaller segments, and a transport can deliver multiple chunks. In those cases this heuristic preserves a timestamp from before the last byte arrived, contradicting spec PR 298's requirement that the receive time be the final byte's arrival. Please always publish the completion timestamp in the connection layer; transports that receive a whole message atomically can pass the same value for the start and completion times (as the host paths already do).

Low severity Explain rationale for inspecting private timestamp state

tests/​test_noise_transport.cpp:2704

This test reads the ring header and pending-message internals without explaining why no caller-visible outcome can distinguish the timestamp path. docs/conventions.md:246-249 requires that rationale whenever tests inspect private state. Please either assert through an observable consumer outcome or document why doing so cannot isolate this transport timestamp behavior.

A message that fits one TCP segment can still arrive in more than one (the
sender may write the header and payload separately), so the first-sight
stamp is dropped: the transport's completion stamp is every message's
receive time, and begin_inbound_message() no longer takes a stamp.
@kahrendt kahrendt changed the title Stamp messages longer than one TCP segment when they finish arriving Stamp every inbound message when its last byte arrives Oct 7, 2026
@kahrendt
kahrendt requested a balanced review from Copilot October 7, 2026 15:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The transport paths are consistently updated and the new tests cover ring, fallback, and fragmented-message timestamp propagation.

0 open findings

🧠 Review effort: Balanced

@kahrendt
kahrendt merged commit 66b14e2 into main Oct 7, 2026
17 checks passed
@kahrendt
kahrendt deleted the receive-stamp-by-size branch October 7, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants