Repository navigation
v1.6.0 polish: documentation #891
Description
Consolidated on 2026-10-02 from the per-PR 'Follow-ups from #N' bundles, by maintainer decision. From now on, review nits are fixed inside the PR before it merges, so no new bundles are filed. Each line names the bundle and item it came from; the bundle's thread keeps the full context. Everything here stays in v1.6.0.
-
Reword merged PR fix(websocket): apply pause/resume under the lock that decides them, and lift a stale pause (celeris#667, celeris#672) #671's throughput headline to 'no loss above the floor by the pre-registered rule' and give the pooled p (io_uring m128 leans ~1-2.5% slower in every non-primary view). (from Follow-ups from #671: measure the engine-worker stall under pausedMu, the epoll tail lean, #705-repro vs M5 coverage, prereg predictions #716 3) (PARTIAL: -; open: GitHub PR-body edit only (no repo file). Body was edited 2026-10-20 by lane ADMIN but for )
-
Correct merged PR fix(engine): keep serving on a paused listener for 1.5 s with TCP_DEFER_ACCEPT cleared, so a switch or PauseAccept no longer resets clients that had not yet sent a request (celeris#662, #675) #674's stall section (27 of 80 containers had no other lane beside them, 40 ran beside this lane's own) and either regenerate the hand-computed figures (Fisher p=0.0023, host-load ranges, 1,501 ledger) with a named script or say they are hand-computed; add the weak-control caveat (B 0/30 vs C 1/10, p=0.25) to the TL;DR. (from Follow-ups from #674: stale ci.yml ramp-quarantine comment, stall write-up accuracy, B1 instrument note #724 2) (Done: on main 713b40d [main])
-
engine/provider.go (UnregisterConn doc, main 24cdb7b line ~88) still says 'Closing fd before UnregisterConn does not affect this engine'; reword to say it keeps the socket open and does not misdirect the cancel on io_uring but is still outside the contract, matching driver.go's armDriverRecv comment. (from Follow-ups from #696: provider.go close-before-unregister contract text, the #691 CI step's test-group count, and its job not being required #726 1) (Done: engine, eventloop: UnregisterConn, RegisterConn and SyncRoundTripper docs match the contracts #1061 [diff])
-
Add a one-line addendum on closed epoll: a timeout reap parks the whole loop thread on cs.detachMu for as long as an async handler runs (the epoll twin of the closed celeris#593) #669 and epoll: hijackConn mutates worker-thread-only liveConns and connCount from the dispatch goroutine (the epoll twin of celeris#539) #668 pointing to fix(epoll): never park the loop on a running async handler, and leave the live set to the loop on an async hijack (celeris#669, celeris#668) #698's merged commit and final numbers (124/0/6/0, x10 380/0/0/0, 21/21 mutants, CI 36280312424); their only comments cite the round-1 head e038a40. (from Follow-ups from #698: shutdown leaves a deferred transplant's descriptor open (pre-existing), EPOLLOUT comment premise, CI visibility of the new test, stale comments #727 5) (Done: on main 713b40d [main])
-
Comment on closed middleware/websocket: on epoll and io_uring, Conn.Query returns garbage once a frame has arrived, because the captured query strings alias the engine's read buffer (200/200 per engine) #714 and Context.Detach clones the headers, method, path and raw query but not the route params, query and cookie caches, Host, or middleware-set strings, so a detached Context reads other bytes on epoll and io_uring (7 fields, 20/20) #718 recording the scope fix: clone the request values a detached stream keeps (websocket Conn.Query, SSE Last-Event-ID, Context.Detach, requestid and otel context values) (#714, #717, #718) #723 actually fixed (response headers set before Detach and the request body under Context.Detach clones the headers, method, path and raw query but not the route params, query and cookie caches, Host, or middleware-set strings, so a detached Context reads other bytes on epoll and io_uring (7 fields, 20/20) #718; requestid EnableStdContext and otel carrier values under middleware/websocket: on epoll and io_uring, Conn.Query returns garbage once a frame has arrived, because the captured query strings alias the engine's read buffer (200/200 per engine) #714), with the T/R1/H counts from fix: clone the request values a detached stream keeps (websocket Conn.Query, SSE Last-Event-ID, Context.Detach, requestid and otel context values) (#714, #717, #718) #723's Failing-first table, for the release notes. (from Follow-ups from #723: otel traceparent clone cost, SSE OnConnect and session c.Set views, stale #714/#718 scope, ENOMEM claim wording, deliberate-red record #740 4) (Done: on main 713b40d [main])
-
Correct merged PR fix(session): keep a copy of a loaded session's ID, so a write-behind save goes to its own session (celeris#731) #734's cost sentence 'Allocations and bytes are exact and identical in every sample': 13 of 45 samples of an arm read 666 B/op against the reported 665 B (counts and conclusion unaffected). (from Follow-ups from #734: store.KV key-borrowing contract, and the cost wording in #734's body #741 2) (Done: on main 713b40d [main])
-
codspeed.yml's 'How to read a flag' step 2 (line 41) says 'A difference inside the floor is not a change'; say it is inconclusive and must be repeated/extended until the floor is below the flagged difference, and the same in bench-ab.sh's usage text. (from Follow-ups from #748: decide the CodSpeed budget lever now, the fork-PR exemption for org members, H2 benchmarks, bench-ab.sh result checks #754 5) (Done: ci(bench): refuse empty or mismatched benchmark sets, exclude OneByteGrant911 from CodSpeed #1048 [diff])
-
Replace line-number locators with symbol citations site-wide in goceleris/docs: deployment.md:504 (server.go:681-699, and say a failed start closes the listener since fix(server): a Start that never serves releases the caller's listener, the CPU monitor and the settle re-opener (#737) #747), deployment.md:600 (server.go:693-751), graceful-shutdown.md:537 (resource/config.go:152-161, now elsewhere), and the many config.go/resource/config.go/server.go ranges in configuration.md and deployment.md. (from Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 1) (Done: docs: cite celeris symbols, not line numbers (configuration, middleware-traffic, websocket) docs#104, docs: cite celeris symbols, not line numbers (routing helpers, data stores, performance, deployment, graceful shutdown) docs#105, docs: cite celeris symbols, not line numbers (remaining twelve pages) docs#106 [main])
-
The graceful-shutdown FAQ numbers come from round2/738/docs_probe_linux_test.go in the maintainer's evidence root; commit the probe (probatorium docs-verification test or a celeris example test) or say in the page that the numbers are maintainer-measured. (from Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 2) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
deployment.md:537 cites 'celeris/server.go (StartWithContext)'; settle the celeris/ path prefix convention in Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 item 1's site-wide locator sweep (fold into Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 item 1). (from Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 3) (Done: docs: cite celeris symbols, not line numbers (configuration, middleware-traffic, websocket) docs#104, docs: cite celeris symbols, not line numbers (routing helpers, data stores, performance, deployment, graceful shutdown) docs#105, docs: cite celeris symbols, not line numbers (remaining twelve pages) docs#106 [main])
-
deployment.md:619-622 (Kubernetes sequence): 'in-flight requests drain within ShutdownTimeout' reads as a guarantee (at the deadline Celeris stops waiting, it does not finish them), and the terminationGracePeriodSeconds formula holds only when every OnShutdown hook returns once its ctx is done; reword both. The std cancel-path part of the original note is superseded by std: a cancel of StartWithContext's context ignores Config.ShutdownTimeout; the drain, the OnShutdown hooks and Start wait for every handler #753's fix (closed 2026-09-29, docs#80). (from Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 4) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
getting-started.md:191-192 code comment says StartWithContext 'returns after the in-flight requests have drained and then the OnShutdown hooks have run'; at the deadline the hooks run while a request is still in flight, so qualify it. The HTTP/2 half is superseded by HTTP/2 streams on the shared H2 worker pool are not drained at shutdown: an async-route stream loses its response on epoll, io_uring and adaptive, and std waits for no h2c stream #759's fix (docs#82); while there, re-check getting-started.md:214 'HTTP/2 has two exceptions' against the post-HTTP/2 streams on the shared H2 worker pool are not drained at shutdown: an async-route stream loses its response on epoll, io_uring and adaptive, and std waits for no h2c stream #759 'What the drain waits for' section. (from Follow-ups from docs#77: stale line locators next to the edited shutdown text, and the uncommitted docs probe #779 5) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
README.md:332 says the engine checks 'rather than by reading the kernel version', but the version decides when the probe gets no or an unrecognised answer; use CodeRabbit's proposed sentence (still unchanged on main). (from Follow-ups from #792: io_uring 5.19 floor — backport probe covers 1 of 4 cancel forms, floor test keyed on version, no CI seam for adaptive/Server, stale comments, SSE flake, docs pages #796 6e/11c) (Done: fix(iouring): refuse io_uring below Linux 5.19 whatever the cancel probe answers #963 [diff])
-
goceleris/docs still says io_uring needs Linux 5.10+ (configuration.md:111, core-concepts.md:267, deployment.md:488, engines.md:39/82/88/121/365, getting-started.md:18, introduction.md:32, performance.md:218, plus src/pages/index.astro:57) and gives epoll 3.10+ (getting-started.md:18, deployment.md:493, index.astro:62) on docs main e13609f; change to 5.19+ (and 5.10+ for epoll) when v1.6.0 is published. (from Follow-ups from #792: io_uring 5.19 floor — backport probe covers 1 of 4 cancel forms, floor test keyed on version, no CI seam for adaptive/Server, stale comments, SSE flake, docs pages #796 9) (open: WORDING)
-
State the post-fix(epoll, iouring): send a large response whole, keep pipelined responses in order, never send a body the handler has given back (celeris#761, celeris#802, celeris#817) #805 memory bound in docs/godoc: an HTTP/2 conn may queue 64 MiB before a refused write (maxPendingBytesH2/maxSendQueueBytesH2), a slow reader of a large HTTP/1 response holds a copy of the unsent tail in writeBuf, and pipelined c.File responses are read whole into writeBuf by unstage with no cap check in makeSendFileFn. (from Follow-ups from #805: the memory bound after the per-request cap, epoll's closing-conn reap by read time, no deterministic H2 refusal test, io_uring's dormant WRITEV path, body nits #818 1a) (Done: docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070 [diff])
-
Server.Shutdown godoc (server.go:536 on main) says the HTTP/2 wait and send drain last 'no longer than [Config.WriteTimeout]'; that holds only while WriteTimeout is set (-1 resolves to 0 and neither sendDrainWait nor h2PoolSettled caps). Qualify the sentence. (from Follow-ups from #808: std h2c conns get no GOAWAY and outlive the drain, a pool-wait bound test that passes on main, cost wording #820 6) (Done: docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070 [diff])
-
goceleris/docs graceful-shutdown.md measured paragraph (~line 301-309 at e13609f) names no run record for the h2c result and the byte counts (2,634,240/3,145,849; 2,634,119/3,145,728); link the CI run or lane evidence with its machine, or drop the machine-specific counts. (from Follow-ups from #808: std h2c conns get no GOAWAY and outlive the drain, a pool-wait bound test that passes on main, cost wording #820 8) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
goceleris/docs graceful-shutdown.md send-drain sentence (~line 286-287) leaves the 250 ms floor's precedence open; say that with a positive WriteTimeout below 250 ms the floor wins (sendDrainWait takes the later end), as the HTTP/2 wait sentence already does. (from Follow-ups from #808: std h2c conns get no GOAWAY and outlive the drain, a pool-wait bound test that passes on main, cost wording #820 9) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
Server.Shutdown godoc (server.go:536-542 on main) and docs graceful-shutdown.md (:264, :286-287, :294) read the 250 ms floor as a minimum duration ('never for less than 250 ms', io_uring 'for 250 ms'); in code it is the least time the drain is allowed and io_uring's is a cap. Reword to 'at least 250 ms ... ends as soon as nothing is left to send' / io_uring 'at most 250 ms'. Public API doc, before v1.6.0; same sentences as Follow-ups from #808: std h2c conns get no GOAWAY and outlive the drain, a pool-wait bound test that passes on main, cost wording #820 items 6 and 9. (from Follow-ups from #808: std h2c conns get no GOAWAY and outlive the drain, a pool-wait bound test that passes on main, cost wording #820 10) (Done: docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070, docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
goceleris/docs src/content/docs/graceful-shutdown.md (current main e13609f) still mixes spellings: 'canceled' at lines 23 (prose), 57 (Go example comment), 64 (prose) and 83 (verbatim quote of celeris's StartWithContext godoc, keep unless the godoc changes); make 23 and 64 (and optionally 57) 'cancelled' per the repo's British-spelling path instructions. (from Follow-ups from docs#80: mixed canceled/cancelled spelling on the graceful-shutdown page #824 1) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main])
-
internal/httprange/httprange.go:59-68 IfRange godoc claims an exact date match is safe ('never holds a date that two versions share'); false when build tools normalise mtimes (ko, Nix, Bazel rules_oci, SOURCE_DATE_EPOCH). Correct the comment. (from Follow-ups from #834: If-Range on pre-compressed .br/.gz variants (rest of #435), the IfRange date-safety comment, an unsatisfiable If-Range test, the stat/open window #846 2a) (Done: fix(static): a pre-compressed variant has its own validators, 304 and range; a strong fs.FS ETag; Root validators from the opened file (celeris#846) #972 [diff])
-
Add a caveat to middleware/static/doc.go and docs: an unsatisfiable range gets 416 and If-Range is honoured (celeris#435) docs#83 that If-Range on the static middleware is only as reliable as the files' mtimes. (from Follow-ups from #834: If-Range on pre-compressed .br/.gz variants (rest of #435), the IfRange date-safety comment, an unsatisfiable If-Range test, the stat/open window #846 2b) (Done: fix(static): a pre-compressed variant has its own validators, 304 and range; a strong fs.FS ETag; Root validators from the opened file (celeris#846) #972, docs: an unsatisfiable range gets 416 and If-Range is honoured (celeris#435) docs#83 [main])
-
docs: an unsatisfiable range gets 416 and If-Range is honoured (celeris#435) docs#83 static-files.md Range example builds a 'strong' ETag from mtime and size (not strong per RFC 9110 8.8.1) and stats the joined path before FileFromDir's traversal check; use a content hash or an explicit caveat, and stat only after the traversal check. (from Follow-ups from #834: If-Range on pre-compressed .br/.gz variants (rest of #435), the IfRange date-safety comment, an unsatisfiable If-Range test, the stat/open window #846 6) (Done: docs: an unsatisfiable range gets 416 and If-Range is honoured (celeris#435) docs#83 [main])
-
GET routes that stream via StreamWriter+Context.Detach+goroutine now answer HEAD: the HEAD response completes at its headers but the detached goroutine runs on with writes dropped and the next request on that keep-alive conn gets unexpected EOF (GET breaks the conn the same way, so not a connection regression; std fine). Add a sentence to the Server.HEAD godoc (server.go:231-234) and docs: HEAD is answered by the GET route and OPTIONS with the Allow list (celeris#421) docs#84 routing.md: such a handler should check c.Method()=="HEAD" and return after WriteHeader, as sse does. (from Follow-ups from #841: HEAD now reaches #837 and #835, HEAD on Detach-streaming GET routes, the per-request isHEAD read on live streams, FullPath sentinels, OPTIONS opt-out #847 2) (PARTIAL: docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070; open: Docs-repo half open (the item names docs: HEAD is answered by the GET route and OPTIONS with the Allow list (celeris#421) docs#84 routing.md). docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070 says the EOF-on-n)
-
Document the FullPath sentinels "" (new, handler.go:432), "" and "" in the Context.FullPath godoc (context_request.go:36-37 still says 'Returns empty string if no route was matched'), middleware/metrics/doc.go (lists only ) and the otel middleware doc; they become API at the public launch. (from Follow-ups from #841: HEAD now reaches #837 and #835, HEAD on Detach-streaming GET routes, the per-request isHEAD read on live streams, FullPath sentinels, OPTIONS opt-out #847 4) (Done: fix(api): the global middleware runs for a request no route matches, so Use-mounted swagger, pprof, debug, healthcheck, metrics and static answer their paths (celeris#852) #916, docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070 [diff])
-
Auto-HEAD/auto-OPTIONS cannot be turned off (server.go:231-245, handler.go:376-383). Decide whether v1.6.0 adds a Config bool (zero value keeps today's behaviour, non-breaking); if not, name the workarounds in docs#84 (explicit route, or global middleware checking c.FullPath()==""). (from Follow-ups from #841: HEAD now reaches #837 and #835, HEAD on Detach-streaming GET routes, the per-request isHEAD read on live streams, FullPath sentinels, OPTIONS opt-out #847 5) (open: CODE-OTHER)
-
Add to ci: pin govulncheck in a tools module and run it with go tool (celeris#827) #839's trade-off paragraph (or the dependabot.yml comment): Scorecard's Vulnerabilities check (osv-scanner, Recursive) scans .github/tools/go.mod, so a future advisory in x/vuln's deps (x/mod, x/sync, x/sys, x/telemetry, x/tools) lowers the public score and, under the named allow rule, no Dependabot PR fixes it; manual fix is go get @ in .github/tools. celeris is clean today (Vulnerabilities 10). (from Follow-ups from #839: an unchecked Dependabot allow list for .github/tools, the Scorecard side of its security trade-off, inline go install pins, a stale evidence label #853 2) (Done: ci: mage and actionlint are tools of .github/tools run with go tool; a Lint step compares the tool pins with Dependabot, CONTRIBUTING and the workflows (v1.6.0 polish items) #1069 [diff])
-
docs: drop the deprecated APIs celeris v1.6.0 removes; basicauth verifies only pbkdf2-sha256 (celeris#830, celeris#826) docs#87 (at e98f734) src/content/docs/middleware-auth.md: the Config table still says
HashedUsers"RequiresHashedUsersFunc" (:116) and thatHashedUsersFuncis "Required wheneverHashedUsersis set —Newpanics otherwise" (:117), while the paragraph docs#87 adds (:160-164) saysHashedUsersFuncdefaults tobasicauth.VerifyPasswordwhen every entry is pbkdf2-sha256, which is what celeris#894 does (middleware/basicauth/config.go:136 at a519143). The table text is already on docs main e13609f, but docs#87 rewrites this section, so the page now contradicts itself. Fix the two cells in docs#87 before it merges. DONE in docs#87 (22fcfdf). (from fix(api): remove every deprecated public API and basicauth.HashPassword before the public v1.6.0 (celeris#830, celeris#826) #894/docs#87 review r1: correctness MINOR) -
docs: drop the deprecated APIs celeris v1.6.0 removes; basicauth verifies only pbkdf2-sha256 (celeris#830, celeris#826) docs#87 (at e98f734) middleware-auth.md: the heading "Hashed passwords (bcrypt / argon2)" (:126) now also documents the built-in PBKDF2 default; and the example comment "New panics, naming the entry, if one does not parse" (:170) holds only for an entry that keeps the
pbkdf2-sha256$tag. An untagged entry (say, a paste that lost the prefix) hits the generic "HashedUsers requires HashedUsersFunc unless every hash is pbkdf2-sha256" panic (celeris config.go:121), which names no entry. Fix both in docs#87 before it merges. DONE in docs#87 (22fcfdf). (from fix(api): remove every deprecated public API and basicauth.HashPassword before the public v1.6.0 (celeris#830, celeris#826) #894/docs#87 review r1: API/perf/security NIT) -
goceleris/docs
src/content/docs/introduction.md:18("The current release is v1.5.8") and:147("currently1.5.8") on docs main 3fc7380 are stale already: the latest celeris release is v1.5.11. docs main also documents unreleased v1.6.0 changes (docs#83's 416 and If-Range, docs#87's removals, each labelled v1.6.0 where it matters). In the v1.6.0 release's docs PR, at the tag and not before, change both lines to v1.6.0. If the site should not show unreleased behaviour before then, say so in the release checklist (Release checklist: v1.6.0 #649). (from docs#87 CodeRabbit outside-diff minor, which proposed the bump now and cited performance.md:642-644 for these introduction.md lines) (open: WORDING) -
CONTRIBUTING.md (main 2ca5d72) documents ten mage targets that
mage -ldoes not list:testLinux,specLinux,checkLinux,benchLinux(:83-86),vmStop,vmDelete(:89),localBenchmark,localProfile(:94-95),middlewareBenchmark,middlewareProfile(:97-98).checkLinuxappears again at :111. Every one fails with "Unknown target". The "Linux Testing from macOS" and "Benchmarking" sections describe a Multipass VM workflow that is no longer in the magefiles. Either remove those lines, or point them at what replaced them: the Linux container recipe the loop uses, probatorium for benchmarks, and.github/scripts/bench-ab.shfor an A/B. Check:scripts/contributing-targets.shin lane CI1's evidence (loglogs/contributing-targets.log). (found by lane CI1 while fixing mage Tools: the h2spec fallback (go install .../h2spec@latest) installs v2.2.1, not the v2.6.0 the download path pins #838, which touches CONTRIBUTING.md:19-22 only) (Done: ci: mage and actionlint are tools of .github/tools run with go tool; a Lint step compares the tool pins with Dependabot, CONTRIBUTING and the workflows (v1.6.0 polish items) #1069 [diff]) -
goceleris/docs
graceful-shutdown.md(and theServer.Shutdowngodoc where it says "std waits for its h2c streams' handlers"): once draft PR fix(std): Shutdown sends h2c connections GOAWAY at the start of the drain and tells a live overlapping caller whose budget ended it #968 merges, std sends every h2c connection GOAWAY (NO_ERROR) when the drain starts, a stream that still reaches a handler path after the drain has ended is answered 503, and aShutdowncall whose own ctx is live when another overlapping call's budget ended the drain returns an error wrapping that call's ctx error. Say so, and state the overlapping-call rule per engine: std ends the drain at the shortest budget; epoll and io_uring store the ctx of the lastShutdowncall as the drain budget (drainBudget.Store(&ctx), last call governs,Shutdownreturns nil), described today only in a comment onh2PoolSettled; adaptive hands its ctx to both sub-engines. Decide whether that difference is intended; if so, document it. (found by lane E12 while fixing std: overlapping Shutdown calls share the shortest caller's budget, and a caller whose own ctx is live gets context.Canceled #879 and std: Shutdown never sends GOAWAY to hijacked h2c conns, so they serve new streams during the drain and after Shutdown returns #878; evidencelanes-20261009/E12/PROGRESS.md, siblings read at main fe7600a:internal/engine/epoll/engine.goShutdown,internal/engine/iouring/engine.goShutdown) (PARTIAL: fix(std): Shutdown sends h2c connections GOAWAY at the start of the drain and tells a live overlapping caller whose budget ended it #968, docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070, docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102; open: PREMATURE: code (fix(std): Shutdown sends h2c connections GOAWAY at the start of the drain and tells a live overlapping caller whose budget ended it #968), godoc (docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070, cites 'item 30' = unchecked ordinal, A32) and docs () -
config.go (
Config.EnableH2Upgradedoc): the paragraph "Engine: Std does not yet honour non-nil false on Auto ..." was folded into draft PR fix(std): the std engine honours EnableH2Upgrade=&false on Protocol Auto #970 (review r1): it now says all four engines honour&falseon Auto and states the two std differences on H2C and HTTP1, tracked as an item in v1.6.0 polish: protocols and router #889. (found by lane H964S) -
config.go (
Config.WriteTimeoutdoc, line 105: "the max duration for writing the response") no longer describes what it bounds for a connection whose close is deferred behind its response (Connection: close, a request error, a refused write, an EPOLLRDHUP mid-response). On epoll with PR fix(epoll): shutdown closes a deferred transplant's descriptor and flushes the batched counters; a closing conn is reaped on its own clock (#863, #876, epoll half of #874) #980 (and on io_uring since fix(epoll, iouring): send a large response whole, keep pipelined responses in order, never send a body the handler has given back (celeris#761, celeris#802, celeris#817) #805, asmax(5 s, WriteTimeout)) the bound is how long the peer may take NOTHING of the response: the clock restarts whenever the queue shrinks, so a client that reads slowly enough to move the queue once per WriteTimeout can hold the closing fd and its buffered response until the response ends; there is no absolute limit on a closing conn, where before fix(epoll): shutdown closes a deferred transplant's descriptor and flushes the batched counters; a closing conn is reaped on its own clock (#863, #876, epoll half of #874) #980 epoll had one (ReadTimeout or IdleTimeout from the last read). With WriteTimeout disabled the bound is 5 s without progress on both engines. State both in the doc, and the slow-read trade-off in the timeouts section of the docs. (found by lane E9, PR fix(epoll): shutdown closes a deferred transplant's descriptor and flushes the batched counters; a closing conn is reaped on its own clock (#863, #876, epoll half of #874) #980 review r1) (PARTIAL: docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070; open: docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, response memory and FullPath sentinels (#891) #1070 'item 31' (unchecked ordinal = A34): 'Docs-repo timeouts section owed'. docs#101 cov) -
docs site landing page (docs repo
src/pages/index.astro:75) advertises a "SIMD parser: SSE2 (amd64) and NEON (arm64) request parsing with a SWAR fallback", but celeris main has no assembly files, no cgo and no SSE2/NEON/SIMD reference in the Go code (git grep -i simdfinds onlyinternal/platform/cpuplan_linux_test.go;internal/protocol/h1is plain Go). Either the feature moved or the line is stale: verify against the parser benchmarks, then fix the claim (and the stale local README copy, if any). Found by the OpenSSF badge answer draft (evidence/openssf-badge-20261011/ANSWERS.md, item 12). (Done: docs: EngineMetrics.Throughput always reads 0; the parser is not SIMD (celeris#653, celeris#424) docs#73, refactor(h1): delete the unused SIMD header scan and stop advertising a SIMD parser (celeris#424) #697 [main]) -
goceleris/docs:
error-handling.md("last-resort layer") describes only the router's 500; add the contract from celeris PR fix(epoll,iouring): a loop or worker goroutine that ends without returning is logged and counted, and sync handlers must not Goexit (#848) #997 (a sync handler must not callruntime.Goexit: on epoll, io_uring and adaptive it ends the event loop, the engine logs an Error... ended without returning; an.Async()route's Goexit leaves the loop serving, but it is per protocol: on HTTP/1.1 that connection is closed and an Error is logged, on HTTP/2 the stream gets no response or RST_STREAM and nothing is logged; on HTTP/2 a stream of a sync route runs inline whether or not its request has a body, so its Goexit ends the loop; only an.Async()route, or a connection that already holds 4 MiB of buffered response data, sends a stream to the h2 worker pool, where the Goexit is silent; the global middleware,NotFound/MethodNotAllowedhandlers of a request no route matches and theOnConnect/OnDisconnectcallbacks run on the loop too, as doesOnExpectContinueunless its connection was promoted to async dispatch, so they must not Goexit; std ends only the request's goroutine). No docs page mentions Goexit today. (found by lane GOEXIT848, epoll, io_uring: runtime.Goexit in a synchronous handler ends the loop or worker goroutine it runs on, and every connection that loop owns goes unserved #848) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main]) -
goceleris/docs: the data-stores pages should say what celeris PR fix(drivers): the drivers and their nine stores compile and run on GOOS=windows through a portable net.Conn fallback (#937) #996 puts in the README (Platform support): the Linux event loop is the supported production path; macOS and the other unix hosts run a goroutine-per-connection fallback; Windows compiles and runs the same kind of fallback for development and testing, tested in CI on windows-latest, not benchmarked, no
WithEnginecolocation. (found by lane WIN937, drivers: redis, postgres, memcached and the nine middleware stores built on them do not compile for GOOS=windows, which the README offers (unix.Dup in the fallback loop, int descriptors in each conn.go) #937) (Done: docs: shutdown, deployment, getting-started, error-handling and data-stores match v1.6.0 docs#102 [main]) -
goceleris/docs: the CPU pinning page should say what celeris PR platform, epoll, io_uring: warn when a NIC interrupt vector is serviced by an allowed CPU that runs no loop and the rx-port rule did not free on purpose; README host-tuning note #1062 puts in the README (CPU pinning, NIC interrupts on big.LITTLE hosts) and in the
Config.Workersgodoc: at start the native engines find the CPUs that service the NIC interrupts of the listener, pin at most 2 extra loops (CELERIS_NIC_IRQ_LOOPS: 0 or a non-number turns it off, 1-4 the cap, unset 2) on loop-free little CPUs that service one, never raise an explicitWorkers, and log one Warn for a serviced CPU that stays without a loop; the host-tuning alternative (move the interrupt onto a CPU that runs a loop,IRQBALANCE_BANNED_CPULIST, persistence across reboots,After=network-online.target) with the evidence caveat (one host, one driver, post hoc). RULE 92: searched the four repos by mechanism (CELERIS_NIC_IRQ_LOOPS, smp_affinity_list, irqbalance docs): no tracker. (from PR platform, epoll, io_uring: warn when a NIC interrupt vector is serviced by an allowed CPU that runs no loop and the rx-port rule did not free on purpose; README host-tuning note #1062, task IRQPLACE) (open: WORDING) -
The StreamWriter changes of PR conn, epoll, iouring: an HTTP/1 StreamWriter reports a closed connection and a refused write, and a response held unsent before Detach is capped (v1.6.0 polish items) #1067 (Refs v1.6.0 polish: io_uring engine #884 item 45, Refs v1.6.0 polish: epoll engine #885 item 30; leave unticked until it merges) make the docs wrong in these places. Docs repo
src/content/docs/streaming.md(origin/main at the time): line 75 (theFlushtable row: "on HTTP/1.1 on epoll, io_uring and adaptive it does nothing"); lines 87-100 (theFlushbullet: say that beforeDetachthe buffered response is capped at 64 MiB, past whichWriteandFlushreturn an error and the connection is cut, and that after the peer is gone they return an error wrappingnet.ErrClosed); lines 104-108 (the NDJSON paragraph: "the rows pile up in memory": add the 64 MiB limit); lines 226-228 (the example's comments "Native H1: Write does not report a gone client" and "Native: Write already did"); lines 428-440 (the FAQ "How do I detect when the client disconnects?": on epoll, io_uring and adaptive over HTTP/1.1WriteandFlushno longer return nil after the client has gone; the engine learns of it on its own thread, so a write at the moment of the reset can still return nil, and a handler that blocks the engine's thread beforeDetachcannot see it;SetWSDetachClosestays the callback). Code comments with the same claim, outside that PR's files (middleware lane):middleware/sse/prepared.go:34-37andmiddleware/sse/sse.go:72-73("a write after the engine has noticed a reset is dropped without an error"), and the known-gap paragraphmiddleware/sse/doc.go:41-46(SSENONREAD's). RULE 92: searched the four repos by mechanism (streaming.md, "does not report a gone client", "Flush does nothing", "held whole in memory"): no tracker. (open: WORDING) -
celeris
README.md:336and:464say Adaptive is "epoll only below 5.19" / "runs on epoll below 5.19", which reads as if Adaptive uses io_uring from 5.19.ioUringViable(internal/adaptive/engine.go, thechooseStartEngineand standby-build gates) lets Adaptive pick or promote to io_uring only on a 6.1+ kernel with the fast tier (DEFER_TASKRUN, SINGLE_ISSUER, multishot recv, provided buffers) or on 6.10+, and with enough RLIMIT_MEMLOCK; on 5.19 to 6.0 and on 6.1 to 6.9 kernels without the fast tier it stays on epoll. Reword to say that. Alsointernal/engine/tier.go:12(theOptionalcomment says "adds SQPOLL") and theengine/iouringtier comments: no tier uses SQPOLL (SQPollIdle()is 0 in both tiers, celeris#377). The docs side is docs: state the io_uring 5.19 floor, the epoll 5.10 floor and the v1.6.0 stamp (do not merge before the tag) docs#108 (review round 1 corrected it there). (from docs: state the io_uring 5.19 floor, the epoll 5.10 floor and the v1.6.0 stamp (do not merge before the tag) docs#108 review, DOCSREL; searched goceleris issues by mechanism, no existing item) (open: WORDING)
Activity
FumingPower3925 commentedon Oct 3, 2026
For the item "State the post-#805 memory bound in docs/godoc: an HTTP/2 conn may queue 64 MiB before a refused write ... (from #818 1a)": the HTTP/2 half changes with the fix for #893, #906 (stacked on #900).
9e54e3b) the 64 MiB (maxPendingBytesH2/maxSendQueueBytesH2) bounds only the socket send queue. The response bytes the peer's windows refuse are copied into the streams' buffers with no bound: about 150 MiB per connection for 99 streams of a 1.5 MiB body (epoll, io_uring: an HTTP/2 client that never sends WINDOW_UPDATE makes the server copy every stream's unsent body into memory, with no per-connection limit (150 MiB per connection for 99 streams of a 1.5 MiB response) #893).stream.OutboundBudget(4 MiB) plus one body inline and one on the worker pool; over the budget a handler runs on the pool and sends as the windows open, holding its own body, as a net/http handler blocked inWritedoes. AStreamWriterresponse's DATA is still unbounded by either (it ignores the windows: h2: StreamWriter DATA and the SETTINGS_INITIAL_WINDOW_SIZE re-flush ignore the peer's flow-control windows (DATA past the window, RFC 9113 §6.9.1) #904).So the docs sentence, once the fix merges, can read: an HTTP/2 connection holds at most 64 MiB in its socket send queue and about 4 MiB (plus one response body) in its streams' buffers for a peer that grants no window; a
StreamWriterresponse is the exception until #904.FumingPower3925 commentedon Oct 3, 2026
Correction and two more items from the round-1 review of #906 (celeris#893, lane F3). The first one replaces the docs sentence that issuecomment-5964016328 proposed.
stream.OutboundBudget(4 MiB) plus one body. That bounds what a connection holds for a body its streams share, such as a static asset or the embedded Swagger UI bundle. It does not bound a body built per request (c.JSON, a rendered page). A handler waiting for window keeps its own body alive until it is sent, as a net/http handler blocked inWritedoes, so a connection holds up to one such body per waiting stream (up toMAX_CONCURRENT_STREAMS). The review measured 99 silent streams of a 512 KiB per-request body at about 50 MiB on the native engines with fix(h2): bound the response bytes a connection holds for the peer's window; over the budget, handlers run on the pool and send as the windows open (celeris#893) #906, and about 51 MiB on std. Proposed sentence: "an HTTP/2 connection holds at most 64 MiB in its socket send queue and, for a peer that grants no window, about 4 MiB plus one response body in copies; a handler waiting for the peer's window keeps its own response body until it is sent, as on net/http; aStreamWriterresponse is not bounded by the windows until h2: StreamWriter DATA and the SETTINGS_INITIAL_WINDOW_SIZE re-flush ignore the peer's flow-control windows (DATA past the window, RFC 9113 §6.9.1) #904."core-concepts.md:296, andgraceful-shutdown.md:254-256("every route that is not async"). Both need a clause for this case.FumingPower3925 commentedon Oct 3, 2026
One item from lane MW (PR #920, celeris#732/#742):
Method,Path,Header,Param,Query,Cookie,Host,ClientIP,RawQueryand the strings derived from them are views of the connection's receive buffer, valid until the handler returns; clone what a map or cache key, a goroutine or channel, a closure, a kept log record, a metric label or a span attribute keeps. The package doc states it as of fix(recovery, otel, singleflight, cache, sse, compress, api): copies for the request strings a log record, a span, a metric attribute set, a coalesced waiter or an SSE client keeps past the request (celeris#732, celeris#742) #920 (doc.go), as doContext.RequestHeadersandContext.Keys.Added after #920's and #918's round-1 reviews (lines at docs
main30f9bcc):RequestHeaders()returns a copy that is safe to retain. On HTTP/1.1 it returns the request's own slice of receive-buffer views, valid until the handler returns, and it always has. fix(recovery, otel, singleflight, cache, sse, compress, api): copies for the request strings a log record, a span, a metric attribute set, a coalesced waiter or an SSE client keeps past the request (celeris#732, celeris#742) #920 corrects the godoc and carries a release note. Lines to fix:src/content/docs/request-handling.md:33-38("documented as safe to retain", "A copy of all headers ... safe for concurrent use"),:156("A copy of all headers ...; safe to retain") and:359("UseBodyCopy()/RequestHeaders()if the data must outlive the request");core-concepts.md:191("copy of all headers"),:206-207(the old godoc quoted),:412("usec.BodyCopy()andc.RequestHeaders()") and:431("pass the copies"). Open docs PR Request accessor overrides (SetClientIP, SetScheme, SetHost) #91 also touchescore-concepts.md, in its H2 sections only.core-concepts.md:192-195says "a Gostringcopy is justx := c.Param("id")". That assignment copies the string header, not the bytes, soxis the same receive-buffer view. Saystrings.Clone(c.Param("id")).websocket.md:271saysRemoteAddris "nilwhenNetConnisnil", and:273thatIP()'s "result is cached after the first call". After fix(websocket): Conn.Param, IP and RemoteAddr answer from the upgrade request on every engine; every test helper that starts a server fails at once with Start's error (celeris#721, celeris#706) #918,RemoteAddrreturns the peer's*net.TCPAddron every engine (nilonly when the engine reported no address), andIP()is computed once, at upgrade. (LocalAddris stillnilon epoll and io_uring; that item is on v1.6.0 polish: middleware #888.)FumingPower3925 commentedon Oct 3, 2026
Item 27 (the FullPath sentinels), from lane SW's review of #916 (correctness lens r1, NIT): add goceleris/docs
src/content/docs/performance.md:74, which says "FullPath()returns""when no route matched (e.g. inside a customNotFoundhandler)"; it returns"<unmatched>"there (handler.gosetsc.fullPathbefore the chain).Every site but the otel one is fixed in the PRs now, so the item can be ticked when they merge:
Context.FullPathgodoc (context_request.go:36-37): celeris#916, commit "docs(api): FullPath names its sentinels for a request no route matched".middleware/metrics/doc.go: celeris#916 (it now names<method-not-allowed>and the_OTHERmethod label too).performance.md:74andobservability.md's metrics labels: docs: the global middleware runs for unmatched requests too, so Use-mounted swagger, pprof, debug, healthcheck, metrics and static answer their paths (celeris#852) docs#90.middleware/otel/doc.gonames no sentinel;otel.go, which has the comment, is being changed by fix(recovery, otel, singleflight, cache, sse, compress, api): copies for the request strings a log record, a span, a metric attribute set, a coalesced waiter or an SSE client keeps past the request (celeris#732, celeris#742) #920, so it is left to after that merges).FumingPower3925 commentedon Oct 3, 2026
One more item from lane MW, after #920's round-2 review (lines at docs
maina8db30b):sse.md, "Resumable streams" (:394-432): say whatClient.Senddoes with the event's strings, and what a customReplayStoremust do. An event'sEvent,DataandIDcan be another request's strings (a handler that relaysc.Paramto a connected client), and on epoll, io_uring and Adaptive those are views of that request's receive buffer. Since fix(recovery, otel, singleflight, cache, sse, compress, api): copies for the request strings a log record, a span, a metric attribute set, a coalesced waiter or an SSE client keeps past the request (celeris#732, celeris#742) #920,Sendkeeps a copy of what it keeps (the queued event inMaxQueueDepthmode, theNewRingBufferentry), andReplayStore.Append's godoc says a store that keeps the event must copy it;NewKVReplayStoreencodes it beforeAppendreturns. TheSource:line numbers in that section (sse.go:466-502,replay_ring.go:15) move with fix(recovery, otel, singleflight, cache, sse, compress, api): copies for the request strings a log record, a span, a metric attribute set, a coalesced waiter or an SSE client keeps past the request (celeris#732, celeris#742) #920.FumingPower3925 commentedon Oct 4, 2026
Added 2026-10-04: found while preparing goceleris/docs#92 (the #443 move's docs pass)
These docs inaccuracies predate the move and are outside #92's scope.
PauseAccept/ResumeAccept"work on the native engines", and that only std lacks accept control. ButServer.PauseAccept's doc (server.go:811-813 at the refactor!: the engine, protocol and driver codec packages move under internal/ (celeris#443) #938 head) says the adaptive engine does not implement it either: onlyinternal/engine/{iouring,epoll}definePauseAccept, and adaptive is the Linux default. The feature matrix also has no adaptive column. Fix the wording and add the column.M443/xrepo-work/D1/scripts/08-drift-check.py) covers bare and repo-root-relative citations and could run as docs CI (PLAN D2).FumingPower3925 commentedon Oct 9, 2026
PR #972 (draft) covers items 2a (the IfRange godoc) and 2b (middleware/static/doc.go; the docs page is goceleris/docs#96). Item 6: the docs main already uses os.OpenRoot and a content-derived ETag note in the Range example, so nothing is left there.
ci(bench): refuse a benchmark set that is empty or differs between th…
ci(codspeed): exclude BenchmarkOneByteGrant911 from the set; say the …
engine, eventloop: UnregisterConn, RegisterConn and SyncRoundTripper …
engine, eventloop: callback and onClose docs match the engines, inter…
engine, eventloop: onClose docs name only the engines that remove the…
engine, eventloop: UnregisterConn, RegisterConn and SyncRoundTripper …
docs: the Linux container recipe in CONTRIBUTING names the image tag …
ci: mage and actionlint are tools of .github/tools run with go tool, …
ci: mage and actionlint are tools of .github/tools run with go tool, …
docs: FileFromFS godoc reads the size cap and read path as Context.Fi…
docs: sendfile applies only on epoll HTTP/1.1 without async or buffer…
docs: godoc limits and caveats for Shutdown, HEAD, WriteTimeout, resp…
ci: mage and actionlint are tools of .github/tools run with go tool; …
ci: mage and actionlint are tools of .github/tools run with go tool; …
Config.DisableAutoHEAD and Config.DisableAutoOPTIONS switch the autom…
refactor!: remove Context.Stream and the bool form of Route/RouteGrou…