private void SetDescription(ServerDescription newDescription, bool forceClearConnectionPool) { // Current assumption is SetDescription is always synchronized under _monitor.Lock. // This synchronization technically can be violated by calling server.Invalidate not under _monitor.Lock. // Therefore _currentDescription and ConnectionPool state can get out of sync. var serverDescriptionChangedEvent = new ServerDescriptionChangedEventArgs(_currentDescription, newDescription); _currentDescription = newDescription; if (newDescription.HeartbeatException != null || forceClearConnectionPool) { // propagate event to upper levels TriggerServerDescriptionChanged(this, serverDescriptionChangedEvent); // pool must be cleared on after cluster update ConnectionPool.Clear(); } else { if (newDescription.IsDataBearing || (newDescription.Type != ServerType.Unknown && IsDirectConnection())) { // The spec requires to check (server.type != Unknown and newTopologyDescription.type == Single) // in C# driver servers in single topology will be only selectable if direct connection was requested // therefore it is sufficient to check whether the connection mode is directConnection. ConnectionPool.SetReady(); } // propagate event to upper levels TriggerServerDescriptionChanged(this, serverDescriptionChangedEvent); } }
protected override void HandleBeforeHandshakeCompletesException(Exception ex) { if (ex is MongoAuthenticationException) { ConnectionPool.Clear(); return; } if (ex is MongoConnectionException mongoConnectionException) { lock (_monitor.Lock) { if (mongoConnectionException.Generation != null && mongoConnectionException.Generation != ConnectionPool.Generation) { return; // stale generation number } if (mongoConnectionException.IsNetworkException && !mongoConnectionException.ContainsTimeoutException) { _monitor.CancelCurrentCheck(); } if (mongoConnectionException.IsNetworkException || mongoConnectionException.ContainsTimeoutException) { Invalidate($"ChannelException during handshake: {ex}.", clearConnectionPool: true, topologyVersion: null); } } } }
private void OnDescriptionChanged(object sender, ServerDescriptionChangedEventArgs e) { if (e.NewServerDescription.HeartbeatException != null) { ConnectionPool.Clear(); } // propagate event to upper levels TriggerServerDescriptionChanged(this, e); }
protected override void HandleBeforeHandshakeCompletesException(Exception ex) { // drivers MUST NOT perform SDAM error handling for any errors that occur before the MongoDB Handshake if (ex is MongoAuthenticationException mongoAuthenticationException && mongoAuthenticationException.ServiceId.HasValue) // this value will be always filled for MongoAuthenticationException, adding this condition just in case { // when requiring the connection pool to be cleared, MUST only clear connections for the serviceId. ConnectionPool.Clear(mongoAuthenticationException.ServiceId.Value); } }
// public methods public override void Invalidate(string reasonInvalidated, bool clearConnectionPool, TopologyVersion topologyVersion) { if (clearConnectionPool) { ConnectionPool.Clear(); } var newDescription = _baseDescription.With( $"InvalidatedBecause:{reasonInvalidated}", lastUpdateTimestamp: DateTime.UtcNow, topologyVersion: topologyVersion); SetDescription(newDescription); // TODO: make the heartbeat request conditional so we adhere to this part of the spec // > Network error when reading or writing: ... Clients MUST NOT request an immediate check of the server; // > since application sockets are used frequently, a network error likely means the server has just become // > unavailable, so an immediate refresh is likely to get a network error, too. RequestHeartbeat(); }
protected override void HandleAfterHandshakeCompletesException(IConnection connection, Exception ex) { lock (_connectionPoolLock) { if (ex is MongoConnectionException mongoConnectionException && mongoConnectionException.Generation.HasValue && mongoConnectionException.Generation.Value != ConnectionPool.GetGeneration(connection.Description?.ServiceId)) { return; // stale generation number } if (ShouldClearConnectionPoolForChannelException(ex, connection.Description.ServerVersion) && connection.Description.ServiceId.HasValue) // this value will be always filled in this place, adding this here just in case { // when requiring the connection pool to be cleared, MUST only clear connections for the serviceId. ConnectionPool.Clear(connection.Description.ServiceId.Value); } } }