Skip to content

rendezvous: stream registry snapshot to disk (drop full-size save buffer) - #130

Merged
TeoSlayer merged 1 commit into
mainfrom
perf/checkpoint-cadence-streaming-snapshot
Oct 1, 2026
Merged

TeoSlayer merged 1 commit into
mainfrom
perf/checkpoint-cadence-streaming-snapshot

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

What

flushSave used to encode the full registry snapshot into a pooled in-memory bytes.Buffer, hash it, splice in the checksum, then hand the entire buffer to AtomicWrite (a second full copy). At fleet scale — ~250k nodes / 8.3M trust pairs / ~191 MB JSON — that buffer dominated the heap and drove GC pauses during saves.

This streams the encode directly to the temp file:

  • json.Encoder writes through an io.MultiWriter(file, sha256).
  • A small trimTrailingNewlineWriter withholds the encoder's trailing \n so the hash covers exactly the bytes load() recomputes (it strips the newline too).
  • The checksum is then backfilled as a trailing JSON member (WriteAt), preserving the on-disk format.
  • atomicWriteStream performs the temp-file → fsync → rename → dir-fsync protocol (same durability as fsutil.AtomicWrite).

Removes flushSaveBufPool / maxPooledSaveBuf.

No change to save cadence — 5 s tick and full-snapshot frequency are untouched. This is a memory/CPU optimization only.

Parity

  • On-disk format, checksum semantics, and recovery (snapshot + WAL replay) are identical.
  • New tests cover the trailing-newline trimming and the atomic stream write.
  • Test suites: go test ./... across the module is green.

Verification (docker, amd64, against the live v0.2.6 base)

Pounded with a local load generator (register + signed heartbeat + signed trust):

  • Snapshot round-trip: crash (SIGKILL) → restart restored nodes and trust pairs from the streamed snapshot; checksum verified.
  • Live deploy: built v0.2.6 + this patch (no cadence change), deployed to pilot-rendezvous-new; 252k nodes preserved, 0 save failures, 0 errors, overlay (directory) green; first post-load save ~19 s vs ~21 s before.

Not included (follow-up)

A checkpoint-cadence reduction (snapshot less often; rely on the WAL between) was prototyped and deliberately dropped: the load test showed the WAL only logs Register/Deregister/Network*/KeyRotation, so trust/hostname/tags/visibility are snapshot-only — lengthening the interval would lose them on crash. That needs the WAL to cover all persistent mutations first.

flushSave previously encoded the full registry snapshot into a pooled
in-memory bytes.Buffer, hashed it, spliced the checksum in, then handed
the whole buffer to AtomicWrite for a second full copy. At fleet scale
(~250k nodes / 8.3M trust pairs / ~191MB JSON) that buffer dominated the
heap and drove GC pauses during saves.

Stream the encode straight to the temp file: json.Encoder writes through
a MultiWriter(file, sha256); a small trimTrailingNewlineWriter withholds
the encoder's trailing newline so the hash matches load()'s
newline-stripped recompute; the checksum is backfilled as a trailing JSON
member via WriteAt. On-disk format, checksum semantics, and crash
recovery (snapshot + WAL replay) are unchanged.

Removes flushSaveBufPool and maxPooledSaveBuf. Verified on the live
v0.2.6 base: identical snapshot bytes/checksum, crash-restart restores
trust pairs from the snapshot, first save ~19s vs ~21s before.

Does NOT change save cadence (5s tick and full-snapshot frequency are
unchanged).
Comment thread server_persist.go
return err
}
if err := fn(f); err != nil {
f.Close()
@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@TeoSlayer
TeoSlayer merged commit 74089a1 into main Oct 1, 2026
9 checks passed
@TeoSlayer
TeoSlayer deleted the perf/checkpoint-cadence-streaming-snapshot branch October 1, 2026 18:47
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.

2 participants