public SiloStatisticsManager(SiloInitializationParameters initializationParams, SerializationManager serializationManager, ITelemetryProducer telemetryProducer, ILoggerFactory loggerFactory)
        {
            MessagingStatisticsGroup.Init(true);
            MessagingProcessingStatisticsGroup.Init();
            NetworkingStatisticsGroup.Init(true);
            ApplicationRequestsStatisticsGroup.Init(initializationParams.GlobalConfig.ResponseTimeout);
            SchedulerStatisticsGroup.Init(loggerFactory);
            StorageStatisticsGroup.Init();
            TransactionsStatisticsGroup.Init();
            this.logger            = new LoggerWrapper <SiloStatisticsManager>(loggerFactory);
            runtimeStats           = new RuntimeStatisticsGroup(loggerFactory);
            this.logStatistics     = new LogStatistics(initializationParams.NodeConfig.StatisticsLogWriteInterval, true, serializationManager, loggerFactory);
            this.MetricsTable      = new SiloPerformanceMetrics(this.runtimeStats, loggerFactory, initializationParams.NodeConfig);
            this.countersPublisher = new CountersStatistics(initializationParams.NodeConfig.StatisticsPerfCountersWriteInterval, telemetryProducer, loggerFactory);

            initializationParams.ClusterConfig.OnConfigChange(
                "Defaults/LoadShedding",
                () => this.MetricsTable.NodeConfig = initializationParams.NodeConfig,
                false);
        }
Example #2
0
 protected override void FailMessage(Message msg, string reason)
 {
     msg.ReleaseBodyAndHeaderBuffers();
     MessagingStatisticsGroup.OnFailedSentMessage(msg);
     if (MsgCenter.Running && msg.Direction == Message.Directions.Request)
     {
         if (Log.IsVerbose)
         {
             Log.Verbose(ErrorCode.ProxyClient_RejectingMsg, "Rejecting message: {0}. Reason = {1}", msg, reason);
         }
         MessagingStatisticsGroup.OnRejectedMessage(msg);
         Message error = msg.CreateRejectionResponse(Message.RejectionTypes.Unrecoverable, reason);
         MsgCenter.QueueIncomingMessage(error);
     }
     else
     {
         Log.Warn(ErrorCode.ProxyClient_DroppingMsg, "Dropping message: {0}. Reason = {1}", msg, reason);
         MessagingStatisticsGroup.OnDroppedSentMessage(msg);
     }
 }
Example #3
0
        private Task ProbeInternal(SiloAddress remoteSilo, int probeNumber)
        {
            Task task;

            try
            {
                RequestContext.Set(RequestContext.PING_APPLICATION_HEADER, true);
                var remoteOracle = this.grainFactory.GetSystemTarget <IMembershipService>(Constants.MembershipOracleType, remoteSilo);
                task = remoteOracle.Ping(probeNumber);

                // Update stats counter. Only count Pings that were successfuly sent, but not necessarily replied to.
                MessagingStatisticsGroup.OnPingSend(remoteSilo);
            }
            finally
            {
                RequestContext.Remove(RequestContext.PING_APPLICATION_HEADER);
            }

            return(task);
        }
Example #4
0
        protected override void OnReceiveMessageFailure(Message message, Exception exception)
        {
            // If deserialization completely failed or the message was one-way, rethrow the exception
            // so that it can be handled at another level.
            if (message?.Headers == null || message.Direction != Message.Directions.Request)
            {
                ExceptionDispatchInfo.Capture(exception).Throw();
            }

            // The message body was not successfully decoded, but the headers were.
            // Send a fast fail to the caller.
            MessagingStatisticsGroup.OnRejectedMessage(message);
            var response = this.messageFactory.CreateResponseMessage(message);

            response.Result     = Message.ResponseTypes.Error;
            response.BodyObject = Response.ExceptionResponse(exception);

            // Send the error response and continue processing the next message.
            this.messageCenter.SendMessage(response);
        }
Example #5
0
        protected override void OnMessageSerializationFailure(Message msg, Exception exc)
        {
            // we only get here if we failed to serialize the msg (or any other catastrophic failure).
            // Request msg fails to serialize on the sending silo, so we just enqueue a rejection msg.
            // Response msg fails to serialize on the responding silo, so we try to send an error response back.
            this.Log.LogWarning(
                (int)ErrorCode.MessagingUnexpectedSendError,
                "Unexpected error serializing message {Message}: {Exception}",
                msg,
                exc);

            msg.ReleaseBodyAndHeaderBuffers();
            MessagingStatisticsGroup.OnFailedSentMessage(msg);

            var retryCount = msg.RetryCount ?? 0;

            if (msg.Direction == Message.Directions.Request)
            {
                messageCenter.SendRejection(msg, Message.RejectionTypes.Unrecoverable, exc.ToString());
            }
            else if (msg.Direction == Message.Directions.Response && retryCount < 1)
            {
                // if we failed sending an original response, turn the response body into an error and reply with it.
                // unless we have already tried sending the response multiple times.
                msg.Result     = Message.ResponseTypes.Error;
                msg.BodyObject = Response.ExceptionResponse(exc);
                msg.RetryCount = retryCount + 1;
                this.messageCenter.SendMessage(msg);
            }
            else
            {
                this.Log.LogWarning(
                    (int)ErrorCode.Messaging_OutgoingMS_DroppingMessage,
                    "Silo {SiloAddress} is dropping message which failed during serialization: {Message}. Exception = {Exception}",
                    this.messageCenter.MyAddress,
                    msg,
                    exc);

                MessagingStatisticsGroup.OnDroppedSentMessage(msg);
            }
        }
Example #6
0
 public SiloStatisticsManager(
     IOptions <SiloStatisticsOptions> statisticsOptions,
     IOptions <MonitoringStorageOptions> azureStorageOptions,
     ILocalSiloDetails siloDetails,
     SerializationManager serializationManager,
     ITelemetryProducer telemetryProducer,
     ILoggerFactory loggerFactory)
 {
     this.siloDetails    = siloDetails;
     this.storageOptions = azureStorageOptions.Value;
     MessagingStatisticsGroup.Init(true);
     MessagingProcessingStatisticsGroup.Init();
     NetworkingStatisticsGroup.Init(true);
     ApplicationRequestsStatisticsGroup.Init();
     SchedulerStatisticsGroup.Init(loggerFactory);
     StorageStatisticsGroup.Init();
     TransactionsStatisticsGroup.Init();
     this.logger            = loggerFactory.CreateLogger <SiloStatisticsManager>();
     this.logStatistics     = new LogStatistics(statisticsOptions.Value.LogWriteInterval, true, serializationManager, loggerFactory);
     this.countersPublisher = new CountersStatistics(statisticsOptions.Value.PerfCountersWriteInterval, telemetryProducer, loggerFactory);
 }
 public ClientOutboundConnection(
     SiloAddress remoteSiloAddress,
     ConnectionContext connection,
     ConnectionDelegate middleware,
     ClientMessageCenter messageCenter,
     ConnectionManager connectionManager,
     ConnectionOptions connectionOptions,
     ConnectionCommon connectionShared,
     ConnectionPreambleHelper connectionPreambleHelper,
     ClusterOptions clusterOptions)
     : base(connection, middleware, connectionShared)
 {
     this.messageCenter            = messageCenter;
     this.connectionManager        = connectionManager;
     this.connectionOptions        = connectionOptions;
     this.connectionPreambleHelper = connectionPreambleHelper;
     this.clusterOptions           = clusterOptions;
     this.RemoteSiloAddress        = remoteSiloAddress ?? throw new ArgumentNullException(nameof(remoteSiloAddress));
     this.MessageReceivedCounter   = MessagingStatisticsGroup.GetMessageReceivedCounter(this.RemoteSiloAddress);
     this.MessageSentCounter       = MessagingStatisticsGroup.GetMessageSendCounter(this.RemoteSiloAddress);
 }
        protected override void OnMessageSerializationFailure(Message msg, Exception exc)
        {
            // we only get here if we failed to serialize the msg (or any other catastrophic failure).
            // Request msg fails to serialize on the sender, so we just enqueue a rejection msg.
            // Response msg fails to serialize on the responding silo, so we try to send an error response back.
            this.Log.LogWarning(
                (int)ErrorCode.ProxyClient_SerializationError,
                "Unexpected error serializing message {Message}: {Exception}",
                msg,
                exc);

            msg.ReleaseBodyAndHeaderBuffers();
            MessagingStatisticsGroup.OnFailedSentMessage(msg);

            var retryCount = msg.RetryCount ?? 0;

            if (msg.Direction == Message.Directions.Request)
            {
                this.MsgCenter.RejectMessage(msg, $"Unable to serialize message. Encountered exception: {exc?.GetType()}: {exc?.Message}", exc);
            }
            else if (msg.Direction == Message.Directions.Response && retryCount < 1)
            {
                // if we failed sending an original response, turn the response body into an error and reply with it.
                // unless we have already tried sending the response multiple times.
                msg.Result     = Message.ResponseTypes.Error;
                msg.BodyObject = Response.ExceptionResponse(exc);
                msg.RetryCount = retryCount + 1;
                this.MsgCenter.SendMessage(msg);
            }
            else
            {
                this.Log.LogWarning(
                    (int)ErrorCode.ProxyClient_DroppingMsg,
                    "Gateway client is dropping message which failed during serialization: {Message}. Exception = {Exception}",
                    msg,
                    exc);

                MessagingStatisticsGroup.OnDroppedSentMessage(msg);
            }
        }
Example #9
0
        /// <summary>
        /// Handles a message receive failure.
        /// </summary>
        /// <returns><see langword="true"/> if the exception should not be caught and <see langword="false"/> if it should be caught.</returns>
        private bool HandleReceiveMessageFailure(Message message, Exception exception)
        {
            this.Log.LogWarning(
                exception,
                "Exception reading message {Message} from remote endpoint {Remote} to local endpoint {Local}",
                message,
                this.RemoteEndPoint,
                this.LocalEndPoint);

            // If deserialization completely failed, rethrow the exception so that it can be handled at another level.
            if (message is null)
            {
                // Returning false here informs the caller that the exception should not be caught.
                return(false);
            }

            // The message body was not successfully decoded, but the headers were.
            MessagingStatisticsGroup.OnRejectedMessage(message);

            if (message.Direction == Message.Directions.Request)
            {
                // Send a fast fail to the caller.
                var response = this.MessageFactory.CreateResponseMessage(message);
                response.Result     = Message.ResponseTypes.Error;
                response.BodyObject = Response.FromException(exception);

                // Send the error response and continue processing the next message.
                this.Send(response);
            }
            else if (message.Direction == Message.Directions.Response)
            {
                // If the message was a response, propagate the exception to the intended recipient.
                message.Result     = Message.ResponseTypes.Error;
                message.BodyObject = Response.FromException(exception);
                this.MessageCenter.DispatchLocalMessage(message);
            }

            // The exception has been handled by propagating it onwards.
            return(true);
        }
Example #10
0
        private bool RecordSuccess(long id, int diagnosticProbeNumber)
        {
            if (this.log.IsEnabled(LogLevel.Trace))
            {
                this.log.LogTrace("Got successful ping response for ping #{ProbeNumber}/{Id} from {Silo}", diagnosticProbeNumber, id, this.SiloAddress);
            }

            MessagingStatisticsGroup.OnPingReplyReceived(this.SiloAddress);

            lock (this.lockObj)
            {
                if (id <= this.highestCompletedProbeId)
                {
                    this.log.Info(
                        "Ignoring success result for ping #{ProbeNumber}/{Id} from {Silo} since a later probe has already completed. Highest ({HighestCompletedProbeId}) > Current ({CurrentProbeId})",
                        diagnosticProbeNumber,
                        id,
                        this.SiloAddress,
                        this.highestCompletedProbeId,
                        id);
                    return(false);
                }
                else if (this.stoppingCancellation.IsCancellationRequested)
                {
                    this.log.Info(
                        "Ignoring success result for ping #{ProbeNumber}/{Id} from {Silo} since this monitor has been stopped",
                        diagnosticProbeNumber,
                        id,
                        this.SiloAddress);
                    return(false);
                }
                else
                {
                    this.highestCompletedProbeId = id;
                    Interlocked.Exchange(ref this.missedProbes, 0);
                    return(true);
                }
            }
        }
Example #11
0
        public void FailMessage(Message msg, string reason)
        {
            if (msg?.Headers != null && msg.IsPing())
            {
                this.Log.LogWarning("Failed ping message {Message}", msg);
            }

            MessagingStatisticsGroup.OnFailedSentMessage(msg);
            if (msg.Direction == Message.Directions.Request)
            {
                if (this.Log.IsEnabled(LogLevel.Debug))
                {
                    this.Log.LogDebug((int)ErrorCode.MessagingSendingRejection, "Silo {SiloAddress} is rejecting message: {Message}. Reason = {Reason}", this.LocalSiloAddress, msg, reason);
                }

                // Done retrying, send back an error instead
                this.messageCenter.SendRejection(msg, Message.RejectionTypes.Transient, $"Silo {this.LocalSiloAddress} is rejecting message: {msg}. Reason = {reason}");
            }
            else
            {
                this.MessagingTrace.OnSiloDropSendingMessage(this.LocalSiloAddress, msg, reason);
            }
        }
Example #12
0
        private void RecordFailure(long id, int diagnosticProbeNumber, Exception failureReason)
        {
            if (this.log.IsEnabled(LogLevel.Trace))
            {
                this.log.LogTrace("Got failed ping response for ping #{ProbeNumber} from {Silo}: {Exception}", diagnosticProbeNumber, this.SiloAddress, failureReason);
            }

            MessagingStatisticsGroup.OnPingReplyMissed(this.SiloAddress);

            lock (this.lockObj)
            {
                if (id <= this.highestCompletedProbeId)
                {
                    this.log.Info("Ignoring failure result for ping #{ProbeNumber} from {Silo} since a later probe has already completed", diagnosticProbeNumber, this.SiloAddress);
                }
                else if (this.stoppingCancellation.IsCancellationRequested)
                {
                    this.log.Info("Ignoring failure result for ping #{ProbeNumber} from {Silo} since this monitor has been stopped", diagnosticProbeNumber, this.SiloAddress);
                }
                else
                {
                    this.highestCompletedProbeId = id;
                    var missed = Interlocked.Increment(ref this.missedProbes);

                    this.log.LogWarning(
                        (int)ErrorCode.MembershipMissedPing,
                        "Did not get ping response for ping #{ProbeNumber} from {Silo}: {Exception}",
                        diagnosticProbeNumber,
                        this.SiloAddress,
                        failureReason);
                    if (this.log.IsEnabled(LogLevel.Trace))
                    {
                        this.log.LogTrace("Current number of failed probes for {Silo}: {MissedProbes}", this.SiloAddress, missed);
                    }
                }
            }
        }
 public SiloStatisticsManager(
     NodeConfiguration nodeConfiguration,
     ILocalSiloDetails siloDetails,
     SerializationManager serializationManager,
     ITelemetryProducer telemetryProducer,
     IHostEnvironmentStatistics hostEnvironmentStatistics,
     IAppEnvironmentStatistics appEnvironmentStatistics,
     ILoggerFactory loggerFactory,
     IOptions <MessagingOptions> messagingOptions)
 {
     this.siloDetails = siloDetails;
     MessagingStatisticsGroup.Init(true);
     MessagingProcessingStatisticsGroup.Init();
     NetworkingStatisticsGroup.Init(true);
     ApplicationRequestsStatisticsGroup.Init(messagingOptions.Value.ResponseTimeout);
     SchedulerStatisticsGroup.Init(loggerFactory);
     StorageStatisticsGroup.Init();
     TransactionsStatisticsGroup.Init();
     this.logger = loggerFactory.CreateLogger <SiloStatisticsManager>();
     this.hostEnvironmentStatistics = hostEnvironmentStatistics;
     this.logStatistics             = new LogStatistics(nodeConfiguration.StatisticsLogWriteInterval, true, serializationManager, loggerFactory);
     this.MetricsTable      = new SiloPerformanceMetrics(this.hostEnvironmentStatistics, appEnvironmentStatistics, loggerFactory, nodeConfiguration);
     this.countersPublisher = new CountersStatistics(nodeConfiguration.StatisticsPerfCountersWriteInterval, telemetryProducer, loggerFactory);
 }
Example #14
0
        protected override void OnReceivedMessage(Message msg)
        {
            // See it's a Ping message, and if so, short-circuit it
            var requestContext = msg.RequestContextData;

            if (requestContext != null &&
                requestContext.TryGetValue(RequestContext.PING_APPLICATION_HEADER, out var pingObj) &&
                pingObj is bool &&
                (bool)pingObj)
            {
                MessagingStatisticsGroup.OnPingReceive(msg.SendingSilo);

                if (this.Log.IsEnabled(LogLevel.Trace))
                {
                    this.Log.Trace("Responding to Ping from {0}", msg.SendingSilo);
                }

                if (!msg.TargetSilo.Equals(messageCenter.MyAddress)) // got ping that is not destined to me. For example, got a ping to my older incarnation.
                {
                    MessagingStatisticsGroup.OnRejectedMessage(msg);
                    Message rejection = this.messageFactory.CreateRejectionResponse(msg, Message.RejectionTypes.Unrecoverable,
                                                                                    $"The target silo is no longer active: target was {msg.TargetSilo.ToLongString()}, but this silo is {messageCenter.MyAddress.ToLongString()}. " +
                                                                                    $"The rejected ping message is {msg}.");
                    messageCenter.OutboundQueue.SendMessage(rejection);
                }
                else
                {
                    var response = this.messageFactory.CreateResponseMessage(msg);
                    response.BodyObject = Response.Done;
                    this.messageCenter.SendMessage(response);
                }
                return;
            }

            // sniff message headers for directory cache management
            this.messageCenter.SniffIncomingMessage?.Invoke(msg);

            // Don't process messages that have already timed out
            if (msg.IsExpired)
            {
                msg.DropExpiredMessage(MessagingStatisticsGroup.Phase.Receive);
                return;
            }

            // If we've stopped application message processing, then filter those out now
            // Note that if we identify or add other grains that are required for proper stopping, we will need to treat them as we do the membership table grain here.
            if (messageCenter.IsBlockingApplicationMessages && (msg.Category == Message.Categories.Application) && !Constants.SystemMembershipTableId.Equals(msg.SendingGrain))
            {
                // We reject new requests, and drop all other messages
                if (msg.Direction != Message.Directions.Request)
                {
                    return;
                }

                MessagingStatisticsGroup.OnRejectedMessage(msg);
                var reject = this.messageFactory.CreateRejectionResponse(msg, Message.RejectionTypes.Unrecoverable, "Silo stopping");
                this.messageCenter.SendMessage(reject);
                return;
            }

            // Make sure the message is for us. Note that some control messages may have no target
            // information, so a null target silo is OK.
            if ((msg.TargetSilo == null) || msg.TargetSilo.Matches(messageCenter.MyAddress))
            {
                // See if it's a message for a client we're proxying.
                if (messageCenter.IsProxying && messageCenter.TryDeliverToProxy(msg))
                {
                    return;
                }

                // Nope, it's for us
                messageCenter.OnReceivedMessage(msg);
                return;
            }

            if (!msg.TargetSilo.Endpoint.Equals(messageCenter.MyAddress.Endpoint))
            {
                // If the message is for some other silo altogether, then we need to forward it.
                if (this.Log.IsEnabled(LogLevel.Trace))
                {
                    this.Log.Trace("Forwarding message {0} from {1} to silo {2}", msg.Id, msg.SendingSilo, msg.TargetSilo);
                }
                messageCenter.OutboundQueue.SendMessage(msg);
                return;
            }

            // If the message was for this endpoint but an older epoch, then reject the message
            // (if it was a request), or drop it on the floor if it was a response or one-way.
            if (msg.Direction == Message.Directions.Request)
            {
                MessagingStatisticsGroup.OnRejectedMessage(msg);
                Message rejection = this.messageFactory.CreateRejectionResponse(msg, Message.RejectionTypes.Transient,
                                                                                string.Format("The target silo is no longer active: target was {0}, but this silo is {1}. The rejected message is {2}.",
                                                                                              msg.TargetSilo.ToLongString(), messageCenter.MyAddress.ToLongString(), msg));

                // Invalidate the remote caller's activation cache entry.
                if (msg.TargetAddress != null)
                {
                    rejection.AddToCacheInvalidationHeader(msg.TargetAddress);
                }

                messageCenter.OutboundQueue.SendMessage(rejection);
                if (this.Log.IsEnabled(LogLevel.Debug))
                {
                    this.Log.Debug("Rejecting an obsolete request; target was {0}, but this silo is {1}. The rejected message is {2}.",
                                   msg.TargetSilo.ToLongString(), messageCenter.MyAddress.ToLongString(), msg);
                }
            }
        }
Example #15
0
        /// <summary>
        /// Probes the remote node via an intermediary silo.
        /// </summary>
        /// <param name="intermediary">The node to probe the target with.</param>
        /// <param name="directProbeTimeout">The amount of time which the intermediary should allow for the target to respond.</param>
        /// <param name="cancellation">A token to cancel and fail the probe attempt.</param>
        /// <returns>The number of failed probes since the last successful probe.</returns>
        private async Task <ProbeResult> ProbeIndirectly(SiloAddress intermediary, TimeSpan directProbeTimeout, CancellationToken cancellation)
        {
            var id = ++_nextProbeId;

            if (_log.IsEnabled(LogLevel.Trace))
            {
                _log.LogTrace("Going to send indirect ping #{Id} to probe silo {Silo} via {Intermediary}", id, SiloAddress, intermediary);
            }

            var         roundTripTimer = ValueStopwatch.StartNew();
            ProbeResult probeResult;

            try
            {
                using var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellation, _stoppingCancellation.Token);
                var cancellationTask = cancellationSource.Token.WhenCancelled();
                var probeTask        = _prober.ProbeIndirectly(intermediary, SiloAddress, directProbeTimeout, id);
                var task             = await Task.WhenAny(cancellationTask, probeTask);

                if (ReferenceEquals(task, cancellationTask) && probeTask.Status != TaskStatus.RanToCompletion)
                {
                    probeTask.Ignore();
                    probeResult = ProbeResult.CreateIndirect(_failedProbes, ProbeResultStatus.Unknown, default);
                }
                else
                {
                    var indirectResult = await probeTask;
                    roundTripTimer.Stop();
                    var roundTripTime = roundTripTimer.Elapsed - indirectResult.ProbeResponseTime;

                    // Record timing regardless of the result.
                    _elapsedSinceLastSuccessfulResponse.Restart();
                    LastRoundTripTime = roundTripTimer.Elapsed - indirectResult.ProbeResponseTime;

                    if (indirectResult.Succeeded)
                    {
                        _log.LogInformation(
                            "Indirect probe request #{Id} to silo {SiloAddress} via silo {IntermediarySiloAddress} succeeded after {RoundTripTime} with a direct probe response time of {ProbeResponseTime}.",
                            id,
                            SiloAddress,
                            intermediary,
                            roundTripTimer.Elapsed,
                            indirectResult.ProbeResponseTime);

                        MessagingStatisticsGroup.OnPingReplyReceived(SiloAddress);

                        _failedProbes = 0;
                        probeResult   = ProbeResult.CreateIndirect(0, ProbeResultStatus.Succeeded, indirectResult);
                    }
                    else
                    {
                        MessagingStatisticsGroup.OnPingReplyMissed(SiloAddress);

                        if (indirectResult.IntermediaryHealthScore > 0)
                        {
                            _log.LogInformation(
                                "Ignoring failure result for ping #{Id} from {Silo} since the intermediary used to probe the silo is not healthy. Intermediary health degradation score: {IntermediaryHealthScore}",
                                id,
                                SiloAddress,
                                indirectResult.IntermediaryHealthScore);
                            probeResult = ProbeResult.CreateIndirect(_failedProbes, ProbeResultStatus.Unknown, indirectResult);
                        }
                        else
                        {
                            _log.LogWarning(
                                "Indirect probe request #{Id} to silo {SiloAddress} via silo {IntermediarySiloAddress} failed after {RoundTripTime} with a direct probe response time of {ProbeResponseTime}. Failure message: {FailureMessage}. Intermediary health score: {IntermediaryHealthScore}",
                                id,
                                SiloAddress,
                                intermediary,
                                roundTripTimer.Elapsed,
                                indirectResult.ProbeResponseTime,
                                indirectResult.FailureMessage,
                                indirectResult.IntermediaryHealthScore);

                            var missed = ++_failedProbes;
                            probeResult = ProbeResult.CreateIndirect(missed, ProbeResultStatus.Failed, indirectResult);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                _log.LogWarning(exception, "Indirect probe request failed");
                probeResult = ProbeResult.CreateIndirect(_failedProbes, ProbeResultStatus.Unknown, default);
            }

            return(probeResult);
        }
Example #16
0
        /// <summary>
        /// Probes the remote silo.
        /// </summary>
        /// <param name="cancellation">A token to cancel and fail the probe attempt.</param>
        /// <returns>The number of failed probes since the last successful probe.</returns>
        private async Task <ProbeResult> ProbeDirectly(CancellationToken cancellation)
        {
            var id = ++_nextProbeId;

            if (_log.IsEnabled(LogLevel.Trace))
            {
                _log.LogTrace("Going to send Ping #{Id} to probe silo {Silo}", id, SiloAddress);
            }

            var         roundTripTimer = ValueStopwatch.StartNew();
            ProbeResult probeResult;
            Exception   failureException;

            try
            {
                var probeCancellation = cancellation.WhenCancelled();
                var probeTask         = _prober.Probe(SiloAddress, id);
                var task = await Task.WhenAny(probeCancellation, probeTask);

                if (ReferenceEquals(task, probeCancellation) && probeTask.Status != TaskStatus.RanToCompletion)
                {
                    probeTask.Ignore();
                    failureException = new OperationCanceledException($"The ping attempt was cancelled after {roundTripTimer.Elapsed}. Ping #{id}");
                }
                else
                {
                    await probeTask;
                    failureException = null;
                }
            }
            catch (Exception exception)
            {
                failureException = exception;
            }
            finally
            {
                roundTripTimer.Stop();
            }

            if (failureException is null)
            {
                MessagingStatisticsGroup.OnPingReplyReceived(SiloAddress);

                if (_log.IsEnabled(LogLevel.Trace))
                {
                    _log.LogTrace(
                        "Got successful ping response for ping #{Id} from {Silo} with round trip time of {RoundTripTime}",
                        id,
                        SiloAddress,
                        roundTripTimer.Elapsed);
                }

                _failedProbes = 0;
                _elapsedSinceLastSuccessfulResponse.Restart();
                LastRoundTripTime = roundTripTimer.Elapsed;
                probeResult       = ProbeResult.CreateDirect(0, ProbeResultStatus.Succeeded);
            }
            else
            {
                MessagingStatisticsGroup.OnPingReplyMissed(SiloAddress);

                var failedProbes = ++_failedProbes;
                _log.LogWarning(
                    (int)ErrorCode.MembershipMissedPing,
                    failureException,
                    "Did not get response for probe #{Id} to silo {Silo} after {Elapsed}. Current number of consecutive failed probes is {FailedProbeCount}",
                    id,
                    SiloAddress,
                    roundTripTimer.Elapsed,
                    failedProbes);

                probeResult = ProbeResult.CreateDirect(failedProbes, ProbeResultStatus.Failed);
            }

            return(probeResult);
        }
Example #17
0
        private void ReceiveMessage(Message msg)
        {
            MessagingProcessingStatisticsGroup.OnImaMessageReceived(msg);

            ISchedulingContext context;

            // Find the activation it targets; first check for a system activation, then an app activation
            if (msg.TargetGrain.IsSystemTarget)
            {
                SystemTarget target = directory.FindSystemTarget(msg.TargetActivation);
                if (target == null)
                {
                    MessagingStatisticsGroup.OnRejectedMessage(msg);
                    Message response = this.messageFactory.CreateRejectionResponse(msg, Message.RejectionTypes.Unrecoverable,
                                                                                   String.Format("SystemTarget {0} not active on this silo. Msg={1}", msg.TargetGrain, msg));
                    messageCenter.SendMessage(response);
                    Log.Warn(ErrorCode.MessagingMessageFromUnknownActivation, "Received a message {0} for an unknown SystemTarget: {1}", msg, msg.TargetAddress);
                    return;
                }
                context = target.SchedulingContext;
                switch (msg.Direction)
                {
                case Message.Directions.Request:
                    MessagingProcessingStatisticsGroup.OnImaMessageEnqueued(context);
                    scheduler.QueueWorkItem(new RequestWorkItem(target, msg), context);
                    break;

                case Message.Directions.Response:
                    MessagingProcessingStatisticsGroup.OnImaMessageEnqueued(context);
                    scheduler.QueueWorkItem(new ResponseWorkItem(target, msg), context);
                    break;

                default:
                    Log.Error(ErrorCode.Runtime_Error_100097, "Invalid message: " + msg);
                    break;
                }
            }
            else
            {
                // Run this code on the target activation's context, if it already exists
                ActivationData targetActivation = directory.FindTarget(msg.TargetActivation);
                if (targetActivation != null)
                {
                    lock (targetActivation)
                    {
                        var target = targetActivation; // to avoid a warning about nulling targetActivation under a lock on it
                        if (target.State == ActivationState.Valid)
                        {
                            var overloadException = target.CheckOverloaded(Log);
                            if (overloadException != null)
                            {
                                // Send rejection as soon as we can, to avoid creating additional work for runtime
                                dispatcher.RejectMessage(msg, Message.RejectionTypes.Overloaded, overloadException, "Target activation is overloaded " + target);
                                return;
                            }

                            // Run ReceiveMessage in context of target activation
                            context = new SchedulingContext(target);
                        }
                        else
                        {
                            // Can't use this activation - will queue for another activation
                            target  = null;
                            context = null;
                        }

                        EnqueueReceiveMessage(msg, target, context);
                    }
                }
                else
                {
                    // No usable target activation currently, so run ReceiveMessage in system context
                    EnqueueReceiveMessage(msg, null, null);
                }
            }
        }
Example #18
0
        public void SendMessage(Message msg)
        {
            if (msg == null)
            {
                throw new ArgumentNullException("msg", "Can't send a null message.");
            }

            if (stopped)
            {
                logger.Info(ErrorCode.Runtime_Error_100112, "Message was queued for sending after outbound queue was stopped: {0}", msg);
                return;
            }

            // Don't process messages that have already timed out
            if (msg.IsExpired)
            {
                msg.DropExpiredMessage(MessagingStatisticsGroup.Phase.Send);
                return;
            }

            if (!msg.QueuedTime.HasValue)
            {
                msg.QueuedTime = DateTime.UtcNow;
            }

            // First check to see if it's really destined for a proxied client, instead of a local grain.
            if (messageCenter.IsProxying && messageCenter.TryDeliverToProxy(msg))
            {
                return;
            }

            if (msg.TargetSilo == null)
            {
                logger.Error(ErrorCode.Runtime_Error_100113, "Message does not have a target silo: " + msg + " -- Call stack is: " + Utils.GetStackTrace());
                messageCenter.SendRejection(msg, Message.RejectionTypes.Unrecoverable, "Message to be sent does not have a target silo");
                return;
            }

            if (!messageCenter.TrySendLocal(msg))
            {
                if (stopped)
                {
                    logger.Info(ErrorCode.Runtime_Error_100115, "Message was queued for sending after outbound queue was stopped: {0}", msg);
                    return;
                }

                // check for simulation of lost messages
                if (messageCenter.ShouldDrop?.Invoke(msg) == true)
                {
                    logger.Info(ErrorCode.Messaging_SimulatedMessageLoss, "Message blocked by test");
                    messageCenter.SendRejection(msg, Message.RejectionTypes.Unrecoverable, "Message blocked by test");
                    return;
                }

                if (this.siloStatusOracle.IsDeadSilo(msg.TargetSilo))
                {
                    MessagingStatisticsGroup.OnFailedSentMessage(msg);
                    var reason = $"Target {msg.TargetSilo.ToLongString()} silo is known to be dead";

                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.LogDebug(
                            (int)ErrorCode.MessagingSendingRejection,
                            "Silo {siloAddress} is rejecting message: {message}. Reason = {reason}",
                            messageCenter.MyAddress,
                            msg,
                            reason);
                    }

                    this.messageCenter.SendRejection(msg, Message.RejectionTypes.Transient, reason);
                    return;
                }

                var senderTask = this.connectionManager.GetConnection(msg.TargetSilo);
                if (senderTask.IsCompletedSuccessfully)
                {
                    var sender = senderTask.Result;
                    sender.Send(msg);
                }
                else
                {
                    _ = SendAsync(senderTask, msg);

                    async Task SendAsync(ValueTask <Connection> c, Message m)
                    {
                        try
                        {
                            var sender = await c;
                            sender.Send(m);
                        }
                        catch (Exception exception)
                        {
                            this.messageCenter.SendRejection(m, Message.RejectionTypes.Transient, $"Exception while sending message: {exception}");
                        }
                    }
                }
            }
        }
Example #19
0
            protected override void Process(OutgoingClientMessage request)
            {
                if (Cts.IsCancellationRequested)
                {
                    return;
                }

                var client = request.Item1;
                var msg    = request.Item2;

                // Find the client state
                ClientState clientState;
                bool        found;

                // TODO: Why do we need this lock here if clients is a ConcurrentDictionary?
                //lock (gateway.lockable)
                {
                    found = gateway.clients.TryGetValue(client, out clientState);
                }

                // This should never happen -- but make sure to handle it reasonably, just in case
                if (!found || (clientState == null))
                {
                    if (msg == null)
                    {
                        return;
                    }

                    Log.Info(ErrorCode.GatewayTryingToSendToUnrecognizedClient, "Trying to send a message {0} to an unrecognized client {1}", msg.ToString(), client);
                    MessagingStatisticsGroup.OnFailedSentMessage(msg);
                    // Message for unrecognized client -- reject it
                    if (msg.Direction == Message.Directions.Request)
                    {
                        MessagingStatisticsGroup.OnRejectedMessage(msg);
                        Message error = this.messageFactory.CreateRejectionResponse(
                            msg,
                            Message.RejectionTypes.Unrecoverable,
                            "Unknown client " + client);
                        gateway.SendMessage(error);
                    }
                    else
                    {
                        MessagingStatisticsGroup.OnDroppedSentMessage(msg);
                    }
                    return;
                }

                // if disconnected - queue for later.
                if (!clientState.IsConnected)
                {
                    if (msg == null)
                    {
                        return;
                    }

                    if (Log.IsEnabled(LogLevel.Trace))
                    {
                        Log.Trace("Queued message {0} for client {1}", msg, client);
                    }
                    clientState.PendingToSend.Enqueue(msg);
                    return;
                }

                // if the queue is non empty - drain it first.
                if (clientState.PendingToSend.Count > 0)
                {
                    if (msg != null)
                    {
                        clientState.PendingToSend.Enqueue(msg);
                    }

                    // For now, drain in-line, although in the future this should happen in yet another asynch agent
                    Drain(clientState);
                    return;
                }
                // the queue was empty AND we are connected.

                // If the request includes a message to send, send it (or enqueue it for later)
                if (msg == null)
                {
                    return;
                }

                if (!Send(msg, clientState.Socket))
                {
                    if (Log.IsEnabled(LogLevel.Trace))
                    {
                        Log.Trace("Queued message {0} for client {1}", msg, client);
                    }
                    clientState.PendingToSend.Enqueue(msg);
                }
                else
                {
                    if (Log.IsEnabled(LogLevel.Trace))
                    {
                        Log.Trace("Sent message {0} to client {1}", msg, client);
                    }
                }
            }
Example #20
0
            private bool SendBatch(List <Message> msgs, Socket sock)
            {
                if (Cts.IsCancellationRequested)
                {
                    return(false);
                }
                if (sock == null)
                {
                    return(false);
                }
                if (msgs == null || msgs.Count == 0)
                {
                    return(true);
                }

                // Send the message
                List <ArraySegment <byte> > data;
                int  headerLengths;
                bool continueSend = OutgoingMessageSender.SerializeMessages(msgs, out data, out headerLengths, OnMessageSerializationFailure);

                if (!continueSend)
                {
                    return(false);
                }

                int length = data.Sum(x => x.Count);

                int    bytesSent            = 0;
                bool   exceptionSending     = false;
                bool   countMismatchSending = false;
                string sendErrorStr;

                try
                {
                    bytesSent = sock.Send(data);
                    if (bytesSent != length)
                    {
                        // The complete message wasn't sent, even though no error was reported; treat this as an error
                        countMismatchSending = true;
                        sendErrorStr         = String.Format("Byte count mismatch on send: sent {0}, expected {1}", bytesSent, length);
                        Log.Warn(ErrorCode.GatewayByteCountMismatch, sendErrorStr);
                    }
                }
                catch (Exception exc)
                {
                    exceptionSending = true;
                    string remoteEndpoint = "";
                    if (!(exc is ObjectDisposedException))
                    {
                        remoteEndpoint = sock.RemoteEndPoint.ToString();
                    }
                    sendErrorStr = String.Format("Exception sending to client at {0}: {1}", remoteEndpoint, exc);
                    Log.Warn(ErrorCode.GatewayExceptionSendingToClient, sendErrorStr, exc);
                }

                MessagingStatisticsGroup.OnMessageBatchSend(msgs[0].TargetSilo, msgs[0].Direction, bytesSent, headerLengths, SocketDirection.GatewayToClient, msgs.Count);
                bool sendError = exceptionSending || countMismatchSending;

                if (sendError)
                {
                    gateway.RecordClosedSocket(sock);
                    SocketManager.CloseSocket(sock);
                }
                gatewaySends.Increment();
                foreach (Message msg in msgs)
                {
                    msg.ReleaseBodyAndHeaderBuffers();
                }

                return(!sendError);
            }
            public void ProcessReceivedBuffer(int bytes)
            {
                offset += bytes;
                if (offset < CurrentLength)
                {
                    return;                         // Nothing to do except start the next receive
                }
#if TRACK_DETAILED_STATS
                ThreadTrackingStatistic tracker = null;
                if (StatisticsCollector.CollectThreadTimeTrackingStats)
                {
                    int id = System.Threading.Thread.CurrentThread.ManagedThreadId;
                    if (!trackers.TryGetValue(id, out tracker))
                    {
                        tracker = new ThreadTrackingStatistic("ThreadPoolThread." + System.Threading.Thread.CurrentThread.ManagedThreadId);
                        bool added = trackers.TryAdd(id, tracker);
                        if (added)
                        {
                            tracker.OnStartExecution();
                        }
                    }
                    tracker.OnStartProcessing();
                }
#endif

                try
                {
                    if (batchingMode)
                    {
                        switch (phase)
                        {
                        case ReceivePhase.MetaHeader:
                            numberOfMessages = BitConverter.ToInt32(metaHeaderBuffer, 0);
                            lengthBuffer     = new byte[numberOfMessages * Message.LENGTH_HEADER_SIZE];
                            lengths          = new List <ArraySegment <byte> >()
                            {
                                new ArraySegment <byte>(lengthBuffer)
                            };
                            phase  = ReceivePhase.Lengths;
                            offset = 0;
                            break;

                        case ReceivePhase.Lengths:
                            headerBodies  = new List <ArraySegment <byte> >();
                            headerLengths = new int[numberOfMessages];
                            bodyLengths   = new int[numberOfMessages];

                            for (int i = 0; i < numberOfMessages; i++)
                            {
                                headerLengths[i]    = BitConverter.ToInt32(lengthBuffer, i * 8);
                                bodyLengths[i]      = BitConverter.ToInt32(lengthBuffer, i * 8 + 4);
                                headerBodiesLength += (headerLengths[i] + bodyLengths[i]);

                                // We need to set the boundary of ArraySegment<byte>s to the same as the header/body boundary
                                headerBodies.AddRange(BufferPool.GlobalPool.GetMultiBuffer(headerLengths[i]));
                                headerBodies.AddRange(BufferPool.GlobalPool.GetMultiBuffer(bodyLengths[i]));
                            }

                            phase  = ReceivePhase.HeaderBodies;
                            offset = 0;
                            break;

                        case ReceivePhase.HeaderBodies:
                            int lengtshSoFar = 0;

                            for (int i = 0; i < numberOfMessages; i++)
                            {
                                header        = ByteArrayBuilder.BuildSegmentListWithLengthLimit(headerBodies, lengtshSoFar, headerLengths[i]);
                                body          = ByteArrayBuilder.BuildSegmentListWithLengthLimit(headerBodies, lengtshSoFar + headerLengths[i], bodyLengths[i]);
                                lengtshSoFar += (headerLengths[i] + bodyLengths[i]);

                                var msg = new Message(header, body);
                                MessagingStatisticsGroup.OnMessageReceive(msg, headerLengths[i], bodyLengths[i]);

                                if (IMA.Log.IsVerbose3)
                                {
                                    IMA.Log.Verbose3("Received a complete message of {0} bytes from {1}", headerLengths[i] + bodyLengths[i], msg.SendingAddress);
                                }
                                if (headerLengths[i] + bodyLengths[i] > Message.LargeMessageSizeThreshold)
                                {
                                    IMA.Log.Info(ErrorCode.Messaging_LargeMsg_Incoming, "Receiving large message Size={0} HeaderLength={1} BodyLength={2}. Msg={3}",
                                                 headerLengths[i] + bodyLengths[i], headerLengths[i], bodyLengths[i], msg.ToString());
                                    if (IMA.Log.IsVerbose3)
                                    {
                                        IMA.Log.Verbose3("Received large message {0}", msg.ToLongString());
                                    }
                                }
                                IMA.HandleMessage(msg, Sock);
                            }
                            MessagingStatisticsGroup.OnMessageBatchReceive(IMA.SocketDirection, numberOfMessages, lengtshSoFar);

                            Reset();
                            break;
                        }
                    }
                    else
                    {
                        // We've completed a buffer. What we do depends on which phase we were in
                        switch (phase)
                        {
                        case ReceivePhase.Lengths:
                            // Pull out the header and body lengths
                            headerLength = BitConverter.ToInt32(lengthBuffer, 0);
                            bodyLength   = BitConverter.ToInt32(lengthBuffer, 4);
                            header       = BufferPool.GlobalPool.GetMultiBuffer(headerLength);
                            body         = BufferPool.GlobalPool.GetMultiBuffer(bodyLength);
                            phase        = ReceivePhase.Header;
                            offset       = 0;
                            break;

                        case ReceivePhase.Header:
                            phase  = ReceivePhase.Body;
                            offset = 0;
                            break;

                        case ReceivePhase.Body:
                            var msg = new Message(header, body);
                            MessagingStatisticsGroup.OnMessageReceive(msg, headerLength, bodyLength);

                            if (IMA.Log.IsVerbose3)
                            {
                                IMA.Log.Verbose3("Received a complete message of {0} bytes from {1}", headerLength + bodyLength, msg.SendingAddress);
                            }
                            if (headerLength + bodyLength > Message.LargeMessageSizeThreshold)
                            {
                                IMA.Log.Info(ErrorCode.Messaging_LargeMsg_Incoming, "Receiving large message Size={0} HeaderLength={1} BodyLength={2}. Msg={3}",
                                             headerLength + bodyLength, headerLength, bodyLength, msg.ToString());
                                if (IMA.Log.IsVerbose3)
                                {
                                    IMA.Log.Verbose3("Received large message {0}", msg.ToLongString());
                                }
                            }
                            IMA.HandleMessage(msg, Sock);
                            Reset();
                            break;
                        }
                    }
                }
                catch (Exception exc)
                {
                    try
                    {
                        // Log details of receive state machine
                        IMA.Log.Error(ErrorCode.MessagingProcessReceiveBufferException,
                                      string.Format(
                                          "Exception trying to process {0} bytes from endpoint {1} at offset {2} in phase {3}"
                                          + " CurrentLength={4} HeaderLength={5} BodyLength={6}",
                                          bytes, RemoteEndPoint, offset, phase,
                                          CurrentLength, headerLength, bodyLength
                                          ),
                                      exc);
                    }
                    catch (Exception) { }
                    Reset(); // Reset back to a hopefully good base state

                    throw;
                }
                finally
                {
#if TRACK_DETAILED_STATS
                    if (StatisticsCollector.CollectThreadTimeTrackingStats)
                    {
                        tracker.IncrementNumberOfProcessed();
                        tracker.OnStopProcessing();
                    }
#endif
                }
            }
        protected virtual void HandleMessage(Message msg, Socket receivedOnSocket)
        {
            if (Message.WriteMessagingTraces)
            {
                msg.AddTimestamp(Message.LifecycleTag.ReceiveIncoming);
            }

            // See it's a Ping message, and if so, short-circuit it
            if (msg.GetScalarHeader <bool>(PingHeader))
            {
                MessagingStatisticsGroup.OnPingReceive(msg.SendingSilo);

                if (Log.IsVerbose2)
                {
                    Log.Verbose2("Responding to Ping from {0}", msg.SendingSilo);
                }

                if (!msg.TargetSilo.Equals(MessageCenter.MyAddress)) // got ping that is not destined to me. For example, got a ping to my older incarnation.
                {
                    MessagingStatisticsGroup.OnRejectedMessage(msg);
                    Message rejection = msg.CreateRejectionResponse(Message.RejectionTypes.Unrecoverable,
                                                                    string.Format("The target silo is no longer active: target was {0}, but this silo is {1}. The rejected ping message is {2}.",
                                                                                  msg.TargetSilo.ToLongString(), MessageCenter.MyAddress.ToLongString(), msg.ToString()));
                    MessageCenter.OutboundQueue.SendMessage(rejection);
                }
                else
                {
                    var response = msg.CreateResponseMessage();
                    response.BodyObject = Response.Done;
                    MessageCenter.SendMessage(response);
                }
                return;
            }

            // sniff message headers for directory cache management
            if (sniffIncomingMessageHandler != null)
            {
                sniffIncomingMessageHandler(msg);
            }

            // Don't process messages that have already timed out
            if (msg.IsExpired)
            {
                msg.DropExpiredMessage(MessagingStatisticsGroup.Phase.Receive);
                return;
            }

            // If we've stopped application message processing, then filter those out now
            // Note that if we identify or add other grains that are required for proper stopping, we will need to treat them as we do the membership table grain here.
            if (MessageCenter.IsBlockingApplicationMessages && (msg.Category == Message.Categories.Application) && (msg.SendingGrain != Constants.SystemMembershipTableId))
            {
                // We reject new requests, and drop all other messages
                if (msg.Direction != Message.Directions.Request)
                {
                    return;
                }

                MessagingStatisticsGroup.OnRejectedMessage(msg);
                var reject = msg.CreateRejectionResponse(Message.RejectionTypes.Unrecoverable, "Silo stopping");
                MessageCenter.SendMessage(reject);
                return;
            }

            // Make sure the message is for us. Note that some control messages may have no target
            // information, so a null target silo is OK.
            if ((msg.TargetSilo == null) || msg.TargetSilo.Matches(MessageCenter.MyAddress))
            {
                // See if it's a message for a client we're proxying.
                if (MessageCenter.IsProxying && MessageCenter.TryDeliverToProxy(msg))
                {
                    return;
                }

                // Nope, it's for us
                MessageCenter.InboundQueue.PostMessage(msg);
                return;
            }

            if (!msg.TargetSilo.Endpoint.Equals(MessageCenter.MyAddress.Endpoint))
            {
                // If the message is for some other silo altogether, then we need to forward it.
                if (Log.IsVerbose2)
                {
                    Log.Verbose2("Forwarding message {0} from {1} to silo {2}", msg.Id, msg.SendingSilo, msg.TargetSilo);
                }
                if (Message.WriteMessagingTraces)
                {
                    msg.AddTimestamp(Message.LifecycleTag.EnqueueForForwarding);
                }
                MessageCenter.OutboundQueue.SendMessage(msg);
                return;
            }

            // If the message was for this endpoint but an older epoch, then reject the message
            // (if it was a request), or drop it on the floor if it was a response or one-way.
            if (msg.Direction == Message.Directions.Request)
            {
                MessagingStatisticsGroup.OnRejectedMessage(msg);
                Message rejection = msg.CreateRejectionResponse(Message.RejectionTypes.Transient,
                                                                string.Format("The target silo is no longer active: target was {0}, but this silo is {1}. The rejected message is {2}.",
                                                                              msg.TargetSilo.ToLongString(), MessageCenter.MyAddress.ToLongString(), msg.ToString()));
                MessageCenter.OutboundQueue.SendMessage(rejection);
                if (Log.IsVerbose)
                {
                    Log.Verbose("Rejecting an obsolete request; target was {0}, but this silo is {1}. The rejected message is {2}.",
                                msg.TargetSilo.ToLongString(), MessageCenter.MyAddress.ToLongString(), msg.ToString());
                }
            }
        }
Example #23
0
        protected override async Task RunInternal()
        {
            Exception error = default;

            try
            {
                if (this.connectionOptions.ProtocolVersion == NetworkProtocolVersion.Version1)
                {
                    // This version of the protocol does not support symmetric preamble, so either send or receive preamble depending on
                    // Whether or not this is an inbound or outbound connection.
                    if (this.RemoteSiloAddress is null)
                    {
                        // Inbound connection
                        var protocolVersion = await ReadPreamble();

                        // To support graceful transition to higher protocol versions, send a preamble if the remote endpoint supports it.
                        if (protocolVersion >= NetworkProtocolVersion.Version2)
                        {
                            await WritePreamble();
                        }
                    }
                    else
                    {
                        // Outbound connection
                        await WritePreamble();
                    }
                }
                else
                {
                    // Later versions of the protocol send and receive preamble at both ends of the connection.
                    if (this.RemoteSiloAddress is null)
                    {
                        // Inbound connection
                        var protocolVersion = await ReadPreamble();

                        // To support graceful transition from lower protocol versions, only send a preamble if the remote endpoint supports it.
                        if (protocolVersion >= NetworkProtocolVersion.Version2)
                        {
                            await WritePreamble();
                        }
                    }
                    else
                    {
                        // Outbound connection
                        await Task.WhenAll(ReadPreamble().AsTask(), WritePreamble());
                    }
                }

                this.MessageReceivedCounter = MessagingStatisticsGroup.GetMessageReceivedCounter(this.RemoteSiloAddress);
                this.MessageSentCounter     = MessagingStatisticsGroup.GetMessageSendCounter(this.RemoteSiloAddress);
                await base.RunInternal();
            }
            catch (Exception exception) when((error = exception) is null)
            {
                Debug.Fail("Execution should not be able to reach this point.");
            }
            finally
            {
                if (!(this.RemoteSiloAddress is null))
                {
                    this.connectionManager.OnConnectionTerminated(this.RemoteSiloAddress, this, error);
                }
            }

            async Task WritePreamble()
            {
                await ConnectionPreamble.Write(
                    this.Context,
                    Constants.SiloDirectConnectionId,
                    this.connectionOptions.ProtocolVersion,
                    this.LocalSiloAddress);
            }

            async ValueTask <NetworkProtocolVersion> ReadPreamble()
            {
                var(grainId, protocolVersion, siloAddress) = await ConnectionPreamble.Read(this.Context);

                if (!grainId.Equals(Constants.SiloDirectConnectionId))
                {
                    throw new InvalidOperationException("Unexpected non-proxied connection on silo endpoint.");
                }

                if (siloAddress is object)
                {
                    this.RemoteSiloAddress = siloAddress;
                    this.connectionManager.OnConnected(siloAddress, this);
                }

                this.RemoteProtocolVersion = protocolVersion;

                return(protocolVersion);
            }
        }
Example #24
0
            private bool Send(Message msg, Socket sock)
            {
                if (Cts.IsCancellationRequested)
                {
                    return(false);
                }

                if (sock == null)
                {
                    return(false);
                }

                // Send the message
                List <ArraySegment <byte> > data;
                int headerLength;

                try
                {
                    int bodyLength;
                    data = msg.Serialize(this.serializationManager, out headerLength, out bodyLength);
                    if (headerLength + bodyLength > this.serializationManager.LargeObjectSizeThreshold)
                    {
                        Log.Info(ErrorCode.Messaging_LargeMsg_Outgoing, "Preparing to send large message Size={0} HeaderLength={1} BodyLength={2} #ArraySegments={3}. Msg={4}",
                                 headerLength + bodyLength + Message.LENGTH_HEADER_SIZE, headerLength, bodyLength, data.Count, this.ToString());
                        if (Log.IsEnabled(LogLevel.Trace))
                        {
                            Log.Trace("Sending large message {0}", msg.ToLongString());
                        }
                    }
                }
                catch (Exception exc)
                {
                    this.OnMessageSerializationFailure(msg, exc);
                    return(true);
                }

                int length = data.Sum(x => x.Count);

                int    bytesSent            = 0;
                bool   exceptionSending     = false;
                bool   countMismatchSending = false;
                string sendErrorStr;

                try
                {
                    bytesSent = sock.Send(data);
                    if (bytesSent != length)
                    {
                        // The complete message wasn't sent, even though no error was reported; treat this as an error
                        countMismatchSending = true;
                        sendErrorStr         = String.Format("Byte count mismatch on send: sent {0}, expected {1}", bytesSent, length);
                        Log.Warn(ErrorCode.GatewayByteCountMismatch, sendErrorStr);
                    }
                }
                catch (Exception exc)
                {
                    exceptionSending = true;
                    string remoteEndpoint = "";
                    if (!(exc is ObjectDisposedException))
                    {
                        try
                        {
                            remoteEndpoint = sock.RemoteEndPoint.ToString();
                        }
                        catch (Exception) {}
                    }
                    sendErrorStr = String.Format("Exception sending to client at {0}: {1}", remoteEndpoint, exc);
                    Log.Warn(ErrorCode.GatewayExceptionSendingToClient, sendErrorStr, exc);
                }
                MessagingStatisticsGroup.OnMessageSend(msg.TargetSilo, msg.Direction, bytesSent, headerLength, SocketDirection.GatewayToClient);
                bool sendError = exceptionSending || countMismatchSending;

                if (sendError)
                {
                    gateway.RecordClosedSocket(sock);
                    SocketManager.CloseSocket(sock);
                }
                gatewaySends.Increment();
                msg.ReleaseBodyAndHeaderBuffers();
                return(!sendError);
            }
Example #25
0
            public void Send(ClientState clientState, Message msg)
            {
                // This should never happen -- but make sure to handle it reasonably, just in case
                if (clientState == null)
                {
                    if (msg == null)
                    {
                        return;
                    }

                    this.log.Info(ErrorCode.GatewayTryingToSendToUnrecognizedClient, "Trying to send a message {0} to an unrecognized client {1}", msg.ToString(), msg.TargetGrain);
                    MessagingStatisticsGroup.OnFailedSentMessage(msg);
                    // Message for unrecognized client -- reject it
                    if (msg.Direction == Message.Directions.Request)
                    {
                        MessagingStatisticsGroup.OnRejectedMessage(msg);
                        Message error = this.messageFactory.CreateRejectionResponse(
                            msg,
                            Message.RejectionTypes.Unrecoverable,
                            "Unknown client " + msg.TargetGrain);
                        messageCenter.SendMessage(error);
                    }
                    else
                    {
                        MessagingStatisticsGroup.OnDroppedSentMessage(msg);
                    }
                    return;
                }

                lock (clientState.PendingToSend)
                {
                    // if disconnected - queue for later.
                    if (!clientState.IsConnected)
                    {
                        if (msg == null)
                        {
                            return;
                        }

                        if (this.log.IsEnabled(LogLevel.Trace))
                        {
                            this.log.Trace("Queued message {0} for client {1}", msg, msg.TargetGrain);
                        }
                        clientState.PendingToSend.Enqueue(msg);
                        return;
                    }

                    // if the queue is non empty - drain it first.
                    if (clientState.PendingToSend.Count > 0)
                    {
                        if (msg != null)
                        {
                            clientState.PendingToSend.Enqueue(msg);
                        }

                        // For now, drain in-line, although in the future this should happen in yet another asynch agent
                        Drain(clientState);
                        return;
                    }
                    // the queue was empty AND we are connected.

                    // If the request includes a message to send, send it (or enqueue it for later)
                    if (msg == null)
                    {
                        return;
                    }

                    if (!Send(msg, clientState))
                    {
                        if (this.log.IsEnabled(LogLevel.Trace))
                        {
                            this.log.Trace("Queued message {0} for client {1}", msg, msg.TargetGrain);
                        }
                        clientState.PendingToSend.Enqueue(msg);
                    }
                    else
                    {
                        if (this.log.IsEnabled(LogLevel.Trace))
                        {
                            this.log.Trace("Sent message {0} to client {1}", msg, msg.TargetGrain);
                        }
                    }
                }
            }
Example #26
0
        /// <summary>
        /// Handles an incoming (proxied) message by rerouting it immediately and unconditionally,
        /// after some header massaging.
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="receivedOnSocket"></param>
        protected override void HandleMessage(Message msg, Socket receivedOnSocket)
        {
            // Don't process messages that have already timed out
            if (msg.IsExpired)
            {
                msg.DropExpiredMessage(MessagingStatisticsGroup.Phase.Receive);
                return;
            }

            if (Message.WriteMessagingTraces)
            {
                msg.AddTimestamp(Message.LifecycleTag.ReceiveIncoming);
            }

            gatewayTrafficCounter.Increment();

            // Are we overloaded?
            if ((MessageCenter.Metrics != null) && MessageCenter.Metrics.IsOverloaded)
            {
                MessagingStatisticsGroup.OnRejectedMessage(msg);
                Message rejection = msg.CreateRejectionResponse(Message.RejectionTypes.GatewayTooBusy, "Shedding load");
                MessageCenter.TryDeliverToProxy(rejection);
                if (Log.IsVerbose)
                {
                    Log.Verbose("Rejecting a request due to overloading: {0}", msg.ToString());
                }
                loadSheddingCounter.Increment();
                return;
            }

            gateway.RecordSendingProxiedGrain(msg.SendingGrain, receivedOnSocket);
            SiloAddress targetAddress = gateway.TryToReroute(msg);

            msg.SendingSilo = MessageCenter.MyAddress;

            if (targetAddress == null)
            {
                // reroute via Dispatcher
                msg.RemoveHeader(Message.Header.TARGET_SILO);
                msg.RemoveHeader(Message.Header.TARGET_ACTIVATION);

                if (msg.TargetGrain.IsSystemTarget)
                {
                    msg.TargetSilo       = MessageCenter.MyAddress;
                    msg.TargetActivation = ActivationId.GetSystemActivation(msg.TargetGrain, MessageCenter.MyAddress);
                }

                if (Message.WriteMessagingTraces)
                {
                    msg.AddTimestamp(Message.LifecycleTag.RerouteIncoming);
                }
                MessagingStatisticsGroup.OnMessageReRoute(msg);
                MessageCenter.RerouteMessage(msg);
            }
            else
            {
                // send directly
                msg.TargetSilo = targetAddress;
                MessageCenter.SendMessage(msg);
            }
        }
Example #27
0
            protected override void ProcessBatch(List <OutgoingClientMessage> requests)
            {
                if (Cts.IsCancellationRequested)
                {
                    return;
                }

                if (requests == null || requests.Count == 0)
                {
                    return;
                }

                // Every Tuple in requests are guaranteed to have the same client
                var client = requests[0].Item1;
                var msgs   = requests.Where(r => r != null).Select(r => r.Item2).ToList();

                // Find the client state
                ClientState clientState;
                bool        found;

                // TODO: Why do we need this lock here if clients is a ConcurrentDictionary?
                //lock (gateway.lockable)
                {
                    found = gateway.clients.TryGetValue(client, out clientState);
                }

                // This should never happen -- but make sure to handle it reasonably, just in case
                if (!found || (clientState == null))
                {
                    if (msgs.Count == 0)
                    {
                        return;
                    }

                    Log.Info(ErrorCode.GatewayTryingToSendToUnrecognizedClient, "Trying to send {0} messages to an unrecognized client {1}. First msg {0}",
                             msgs.Count, client, msgs[0].ToString());

                    foreach (var msg in msgs)
                    {
                        MessagingStatisticsGroup.OnFailedSentMessage(msg);
                        // Message for unrecognized client -- reject it
                        if (msg.Direction == Message.Directions.Request)
                        {
                            MessagingStatisticsGroup.OnRejectedMessage(msg);
                            Message error = msg.CreateRejectionResponse(Message.RejectionTypes.Unrecoverable, "Unknown client " + client);
                            gateway.SendMessage(error);
                        }
                        else
                        {
                            MessagingStatisticsGroup.OnDroppedSentMessage(msg);
                        }
                    }
                    return;
                }

                // if disconnected - queue for later.
                if (!clientState.IsConnected)
                {
                    if (msgs.Count == 0)
                    {
                        return;
                    }

                    if (Log.IsVerbose3)
                    {
                        Log.Verbose3("Queued {0} messages for client {1}", msgs.Count, client);
                    }
                    clientState.PendingBatchesToSend.Enqueue(msgs);
                    return;
                }

                // if the queue is non empty - drain it first.
                if (clientState.PendingBatchesToSend.Count > 0)
                {
                    if (msgs.Count != 0)
                    {
                        clientState.PendingBatchesToSend.Enqueue(msgs);
                    }

                    // For now, drain in-line, although in the future this should happen in yet another asynch agent
                    DrainBatch(clientState);
                    return;
                }
                // the queue was empty AND we are connected.

                // If the request includes a message to send, send it (or enqueue it for later)
                if (msgs.Count == 0)
                {
                    return;
                }

                if (!SendBatch(msgs, clientState.Socket))
                {
                    if (Log.IsVerbose3)
                    {
                        Log.Verbose3("Queued {0} messages for client {1}", msgs.Count, client);
                    }
                    clientState.PendingBatchesToSend.Enqueue(msgs);
                }
                else
                {
                    if (Log.IsVerbose3)
                    {
                        Log.Verbose3("Sent {0} message to client {1}", msgs.Count, client);
                    }
                }
            }
Example #28
0
        private async Task ProcessOutgoing()
        {
            await Task.Yield();

            Exception error      = default;
            var       serializer = this.shared.ServiceProvider.GetRequiredService <IMessageSerializer>();

            try
            {
                var output = this._transport.Output;
                var reader = this.outgoingMessages.Reader;

                while (true)
                {
                    var more = await reader.WaitToReadAsync();

                    if (!more)
                    {
                        break;
                    }

                    Message message = default;
                    try
                    {
                        while (inflight.Count < inflight.Capacity && reader.TryRead(out message) && this.PrepareMessageForSend(message))
                        {
                            inflight.Add(message);
                            var(headerLength, bodyLength) = serializer.Write(ref output, message);
                            MessagingStatisticsGroup.OnMessageSend(this.MessageSentCounter, message, headerLength + bodyLength, headerLength, this.ConnectionDirection);
                        }
                    }
                    catch (Exception exception) when(message != default)
                    {
                        this.OnMessageSerializationFailure(message, exception);
                    }

                    var flushResult = await output.FlushAsync();

                    if (flushResult.IsCompleted || flushResult.IsCanceled)
                    {
                        break;
                    }

                    inflight.Clear();
                }
            }
            catch (Exception exception)
            {
                if (IsValid)
                {
                    this.Log.LogWarning(
                        exception,
                        "Exception while processing messages to remote endpoint {EndPoint}",
                        this.RemoteEndPoint);
                }

                error = exception;
            }
            finally
            {
                _transport.Output.Complete();
                (serializer as IDisposable)?.Dispose();
                this.StartClosing(error);
            }
        }
        protected override void Process(Message msg)
        {
            if (Log.IsVerbose2)
            {
                Log.Verbose2("Got a {0} message to send: {1}", msg.Direction, msg);
            }
            bool continueSend = PrepareMessageForSend(msg);

            if (!continueSend)
            {
                return;
            }

            Socket      sock;
            string      error;
            SiloAddress targetSilo;

            continueSend = GetSendingSocket(msg, out sock, out targetSilo, out error);
            if (!continueSend)
            {
                OnGetSendingSocketFailure(msg, error);
                return;
            }

            List <ArraySegment <byte> > data;
            int headerLength = 0;

            try
            {
                data = msg.Serialize(this.serializationManager, out headerLength);
            }
            catch (Exception exc)
            {
                OnMessageSerializationFailure(msg, exc);
                return;
            }

            int    length               = data.Sum(x => x.Count);
            int    bytesSent            = 0;
            bool   exceptionSending     = false;
            bool   countMismatchSending = false;
            string sendErrorStr         = null;

            try
            {
                bytesSent = sock.Send(data);
                if (bytesSent != length)
                {
                    // The complete message wasn't sent, even though no error was reported; treat this as an error
                    countMismatchSending = true;
                    sendErrorStr         = String.Format("Byte count mismatch on sending to {0}: sent {1}, expected {2}", targetSilo, bytesSent, length);
                    Log.Warn(ErrorCode.Messaging_CountMismatchSending, sendErrorStr);
                }
            }
            catch (Exception exc)
            {
                exceptionSending = true;
                if (!(exc is ObjectDisposedException))
                {
                    sendErrorStr = String.Format("Exception sending message to {0}. Message: {1}. {2}", targetSilo, msg, exc);
                    Log.Warn(ErrorCode.Messaging_ExceptionSending, sendErrorStr, exc);
                }
            }
            MessagingStatisticsGroup.OnMessageSend(targetSilo, msg.Direction, bytesSent, headerLength, GetSocketDirection());
            bool sendError = exceptionSending || countMismatchSending;

            if (sendError)
            {
                OnSendFailure(sock, targetSilo);
            }

            ProcessMessageAfterSend(msg, sendError, sendErrorStr);
        }
Example #30
0
            public void ProcessReceived(SocketAsyncEventArgs e)
            {
#if TRACK_DETAILED_STATS
                ThreadTrackingStatistic tracker = null;
                if (StatisticsCollector.CollectThreadTimeTrackingStats)
                {
                    int id = System.Threading.Thread.CurrentThread.ManagedThreadId;
                    if (!trackers.TryGetValue(id, out tracker))
                    {
                        tracker = new ThreadTrackingStatistic("ThreadPoolThread." + System.Threading.Thread.CurrentThread.ManagedThreadId);
                        bool added = trackers.TryAdd(id, tracker);
                        if (added)
                        {
                            tracker.OnStartExecution();
                        }
                    }
                    tracker.OnStartProcessing();
                }
#endif
                try
                {
                    _buffer.UpdateReceivedData(e.Buffer, e.BytesTransferred);

                    while (true)
                    {
                        Message msg = null;
                        try
                        {
                            if (!this._buffer.TryDecodeMessage(out msg))
                            {
                                break;
                            }
                            this.IMA.HandleMessage(msg, this.Socket);
                        }
                        catch (Exception exception)
                        {
                            // If deserialization completely failed or the message was one-way, rethrow the exception
                            // so that it can be handled at another level.
                            if (msg?.Headers == null || msg.Direction != Message.Directions.Request)
                            {
                                throw;
                            }

                            // The message body was not successfully decoded, but the headers were.
                            // Send a fast fail to the caller.
                            MessagingStatisticsGroup.OnRejectedMessage(msg);
                            var response = this.messageFactory.CreateResponseMessage(msg);
                            response.Result     = Message.ResponseTypes.Error;
                            response.BodyObject = Response.ExceptionResponse(exception);

                            // Send the error response and continue processing the next message.
                            this.IMA.MessageCenter.SendMessage(response);
                        }
                    }
                }
                catch (Exception exc)
                {
                    try
                    {
                        // Log details of receive state machine
                        IMA.Log.Error(ErrorCode.MessagingProcessReceiveBufferException,
                                      $"Exception trying to process {e.BytesTransferred} bytes from endpoint {RemoteEndPoint}",
                                      exc);
                    }
                    catch (Exception) { }
                    _buffer.Reset(); // Reset back to a hopefully good base state

                    throw;
                }
#if TRACK_DETAILED_STATS
                finally
                {
                    if (StatisticsCollector.CollectThreadTimeTrackingStats)
                    {
                        tracker.IncrementNumberOfProcessed();
                        tracker.OnStopProcessing();
                    }
                }
#endif
            }