Skip to content

server: release the connection slot exactly once (fixes permanent connection-limit lockout after client hang-ups) - #911

Open
tawei-amd wants to merge 1 commit into
mainfrom
fix/connection-slot-leak
Open

tawei-amd wants to merge 1 commit into
mainfrom
fix/connection-slot-leak

Conversation

@tawei-amd

Copy link
Copy Markdown
Collaborator

Problem

After clients hang up on requests, flm serve can stop answering everything (even GET /v1/models), permanently, until restarted. The process is alive and idle (~0% CPU) and the log only repeats Connection limit reached (10), rejecting new connection.

Root cause

Each connection holds a slot in active_connections_ (limit --socket, default 10). Several paths never gave it back when the client went away. The clearest one is the streaming write-error path in HttpSession::send_chunk_data: it cancels the request and closes the socket, but its active_connections_.fetch_sub(1) was commented out. After 10 leaked slots the accept loop rejects every new connection.

Reproduced deterministically without images, prefix cache, or load: with the default limit, drop 10 requests (streaming hung up before the first chunk, non-streaming hung up while generating), then even GET /v1/models is reset. The log shows the accounting: 21 connections established, 11 closed before the first rejection.

Fix

HttpSession::release_connection() releases the slot exactly once (atomic flag). It is used at every close path, including the stream write error, and called from the destructor so a session can never outlive its slot.

Tests

New src/test/openai_api_test/test_connections.py (added to run_all.py and the README table). It drops more clients than the limit allows, finishes requests, and sends idle and malformed connections. It then checks that limit-1 idle sockets leave room for one request and limit idle sockets do not, which catches both leaked slots and double releases. A barrier request (the server runs one at a time) makes it deterministic.

  • Unfixed build: 0/6 pass; the server wedges.
  • This PR: 6/6 pass, stable across repeated runs.
  • Whole openai_api_test suite on the fixed build (qwen3.5:2b): 87 passed, 0 failed.
  • Ad-hoc stress on the fixed build (23 checks): streaming and non-streaming drops on /v1/chat/completions and /api/chat, at three points in the request (just after sending, after first bytes, mid-response), completed requests, idle/garbage connections, and 4 rounds of 14 concurrent clients mixing completions and drops. Each was followed by the exact-limit probe. No leaks, no underflow.

Server-code-only change; no binaries or xclbins touched. Tested on Linux only (Windows shares the same code path).

🤖 Generated with Claude Code

…n ends

A client that hung up on a request kept its slot in the --socket limit
forever: the write-error path in HttpSession::send_chunk_data closed the
socket but had its active_connections_ decrement commented out, and other
abandoned-request paths never released either. After 10 such clients the
server rejected every connection ("Connection limit reached (10)") and
had to be restarted, while idle at 0% CPU.

Add HttpSession::release_connection(), idempotent via an atomic flag, use
it at every close path (including the stream write error), and call it
from the destructor so a session can never outlive its slot.

Add test_connections.py to the openai_api_test suite: drop more clients
than the limit allows (mid-stream, before the first chunk, non-streaming),
finish and malform connections, then check that limit-1 idle sockets leave
room for a request and limit idle sockets do not (catches leaks and double
releases). 0/6 pass on the unfixed server, 6/6 with the fix; the existing
openai_api_test suite still passes (87/87).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tawei-amd
tawei-amd requested a review from zaneni6 November 1, 2026 18:25

This branch has not been deployed

No deployments
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.

1 participant