/// <summary> /// Handles the reconnection attempts. /// If it succeeds, it marks the host as UP. /// If not, it marks the host as DOWN /// </summary> internal void AttemptReconnection() { _poolModificationSemaphore.Wait(); var toRemove = _connections.Where(c => c.IsClosed).ToArray(); foreach (var c in toRemove) { _connections.Remove(c); } if (_connections.Count > 0) { //there is already an open connection _poolModificationSemaphore.Release(); return; } Logger.Info("Attempting reconnection to host {0}", _host.Address); CreateConnection().ContinueWith(t => { if (t.Status == TaskStatus.RanToCompletion) { _connections.Add(t.Result); //Release as soon as possible _poolModificationSemaphore.Release(); Logger.Info("Reconnection attempt to host {0} succeeded", _host.Address); _host.BringUpIfDown(); } else { _poolModificationSemaphore.Release(); Logger.Info("Reconnection attempt to host {0} failed", _host.Address); _host.SetDown(); } }, TaskContinuationOptions.ExecuteSynchronously); }
/// <summary> /// Handles the reconnection attempts. /// If it succeeds, it marks the host as UP. /// If not, it marks the host as DOWN /// </summary> internal void AttemptReconnection() { _isShuttingDown = false; if (_isDisposed) { return; } var tcs = new TaskCompletionSource <Connection[]>(); //While there is a single thread here, there might be another thread //Calling MaybeCreateFirstConnection() //Guard for multiple creations var creationTcs = Interlocked.CompareExchange(ref _creationTcs, tcs, null); if (creationTcs != null || _connections.Count > 0) { //Already creating as host is back UP (possibly via events) return; } Logger.Info("Attempting reconnection to host {0}", _host.Address); //There is a single thread creating a connection CreateConnection().ContinueWith(t => { if (t.Status == TaskStatus.RanToCompletion) { if (_isShuttingDown) { t.Result.Dispose(); TransitionCreationTask(tcs, EmptyConnectionsArray); return; } _connections.Add(t.Result); Logger.Info("Reconnection attempt to host {0} succeeded", _host.Address); _host.BringUpIfDown(); TransitionCreationTask(tcs, new [] { t.Result }); return; } Logger.Info("Reconnection attempt to host {0} failed", _host.Address); Exception ex = null; if (t.Exception != null) { t.Exception.Handle(e => true); ex = t.Exception.InnerException; //This makes sure that the exception is observed, but still sets _creationTcs' exception //for MaybeCreateFirstConnection tcs.Task.ContinueWith(x => { if (x.Exception != null) { x.Exception.Handle(_ => true); } }); } TransitionCreationTask(tcs, EmptyConnectionsArray, ex); _host.SetDown(failedReconnection: true); }, TaskContinuationOptions.ExecuteSynchronously); }
/// <summary> /// Asynchronously starts to create a new connection (if its not already being created). /// A <c>null</c> schedule signals that the pool is not reconnecting but growing to the expected size. /// </summary> /// <param name="schedule"></param> private void StartCreatingConnection(IReconnectionSchedule schedule) { var count = _connections.Count; if (count >= _expectedConnectionLength) { return; } if (schedule != null && schedule != _reconnectionSchedule) { // There's another reconnection schedule, leave it return; } CreateOpenConnection(false).ContinueWith(t => { if (t.Status == TaskStatus.RanToCompletion) { StartCreatingConnection(null); _host.BringUpIfDown(); return; } t.Exception?.Handle(_ => true); // The connection could not be opened if (IsClosing) { // don't mind, the pool is not supposed to be open return; } if (schedule == null) { // As it failed, we need a new schedule for the following attempts schedule = _config.Policies.ReconnectionPolicy.NewSchedule(); _reconnectionSchedule = schedule; } if (schedule != _reconnectionSchedule) { // There's another reconnection schedule, leave it return; } OnConnectionClosing(); }, TaskContinuationOptions.ExecuteSynchronously); }
/// <summary> /// Gets a connection from the next host according to the load balancing policy /// </summary> /// <exception cref="NoHostAvailableException"/> /// <exception cref="InvalidQueryException">When keyspace does not exist</exception> /// <exception cref="UnsupportedProtocolVersionException"/> internal Connection GetNextConnection(IStatement statement, bool isLastChance = false) { var hostEnumerable = _session.Policies.LoadBalancingPolicy.NewQueryPlan(statement); Host lastChanceHost = null; //hostEnumerable GetEnumerator will return a NEW enumerator, making this call thread safe foreach (var host in hostEnumerable) { if (!host.IsConsiderablyUp) { if (!isLastChance && host.Resurrect) { lastChanceHost = host; } continue; } _currentHost = host; _triedHosts[host.Address] = null; Connection connection = null; try { var distance = _session.Policies.LoadBalancingPolicy.Distance(host); var hostPool = _session.GetConnectionPool(host, distance); connection = hostPool.BorrowConnection(); if (connection == null) { continue; } connection.Keyspace = _session.Keyspace; return(connection); } catch (SocketException ex) { _session.SetHostDown(host, connection); _triedHosts[host.Address] = ex; host.Resurrect = CanBeResurrected(ex, connection); if (!isLastChance && host.Resurrect) { lastChanceHost = host; } } catch (InvalidQueryException) { //The keyspace does not exist throw; } catch (UnsupportedProtocolVersionException) { //The version of the protocol is not supported throw; } catch (Exception ex) { _logger.Error(ex); _triedHosts[host.Address] = ex; } } _currentHost = null; if (lastChanceHost != null) { //There are no host available and some of them are due to network events. //Probably there was a network event that reset all connections and it does not mean the connection _logger.Warning("Suspected network reset. Getting one host up and retrying for a last chance"); lastChanceHost.BringUpIfDown(); return(GetNextConnection(statement, true)); } throw new NoHostAvailableException(_triedHosts); }