/*
         * Only called under this.thisLock
         */
        void StartIdleConnectionTimer(IotHubDeviceMuxConnection iotHubDeviceMuxConnection)
        {
#if WINDOWS_UWP
            var idleTimer = new IOThreadTimerSlim(this.IdleConnectionTimerCallback, iotHubDeviceMuxConnection, false);
#else
            var idleTimer = new IOThreadTimer(this.IdleConnectionTimerCallback, iotHubDeviceMuxConnection, false);
#endif
            this.idleTimers.Add(iotHubDeviceMuxConnection, idleTimer);
            idleTimer.Set(this.idleTimeout);
        }
        void RemoveConnection(IotHubDeviceMuxConnection iotHubDeviceMuxConnection)
        {
            lock (this.thisLock)
            {
                this.connectionPool.Remove(iotHubDeviceMuxConnection.GetCacheKey());

                this.idleTimers.Remove(iotHubDeviceMuxConnection);

                if (this.connectionPool.Count == 0)
                {
                    this.cache.RemoveDeviceScopeConnectionPool(this.connectionString);
                }
            }
        }
        // Making this virtual to allow Moq to override
        public virtual IotHubConnection GetConnection(IotHubConnectionString connectionString, AmqpTransportSettings amqpTransportSetting)
        {
            // Only the initial transportSetting is used, subsequent ones are ignored
            if (this.amqpTransportSettings == null)
            {
                Interlocked.CompareExchange(ref this.amqpTransportSettings, amqpTransportSetting, null);
            }

            IotHubConnection iotHubConnection;

            if (connectionString.SharedAccessKeyName != null || connectionString.SharedAccessSignature != null)
            {
                // Connections are not pooled when SAS signatures are used. However, we still use a connection pool object
                // Connections are not shared since the SAS signature will not match another connection string
                IotHubScopeConnectionPool iotHubScopeConnectionPool;
                do
                {
                    iotHubScopeConnectionPool =
                        this.hubScopeConnectionPools.GetOrAdd(
                            connectionString,
                            k => new IotHubScopeConnectionPool(this, k, this.amqpTransportSettings)
                            );
                }while (!iotHubScopeConnectionPool.TryAddRef());

                iotHubConnection = iotHubScopeConnectionPool.Connection;
            }
            else if (this.amqpTransportSettings.AmqpConnectionPoolSettings.Pooling)
            {
                // use connection pooling for device scope connection string
                do
                {
                    IotHubDeviceScopeConnectionPool iotHubDeviceScopeConnectionPool =
                        this.deviceScopeConnectionPools.GetOrAdd(
                            connectionString,
                            k => new IotHubDeviceScopeConnectionPool(this, k, this.amqpTransportSettings)
                            );
                    iotHubConnection = iotHubDeviceScopeConnectionPool.GetConnection(connectionString.DeviceId);
                }while (iotHubConnection == null);
            }
            else
            {
                // Connection pooling is turned off for device-scope connection strings
                iotHubConnection = new IotHubDeviceMuxConnection(null, long.MaxValue, connectionString, this.amqpTransportSettings);
            }

            return(iotHubConnection);
        }
        public void DeviceScopeMuxConnection_PoolingOffReleaseTest()
        {
            // Arrange
            var amqpConnectionPoolSettings = new AmqpConnectionPoolSettings();
            amqpConnectionPoolSettings.Pooling = false;
            var amqpTransportSettings = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only, 200, amqpConnectionPoolSettings);
            string connectionString = "HostName=acme.azure-devices.net;DeviceId=device1;SharedAccessKey=CQN2K33r45/0WeIjpqmErV5EIvX8JZrozt3NEHCEkG8=";
            var iotHubConnectionString = IotHubConnectionStringBuilder.Create(connectionString).ToIotHubConnectionString();
            var connectionCache = new Mock<IotHubConnectionCache>();
            // Pooling is off - pass null for ConnectionPoolCache
            var iotHubConnection = new IotHubDeviceMuxConnection(null, 1, iotHubConnectionString, amqpTransportSettings);
            connectionCache.Setup(cache => cache.GetConnection(It.IsAny<IotHubConnectionString>(), It.IsAny<AmqpTransportSettings>())).Returns(iotHubConnection);

            // Act
            var connection = connectionCache.Object.GetConnection(iotHubConnectionString, amqpTransportSettings);
            connection.Release("device"); // does not match "device1" above, However pooling is off. Thus, this iothubconnection object is closed

            // Success
        }
        public void RemoveDeviceFromConnection(IotHubDeviceMuxConnection iotHubDeviceMuxConnection, string deviceId)
        {
            lock (this.thisLock)
            {
                var cacheIndex = this.HashDevice(deviceId);
                Tuple <IotHubDeviceMuxConnection, uint> selectedConnectionTuple;
                if (!this.connectionPool.TryGetValue(cacheIndex, out selectedConnectionTuple))
                {
                    throw new InvalidOperationException("Unable to find iotHubMuxConnection to remove device from connection");
                }

                var numDevices             = selectedConnectionTuple.Item2;
                var updatedConnectionTuple = Tuple.Create(selectedConnectionTuple.Item1, --numDevices);
                this.connectionPool[cacheIndex] = updatedConnectionTuple;

                if (numDevices == 0)
                {
                    this.StartIdleConnectionTimer(iotHubDeviceMuxConnection);
                }
            }
        }
        public IotHubConnection GetConnection(string deviceId)
        {
            lock (this.thisLock)
            {
                var cacheIndex = this.HashDevice(deviceId);
                Tuple <IotHubDeviceMuxConnection, uint> selectedConnectionTuple;
                if (!this.connectionPool.TryGetValue(cacheIndex, out selectedConnectionTuple) &&
                    this.connectionPool.Count <= this.amqpTransportSettings.AmqpConnectionPoolSettings.MaxPoolSize)
                {
                    var newConnection = new IotHubDeviceMuxConnection(this, cacheIndex, this.connectionString, this.amqpTransportSettings);
                    selectedConnectionTuple         = Tuple.Create(newConnection, (uint)0);
                    this.connectionPool[cacheIndex] = selectedConnectionTuple;
                    this.StartIdleConnectionTimer(newConnection);
                }

                if (selectedConnectionTuple == null)
                {
                    throw new InvalidOperationException("device scope connection pool capacity reached. Consider increasing AmqpConnectionSettings.MaxPoolSize");
                }

                var numDevices = selectedConnectionTuple.Item2;
                if (numDevices == 0)
                {
                    if (!this.TryCancelIdleTimer(selectedConnectionTuple.Item1))
                    {
                        return(null);
                    }
                }

                if (++numDevices > AmqpConnectionPoolSettings.MaxDevicesPerConnection)
                {
                    throw new InvalidOperationException("device scope connection pool capacity reached. Consider increasing AmqpConnectionSettings.MaxPoolSize");
                }

                var updatedConnectionTuple = Tuple.Create(selectedConnectionTuple.Item1, numDevices);
                this.connectionPool[cacheIndex] = updatedConnectionTuple;

                return(selectedConnectionTuple.Item1);
            }
        }
        /*
         * Only called under this.thisLock
         */
        bool TryCancelIdleTimer(IotHubDeviceMuxConnection iotHubDeviceMuxConnection)
        {
#if WINDOWS_UWP
            IOThreadTimerSlim idleTimer;
#else
            IOThreadTimer idleTimer;
#endif
            if (this.idleTimers.TryGetValue(iotHubDeviceMuxConnection, out idleTimer))
            {
                if (!idleTimer.Cancel())
                {
                    return(false);
                }

                this.idleTimers.Remove(iotHubDeviceMuxConnection);
            }
            else
            {
                throw new InvalidOperationException("IdleTimer could not be found");
            }

            return(true);
        }
Beispiel #8
0
        public void RemoveDeviceFromConnection(IotHubDeviceMuxConnection iotHubDeviceMuxConnection, string deviceId)
        {
            lock (this.thisLock)
            {
                long cacheIndex = this.HashDevice(deviceId);
                Tuple <IotHubDeviceMuxConnection, uint> selectedConnectionTuple;
                if (!this.connectionPool.TryGetValue(cacheIndex, out selectedConnectionTuple))
                {
                    throw new InvalidOperationException("Unable to find iotHubMuxConnection to remove device from connection");
                }

                uint numDevices = selectedConnectionTuple.Item2;
                Fx.AssertAndThrow(numDevices != 0, "The number of devices should be greater than zero");

                Tuple <IotHubDeviceMuxConnection, uint> updatedConnectionTuple = Tuple.Create(selectedConnectionTuple.Item1, --numDevices);
                this.connectionPool[cacheIndex] = updatedConnectionTuple;

                if (numDevices == 0)
                {
                    this.StartIdleConnectionTimer(iotHubDeviceMuxConnection);
                }
            }
        }
        public async Task DeviceScopeMuxConnection_ConnectionIdleTimeoutTest()
        {
            // Arrange

            var amqpConnectionPoolSettings = new AmqpConnectionPoolSettings();
            amqpConnectionPoolSettings.ConnectionIdleTimeout = TimeSpan.FromSeconds(5);
            var amqpTransportSettings = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only, 200, amqpConnectionPoolSettings);
            string connectionString = "HostName=acme.azure-devices.net;DeviceId=device1;SharedAccessKey=CQN2K33r45/0WeIjpqmErV5EIvX8JZrozt3NEHCEkG8=";
            var iotHubConnectionString = IotHubConnectionStringBuilder.Create(connectionString).ToIotHubConnectionString();
            var connectionCache = new Mock<IotHubConnectionCache>();
            var connectionPool = new IotHubDeviceScopeConnectionPool(connectionCache.Object, iotHubConnectionString, amqpTransportSettings);

            // Act

            var connections = new IotHubDeviceMuxConnection[10];
            // Create 10 Muxed Device Connections - these should hash into different mux connections
            for (int i = 0; i < 10; i++)
            {
                connections[i] = (IotHubDeviceMuxConnection)connectionPool.GetConnection(i.ToString());
            }

            for (int j = 0; j < 10; j++)
            {
                connectionPool.RemoveDeviceFromConnection(connections[j], j.ToString());
            }

            await Task.Delay(TimeSpan.FromSeconds(6));

            // Assert
            Assert.IsTrue(connectionPool.GetCount() == 0, "Did not cleanup all Connection objects");
        }