예제 #1
0
        /// <summary>
        /// This method marshals the RepositoryHolder and transmits it to the remote Microservice.
        /// </summary>
        /// <typeparam Name="KT">The key type.</typeparam>
        /// <typeparam Name="ET">The entity type.</typeparam>
        /// <param name="actionType">The action type.</param>
        /// <param name="rq">The repository holder request.</param>
        /// <param name="routing"></param>
        /// <returns>Returns an async task that will be signalled when the request completes or times out.</returns>
        protected override async Task <RepositoryHolder <KT, ET> > TransmitInternal <KT, ET>(string actionType, RepositoryHolder <KT, ET> rq, ProcessOptions?routing = null, IPrincipal principal = null)
        {
            StatisticsInternal.ActiveIncrement();

            var payloadRq = TransmissionPayload.Create(mPolicy.TransmissionPayloadTraceEnabled);

            // Set the originator key to the correlation id if passed through the rq settings
            if (!string.IsNullOrEmpty(rq.Settings?.CorrelationId))
            {
                payloadRq.Message.ProcessCorrelationKey = rq.Settings.CorrelationId;
            }

            bool processAsync = rq.Settings?.ProcessAsync ?? false;

            payloadRq.Options = ProcessOptions.RouteInternal;
            var message = payloadRq.Message;

            payloadRq.MaxProcessingTime = rq.Settings?.WaitTime ?? mDefaultRequestTimespan;
            payloadRq.MessageObject     = rq;
            message.ChannelId           = ChannelId;
            message.ChannelPriority     = processAsync ? 0:-1;
            message.MessageType         = mMessageType;
            message.ActionType          = actionType;

            message.ResponseChannelId       = mResponseChannel;
            message.ResponseChannelPriority = -1; //Always internal

            message.Blob = PayloadSerializer.PayloadSerialize(rq);

            return(await OutgoingRequestOut(payloadRq, ProcessResponse <KT, ET>, processAsync : processAsync));
        }
예제 #2
0
        /// <summary>
        /// This method marshals the RepositoryHolder and transmits it to the remote Microservice.
        /// </summary>
        /// <typeparam Name="KT">The key type.</typeparam>
        /// <typeparam Name="ET">The entity type.</typeparam>
        /// <param name="actionType">The action type.</param>
        /// <param name="rq">The repository holder request.</param>
        /// <param name="routing"></param>
        /// <returns>Returns an async task that will be signalled when the request completes or times out.</returns>
        protected override async Task <RepositoryHolder <KT, ET> > TransmitInternal <KT, ET>(string actionType, RepositoryHolder <KT, ET> rq, ProcessOptions?routing = null)
        {
            StatisticsInternal.ActiveIncrement();

            var payloadRq = TransmissionPayload.Create();

            bool processAsync = rq.Settings?.ProcessAsync ?? false;

            payloadRq.Options = ProcessOptions.RouteInternal;
            var message = payloadRq.Message;

            payloadRq.MaxProcessingTime = rq.Settings?.WaitTime ?? mDefaultRequestTimespan;
            payloadRq.MessageObject     = rq;
            message.ChannelId           = ChannelId;
            message.ChannelPriority     = processAsync ? 0:-1;
            message.MessageType         = mMessageType;
            message.ActionType          = actionType;

            message.ResponseChannelId       = mResponseChannel;
            message.ResponseChannelPriority = -1; //Always internal

            message.Blob = PayloadSerializer.PayloadSerialize(rq);

            return(await TransmitAsync(payloadRq, ProcessResponse <KT, ET>, processAsync : processAsync));
        }
예제 #3
0
        private void ActionSync(TransmissionPayload incoming, List <TransmissionPayload> outgoing)
        {
            var blahIn = PayloadSerializer.PayloadDeserialize <Blah>(incoming);
            var rs     = incoming.ToResponse();

            rs.Message.Blob              = PayloadSerializer.PayloadSerialize(blahIn.Message);
            rs.Message.Status            = "204";
            rs.Message.StatusDescription = "Hello";
            outgoing.Add(rs);
        }
예제 #4
0
        private async Task ActionAsync(TransmissionPayload incoming, List <TransmissionPayload> outgoing)
        {
            var blahIn = PayloadSerializer.PayloadDeserialize <Blah>(incoming);
            var rs     = incoming.ToResponse();

            rs.Message.Blob = PayloadSerializer.PayloadSerialize(new Blah {
                ContentId = blahIn.ContentId, Message = "Howdy"
            });
            rs.MessageObject             = "Freaky";
            rs.Message.Status            = "204";
            rs.Message.StatusDescription = "Hello";
            outgoing.Add(rs);
        }
예제 #5
0
        /// <summary>
        /// This method marshals the RepositoryHolder and transmits it to the remote Microservice.
        /// </summary>
        /// <typeparam Name="KT">The key type.</typeparam>
        /// <typeparam Name="ET">The entity type.</typeparam>
        /// <param Name="actionType">The action type.</param>
        /// <param Name="rq">The repository holder request.</param>
        /// <returns>Returns an async task that will be signalled when the request completes or times out.</returns>
        protected override async Task <RepositoryHolder <KT, ET> > TransmitInternal <KT, ET>(
            string actionType, RepositoryHolder <KT, ET> rq, ProcessOptions?routing = null, IPrincipal principal = null)
        {
            try
            {
                StatisticsInternal.ActiveIncrement();

                var payload = TransmissionPayload.Create(Policy.TransmissionPayloadTraceEnabled);

                payload.SecurityPrincipal = TransmissionPayload.ConvertToClaimsPrincipal(principal ?? Thread.CurrentPrincipal);

                // Set the process correlation key to the correlation id if passed through the rq settings
                if (!string.IsNullOrEmpty(rq.Settings?.CorrelationId))
                {
                    payload.Message.ProcessCorrelationKey = rq.Settings.CorrelationId;
                }

                bool processAsync = rq.Settings?.ProcessAsync ?? false;

                payload.Message.ChannelPriority = processAsync ? 0 : 1;

                payload.Options = routing ?? RoutingDefault ?? ProcessOptions.RouteExternal;

                payload.Message.Blob = PayloadSerializer.PayloadSerialize(rq);

                payload.Message.ResponseChannelId = ResponseChannelId;

                payload.Message.ResponseChannelId   = ResponseId.Header.ChannelId;
                payload.Message.ResponseMessageType = ResponseId.Header.MessageType;
                payload.Message.ResponseActionType  = ResponseId.Header.ActionType;

                payload.Message.ResponseChannelPriority = payload.Message.ChannelPriority;

                payload.Message.ChannelId   = ChannelId;
                payload.Message.MessageType = EntityType;
                payload.Message.ActionType  = actionType;

                payload.MaxProcessingTime = rq.Settings?.WaitTime ?? mDefaultRequestTimespan;

                return(await OutgoingRequestOut(payload, ProcessResponse <KT, ET>, processAsync));
            }
            catch (Exception ex)
            {
                string key = rq != null && rq.Key != null?rq.Key.ToString() : string.Empty;

                Collector?.LogException($"Error transmitting {actionType}-{key} internally", ex);
                throw;
            }
        }
        /// <summary>
        /// This method marshals the RepositoryHolder and transmits it to the remote Microservice.
        /// </summary>
        /// <typeparam Name="KT">The key type.</typeparam>
        /// <typeparam Name="ET">The entity type.</typeparam>
        /// <param Name="actionType">The action type.</param>
        /// <param Name="rq">The repository holder request.</param>
        /// <returns>Returns an async task that will be signalled when the request completes or times out.</returns>
        protected override async Task <RepositoryHolder <KT, ET> > TransmitInternal <KT, ET>(string actionType, RepositoryHolder <KT, ET> rq, ProcessOptions?routing = null)
        {
            try
            {
                StatisticsInternal.ActiveIncrement();

                var payload = TransmissionPayload.Create();

                // Set the originator key to the correlation id if passed through the rq settings
                if (rq.Settings != null && !string.IsNullOrEmpty(rq.Settings.CorrelationId))
                {
                    payload.Message.OriginatorKey = rq.Settings.CorrelationId;
                }

                bool processAsync = rq.Settings == null ? false : rq.Settings.ProcessAsync;

                payload.Message.ChannelPriority = processAsync ? 0 : 1;

                payload.Options = routing ?? RoutingDefault ?? ProcessOptions.RouteExternal;

                payload.Message.Blob = PayloadSerializer.PayloadSerialize(rq);

                payload.Message.ResponseChannelId       = ResponseChannelId;
                payload.Message.ResponseChannelPriority = payload.Message.ChannelPriority;

                payload.Message.ChannelId   = ChannelId;
                payload.Message.MessageType = EntityType;
                payload.Message.ActionType  = actionType;

                payload.MaxProcessingTime = rq.Settings?.WaitTime ?? mDefaultRequestTimespan;

                return(await TransmitAsync(payload, ProcessResponse <KT, ET>, processAsync));
            }
            catch (Exception ex)
            {
                string key = rq != null && rq.Key != null?rq.Key.ToString() : string.Empty;

                Logger.LogException(string.Format("Error transmitting {0}-{1} internally", actionType, key), ex);
                throw;
            }
        }
        /// <summary>
        /// This method is used to send requests to the remote command.
        /// </summary>
        /// <typeparam name="RQ">The request type.</typeparam>
        /// <typeparam name="RS">The response type.</typeparam>
        /// <param name="channelId">The header routing information.</param>
        /// <param name="messageType">The header routing information.</param>
        /// <param name="actionType">The header routing information.</param>
        /// <param name="rq">The request object.</param>
        /// <param name="rqSettings">The request settings. Use this to specifically set the timeout parameters.</param>
        /// <param name="routingOptions">The routing options by default this will try internal and then external.</param>
        /// <param name="processResponse"></param>
        /// <param name="fallbackMaxProcessingTime">This is the fallback max processing time used if the timeout
        /// is not set in the request settings.
        /// If this is also null, the max time out will fall back to the policy settings.</param>
        /// <param name="principal">This is the principal that you wish the command to be executed under.
        /// By default this is taken from the calling thread if not passed.</param>
        /// <returns>Returns the async response wrapper.</returns>
        protected internal virtual async Task <ResponseWrapper <RS> > ProcessOutgoing <RQ, RS>(
            string channelId, string messageType, string actionType
            , RQ rq
            , RequestSettings rqSettings    = null
            , ProcessOptions?routingOptions = null
            , Func <TaskStatus, TransmissionPayload, bool, ResponseWrapper <RS> > processResponse = null
            , TimeSpan?fallbackMaxProcessingTime = null
            , IPrincipal principal = null
            )
        {
            if (!Policy.OutgoingRequestsEnabled)
            {
                throw new OutgoingRequestsNotEnabledException();
            }

            TransmissionPayload payload = null;

            try
            {
                StatisticsInternal.ActiveIncrement();

                payload = TransmissionPayload.Create(Policy.TransmissionPayloadTraceEnabled);
                payload.SecurityPrincipal = TransmissionPayload.ConvertToClaimsPrincipal(principal ?? Thread.CurrentPrincipal);

                // Set the process correlation key to the correlation id, if passed through the rq settings
                if (!string.IsNullOrEmpty(rqSettings?.CorrelationId))
                {
                    payload.Message.ProcessCorrelationKey = rqSettings.CorrelationId;
                }

                bool processAsync = rqSettings?.ProcessAsync ?? false;

                payload.Options = routingOptions ?? ProcessOptions.RouteExternal | ProcessOptions.RouteInternal;

                //Set the destination message
                payload.Message.ChannelId       = channelId ?? ChannelId;
                payload.Message.MessageType     = messageType;
                payload.Message.ActionType      = actionType;
                payload.Message.ChannelPriority = processAsync ? 0 : 1;

                //Set the response path
                payload.Message.ResponseChannelId       = ResponseId.Header.ChannelId;
                payload.Message.ResponseMessageType     = ResponseId.Header.MessageType;
                payload.Message.ResponseActionType      = ResponseId.Header.ActionType;
                payload.Message.ResponseChannelPriority = payload.Message.ChannelPriority;

                //Set the payload
                payload.Message.Blob = PayloadSerializer.PayloadSerialize(rq);

                //Set the processing time
                payload.MaxProcessingTime = rqSettings?.WaitTime ?? fallbackMaxProcessingTime ?? Policy.OutgoingRequestMaxProcessingTimeDefault;

                //Transmit
                return(await OutgoingRequestOut(payload, processResponse ?? ProcessOutgoingResponse <RS>, processAsync));
            }
            catch (Exception ex)
            {
                string key = payload?.Id.ToString() ?? string.Empty;
                Collector?.LogException(string.Format("Error transmitting {0}-{1} internally", actionType, key), ex);
                throw;
            }
        }
예제 #8
0
        /// <summary>
        /// This method registers the specific persistence handler.
        /// </summary>
        /// <typeparam Name="KT">The key type.</typeparam>
        /// <typeparam Name="ET">The entity type.</typeparam>
        /// <param name="actionType">The action type identifier</param>
        /// <param name="action">The action to process the command.</param>
        /// <param name="logEventOnSuccess"></param>
        /// <param name="preaction"></param>
        /// <param name="postaction"></param>
        /// <param name="timeoutcorrect"></param>
        /// <param name="retryOnTimeout"></param>
        /// <param name="channelId"></param>
        /// <param name="entityType"></param>
        protected virtual void PersistenceCommandRegister <KT, ET>(string actionType
                                                                   , Func <PersistenceRequestHolder <KT, ET>, Task> action
                                                                   , bool logEventOnSuccess = false
                                                                   , Func <PersistenceRequestHolder <KT, ET>, Task <bool> > preaction      = null
                                                                   , Func <PersistenceRequestHolder <KT, ET>, Task> postaction             = null
                                                                   , Func <PersistenceRequestHolder <KT, ET>, Task <bool> > timeoutcorrect = null
                                                                   , int?retryOnTimeout = null
                                                                   , string channelId   = null
                                                                   , string entityType  = null
                                                                   )
        {
            Func <TransmissionPayload, List <TransmissionPayload>, Task> actionPayload = async(incoming, outgoing) =>
            {
                var profileHolder = ProfileStart <KT, ET>(incoming, outgoing);

                try
                {
                    var rsMessage = incoming.Message.ToResponse();

                    rsMessage.ChannelId       = incoming.Message.ResponseChannelId;
                    rsMessage.ChannelPriority = incoming.Message.ResponseChannelPriority;
                    rsMessage.MessageType     = incoming.Message.MessageType;
                    rsMessage.ActionType      = "";

                    var rsPayload = new TransmissionPayload(rsMessage);

                    bool hasTimedOut = false;

                    try
                    {
                        RepositoryHolder <KT, ET> rqTemp = incoming.MessageObject as RepositoryHolder <KT, ET>;

                        //Deserialize the incoming payloadRq request
                        if (rqTemp == null)
                        {
                            rqTemp = PayloadSerializer.PayloadDeserialize <RepositoryHolder <KT, ET> >(incoming);
                        }

                        profileHolder.Rq = new PersistenceRepositoryHolder <KT, ET>(rqTemp);

                        if (profileHolder.Rq.Timeout == null)
                        {
                            profileHolder.Rq.Timeout = TimeSpan.FromSeconds(10);
                        }

                        bool preactionFailed = false;

                        try
                        {
                            bool retryExceeded = false;

                            do
                            {
                                int attempt = Environment.TickCount;

                                //Create the payloadRs holder, and discard any previous version.
                                profileHolder.Rs = new PersistenceRepositoryHolder <KT, ET>();

                                if (preaction != null && !(await preaction(profileHolder)))
                                {
                                    preactionFailed = true;
                                    break;
                                }

                                //Call the specific command to process the action, i.e Create, Read, Update, Delete ... etc.
                                await action(profileHolder);

                                //Flag if the request times out at any point.
                                //This may be required later when checking whether the action was actually successful.
                                hasTimedOut |= profileHolder.Rs.IsTimeout;

                                //OK, if this is not a time out then it is successful
                                if (!profileHolder.Rs.IsTimeout && !profileHolder.Rs.ShouldRetry)
                                {
                                    break;
                                }

                                ProfileRetry(profileHolder, attempt);

                                if (profileHolder.Rs.IsTimeout)
                                {
                                    Logger.LogMessage(LoggingLevel.Warning, $"Timeout occured for {EntityType} {actionType} for request:{profileHolder.Rq} with response:{profileHolder.Rs}", "DBTimeout");
                                }

                                profileHolder.Rq.IsRetry = true;
                                //These should not be counted against the limit.
                                if (!profileHolder.Rs.ShouldRetry)
                                {
                                    profileHolder.Rq.Retry++;
                                }

                                profileHolder.Rq.IsTimeout = false;

                                retryExceeded = incoming.Cancel.IsCancellationRequested ||
                                                profileHolder.Rq.Retry > mPolicy.PersistenceRetryPolicy.GetMaximumRetries(incoming);
                            }while (!retryExceeded);

                            //Signal to the underlying comms channel that the message has been processed successfully.
                            incoming.Signal(!retryExceeded);

                            // If we have exceeded the retry limit then Log error
                            if (retryExceeded)
                            {
                                Log(actionType
                                    , profileHolder
                                    , LoggingLevel.Error
                                    , $"Retry limit has been exceeded (cancelled ({incoming.Cancel.IsCancellationRequested})) for {EntityType} {actionType} for {profileHolder.Rq} after {incoming.Message?.FabricDeliveryCount} delivery attempts"
                                    , "DBRetry");

                                profileHolder.result = ResourceRequestResult.RetryExceeded;
                            }
                        }
                        catch (Exception ex)
                        {
                            LogException(actionType, profileHolder, ex);
                            incoming.SignalFail();
                            profileHolder.result = ResourceRequestResult.Exception;
                        }

                        bool logEventSource = !preactionFailed && logEventOnSuccess && profileHolder.Rs.IsSuccess;

                        if (!profileHolder.Rs.IsSuccess && hasTimedOut && timeoutcorrect != null && profileHolder.result != ResourceRequestResult.Exception)
                        {
                            if (await timeoutcorrect(profileHolder))
                            {
                                logEventSource = true;
                                Logger.LogMessage(LoggingLevel.Info
                                                  , string.Format("Recovered timeout sucessfully for {0}-{1} for request:{2} - response:{3}", EntityType, actionType, profileHolder.Rq, profileHolder.Rs)
                                                  , "DBTimeout");
                            }
                            else
                            {
                                Logger.LogMessage(LoggingLevel.Error
                                                  , string.Format("Not recovered timeout for {0}-{1} for request:{2} - response:{3}", EntityType, actionType, profileHolder.Rq, profileHolder.Rs)
                                                  , "DBTimeout");
                            }
                        }

                        if (logEventSource && profileHolder.Rs.ShouldLogEventSource)
                        {
                            await LogEventSource(actionType, profileHolder);
                        }

                        if (!preactionFailed && postaction != null)
                        {
                            await postaction(profileHolder);
                        }

                        //Serialize the payloadRs
                        var reposHolder = profileHolder.Rs.ToRepositoryHolder();

                        rsPayload.MessageObject = reposHolder;
                        rsPayload.Message.Blob  = PayloadSerializer.PayloadSerialize(reposHolder);

                        rsPayload.Message.Status = "200";

                        if (!profileHolder.result.HasValue)
                        {
                            profileHolder.result = ResourceRequestResult.Success;
                        }
                    }
                    catch (Exception ex)
                    {
                        incoming.SignalFail();
                        rsPayload.Message.Status            = "500";
                        rsPayload.Message.StatusDescription = ex.Message;
                        Logger.LogException($"Error processing message (was cancelled({incoming.Cancel.IsCancellationRequested}))-{EntityType}-{actionType}-{profileHolder.Rq}", ex);
                        profileHolder.result = ResourceRequestResult.Exception;
                    }

                    // check whether we need to send a response message. If this is async and AsyncResponse is not set to true,
                    // then by default we do not send a response message to cut down on unnecessary traffic.
                    if (profileHolder.Rq == null || profileHolder.Rq.Settings == null || !profileHolder.Rq.Settings.ProcessAsync)
                    {
                        outgoing.Add(rsPayload);
                    }
                }
                finally
                {
                    ProfileEnd(profileHolder);
                }
            };

            if (channelId == null)
            {
                channelId = ChannelId ?? string.Empty;
            }

            if (entityType == null)
            {
                entityType = EntityType;
            }

            CommandRegister(
                channelId.ToLowerInvariant()
                , entityType.ToLowerInvariant()
                , actionType.ToLowerInvariant()
                , actionPayload);
        }