Kacho.ASyncObjectPool 10.1.0

dotnet add package Kacho.ASyncObjectPool --version 10.1.0
                    
NuGet\Install-Package Kacho.ASyncObjectPool -Version 10.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Kacho.ASyncObjectPool" Version="10.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Kacho.ASyncObjectPool" Version="10.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Kacho.ASyncObjectPool" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Kacho.ASyncObjectPool --version 10.1.0
                    
#r "nuget: Kacho.ASyncObjectPool, 10.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Kacho.ASyncObjectPool@10.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Kacho.ASyncObjectPool&version=10.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Kacho.ASyncObjectPool&version=10.1.0
                    
Install as a Cake Tool

AsyncObjectPool

A high-performance, production-ready async object pool for .NET with comprehensive metrics and adaptive scaling.

Overview

AsyncObjectPool<T> provides a thread-safe, asynchronous object pool that dynamically adapts to workload patterns. Built on Channel<T> for lock-free operations, it features EMA-based adaptive scaling, extensive telemetry, and configurable timeout behaviors. Perfect for managing expensive resources like database connections, HTTP clients, or any heavy objects requiring pooling.

Features

  • Zero-Allocation Fast Path: Wrapper object pooling eliminates allocations on hot paths
  • Asynchronous Operations: Fully async object creation, renting, and disposal
  • Adaptive Scaling: EMA-based dynamic growth and shrinking based on demand patterns
  • Four Timeout Behaviors: Flexible policies for handling resource exhaustion
  • Comprehensive Metrics: 20+ metrics covering lifetime statistics, performance, and pool dynamics
  • Channel-Based: Lock-free Channel<T> for high-throughput, low-contention operations
  • Aggressive Inlining: Optimized hot paths with MethodImpl attributes
  • Proper Cleanup: Handles both IDisposable and IAsyncDisposable with graceful disposal

Installation

Install via NuGet:

dotnet add package Kacho.AsyncObjectPool

Usage

Basic Example

using Kacho.AsyncObjectPool;
using System.Threading.Channels;

async Task Main()
{
    // Create a pool for strings with an async generator
    var pool = new AsyncObjectPool<string>(
        objectGenerator: async () =>
        {
            await Task.Delay(100); // Simulate expensive creation
            return "PooledObject";
        },
        options: new AsyncObjectPoolOptions
        {
            InitialPoolSize = 5,
            MinPoolSize = 2,
            MaxPoolSize = 10,
            TimeoutBehavior = TimeoutBehavior.CreateNewImmediately
        });

    // Initialize the pool
    await pool.InitializeAsync();

    // Use an object from the pool
    await pool.UseAsync(async obj =>
    {
        Console.WriteLine($"Using object: {obj}");
        await Task.Delay(50);
    });

    // Use with return value
    string result = await pool.UseAsync(async obj =>
    {
        await Task.Delay(50);
        return obj + "-Processed";
    });

    Console.WriteLine($"Result: {result}");

    // Check pool metrics
    var metrics = pool.Metrics;
    Console.WriteLine($"Current Pool Size: {metrics.CurrentSize}, Active Rentals: {metrics.ActiveRentals}");

    // Dispose when done
    await pool.DisposeAsync();
}

Advanced Example with Monitoring

// Production-ready setup with comprehensive monitoring
var pool = new AsyncObjectPool<HttpClient>(
    objectGenerator: async () =>
    {
        var client = new HttpClient();
        client.Timeout = TimeSpan.FromSeconds(30);
        return client;
    },
    options: new AsyncObjectPoolOptions
    {
        InitialPoolSize = 20,
        MinPoolSize = 10,
        MaxPoolSize = 200,
        GrowthFactor = 0.5,
        ShrinkIdleThreshold = 0.3,
        EmaIdleRatioAlpha = 0.2,  // Slower shrinking
        TimeoutBehavior = TimeoutBehavior.CreateNewImmediately
    });

await pool.InitializeAsync();

// Background metrics monitoring
_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(10));
        var m = pool.Metrics;

        // Log metrics to your monitoring system
        Console.WriteLine($"[Pool Metrics]");
        Console.WriteLine($"  Size: {m.CurrentSize}/{m.PeakPoolSize} | Active: {m.ActiveRentals}/{m.PeakActiveRentals}");
        Console.WriteLine($"  Success Rate: {m.SuccessfulRentalRate:P2} | Total Rentals: {m.TotalRentals:N0}");
        Console.WriteLine($"  Growth: {m.GrowthEventCount} | Shrink: {m.ShrinkEventCount}");
        Console.WriteLine($"  EMA Idle: {m.EmaIdleRatio:P2}");

        // Alert if success rate drops
        if (m.SuccessfulRentalRate < 0.95)
        {
            Console.WriteLine($"  ⚠️ WARNING: Low success rate! Consider increasing MaxPoolSize.");
        }
    }
});

// Use the pool
await pool.UseAsync(async client =>
{
    var response = await client.GetAsync("https://api.example.com/data");
    return await response.Content.ReadAsStringAsync();
});

Configuration Options

The AsyncObjectPoolOptions class allows customization of pool behavior:

  • InitialPoolSize: Number of objects to create on initialization (default: 10).
  • MinPoolSize: Minimum number of objects in the pool (default: 5).
  • MaxPoolSize: Maximum number of objects in the pool (default: 100).
  • GrowthFactor: Factor for pool growth when timeouts occur (default: 0.5).
  • MaxShrinkSize: Maximum number of objects to remove in one shrink cycle (default: int.MaxValue).
  • MaxGrowthSize: Maximum number of objects to add in one growth cycle (default: int.MaxValue).
  • ShrinkIdleThreshold: Idle ratio threshold for shrinking the pool (default: 0.2).
  • EmaRentTimeAlpha: Smoothing factor for rental time EMA (default: 0.1).
  • EmaIdleRatioAlpha: Smoothing factor for idle ratio EMA (default: 0.5).
  • MonitoringLoopDelayMs: Delay between monitoring cycles in milliseconds (default: 2000).
  • TimeoutBehavior: Behavior when no objects are available within the timeout period (default: CreateNewImmediately).

Timeout Behaviors

The TimeoutBehavior enum defines how the pool handles cases when no objects are available within the specified timeout:

  • WaitForAvailable: Waits until an object becomes available.
  • CreateNewImmediately: Creates a new object immediately.
  • TriggerMonitoringWaitForAvailable: Signals the monitoring loop and waits for an object.
  • TriggerMonitoringCreateNewImmediately: Signals the monitoring loop and creates a new object.

Metrics

The Metrics property provides comprehensive insights into pool performance and behavior:

Current State

  • CurrentSize: Total objects in the pool (active + idle)
  • ActiveRentals: Number of objects currently in use
  • IdleCount: Number of objects available in the pool
  • CurrentTimeouts: Timeouts since last monitoring cycle
  • ActiveRatio: Ratio of active rentals to total pool size
  • IdleRatio: Ratio of idle objects to total pool size
  • EmaRentTimeTicks: Exponential moving average of rental durations (ticks)
  • EmaIdleRatio: Exponential moving average of idle ratios

Lifetime Statistics

  • TotalRentals: Total number of rentals since pool creation
  • TotalObjectsCreated: Total objects created by the pool
  • TotalObjectsDisposed: Total objects disposed (including shrink operations)
  • ObjectGeneratorFailures: Number of times object creation failed
  • TotalTimeouts: Cumulative timeout count
  • SuccessfulRentalRate: Ratio of successful rentals (rentals without timeouts)

Pool Dynamics

  • GrowthEventCount: Number of times the pool grew
  • ShrinkEventCount: Number of times the pool shrank
  • PeakPoolSize: Maximum pool size reached during lifetime
  • PeakActiveRentals: Maximum concurrent active rentals
  • LastGrowthTime: Timestamp of last growth event
  • LastShrinkTime: Timestamp of last shrink event
  • ObjectsCreatedByTimeout: Objects created due to timeout behaviors

Example Usage

var metrics = pool.Metrics;
Console.WriteLine($"Pool Health:");
Console.WriteLine($"  Current Size: {metrics.CurrentSize} (Peak: {metrics.PeakPoolSize})");
Console.WriteLine($"  Active: {metrics.ActiveRentals} (Peak: {metrics.PeakActiveRentals})");
Console.WriteLine($"  Success Rate: {metrics.SuccessfulRentalRate:P2}");
Console.WriteLine($"  Total Rentals: {metrics.TotalRentals:N0}");
Console.WriteLine($"  Growth Events: {metrics.GrowthEventCount}, Shrink Events: {metrics.ShrinkEventCount}");

Performance Optimizations

The pool is designed for high-throughput scenarios with several optimizations:

  • Wrapper Object Pooling: PooledObject<T> wrappers are reused via ConcurrentBag, eliminating per-rental allocations
  • ArrayPool Integration: Temporary arrays use ArrayPool<T> to avoid allocations during growth
  • Aggressive Inlining: Hot path methods use [MethodImpl(AggressiveInlining)] for reduced call overhead
  • Lock-Free Operations: Channel<T> provides lock-free rent/return operations
  • Batched Metrics: Volatile reads are batched to reduce memory barriers
  • Fast Path Optimization: Immediate TryRead success avoids async machinery

Best Practices

  • Initialize Early: Call InitializeAsync() after construction to pre-populate the pool
  • Tune Pool Sizes: Set MinPoolSize based on steady-state load, MaxPoolSize for burst capacity
  • Monitor Metrics: Track SuccessfulRentalRate and PeakActiveRentals for capacity planning
  • Adjust EMA Alpha: Lower EmaIdleRatioAlpha (e.g., 0.1) for slower, smoother shrinking
  • Thread-Safe Generators: Ensure objectGenerator is thread-safe and handles exceptions
  • Proper Disposal: Objects should implement IDisposable or IAsyncDisposable for cleanup
  • Timeout Tuning: Use CreateNewImmediately for latency-sensitive workloads, WaitForAvailable for resource-constrained scenarios

Implementation Details

  • Channel-Based Storage: Uses Channel<T> for efficient, thread-safe object management
  • EMA Smoothing: Exponential moving averages prevent over-reaction to temporary spikes
  • Monitoring Loop: Background task runs every 2 seconds (configurable) to adjust pool size
  • Graceful Disposal: IAsyncDisposable implementation ensures proper cleanup with cancellation support
  • Exception Safety: Monitoring loop catches exceptions to prevent loop death; object creation failures are tracked

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Kacho.ASyncObjectPool:

Package Downloads
Kacho.RabbitMQ

Lightweight, async .NET wrapper for RabbitMQ.Client with robust connection management and dynamic entity declaration. Publishers leverage efficient channel pooling via Kacho.ASyncObjectPool, while consumers achieve high performance with parallel workers and bounded channels for fast, scalable message processing in .NET 8.0.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.1.0 131 1/19/2026
10.0.0 123 1/3/2026
1.1.2 248 6/2/2025
1.1.1 199 6/1/2025
1.1.0 149 6/1/2025
1.0.0 197 4/30/2025