feat(archon,horismos): live QUIC dual-endpoint drain + HTTP listener rebind on config reload (#529 step 5) - #585
Merged
Merged
Conversation
…rebind on config reload (#529) Gate-Passed: kanon 0.1.5 +stages:fmt,check,clippy,nextest,lint sha:69d679041a23d0c5fa8caf209ec38e734c08479d
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.
Step 5 of the #529 reactive-config migration — the QUIC and HTTP listen endpoints rebind live on a config reload, with the operator-chosen dual-endpoint + unbounded-drain posture (no dropped connections, no downtime window).
New config field
paroche.renderer_quic_port: u16(serde default 4433, replacing the hardcodedDEFAULT_QUIC_PORT). Validation rejects< 1024and== paroche.port.QUIC supervisor (
render/server.rs) — the cancellation-token scoping is the crux:accept_ct = process_shutdown.child_token()is per-generation; cancelling it stops that generation's admissions only.process_shutdown.child_token()— not a child ofaccept_ct— so retiring a generation never cancels its live sessions; only process shutdown does.(listen_addr, renderer_quic_port)change: make-before-break — bind the new endpoint first, thenretire()the old (cancelaccept_ct, spawn an unboundedwait_idle().awaitdrain with a 30sopen_connectionsheartbeat). A retiring generation is neverclose()d —endpoint.close()appears at exactly two process-shutdown-only sites. On bind failure, a bounded break-before-make fallback (5×200ms) with rollback-bind and loud logging keeps the server running.resolve_listen_addrparse failure keeps the old generation. TheArc<RendererRegistry>and admissionLiveGateare shared across generations, so draining sessions stay listed and functional.HTTP supervisor (
serve.rs) mirrors this: each generation serves underaxum::serve(...).with_graceful_shutdown(sel)whereselfires on the process signal or a per-generation rebind token; a rebind stands up the new listener, then graceful-drains the old with no bound. CLI--listen/--portpinning still holds (overrides re-pin every publish, so the supervisor never observes a pinned change).start_renderer_server/the HTTP path now take theConfigHandleand derive theirSection+ watcher internally — one place, no watcher-vs-boot-snapshot ordering race.Tests (real
ConfigManager/Section): a held renderer session survives a QUIC port rebind and keeps reporting status into the shared registry while the old port stops answering and the new port serves, and the old socket frees after the held client disconnects (provingwait_idlecompletion); an in-flight slow HTTP request completes on the old listener while new requests are served on the new one; the bind-conflict fallback + rollback path; and therenderer_quic_portvalidation rules.Gate:
kanon gate --fullgreen — fmt, check, clippy (-D warnings), nextest 272 (archon+horismos) / full workspace, deny, kanon lint (0/0).Gate-Passedstamped.Refs #529