/* * 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); }
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"); }