feat(coord): consolidate H2 recv/JSON buffers into one dynamically-sized window - #75
Merged
Merged
Conversation
…zed window do_fetch_peers() previously allocated two fixed 512KB PSRAM buffers (H2 receive + a separate JSON parse buffer). Merge them into a single buffer, compacting the extracted MapResponse JSON in place via memmove instead of copying into a second buffer -- halves peak footprint (~1MB -> ~512KB at defaults). The buffer's size is now also clamped at connect time to the largest actually-free heap block (min 64KB, never above the configured ML_H2_BUFFER_SIZE_KB ceiling), via new choose_h2_rx_window_size(). CONFIG_ML_JSON_BUFFER_SIZE_KB is removed -- the merged buffer only needs one size knob. Adapted from djorr5/microlink's `67b230b2` piece (a) (dynamic H2 RX window sizing) -- piece (b), `ip4_route_src_hook`, is unrelated and untouched (tracked separately as #39). Kept this fork's existing frame_buf scratch-then-copy pattern in the initial receive loop rather than reading noise_recv() straight into the shrinking window, since this fork's noise_recv() doesn't drain the ciphertext off the socket when a frame doesn't fit the destination buffer -- reading directly into a near-full window risked desyncing the coordination stream. Verified with a from-clean `pio run` against zen-clock (a real downstream consumer, LilyGo T-Display-S3) using idf_component.yml's override_path pointed at this working tree: full firmware build + link succeeded. The adaptive clamp-under-heap-pressure path itself wasn't exercised on hardware since that board has ample free PSRAM. Closes #38 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Board became available mid-session -- re-flashed zen-clock (LilyGo T-Display-S3) against a real tailnet and captured the actual boot log through do_fetch_peers(): H2 rx window computed correctly (128KB, clamped to the configured ceiling since free heap was ample), a real 22KB/13-frame MapResponse reassembled and JSON-compacted in place without truncation or corruption, and all 5 real tailnet peers completed WireGuard handshakes normally afterward. Upgrades the earlier compile-only verification note. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports piece (a) of
djorr5/microlink's67b230b2— dynamic H2 RX window sizing — tracked as issue #38 inFORK_PRS.mdrow 19. Piece (b) (ip4_route_src_hook) is unrelated and untouched, still tracked separately as #39.do_fetch_peers()previously allocated two fixed 512KB PSRAM buffers: an H2 receive buffer and a separate JSON parse buffer. Merged into one buffer — the extracted MapResponse JSON is now compacted in place viamemmoveinstead of copied into a second buffer, halving peak footprint (~1MB → ~512KB at defaults, less under heap pressure).choose_h2_rx_window_size()(ml_coord.c): largest free block across SPIRAM/internal heap, minus a 32KB safety margin, clamped to[64KB, CONFIG_ML_H2_BUFFER_SIZE_KB]. Stored in a newml->h2_rx_window_sizefield, threaded throughml_h2_build_preface()'s signature anddo_h2_preface()'s window-update math.CONFIG_ML_JSON_BUFFER_SIZE_KBremoved — the merged buffer only needs one size knob now.CLAUDE.md/README.mdupdated to match.Adapted, not a literal port: kept this fork's existing
frame_bufscratch-then-copy pattern in the initial H2 receive loop, rather than readingnoise_recv()directly into the shrinking destination window as the source commit does. This fork'snoise_recv()returns-1without draining the ciphertext off the socket when a frame doesn't fit the caller's buffer — reading straight into a near-full window would risk desyncing the coordination stream. The scratch-buffer indirection avoids that regardless of remaining space.Credit to
djorr5/microlinkfor the original fix.Test plan
pio run -e lilygo-t-display-s3againstzen-clock(a real downstream consumer, LilyGo T-Display-S3/ESP32-S3) withidf_component.yml'soverride_pathpointed at this branch's working tree — bootloader + firmware built and linked with zero errors.dependencies.lockconfirmed the build resolvedfugo101/microlinkfrom the local path, not the registry.husky-firefighter.ts.net). Boot log throughdo_fetch_peers():memmovecompaction didn't corrupt the accumulated H2/JSON data across a real 13-frame, 22KB MapResponse.Closes #38
🤖 Generated with Claude Code