Skip to content

fix: embed metadata in piped stream fragment #1 by reducing payload#3457

Closed
stephensong wants to merge 2 commits intofreenet:mainfrom
stephensong:fix/pipe-stream-metadata-embedding
Closed

fix: embed metadata in piped stream fragment #1 by reducing payload#3457
stephensong wants to merge 2 commits intofreenet:mainfrom
stephensong:fix/pipe-stream-metadata-embedding

Conversation

@stephensong
Copy link
Copy Markdown
Contributor

Summary

  • Bug: pipe_stream() never embedded metadata in fragment Overall architecture RFC #1 because the size check used packet_data::MAX_DATA_SIZE (1463) instead of the module-level MAX_DATA_SIZE (1422, which accounts for the 41-byte StreamFragment serialization overhead). This made every piped stream rely solely on the standalone UDP metadata message — when lost, the PUT times out.
  • Fix: Reduce fragment Overall architecture RFC #1 payload to make room for metadata (matching send_stream()'s approach). Leftover bytes are prepended to the next fragment and propagate through the chain until the last fragment absorbs them, preserving the fragment count across multi-hop pipes.
  • Without embedded metadata, ~25% of PUT operations through intermediate nodes timeout because the standalone metadata UDP message is the single point of failure.

Details

The metadata embedding feature (fix #2757) provides a reliability backup: metadata is embedded in fragment #1 so that if the standalone UDP metadata message is lost, the receiver can still process the stream. However, in pipe_stream(), the inbound fragment #1 payload (already reduced by the upstream sender) plus the pipe's metadata exceeds the maximum. The old code simply skipped embedding.

The fix:

  1. Uses the correct MAX_DATA_SIZE constant (1422 vs 1463) for the availability check
  2. When metadata doesn't fit, reduces the payload and stores the remainder as leftover
  3. Leftover is prepended to the next inbound fragment's payload
  4. If combined payload exceeds MAX_DATA_SIZE, truncates again and carries forward
  5. The last fragment (typically < MAX_DATA_SIZE) absorbs the final leftover
  6. Fragment count stays constant across any number of hops

Test plan

  • cargo check -p freenet — compiles clean
  • Node integration tests (CREAM): 12/12 steps pass consistently
  • No "Skipping metadata embedding" warnings in logs (the old failure path)
  • Multi-hop piping works: PUT routes through 4+ nodes without fragment count overflow

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 7, 2026

Claude Rule Review: Failed to run

The Claude rule review step failed (likely an authentication or API issue). Check the workflow logs for details.


Advisory review against .claude/rules/. Critical patterns are enforced by the Rule Lint CI job.

stephensong added a commit to stephensong/freenet-core that referenced this pull request Mar 7, 2026
Address review findings on PR freenet#3457:
- Replace .unwrap() calls in tracing::debug! with local bindings
- Replace debug_assert! with actual leftover fragment send for
  degenerate single-chunk streams, preventing silent data loss

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sanity
Copy link
Copy Markdown
Collaborator

sanity commented Mar 14, 2026

@iduartgomez — this PR and its companion #3455 are non-trivial changes to piped stream metadata embedding and overflow fragment handling in the transport layer. They've been open for a week with no review.

Could you take a look at whether this "leftover bytes propagation" approach is the right design? The referenced issue #3451 is already closed, so it's also worth confirming whether these fixes are still needed.

[AI-assisted - Claude]

stephensong and others added 2 commits March 28, 2026 13:43
…yload

pipe_stream() never embedded metadata in fragment freenet#1 because the inbound
payload (from upstream's send_stream()) plus metadata always exceeded
MAX_DATA_SIZE. The check used packet_data::MAX_DATA_SIZE (1463) instead
of the module-level MAX_DATA_SIZE (1422, which accounts for the 41-byte
StreamFragment serialization overhead).

Even with the correct constant, the inbound fragment freenet#1 payload may still
be too large when the pipe's metadata differs in size from the upstream's.
The fix reduces fragment freenet#1 payload to make room for metadata (matching
send_stream's approach) and carries the remainder as leftover bytes that
get prepended to the next fragment. This leftover propagates through
subsequent fragments until the last one absorbs it, maintaining the same
fragment count and avoiding cascading growth across multi-hop pipes.

Without this fix, piped streams rely solely on the standalone UDP metadata
message. When that message is lost or delayed, the receiving node cannot
process the RequestStreaming, causing the entire PUT to timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address review findings on PR freenet#3457:
- Replace .unwrap() calls in tracing::debug! with local bindings
- Replace debug_assert! with actual leftover fragment send for
  degenerate single-chunk streams, preventing silent data loss

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@stephensong stephensong force-pushed the fix/pipe-stream-metadata-embedding branch from 7c261dd to b7babfa Compare March 28, 2026 02:50
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.

GET streaming fails when ResponseStreaming metadata message is lost

3 participants