private async Task HeartbeatAsync(CancellationToken cancellationToken) { CommandWireProtocol <BsonDocument> isMasterProtocol = null; bool processAnother = true; while (processAnother && !cancellationToken.IsCancellationRequested) { IsMasterResult heartbeatIsMasterResult = null; Exception heartbeatException = null; var previousDescription = _currentDescription; try { IConnection connection; lock (_lock) { connection = _connection; } if (connection == null) { var initializedConnection = await InitializeConnectionAsync(cancellationToken).ConfigureAwait(false); lock (_lock) { if (_state.Value == State.Disposed) { try { initializedConnection.Dispose(); } catch { } throw new OperationCanceledException("The ServerMonitor has been disposed."); } _connection = initializedConnection; _handshakeBuildInfoResult = _connection.Description.BuildInfoResult; heartbeatIsMasterResult = _connection.Description.IsMasterResult; } } else { isMasterProtocol = isMasterProtocol ?? InitializeIsMasterProtocol(connection); heartbeatIsMasterResult = await GetIsMasterResultAsync(connection, isMasterProtocol, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { return; } catch (Exception ex) { IConnection toDispose = null; lock (_lock) { isMasterProtocol = null; heartbeatException = ex; _roundTripTimeMonitor.Reset(); toDispose = _connection; _connection = null; } toDispose?.Dispose(); } lock (_lock) { if (cancellationToken.IsCancellationRequested) { return; } } ServerDescription newDescription; if (heartbeatIsMasterResult != null) { if (_handshakeBuildInfoResult == null) { // we can be here only if there is a bug in the driver throw new ArgumentNullException("BuildInfo has been lost."); } var averageRoundTripTime = _roundTripTimeMonitor.Average; var averageRoundTripTimeRounded = TimeSpan.FromMilliseconds(Math.Round(averageRoundTripTime.TotalMilliseconds)); newDescription = _baseDescription.With( averageRoundTripTime: averageRoundTripTimeRounded, canonicalEndPoint: heartbeatIsMasterResult.Me, electionId: heartbeatIsMasterResult.ElectionId, lastWriteTimestamp: heartbeatIsMasterResult.LastWriteTimestamp, logicalSessionTimeout: heartbeatIsMasterResult.LogicalSessionTimeout, maxBatchCount: heartbeatIsMasterResult.MaxBatchCount, maxDocumentSize: heartbeatIsMasterResult.MaxDocumentSize, maxMessageSize: heartbeatIsMasterResult.MaxMessageSize, replicaSetConfig: heartbeatIsMasterResult.GetReplicaSetConfig(), state: ServerState.Connected, tags: heartbeatIsMasterResult.Tags, topologyVersion: heartbeatIsMasterResult.TopologyVersion, type: heartbeatIsMasterResult.ServerType, version: _handshakeBuildInfoResult.ServerVersion, wireVersionRange: new Range <int>(heartbeatIsMasterResult.MinWireVersion, heartbeatIsMasterResult.MaxWireVersion)); } else { newDescription = _baseDescription.With(lastUpdateTimestamp: DateTime.UtcNow); } if (heartbeatException != null) { var topologyVersion = default(Optional <TopologyVersion>); if (heartbeatException is MongoCommandException heartbeatCommandException) { topologyVersion = TopologyVersion.FromMongoCommandException(heartbeatCommandException); } newDescription = newDescription.With(heartbeatException: heartbeatException, topologyVersion: topologyVersion); } newDescription = newDescription.With(reasonChanged: "Heartbeat", lastHeartbeatTimestamp: DateTime.UtcNow); lock (_lock) { cancellationToken.ThrowIfCancellationRequested(); SetDescription(newDescription); } processAnother = // serverSupportsStreaming (newDescription.Type != ServerType.Unknown && heartbeatIsMasterResult != null && heartbeatIsMasterResult.TopologyVersion != null) || // connectionIsStreaming (isMasterProtocol != null && isMasterProtocol.MoreToCome) || // transitionedWithNetworkError (IsNetworkError(heartbeatException) && previousDescription.Type != ServerType.Unknown); } bool IsNetworkError(Exception ex) { return(ex is MongoConnectionException mongoConnectionException && mongoConnectionException.IsNetworkException); } }
private async Task <bool> HeartbeatAsync(CancellationToken cancellationToken) { const int maxRetryCount = 2; HeartbeatInfo heartbeatInfo = null; Exception heartbeatException = null; for (var attempt = 1; attempt <= maxRetryCount; attempt++) { var connection = _connection; try { if (connection == null) { connection = _connectionFactory.CreateConnection(_serverId, _endPoint); // if we are cancelling, it's because the server has // been shut down and we really don't need to wait. await connection.OpenAsync(cancellationToken).ConfigureAwait(false); } heartbeatInfo = await GetHeartbeatInfoAsync(connection, cancellationToken).ConfigureAwait(false); heartbeatException = null; _connection = connection; break; } catch (Exception ex) { heartbeatException = ex; _connection = null; if (connection != null) { connection.Dispose(); } } } ServerDescription newDescription; if (heartbeatInfo != null) { var averageRoundTripTime = _averageRoundTripTimeCalculator.AddSample(heartbeatInfo.RoundTripTime); var averageRoundTripTimeRounded = TimeSpan.FromMilliseconds(Math.Round(averageRoundTripTime.TotalMilliseconds)); var isMasterResult = heartbeatInfo.IsMasterResult; var buildInfoResult = heartbeatInfo.BuildInfoResult; newDescription = _baseDescription.With( averageRoundTripTime: averageRoundTripTimeRounded, canonicalEndPoint: isMasterResult.Me, electionId: isMasterResult.ElectionId, lastWriteTimestamp: isMasterResult.LastWriteTimestamp, logicalSessionTimeout: isMasterResult.LogicalSessionTimeout, maxBatchCount: isMasterResult.MaxBatchCount, maxDocumentSize: isMasterResult.MaxDocumentSize, maxMessageSize: isMasterResult.MaxMessageSize, replicaSetConfig: isMasterResult.GetReplicaSetConfig(), state: ServerState.Connected, tags: isMasterResult.Tags, topologyVersion: isMasterResult.TopologyVersion, type: isMasterResult.ServerType, version: buildInfoResult.ServerVersion, wireVersionRange: new Range <int>(isMasterResult.MinWireVersion, isMasterResult.MaxWireVersion)); } else { newDescription = _baseDescription.With(lastUpdateTimestamp: DateTime.UtcNow); } if (heartbeatException != null) { var topologyVersion = default(Optional <TopologyVersion>); if (heartbeatException is MongoCommandException heartbeatCommandException) { topologyVersion = TopologyVersion.FromMongoCommandException(heartbeatCommandException); } newDescription = newDescription.With(heartbeatException: heartbeatException, topologyVersion: topologyVersion); } newDescription = newDescription.With(reasonChanged: "Heartbeat", lastHeartbeatTimestamp: DateTime.UtcNow); SetDescription(newDescription); return(true); }
private void Heartbeat(CancellationToken cancellationToken) { CommandWireProtocol <BsonDocument> helloProtocol = null; bool processAnother = true; while (processAnother && !cancellationToken.IsCancellationRequested) { HelloResult heartbeatHelloResult = null; Exception heartbeatException = null; var previousDescription = _currentDescription; try { IConnection connection; lock (_lock) { connection = _connection; } if (connection == null) { var initializedConnection = InitializeConnection(cancellationToken); lock (_lock) { if (_state.Value == State.Disposed) { try { initializedConnection.Dispose(); } catch { } throw new OperationCanceledException("The ServerMonitor has been disposed."); } _connection = initializedConnection; heartbeatHelloResult = _connection.Description.HelloResult; } } else { // If MoreToCome is true, that means we are streaming hello or legacy hello results and must // continue using the existing helloProtocol object. // Otherwise helloProtocol has either not been initialized or we may need to switch between // heartbeat commands based on the last heartbeat response. if (helloProtocol == null || helloProtocol.MoreToCome == false) { helloProtocol = InitializeHelloProtocol(connection, previousDescription?.HelloOk ?? false); } heartbeatHelloResult = GetHelloResult(connection, helloProtocol, cancellationToken); } } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { return; } catch (Exception ex) { IConnection toDispose = null; lock (_lock) { helloProtocol = null; heartbeatException = ex; _roundTripTimeMonitor.Reset(); toDispose = _connection; _connection = null; } toDispose?.Dispose(); } lock (_lock) { if (cancellationToken.IsCancellationRequested) { return; } } ServerDescription newDescription; if (heartbeatHelloResult != null) { var averageRoundTripTime = _roundTripTimeMonitor.Average; var averageRoundTripTimeRounded = TimeSpan.FromMilliseconds(Math.Round(averageRoundTripTime.TotalMilliseconds)); newDescription = _baseDescription.With( averageRoundTripTime: averageRoundTripTimeRounded, canonicalEndPoint: heartbeatHelloResult.Me, electionId: heartbeatHelloResult.ElectionId, helloOk: heartbeatHelloResult.HelloOk, lastWriteTimestamp: heartbeatHelloResult.LastWriteTimestamp, logicalSessionTimeout: heartbeatHelloResult.LogicalSessionTimeout, maxBatchCount: heartbeatHelloResult.MaxBatchCount, maxDocumentSize: heartbeatHelloResult.MaxDocumentSize, maxMessageSize: heartbeatHelloResult.MaxMessageSize, replicaSetConfig: heartbeatHelloResult.GetReplicaSetConfig(), state: ServerState.Connected, tags: heartbeatHelloResult.Tags, topologyVersion: heartbeatHelloResult.TopologyVersion, type: heartbeatHelloResult.ServerType, version: WireVersion.ToServerVersion(heartbeatHelloResult.MaxWireVersion), wireVersionRange: new Range <int>(heartbeatHelloResult.MinWireVersion, heartbeatHelloResult.MaxWireVersion)); } else { newDescription = _baseDescription.With(lastUpdateTimestamp: DateTime.UtcNow); } if (heartbeatException != null) { var topologyVersion = default(Optional <TopologyVersion>); if (heartbeatException is MongoCommandException heartbeatCommandException) { topologyVersion = TopologyVersion.FromMongoCommandException(heartbeatCommandException); } newDescription = newDescription.With(heartbeatException: heartbeatException, topologyVersion: topologyVersion); } newDescription = newDescription.With(reasonChanged: "Heartbeat", lastHeartbeatTimestamp: DateTime.UtcNow); lock (_lock) { cancellationToken.ThrowIfCancellationRequested(); SetDescription(newDescription); } processAnother = // serverSupportsStreaming (newDescription.Type != ServerType.Unknown && heartbeatHelloResult != null && heartbeatHelloResult.TopologyVersion != null) || // connectionIsStreaming (helloProtocol != null && helloProtocol.MoreToCome) || // transitionedWithNetworkError (IsNetworkError(heartbeatException) && previousDescription.Type != ServerType.Unknown); } bool IsNetworkError(Exception ex) { return(ex is MongoConnectionException mongoConnectionException && mongoConnectionException.IsNetworkException); } }