-
Notifications
You must be signed in to change notification settings - Fork 0
Preparation Strategies
A pooled item can go stale between leases — an SMTP server drops an idle connection, a token expires. A preparation strategy checks an item on each lease and restores it when needed, so callers always receive a ready item. This guide covers the interface, registration, and the failure contract. The item factory builds items cheaply; the preparation strategy does the expensive readiness work here.
public interface IPreparationStrategy<TPoolItem>
where TPoolItem : class
{
ValueTask<bool> IsReadyAsync(TPoolItem item, CancellationToken cancellationToken);
Task PrepareAsync(TPoolItem item, CancellationToken cancellationToken);
}On each lease the pool calls IsReadyAsync. When it returns false, the pool calls PrepareAsync before handing the item over. Both calls are bounded by PreparationTimeout from Pool options, which defaults to Timeout.InfiniteTimeSpan. When no strategy is registered, the pool hands items over without a readiness check.
A readiness check and preparation over MailKit's IMailTransport:
public async ValueTask<bool> IsReadyAsync(IMailTransport item, CancellationToken cancellationToken) =>
item.IsConnected
&& item.IsAuthenticated
&& await NoOpAsync(item, cancellationToken);
public async Task PrepareAsync(IMailTransport item, CancellationToken cancellationToken)
{
await item.ConnectAsync(host.Name, host.Port, host.UseSsl, cancellationToken);
await item.AuthenticateAsync(credentials.UserName, credentials.Password, cancellationToken);
}Make IsReadyAsync cheap — it runs on every lease. A liveness probe like SMTP's NOOP costs a round trip, so gate it behind a recency check and skip it for connections used moments ago. The SMTP connection pool recipe shows that pattern, plus aging connections out before the server drops them.
Register a custom strategy with AddPreparationStrategy<TPoolItem, TStrategy>(), then add the pool:
services
.AddPreparationStrategy<SmtpConnection, SmtpConnectionPreparationStrategy>()
.AddPool<SmtpConnection>(configuration, options =>
{
options.MinSize = 2;
options.MaxSize = 10;
});For the built-in strategy that reports every item ready — a no-op readiness check — set UseDefaultPreparationStrategy = true instead of registering your own.
When PrepareAsync throws, the pool does not recirculate a half-prepared item. It disposes that item, frees the capacity permit, and rethrows. The exception surfaces at your LeaseAsync call site. Catch it and retry the lease for a fresh item:
SmtpConnection connection;
try
{
connection = await pool.LeaseAsync(cancellationToken);
}
catch (SmtpException)
{
connection = await pool.LeaseAsync(cancellationToken); // fresh item, fresh preparation
}A preparation failure is also counted on the pool.preparation.exceptions metric, tagged with the exception type — see Metrics.
- SMTP connection pool — a complete readiness-and-recycle strategy against a real server.
- Avoiding footguns — why a discarded item still frees its permit, and what you must retry.