private async ValueTask <Connection> GetConnectionAsync(SiloAddress endpoint) { ImmutableArray <Connection> result; ConnectionEntry entry = default; var acquiredConnectionLock = false; try { // Lock the entry to ensure it will not be removed while the connectio attempt is occuring. while (!acquiredConnectionLock) { entry = GetOrCreateEntry(endpoint, ref acquiredConnectionLock); if (entry.Connections.Length >= this.connectionOptions.ConnectionsPerEndpoint) { result = entry.Connections; break; } ThrowIfRecentFailure(endpoint, entry); // Wait a short time before reattempting to acquire the lock await Task.Delay(10); } // Attempt to connect. Connection connection = default; var now = DateTime.UtcNow; try { connection = await this.ConnectAsync(endpoint); entry = OnConnectedInternal(endpoint, connection); result = entry.Connections; } catch (Exception exception) { OnConnectionFailed(endpoint, now); throw new ConnectionFailedException( $"Unable to connect to endpoint {endpoint}. See {nameof(exception.InnerException)}", exception); } } finally { if (acquiredConnectionLock) { entry.ReleaseLock(); } } nextConnection = (nextConnection + 1) % result.Length; return(result[nextConnection]); }
private void OnConnectionFailed(SiloAddress address, DateTime lastFailure) { bool acquiredConnectionLock = false; ConnectionEntry entry = default; lock (this.collectionLock) { try { entry = this.GetOrCreateEntry(address, ref acquiredConnectionLock); if (entry.LastFailure.HasValue) { var ticks = Math.Max(lastFailure.Ticks, entry.LastFailure.Value.Ticks); lastFailure = new DateTime(ticks); } // Clean up defunct connections var connections = entry.Connections; foreach (var c in connections) { if (!c.IsValid) { connections = connections.Remove(c); } } this.connections[address] = entry.WithLastFailure(lastFailure).WithConnections(connections); } finally { if (acquiredConnectionLock) { entry.ReleaseLock(); } } } }