Kacho.ASyncObjectPool
10.1.0
dotnet add package Kacho.ASyncObjectPool --version 10.1.0
NuGet\Install-Package Kacho.ASyncObjectPool -Version 10.1.0
<PackageReference Include="Kacho.ASyncObjectPool" Version="10.1.0" />
<PackageVersion Include="Kacho.ASyncObjectPool" Version="10.1.0" />
<PackageReference Include="Kacho.ASyncObjectPool" />
paket add Kacho.ASyncObjectPool --version 10.1.0
#r "nuget: Kacho.ASyncObjectPool, 10.1.0"
#:package Kacho.ASyncObjectPool@10.1.0
#addin nuget:?package=Kacho.ASyncObjectPool&version=10.1.0
#tool nuget:?package=Kacho.ASyncObjectPool&version=10.1.0
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
MethodImplattributes - Proper Cleanup: Handles both
IDisposableandIAsyncDisposablewith 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 useIdleCount: Number of objects available in the poolCurrentTimeouts: Timeouts since last monitoring cycleActiveRatio: Ratio of active rentals to total pool sizeIdleRatio: Ratio of idle objects to total pool sizeEmaRentTimeTicks: Exponential moving average of rental durations (ticks)EmaIdleRatio: Exponential moving average of idle ratios
Lifetime Statistics
TotalRentals: Total number of rentals since pool creationTotalObjectsCreated: Total objects created by the poolTotalObjectsDisposed: Total objects disposed (including shrink operations)ObjectGeneratorFailures: Number of times object creation failedTotalTimeouts: Cumulative timeout countSuccessfulRentalRate: Ratio of successful rentals (rentals without timeouts)
Pool Dynamics
GrowthEventCount: Number of times the pool grewShrinkEventCount: Number of times the pool shrankPeakPoolSize: Maximum pool size reached during lifetimePeakActiveRentals: Maximum concurrent active rentalsLastGrowthTime: Timestamp of last growth eventLastShrinkTime: Timestamp of last shrink eventObjectsCreatedByTimeout: 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 viaConcurrentBag, 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
TryReadsuccess avoids async machinery
Best Practices
- Initialize Early: Call
InitializeAsync()after construction to pre-populate the pool - Tune Pool Sizes: Set
MinPoolSizebased on steady-state load,MaxPoolSizefor burst capacity - Monitor Metrics: Track
SuccessfulRentalRateandPeakActiveRentalsfor capacity planning - Adjust EMA Alpha: Lower
EmaIdleRatioAlpha(e.g., 0.1) for slower, smoother shrinking - Thread-Safe Generators: Ensure
objectGeneratoris thread-safe and handles exceptions - Proper Disposal: Objects should implement
IDisposableorIAsyncDisposablefor cleanup - Timeout Tuning: Use
CreateNewImmediatelyfor latency-sensitive workloads,WaitForAvailablefor 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:
IAsyncDisposableimplementation 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 | Versions 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. |
-
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.