# flare.http.response_pool

> A module of flare_mlake's API.

Per-worker ``Response`` pool.

Holds a small stack of pre-allocated, reset-in-place ``Response``
objects so the keep-alive hot path can reuse the same struct
(plus its ``HeaderMap`` + ``body`` ``List[UInt8]`` backings)
across consecutive requests on the same connection.

Background
----------

Each ``Response.__init__`` allocates:

1. A fresh ``HeaderMap`` (two empty ``List[String]`` instances).
2. The body ``List[UInt8]`` (default-constructed; capacity 0).
3. A fresh ``reason`` String + ``version`` String.

Even with ownership-by-move responses (``var body``, no copy),
this is one struct + two lists per response. On a long-running
keep-alive connection at 220K req/s, that's ~2.2M list
allocations / s — measurable. ``ResponsePool.acquire()`` returns
a ``Response`` whose backings still carry the previous request's
capacity but are emptied via ``Response.reset(...)``, so the
next ``headers.append`` / ``body.append`` reuses the same
malloc'd backing as long as it fits.

Per-worker, not shared
-----------------------

This pool, like ``DateCache`` and ``Pool[BufferHandle]``,
is **per-worker**. There's no atomic, no mutex, no cross-thread
coordination. Each reactor worker constructs its own
``ResponsePool`` and calls ``acquire`` / ``release`` on its local
instance. Two workers handing the same FD's response cell across
a thread boundary is not a supported pattern.

Storage strategy
----------------

``Response`` is ``Movable`` but not ``Copyable``, so we can't
hold a ``List[Response]`` directly (Mojo's ``List`` requires
``Copyable``). Instead we store ``List[Int]`` where each entry
is a heap-allocated ``Response`` cell address, managed via the
existing ``flare.runtime.Pool[Response]`` typed allocator.
``acquire`` pops an address, copies the Response value out via
``Pool.get_ptr().take_pointee()``, and frees the cell.
``release`` moves the supplied Response into a fresh cell and
pushes the address. Net allocation cost: ``alloc/free`` per
acquire/release pair, but the underlying body + header List
buffers are **preserved across the move**, which is the actual
B6 win.

Wiring into the reactor's per-connection state machine
(``flare.http._server_reactor_impl.ConnHandle``) is a follow-up
commit that bolts onto this primitive without changing the
serializer's hot-path layout. The primitive exists today so
the wiring change is a mechanical swap of
``Response(status=200, ...)`` for
``response_pool.acquire(status=200, ...)``.


v0.10 wiring result -- NOT WIRED, and the reason is structural rather
than a measurement. The hot-path response is built by the handler
(``resp = handler.serve(req^).lower()`` in ``ConnHandle``), so there is
no point at which the reactor could hand it a pooled ``Response``.
``release`` without a matching ``acquire`` just grows the pool. Wiring
this would mean giving handlers access to the pool, i.e. changing the
``Handler`` contract -- a much larger question than a hot-path tweak,
and not one this pool answers on its own.

## Structs

### `ResponsePool`

```mojo
struct ResponsePool
```

Stack of recycled ``Response`` objects for keep-alive reuse.

The pool holds at most ``capacity`` items. Acquiring from an
empty pool constructs a fresh ``Response``; releasing into a
full pool drops the response on the floor (Mojo destructor
runs).

Fields:
    _slots: Top-of-stack-grows-up storage of heap-allocated
            ``Response`` cell addresses (returned by
            ``Pool[Response].alloc_move``). Each cell holds
            a Response whose backing buffers (header lists,
            body bytes) have been ``reset()`` empty but
            retain their allocated capacity.
    _capacity: Maximum number of items the pool will hold.
            Releases past this cap drop the released
            response.

Implements: `Movable`

#### `__init__`

```mojo
def __init__(out self)
```

Construct an empty pool with the default capacity (8).

#### `__deinit__`

```mojo
def __deinit__(deinit self)
```

Free every retained cell when the pool is dropped.

Without this, dropping a non-empty pool would leak every
cell + its body / header backings.

#### `with_capacity`

```mojo
def with_capacity(capacity: Int) -> Self
```

Construct an empty pool with a custom maximum capacity.

Args:

- `capacity`: Maximum number of pooled ``Response``
          objects. Must be > 0.

#### `acquire`

```mojo
def acquire(mut self, status: Int = Int(200), var reason: String = "") -> Response
```

Return a reusable ``Response`` from the top of the pool, or construct a fresh one if the pool is empty.

On a hit, the returned ``Response`` has empty headers + body
but retains its prior backing capacity, so the next
``headers.append`` / ``body.append`` call reuses the same
malloc'd backing as long as the new payload fits.

Args:

- `status`: Status code for the returned response (default
        200).
- `reason`: Reason phrase (default empty — the serializer
        will fill from the status code at write time).

#### `release`

```mojo
def release(mut self, var response: ResponseImpl)
```

Return ``response`` to the pool for future ``acquire`` calls.

If the pool is at capacity the response is destroyed
(Mojo's ``var`` destructor runs at the end of this
function); the pool itself never grows past
``_capacity``.

Args:

- `response`: Owned ``Response`` to recycle.

#### `size`

```mojo
def size(self) -> Int
```

Return the number of recycled responses currently sitting in the pool.

Useful for tests + telemetry.

#### `capacity`

```mojo
def capacity(self) -> Int
```

Return the maximum number of items the pool will hold.
