/// <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)); }
/// <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)); }
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); }
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); }
/// <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; } }
/// <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); }