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);
            }
        }
示例#2
0
        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);
        }
示例#3
0
        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);
            }
        }