AoAH Day 12: Eio Connection pooling and event tracing / Dec 2025

After yesterday's library bonanza for HTTP cookie handling, I implemented a TCP/TLS connection pooling library. 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, inspired by Jon Ludlam rediscovering the joy of SVGs yesterday!

Approach

The core library itself is pretty straightforward, as it's reasonably similar to the 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 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 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. 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 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 of the results of a conpool run, with all the various configurations tests pushed together into a visualisation. I've also seen Sadiq Jaffer do the same thing when iterating on TESSERA CNN visualisations for his intermediate runs.

Standalone visualiation of the connection pool stress tests on localhost
Standalone visualiation of the connection pool stress tests on localhost

A negative result with event tracing

I figured I'd have a go at integrating at event tracing, but the various packaging problems around the tools defeated my quick attempt. There's 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. 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.

The cleanup agent does a reasonable job of pulling out reusable functions after several generation passes
The cleanup agent does a reasonable job of pulling out reusable functions after several generation passes

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 that Sadiq Jaffer 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 or web monitors.

Tomorrow, we'll pull this all together into a Day 13 HTTP client!


Loading recent items...