Skip to content

Named Pools

Mark Lauter edited this page Jun 15, 2026 · 1 revision

Named pools

Run several independently configured pools for one item type — a small read pool and a large write pool over the same connection. This guide covers registering named pools, resolving them through the pool factory, per-pool configuration, and binding a pool to a dedicated client type. The registration extensions are listed in Dependency injection.

Register named pools

Register the pool factory once, then a named pool per configuration:

services.AddPoolFactory<DbConnection>();

services.AddNamedPool<DbConnection>("ReadPool", configuration, options =>
{
    options.MinSize = 5;
    options.MaxSize = 20;
});

services.AddNamedPool<DbConnection>("WritePool", configuration, options =>
{
    options.MinSize = 2;
    options.MaxSize = 10;
});

Each named pool is keyed in the container. The key has the form "{name}.{typeof(TPoolItem).Name}.Pool" — for example, "ReadPool.DbConnection.Pool".

Resolve a named pool

Build the key with ServiceKey.Create<TPoolItem>(name) rather than hand-formatting the string, then resolve through IPoolFactory<TPoolItem>:

public sealed class Repository(IPoolFactory<DbConnection> pools)
{
    private readonly IPool<DbConnection> readPool =
        pools.CreatePool(ServiceKey.Create<DbConnection>("ReadPool"));
    private readonly IPool<DbConnection> writePool =
        pools.CreatePool(ServiceKey.Create<DbConnection>("WritePool"));
}

CreatePool caches the pool per key, so repeated calls with the same key return the same instance. Calling it with a key no pool was registered under throws.

Configure each pool independently

A named pool reads an optional per-pool configuration section, "{key}_PoolOptions", layered over the shared "PoolOptions" section. The shared section binds first; the per-pool section overrides it property by property; the configureOptions action runs last. So shared defaults live in "PoolOptions", and only the differences live in each pool's section:

{
  "PoolOptions": {
    "LeaseTimeout": "00:00:30"
  },
  "ReadPool.DbConnection.Pool_PoolOptions": {
    "MinSize": 5,
    "MaxSize": 20
  }
}

Bind a pool to a typed client

To give a client type its own pool, register the two together. AddPool<TPoolItem, TClient> keys the pool by the client type and injects the IPool<TPoolItem> into the client's constructor:

services.AddPool<DbConnection, ReadClient>(configuration,
    options =>
    {
        options.MinSize = 5;
        options.MaxSize = 20;
    },
    client =>
    {
        // configure the resolved client
    });

Inject ReadClient directly and it holds its own dedicated pool. This is the cleanest option when each pool has exactly one consumer.

Next

  • Dependency injection — every registration extension and what it adds.
  • Metrics — each named pool reports under the same meter, distinguished by its pool.name tag.

Clone this wiki locally