public async Task ValidateInvalidation() { var maxCapacity = 10; var clock = new MemoryClock(); var maxAgeMinutes = maxCapacity + 10; // No resources should expire. var pool = new ResourcePool <Key, Resource>(new Context(TestGlobal.Logger), new ResourcePoolConfiguration() { MaximumAge = TimeSpan.FromMinutes(maxAgeMinutes), MaximumResourceCount = maxCapacity, EnableInstanceInvalidation = true, }, resourceFactory: _ => new Resource(), clock); using (var wrapper = await pool.CreateAsync(new Key(1))) { wrapper.Invalidate(); } // We actually do the cleanup during the next operation using (var wrapper = await pool.CreateAsync(new Key(2))) { } Assert.Equal(1, pool.Counter[ResourcePoolCounters.Cleaned].Value); Assert.Equal(2, pool.Counter[ResourcePoolCounters.Created].Value); }
public async Task NoContractExceptionsWhenCleaning() { var capacity = 2; var context = new Context(TestGlobal.Logger); for (var i = 0; i < 100_000; i++) { var pool = new ResourcePool <Key, Resource>(context, maxResourceCount: capacity, maxAgeMinutes: 1, resourceFactory: _ => new Resource()); for (var j = 0; j < capacity; j++) { using var wrapper = await pool.CreateAsync(new Key (j)); var value = wrapper.Value; } var createTask = pool.CreateAsync(new Key(capacity)); var accessTask = pool.CreateAsync(new Key(0)); var results = await Task.WhenAll(createTask, accessTask); results[0].Dispose(); results[1].Dispose(); } }
public async Task DuplicateClientsAreTheSameObject() { var capacity = 2; var context = new Context(TestGlobal.Logger); var pool = new ResourcePool <Key, Resource>(context, maxResourceCount: capacity, maxAgeMinutes: 1, resourceFactory: _ => new Resource()); using var obj0 = await pool.CreateAsync(new Key (0)); using var obj1 = await pool.CreateAsync(new Key (0)); Assert.Same(obj0.Value, obj1.Value); }
public async Task DuplicateClientsAreTheSameObject() { var capacity = 2; var context = new Context(TestGlobal.Logger); var pool = new ResourcePool <Key, Resource>(context, new ResourcePoolConfiguration() { MaximumResourceCount = capacity, MaximumAge = TimeSpan.FromMinutes(1), }, resourceFactory: _ => new Resource()); using var obj0 = await pool.CreateAsync(new Key (0)); using var obj1 = await pool.CreateAsync(new Key (0)); Assert.Same(obj0.Value, obj1.Value); }
public async Task IssueSameClientManyTimes() { var clock = new MemoryClock(); var pool = new ResourcePool <Key, Resource>(new Context(TestGlobal.Logger), maxResourceCount: 1, maxAgeMinutes: 1, resourceFactory: _ => new Resource(), clock); var key = new Key(1); using var originalWrapper = await pool.CreateAsync(key); var originalResource = originalWrapper.Value; for (var i = 0; i < 1000; i++) { using var newWrapper = await pool.CreateAsync(key); Assert.Same(newWrapper.Value, originalResource); } }
public async Task ValidateSingleCleanup() { var maxCapacity = 10; var clock = new MemoryClock(); var maxAgeMinutes = maxCapacity + 10; // No resources should expire. var pool = new ResourcePool <Key, Resource>(new Context(TestGlobal.Logger), new ResourcePoolConfiguration() { MaximumResourceCount = maxCapacity, MaximumAge = TimeSpan.FromMinutes(maxAgeMinutes), }, resourceFactory: _ => new Resource(), clock); var resources = new List <Resource>(); foreach (var num in Enumerable.Range(1, maxCapacity)) { var key = new Key(num); using var wrapper = await pool.CreateAsync(key); resources.Add((wrapper.Value)); clock.UtcNow += TimeSpan.FromMinutes(1); // First resource will be the oldest. } Assert.Equal(0, pool.Counter[ResourcePoolCounters.Cleaned].Value); Assert.Equal(maxCapacity, pool.Counter[ResourcePoolCounters.Created].Value); using (var wrapper = await pool.CreateAsync(new Key(maxCapacity + 1))) { resources.Add((wrapper.Value)); } // We actually do the cleanup during the next operation using (var wrapper = await pool.CreateAsync(new Key(maxCapacity))) { } Assert.Equal(1, pool.Counter[ResourcePoolCounters.Cleaned].Value); Assert.Equal(maxCapacity + 1, pool.Counter[ResourcePoolCounters.Created].Value); foreach (var client in resources.Skip(1)) { Assert.False(client.ShutdownStarted); } Assert.True(resources.First().ShutdownCompleted); }
public async Task ValidateCleanupOfExpired() { var resourceCount = 10; var clock = new MemoryClock(); var maxAgeMinutes = 1; var pool = new ResourcePool <Key, Resource>(new Context(TestGlobal.Logger), new ResourcePoolConfiguration() { MaximumResourceCount = resourceCount, MaximumAge = TimeSpan.FromMinutes(maxAgeMinutes), }, resourceFactory: _ => new Resource(), clock); var wrappers = new List <ResourceWrapper <Resource> >(); for (var i = 0; i < resourceCount; i++) { using var wrapper = await pool.CreateAsync(new Key (i)); Assert.True(wrapper.Value.StartupStarted); wrappers.Add(wrapper); } // Expire the resources clock.UtcNow += TimeSpan.FromMinutes(maxAgeMinutes); ResourceWrapper <Resource> wrapper2; using (wrapper2 = await pool.CreateAsync(new Key(-1))) { } Assert.Equal(resourceCount, pool.Counter[ResourcePoolCounters.Cleaned].Value); for (var i = 0; i < resourceCount; i++) { using var wrapper = await pool.CreateAsync(new Key (i)); Assert.NotSame(wrapper.Value, wrappers[i].Value); Assert.Equal(1, wrapper.Uses); Assert.True(wrappers[i].Value.ShutdownCompleted); } }
public async Task IssueSameClientManyTimes() { var clock = new MemoryClock(); var pool = new ResourcePool <Key, Resource>(new Context(TestGlobal.Logger), new ResourcePoolConfiguration() { MaximumResourceCount = 1, MaximumAge = TimeSpan.FromMinutes(1), }, resourceFactory: _ => new Resource(), clock); var key = new Key(1); using var originalWrapper = await pool.CreateAsync(key); var originalResource = originalWrapper.Value; for (var i = 0; i < 1000; i++) { using var newWrapper = await pool.CreateAsync(key); Assert.Same(newWrapper.Value, originalResource); } }
public async Task CreateFailsAfterDispose() { var capacity = 2; var context = new Context(TestGlobal.Logger); var pool = new ResourcePool <Key, Resource>(context, maxResourceCount: capacity, maxAgeMinutes: 1, resourceFactory: _ => new Resource()); pool.Dispose(); await Assert.ThrowsAsync <ObjectDisposedException>(async() => { using var obj1 = await pool.CreateAsync(new Key(0)); }); }
public Task CreateFailsAfterDispose() { var capacity = 2; var context = new Context(TestGlobal.Logger); var pool = new ResourcePool <Key, Resource>(context, new ResourcePoolConfiguration() { MaximumResourceCount = capacity, MaximumAge = TimeSpan.FromMinutes(1), }, resourceFactory: _ => new Resource()); pool.Dispose(); return(Assert.ThrowsAsync <ObjectDisposedException>(async() => { using var obj1 = await pool.CreateAsync(new Key(0)); })); }
/// <summary> /// Use an existing <see cref="GrpcCopyClient"/> if possible, else create a new one. /// </summary> public async Task <TResult> UseWithInvalidationAsync <TResult>(OperationContext context, string host, int grpcPort, Func <OperationContext, IResourceWrapperAdapter <GrpcCopyClient>, Task <TResult> > operation) { var key = new GrpcCopyClientKey(host, grpcPort); switch (_configuration.ResourcePoolVersion) { case GrpcCopyClientCacheConfiguration.PoolVersion.Disabled: { var client = new GrpcCopyClient(key, _configuration.GrpcCopyClientConfiguration, sharedBufferPool: _grpcCopyClientBufferPool); await client.StartupAsync(context).ThrowIfFailure(); var result = await operation(context, new DefaultResourceWrapperAdapter <GrpcCopyClient>(client)); await client.ShutdownAsync(context).ThrowIfFailure(); return(result); } case GrpcCopyClientCacheConfiguration.PoolVersion.V1: { Contract.AssertNotNull(_resourcePool); using var resourceWrapper = await _resourcePool.CreateAsync(key); return(await operation(context, new ResourceWrapperV1Adapter <GrpcCopyClient>(resourceWrapper))); } case GrpcCopyClientCacheConfiguration.PoolVersion.V2: { Contract.AssertNotNull(_resourcePoolV2); return(await _resourcePoolV2.UseAsync(context, key, async resourceWrapper => { // This ensures that the operation we want to perform conforms to the cancellation. When the // resource needs to be removed, the token will be cancelled. Once the operation completes, we // will be able to proceed with shutdown. using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(context.Token, resourceWrapper.ShutdownToken); var nestedContext = new OperationContext(context, cancellationTokenSource.Token); return await operation(nestedContext, new ResourceWrapperV2Adapter <GrpcCopyClient>(resourceWrapper)); })); } } throw new NotImplementedException($"Unhandled resource pool version `{_configuration.ResourcePoolVersion}`"); }
public async Task FillCacheWithoutRemovingClients() { var maxCapacity = 10; var clock = new MemoryClock(); var pool = new ResourcePool <Key, Resource>(new Context(TestGlobal.Logger), maxResourceCount: maxCapacity, maxAgeMinutes: 1, resourceFactory: _ => new Resource(), clock); for (var i = 0; i < maxCapacity; i++) { using var wrapper = await pool.CreateAsync(new Key (i)); } // Created all resources Assert.Equal(maxCapacity, pool.Counter.GetCounterValue(ResourcePoolCounters.Created)); // Zero resources were cleaned Assert.Equal(0, pool.Counter.GetCounterValue(ResourcePoolCounters.Cleaned)); // Zero resources were reused Assert.Equal(0, pool.Counter.GetCounterValue(ResourcePoolCounters.Reused)); }