From 09a27cfce9d60c6acd650bba53d04aef13efd59e Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Wed, 22 Jul 2026 23:43:27 -0700 Subject: [PATCH] docs(#881): faithful token_hex(8) test epoch; document inline shutdown put PR #896 review follow-up, no behavior change: - Pinned test EPOCH was 32-bit (token_hex(4)) with a matching comment, but production widened to token_hex(8) in 2b3d0687 and the same file already pins token_hex(8) at line 401. Widen EPOCH to 16 hex chars + fix the comment. - Document why the fanout shutdown sentinel put stays inline on the loop: the consumer is still alive and drains via non-blocking fan-out, so it returns at once; the 1s timeout is a ceiling that never binds (off-loop is reserved for the multi-second joins). --- tests/test_global_sse_boot_epoch.py | 2 +- turnstone/server.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_global_sse_boot_epoch.py b/tests/test_global_sse_boot_epoch.py index 876f6e48..e111f4b9 100644 --- a/tests/test_global_sse_boot_epoch.py +++ b/tests/test_global_sse_boot_epoch.py @@ -40,7 +40,7 @@ from starlette.requests import Request import turnstone.server as server_mod -EPOCH = "0badf00d" # test-pinned boot epoch (hex, like secrets.token_hex(4)) +EPOCH = "0badf00dcafebabe" # test-pinned boot epoch (hex, like secrets.token_hex(8)) def _make_app_state( diff --git a/turnstone/server.py b/turnstone/server.py index f2b418e7..ab6afb4d 100644 --- a/turnstone/server.py +++ b/turnstone/server.py @@ -4763,6 +4763,11 @@ async def _lifespan(app: Starlette) -> AsyncGenerator[None, None]: # off-loop; a thread that outlives its budget is logged and left to # the daemon flag rather than wedging shutdown. daemon_stop.set() + # The sentinel put stays inline (unlike the off-loop joins below): the + # fanout consumer is still alive here — it exits only on drawing this + # sentinel — and fans out with non-blocking put_nowait, so the bounded + # queue drains fast and put() returns at once; timeout=1 is a ceiling + # that never binds. Off-loop is reserved for the multi-second joins. with contextlib.suppress(queue.Full): app.state.global_queue.put(_FANOUT_SHUTDOWN, timeout=1) for _t in (fanout, agg_emitter, cleanup):