Skip to content

Execution model

Dexpot chooses its scheduler once when the module imports. The application keeps the same synchronous handlers in every mode.

Runtime Default serving model Overload behavior
Free-threaded CPython (sys._is_gil_enabled() == False) One process; each admitted connection owns a thread Active connections are capped at 1,024 by default; excess connections receive 503 before thread creation
Standard GIL CPython A bounded pool of CPU * 2 + 2 connection-owning threads The queue is capped at 2 * pool; excess connections receive 503
Standard GIL CPython with DEXPOT_WORKERS>1 POSIX SO_REUSEPORT processes, each with its own bounded pool Each worker sheds independently

A worker owns a keep-alive connection until it closes. Idle keep-alive sockets are not returned to a shared admission queue.

Set limits before the process imports Dexpot:

Terminal window
# Free-threaded process-wide active-connection cap
DEXPOT_MAX_CONNECTIONS=512 dexpot serve main:app
# Standard GIL pool and queue
DEXPOT_POOL=16 DEXPOT_MAX_QUEUE=32 dexpot serve main:app

DEXPOT_POOL=0 keeps automatic sizing. Negative pool sizes and nonpositive queue or connection limits fail during import, before a listener can open.

On supported POSIX systems:

Terminal window
DEXPOT_WORKERS=4 dexpot serve main:app

DEXPOT_WORKERS>1 requires POSIX fork and SO_REUSEPORT. Dexpot rejects the setting on unsupported platforms. Free-threaded builds intentionally remain single-process because their threads can execute Python in parallel.

SIGINT and SIGTERM stop admission and allow active connections up to five seconds to drain. The standard-GIL supervisor restarts a worker that exits unexpectedly.

Registration produces immutable EndpointPlan objects for binding, path conversion, and body and response codecs. Serving freezes those endpoints into one ApplicationPlan and a length-grouped RouterPlan before opening a listener. Late route registration fails instead of diverging from the plan traffic uses.