public void TransportProducerPoolRefreshesAccessedItems()
        {
            DateTimeOffset oneMinuteAgo      = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMinutes(1));
            var            transportProducer = new ObservableTransportProducerMock();
            var            connection        = new MockConnection(() => transportProducer);
            var            retryPolicy       = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            var            startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                // An expired item in the pool
                ["0"] = new TransportProducerPool.PoolItem("0", transportProducer, removeAfter: oneMinuteAgo)
            };
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy), startingPool);

            // This call should refresh the timespan associated to the item
            _ = transportProducerPool.GetPooledProducer("0");

            // The expiration call back should not remove the item
            GetExpirationCallBack(transportProducerPool).Invoke(null);

            Assert.That(startingPool.TryGetValue("0", out _), Is.True, "The item in the pool should be refreshed and not have been removed.");
        }
        public async Task PoolItemsAreRefreshedOnDisposal()
        {
            var transportProducer = new ObservableTransportProducerMock();
            var startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                ["0"] = new TransportProducerPool.PoolItem("0", transportProducer)
            };
            var connection  = new MockConnection(() => transportProducer);
            var retryPolicy = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy));
            var expectedTime = DateTimeOffset.UtcNow.AddMinutes(10);

            await using var pooledProducer = transportProducerPool.GetPooledProducer("0");

            // This call should refresh the timespan associated to an item in the pool
            await pooledProducer.DisposeAsync();

            Assert.That(startingPool["0"].RemoveAfter, Is.InRange(expectedTime.AddMinutes(-1), expectedTime.AddMinutes(1)), $"The remove after of a pool item should be extended.");
        }
        public void TransportProducerPoolRemovesExpiredItems()
        {
            var            transportProducer = new ObservableTransportProducerMock();
            var            connection        = new MockConnection(() => transportProducer);
            var            retryPolicy       = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            DateTimeOffset oneMinuteAgo      = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMinutes(1));
            var            startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                // An expired item in the pool
                ["0"] = new TransportProducerPool.PoolItem("0", transportProducer, removeAfter: oneMinuteAgo),
                ["1"] = new TransportProducerPool.PoolItem("0", transportProducer),
                ["2"] = new TransportProducerPool.PoolItem("0", transportProducer),
            };
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy), startingPool);

            GetExpirationCallBack(transportProducerPool).Invoke(null);

            Assert.That(startingPool.TryGetValue("0", out _), Is.False, "PerformExpiration should remove an expired producer from the pool.");
            Assert.That(transportProducer.CloseCallCount, Is.EqualTo(1), "PerformExpiration should close an expired producer.");
            Assert.That(startingPool.TryGetValue("1", out _), Is.True, "PerformExpiration should not remove valid producers.");
            Assert.That(startingPool.TryGetValue("2", out _), Is.True, "PerformExpiration should not remove valid producers.");
        }
        public void CloseAsyncSurfacesExceptionsForPartitionTransportProducer()
        {
            var transportProducer = new Mock <TransportProducer>();
            var partitionProducer = new Mock <TransportProducer>();
            var connection        = new MockConnection(() => partitionProducer.Object);
            var retryPolicy       = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            var startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                ["0"] = new TransportProducerPool.PoolItem("0", partitionProducer.Object)
            };
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy), eventHubProducer: transportProducer.Object);

            partitionProducer
            .Setup(producer => producer.CloseAsync(It.IsAny <CancellationToken>()))
            .Returns(Task.FromException(new InvalidCastException()));

            var _ = transportProducerPool.GetPooledProducer("0");

            Assert.That(async() => await transportProducerPool.CloseAsync(), Throws.InstanceOf <InvalidCastException>());
        }
        public void TransportProducerPoolAllowsTakingTheRightTransportProducer(string partitionId)
        {
            var transportProducer = new ObservableTransportProducerMock();
            var partitionProducer = new ObservableTransportProducerMock(partitionId);
            var connection        = new MockConnection(() => partitionProducer);
            var retryPolicy       = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            var startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                ["0"] = new TransportProducerPool.PoolItem("0", partitionProducer)
            };
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy), eventHubProducer: transportProducer);

            var returnedProducer = transportProducerPool.GetPooledProducer(partitionId).TransportProducer as ObservableTransportProducerMock;

            Assert.That(returnedProducer.PartitionId, Is.EqualTo(partitionId));
        }
        public async Task TransportProducerPoolAllowsConfiguringRemoveAfter()
        {
            var transportProducer = new ObservableTransportProducerMock();
            var connection        = new MockConnection(() => transportProducer);
            var retryPolicy       = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            var startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                ["0"] = new TransportProducerPool.PoolItem("0", transportProducer)
            };
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy), startingPool);

            var pooledProducer = transportProducerPool.GetPooledProducer("0", TimeSpan.FromMinutes(-1));

            await using (var _ = pooledProducer.ConfigureAwait(false))
            {
            };

            GetExpirationCallBack(transportProducerPool).Invoke(null);

            Assert.That(transportProducer.CloseCallCount, Is.EqualTo(1));
        }
        public async Task TransportProducerPoolTracksAProducerUsage()
        {
            DateTimeOffset oneMinuteAgo      = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMinutes(1));
            var            transportProducer = new ObservableTransportProducerMock();
            var            connection        = new MockConnection(() => transportProducer);
            var            retryPolicy       = new EventHubProducerClientOptions().RetryOptions.ToRetryPolicy();
            var            startingPool      = new ConcurrentDictionary <string, TransportProducerPool.PoolItem>
            {
                // An expired item in the pool
                ["0"] = new TransportProducerPool.PoolItem("0", transportProducer, removeAfter: oneMinuteAgo)
            };
            TransportProducerPool transportProducerPool = new TransportProducerPool(partition => connection.CreateTransportProducer(partition, TransportProducerFeatures.None, null, retryPolicy), startingPool);

            var pooledProducer = transportProducerPool.GetPooledProducer("0");

            startingPool.TryGetValue("0", out var poolItem);

            await using (pooledProducer)
            {
                Assert.That(poolItem.ActiveInstances.Count, Is.EqualTo(1), "The usage of a transport producer should be tracked.");
            }

            Assert.That(poolItem.ActiveInstances.Count, Is.EqualTo(0), "After usage an active instance should be removed from the pool.");
        }