Repository navigation
fix(server): bound connection concurrency so a scan cannot exhaust the fd table - #3394
Merged
Merged
Conversation
…e fd table A burst of concurrent connections walked the server container's file descriptor table to its stock 1024 ceiling. Past that, SQLite could not open the database, every request 500d, and the 500 handler could not open 500.html either. - raise the soft descriptor limit to 8192, which a process may do for itself up to the hard limit, so no compose or balena change is needed - pass --limit-concurrency (default 256, ANTHIAS_LIMIT_CONCURRENCY overrides) to both the development and production invocations, so a burst is shed with a cheap 503 instead of a database connect per socket Measured on the testbed: unguarded, a Pi 2 refused every connection past ~1005 and the container died and was restarted by the policy; a Pi 5 peaked at ~930 descriptors and served the ANTHIAS-63 signature, "unable to open database file" out of get_new_connection. Guarded, the same 1500-connection burst left both boards alive with no descriptor or database errors, peaking at 489 and 784 descriptors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
There was a problem hiding this comment.
Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.
vpetersson
approved these changes
Oct 17, 2026
vpetersson-bot
force-pushed
the
agent/anthias-claude/520fd277d119
branch
from
October 19, 2026 08:54
c0bf241 to
37d0009
Compare
|
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.



Issues Fixed
Sentry
ANTHIAS-62(OSError: [Errno 24] Too many open files) andANTHIAS-63(OperationalError: unable to open database file).Description
Nothing bounds how many connections a client may open against an Anthias box, and the stock container descriptor limit is 1024. A burst walks the table to that ceiling, and the failure is not graceful: SQLite can no longer open the database, every request 500s, and the 500 handler cannot open
500.htmleither.The device in Sentry was hit by a Qualys scan. The ordering there settles the cause: the
Errno 24events land at 13:39:21, the 371database is lockedevents only from 13:39:22. Descriptor exhaustion came first — it is not downstream of the lock failures fixed by #3383, which closed a leak that made this worse but did not bound the burst.Two guards, because neither is sufficient alone:
ulimits:key — and therefore works identically under balena, whose supervisor renders its own compose file. Failure to raise is non-fatal; a headless device that will not boot is worse than one without the headroom.--limit-concurrencyhas uvicorn answer 503 as soon as the in-flight count is reached, costing no database handle and no template render. Default 256 — about six times the ~39 sockets a device holds while idle with the viewer polling — overridable withANTHIAS_LIMIT_CONCURRENCY.Applied to the development invocation as well: a dev stack on a routable address is as reachable as a fleet device.
Testing
Measured on the testbed with a 1500-connection burst, against a Pi 5 and a Pi 2 (slowest board, worst case for recovery):
unable to open database fileout ofget_new_connectionrestart: alwaysBoth boards stayed healthy afterwards. A 5000-connection burst against the guarded Pi 5 plateaued at 776 descriptors and still produced no errors.
Unit tests cover both guards, including that the development branch keeps its limit and that a refused descriptor raise cannot abort startup. Verified by mutation — removing either guard fails the suite.
Checklist
Note: one pre-existing failure,
tests/test_viewer.py::TestWaitForWaylandSocket::test_probe_reports_not_ready_for_a_non_socket_path, fails underpytest -n autoon master as well as here, and passes serially. Unrelated to this change and left alone.🤖 Generated with Claude Code