# AoAH Day 12: Eio Connection pooling and event tracing

*2025-12-12 — note*


After yesterday's [library bonanza](https://anil.recoil.org/notes/aoah-2025-11) for HTTP cookie handling, I implemented a TCP/TLS [connection pooling library](https://tangled.org/anil.recoil.org/ocaml-conpool). This is useful for an HTTP client as it provides the network-level mechanisms for keeping track of outgoing network connections _by their DNS name_.  This allows for more flexible outgoing connection management without worrying about overloading remote endpoints.

For example, `github.io` has four A records:

```
> host github.io
github.io has address 185.199.110.153
github.io has address 185.199.109.153
github.io has address 185.199.108.153
github.io has address 185.199.111.153
```

With this new connection pooling library, my application should be able to
connect to the `github.io` name and keep track of all the outgoing connections
on the basis of it being called `github.io` and load balance the number of
outgoing connections accordingly.

In the interests of exploring something new, I also decided to add in visualisation
support to figure out what the library is spending its time on.
I decided to generate [self-contained visualisations](https://jon.recoil.org/blog/2025/12/an-svg-is-all-you-need.html),
inspired by [Jon Ludlam](https://jon.recoil.org) rediscovering the joy of SVGs yesterday\!


## Approach

The core library itself is pretty straightforward, as it's reasonably similar
to the [ocaml-conduit](https://github.com/mirage/ocaml-conduit) mechanism of
providing a name-based resolver. The interface should be a matter of creating a
connection pool to keep track of state and then requesting a connection from it
to a hostname:

```
module Endpoint: sig
 type t
 val make : host:string -> port:int -> t
end

type connection_ty = [Eio.Resource.close_ty | Eio.Flow.two_way_ty]                                                                                            
type connection = connection_ty Eio.Resource.t
val connection : sw:Eio.Switch.t -> t -> Endpoint.t -> connection
```

This uses Eio's [resource mechanism](https://github.com/ocaml-multicore/eio?tab=readme-ov-file#provider-interfaces) to allow this connection to be used like any other.

### Stacking IO errors

One of the coolest things about Eio's error handling is the ability to [stack errors](https://github.com/ocaml-multicore/eio?tab=readme-ov-file#provider-interfaces) by re-raising exceptions and adding more context to it.  I prompted the agent to also create connection-specific errors for Eio, but to integrate them into the [Eio.Io extensible type](https://ocaml.org/p/eio/1.3/doc/eio/Eio/index.html#exception-Io). This allows errors from failures to look like this:

```
[WARNING] Connection attempt 3 to localhost:8088 failed:
  Eio.Io Net Connection_failure Refused Unix_error
  (Connection refused, "connect-in-progress", ""),
   connecting to tcp:127.0.0.1:8088,
   connecting to localhost:8088,
   after 3 retry attempts
```

The conpool library can keep a lightweight reporting stack of what it's been
doing while propagating the error, which makes a big different to the quality
of the end logging.

## Results

### Self-contained visualisations work well

I activated [Claude Marketplace's front end module](https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design)
and instructed it to generate me a self-contained HTML file with the results of
the output of the stress test.

This...just worked. Here's a [HTML
snapshot](https://www.cl.cam.ac.uk/~avsm2/conpool-stress.html) of the results
of a conpool run, with all the various configurations tests pushed together
into a visualisation. I've also seen [Sadiq Jaffer](https://toao.com) do the same thing when iterating
on [TESSERA](https://anil.recoil.org/papers/2025-tessera) CNN visualisations for his intermediate runs.

<a href="https://www.cl.cam.ac.uk/~avsm2/conpool-stress.html"> <figure class="image-center"><img src="/images/aoah-vis-ss-1.webp" alt="Standalone visualiation of the connection pool stress tests on localhost" title="Standalone visualiation of the connection pool stress tests on localhost" loading="lazy" srcset="/images/aoah-vis-ss-1.768.webp 768w, /images/aoah-vis-ss-1.640.webp 640w, /images/aoah-vis-ss-1.480.webp 480w, /images/aoah-vis-ss-1.320.webp 320w, /images/aoah-vis-ss-1.2560.webp 2560w, /images/aoah-vis-ss-1.1920.webp 1920w, /images/aoah-vis-ss-1.1600.webp 1600w, /images/aoah-vis-ss-1.1440.webp 1440w, /images/aoah-vis-ss-1.1280.webp 1280w, /images/aoah-vis-ss-1.1024.webp 1024w"><figcaption>Standalone visualiation of the connection pool stress tests on localhost</figcaption></figure> </a>

### A negative result with event tracing

I figured I'd have a go at integrating at [event tracing](https://ocaml.org/manual/5.2/api/Runtime_events.html), but the various
packaging problems around the tools defeated my quick attempt. There's
[meio](https://github.com/ocaml-multicore/meio) which I really want to use, but
it requires various upstream PRs merging, and I'll also need to figure out what
to do if the ring buffer overflows. I'll have to come back to this later; for
now the self-contained visualisations are fine.

### Another Claude skill for tidying OCaml code

I found myself doing the same prompting repeatedly to tidy up generated code to
make it more idiomatic, so I had Claude go back over my history to compact my
instructions into a reusable skill, which I uploaded to
[ocaml-tidy-code](https://tangled.org/anil.recoil.org/claude-ocaml-tidy-code).
Running this over all the code is good to do _after_ test cases have been
generated, as then it's fairly easy to verify that things are working well.

<figure class="image-center"><img src="/images/aoah-cleanup-ss-1.webp" alt="The cleanup agent does a reasonable job of pulling out reusable functions after several generation passes" title="The cleanup agent does a reasonable job of pulling out reusable functions after several generation passes" loading="lazy" srcset="/images/aoah-cleanup-ss-1.768.webp 768w, /images/aoah-cleanup-ss-1.640.webp 640w, /images/aoah-cleanup-ss-1.480.webp 480w, /images/aoah-cleanup-ss-1.320.webp 320w, /images/aoah-cleanup-ss-1.2560.webp 2560w, /images/aoah-cleanup-ss-1.1920.webp 1920w, /images/aoah-cleanup-ss-1.1600.webp 1600w, /images/aoah-cleanup-ss-1.1440.webp 1440w, /images/aoah-cleanup-ss-1.1280.webp 1280w, /images/aoah-cleanup-ss-1.1024.webp 1024w"><figcaption>The cleanup agent does a reasonable job of pulling out reusable functions after several generation passes</figcaption></figure>

## Reflections

I find the Eio types to be quite complex after we stripped out objects, and so
having an agent code up the idiomatic boilerplate required for registering Eio
exceptions, pretty printers and resources was quite nice. It's also good to
have another Claude skill to further refine my workflow for cleaning up code.

The self-contained visualisations are also extremely useful; in addition to the
standalone HTML one, I also vibed up a full event logging system using the
[Runtime\_events](https://ocaml.org/manual/5.2/api/Runtime_events.html) that
[Sadiq Jaffer](https://toao.com) implemented. I decided not to go with that due to some difficulties
with dealing with full ring buffers, but I'll come back to in the future.
There's clearly a need for a library that can register library-level events and
dispatch them to the OCaml custom events buffer, to remote logging endpoints,
and to clients like [terminal UIs](https://anil.recoil.org/notes/aoah-2025-9) or web monitors.

Tomorrow, we'll pull this all together into a [Day 13 HTTP client](https://anil.recoil.org/notes/aoah-2025-13)\!
Synopsis: Building a TCP/TLS connection pooling library for Eio with DNS-based load balancing, stacked error handling, and self-contained HTML visualisations for stress test results.
Words: 792

## Related

- [2025 Advent of Agentic Humps: Building a useful O(x)Caml library every day](https://anil.recoil.org/notes/aoah-2025) (note, 2025-12-26)
- [AoAH Day 22: Assembling monorepos for agentic OCaml development](https://anil.recoil.org/notes/aoah-2025-22) (note, 2025-12-22)
- [AoAH Day 17: OCaml JMAP to plaster my painful email papercuts](https://anil.recoil.org/notes/aoah-2025-17) (note, 2025-12-17)
- [AoAH Day 13: Heckling an OCaml HTTP client from 50 implementations in 10 languages](https://anil.recoil.org/notes/aoah-2025-13) (note, 2025-12-13)
- [AoAH Day 11: HTTP Cookies and vibing RFCs for breakfast](https://anil.recoil.org/notes/aoah-2025-11) (note, 2025-12-10)
- [AoAH Day 9: Adding a Bonsai terminal UI to Sortal](https://anil.recoil.org/notes/aoah-2025-9) (note, 2025-12-09)
- [TESSERA: Temporal Embeddings of Surface Spectra for Earth Representation and Analysis](https://anil.recoil.org/papers/2025-tessera) (paper, 2025-11-01)

---
Canonical: https://anil.recoil.org/notes/aoah-2025-12
Type: note
Tags: aoah, ocaml, agents, llms, ai
