Architecture

How Pounce's server, supervisor, and worker layers fit together

3 min read 614 words

Overview

Pounce follows a three-layer architecture: Server orchestrates lifecycle, Supervisor manages workers, and Workers handle requests. All layers share a single frozenServerConfig— no synchronization needed.

flowchart TD Server["Server\nCONFIG → DETECT → BIND → LIFESPAN → SERVE → SHUTDOWN"] Supervisor["Supervisor\ndetect nogil → threads\ndetect GIL → processes"] Server --> Supervisor Supervisor -- "spawn N workers" --> W1["Worker 1\nasyncio event loop"] Supervisor -- "spawn N workers" --> W2["Worker 2\nasyncio event loop"] Supervisor -- "spawn N workers" --> WN["Worker N\nasyncio event loop"] W1 --> Shared["Shared Immutable State\n(config, app reference)"] W2 --> Shared WN --> Shared

Server Layer

TheServerclass orchestrates the full lifecycle:

  1. 1

    CONFIG

    Validate and freezeServerConfig.

  2. 2

    DETECT

    Check for free-threading viasys._is_gil_enabled().

  3. 3

    BIND

    Create listening sockets withSO_REUSEPORT.

  4. 4

    LIFESPAN

    Run ASGI lifespan protocol (startup / shutdown).

  5. 5

    SERVE

    Delegate to single-worker fast path or multi-worker supervisor.

  6. 6

    SHUTDOWN

    Graceful connection draining, signal handling.

For single-worker mode (workers=1), the server skips the supervisor entirely and runs the worker directly — no thread/process overhead.

Supervisor Layer

TheSupervisormanages worker lifecycle:

  • Spawn — Creates N worker threads (nogil) or processes (GIL)
  • Monitor — Health-check loop with automatic restart (max 5 restarts per 60s window)
  • Reload — Graceful restart of all workers when--reloaddetects changes
  • Shutdown — Signals all workers to stop, waits for connection draining

Worker Layer

EachWorkerruns its own asyncio event loop:

  1. 1

    Accept

    Receive TCP connections from the shared socket.

  2. 2

    Parse

    Feed bytes to protocol parser (h11, h2, or wsproto).

  3. 3

    Bridge

    Build ASGI scope, createreceive/sendcallables.

  4. 4

    Dispatch

    Callapp(scope, receive, send).

  5. 5

    Respond

    Serialize response and write to socket (streaming).

Workers are fully independent. No shared mutable state or cross-worker coordination during request handling.

Request Pipeline

A single HTTP request flows through:

flowchart LR A[Socket Accept] --> B[TLS Unwrap] B --> C{Protocol\nDetection} C -->|h1| D1[h11 / fast parser] C -->|h2| D2[h2] D1 --> E[ASGI Scope\nConstruction] D2 --> E E --> E2[Proxy Header\nValidation] E2 --> E3[Request ID\nGeneration] E3 --> E4{Health\nCheck?} E4 -->|yes| E5[Health Response] E4 -->|no| F["app(scope, receive, send)"] F --> G[Response\nSerialization] G --> G2[CRLF\nSanitization] G2 --> H[Compression\nzstd / gzip / identity] H --> I[Server-Timing\nHeader Injection] I --> J[Socket Write]

The bridge is per-request — created and destroyed within a single connection handler. This ensures zero cross-request state leakage.

See Also