public virtual async Task SaveRequest(string actionName, ActorRequestContext actorRequestContext, Type typeOfPayload, byte[] entity, CancellationToken cancellationToken) { try { await StateManager.AddStateAsync(NameCompositionResolver.GenerateRequestContextStateName(actionName, actorRequestContext.RequestId), actorRequestContext, cancellationToken); await StateManager.AddStateAsync(NameCompositionResolver.GenerateRequestPayloadStateName(actionName, actorRequestContext.RequestId), entity, cancellationToken); await StateManager.AddStateAsync(NameCompositionResolver.GenerateRequestPayloadTypeStateName(actionName, actorRequestContext.RequestId), typeOfPayload.AssemblyQualifiedName, cancellationToken); //As cancellation token cant be serialized & replicated to other node and cant be used to invoke cancellation //await StateManager.AddStateAsync(NameCompositionResolver.GenerateRequestCanncellationTokenStateName(actionName, actorRequestContext.RequestId), cancellationToken, cancellationToken); //UnComment if wantting to deal with status of the requests /* * await StateManager.AddStateAsync( * NameCompositionResolver.GenerateRequestStateStateName(actionName, * actorRequestContext.RequestId), false, cancellationToken); * //add current latest reminder name for any current info. due to the single thread behaviour of actors, this is valid * await StateManager.AddOrUpdateStateAsync(ACTOR_REMINDER_LATEST_STATE_NAME, NameCompositionResolver.GenerateReminderName(actionName, actorRequestContext.RequestId), * (s, reminderName) => reminderName, cancellationToken); */ } catch (InvalidOperationException ex) { //add an already existed state //throw; } }
public Task <string> StoreMessageAsync(string key, object payload, Type typeOfPayload, CancellationToken cancellationToken) { var serializedData = BinaryMessageSerializer.SerializePayload(payload, typeOfPayload); var proxy = ActorProxy.Create <IStorageActor>(new ActorId($"{NameCompositionResolver.ExtractFlowInstanceIdFromFlowVariableKey(key)}"), StorageServiceUri); return(proxy.SaveMessageAsync(new ActorRequestContext(Guid.NewGuid().ToString(), null, Guid.NewGuid().ToString()), key, serializedData, cancellationToken)); }
public async Task <object> RetrieveMessageAsync(string key, Type typeOfPayload, CancellationToken cancellationToken) { var proxy = ActorProxy.Create <IStorageActor>(new ActorId($"{NameCompositionResolver.ExtractFlowInstanceIdFromFlowVariableKey(key)}"), StorageServiceUri); var rawData = await proxy.RetrieveMessageAsync(new ActorRequestContext(Guid.NewGuid().ToString(), null, Guid.NewGuid().ToString()), key, false, cancellationToken); var data = rawData == null ? null : BinaryMessageSerializer.DeserializePayload(rawData, typeOfPayload); return(data); }
/// <summary> /// Add flowinstance id to key /// </summary> /// <param name="key"></param> /// <returns></returns> protected string MakeFlowVariableKey(string key) { if (CurrentFlowInstanceId?.Id == null || CurrentFlowInstanceId?.Id == NOT_ASSIGNED_FLOW_INSTANCE) { throw new ArgumentException($"{CurrentActor} failed to make flow variable as current flowinstance id is not valid"); } if (!IsGlobalVariableKey(key)) { return(NameCompositionResolver.GenerateFlowVariableStorageKey(key, CurrentFlowInstanceId?.Id)); } return(key); }
public async Task <Type> RetrieveRequestPayloadTypeAsync(string actionName, string requestId, CancellationToken cancellationToken) { try { var fullNameType = await StateManager.GetStateAsync <string>(NameCompositionResolver.GenerateRequestPayloadTypeStateName(actionName, requestId)); if (fullNameType == null) { return(null); } return(Type.GetType(fullNameType)); } catch (Exception ex) { return(null); } }
/// <summary> /// Non generic version of ChainRequestAsync /// </summary> /// <param name="requestContext"></param> /// <param name="payload"></param> /// <param name="typeOfPayload"></param> /// <param name="actionName"></param> /// <param name="cancellationToken"></param> /// <returns></returns> protected virtual async Task ChainRequestAsyncNonGeneric(ActorRequestContext requestContext, byte[] payload, Type typeOfPayload, string actionName = null, CancellationToken cancellationToken = default(CancellationToken)) { try { //any specify reminder name? if not, use default var currentActionName = actionName ?? requestContext.ActionName; var currentReminderName = NameCompositionResolver.GenerateReminderName(currentActionName, requestContext.RequestId); //persist request to state await ActorRequestPersistence.SaveRequest(currentActionName, requestContext, typeOfPayload, payload, cancellationToken); // register reminder to process the request later await RegisterReminderAsync(currentReminderName, null, _dueTime, _periodTimeInfinite); } catch (Exception e) { Logger.LogError(e, $"[{CurrentActor}-ChainRequestAsync] Failed to chain request of type {typeOfPayload.Name}: " + e.Message); throw; } }
public async Task <CancellationToken> RetrieveRequestCancellationToken(string actionName, string requestId) { return(await StateManager.GetStateAsync <CancellationToken>(NameCompositionResolver.GenerateRequestCanncellationTokenStateName(actionName, requestId))); }
public async Task <ActorRequestContext> RetrieveRequestContextAsync(string actionName, string requestId, CancellationToken cancellationToken) { return(await StateManager.GetStateAsync <ActorRequestContext>(NameCompositionResolver.GenerateRequestContextStateName(actionName, requestId))); }
public async Task <TModel> RetrieveRequestPayloadAsync <TModel>(string actionName, string requestId, CancellationToken cancellationToken) { return(await StateManager.GetStateAsync <TModel>(NameCompositionResolver.GenerateRequestPayloadStateName(actionName, requestId))); }
public bool IsRequestPersistenceReminder(string reminderName) { return(NameCompositionResolver.IsValidRequestPersistenceForNonBlocking(reminderName)); }
/// <summary> /// Reminder will tick very quickly after the actor saves request to state. /// Reminder will load up states based on reminder names and pass to InternalProcessAsync /// </summary> /// <param name="reminderName"></param> /// <param name="state"></param> /// <param name="dueTime"></param> /// <param name="period"></param> /// <returns></returns> public virtual async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) { if (IsRequestPersistenceReminder(reminderName)) { object payload = null; CancellationToken cancellationToken = CancellationToken.None; var actionName = NameCompositionResolver.ExtractActionNameFromReminderName(reminderName); try { var currentRequestContextId = NameCompositionResolver.ExtractRequestContextIdFromReminderName(reminderName); //assign memory values, can be done with get/set CurrentRequestContext = await ActorRequestPersistence.RetrieveRequestContextAsync(actionName, currentRequestContextId, cancellationToken); payload = await ActorRequestPersistence.RetrieveRequestPayloadAsync <object>(actionName, currentRequestContextId, cancellationToken); //{current actor} only resolved fully from here, when current request context is available var serviceName = this.ActorService.Context.ServiceName; var dict = new Dictionary <string, object> { { "ServiceUri", serviceName }, { "Payload", null }, { "ActionName", actionName }, { "Actor", CurrentActor }, { "ReminderName", reminderName }, { "OperationId", CurrentFlowInstanceId.Id }, { "FlowName", CurrentFlowInstanceId.FlowName }, { "ApplicationName", ApplicationName }, { "Resendable", false } }; LogPayload(payload, actionName, dict, null, reminderName); //for non-generic cases var typeOfPayload = await ActorRequestPersistence.RetrieveRequestPayloadTypeAsync(actionName, currentRequestContextId, cancellationToken); //TODO: current implementation supports generic/non-generic payload, consider using byte[] for all purpose if (typeOfPayload != null) { payload = DeserializePayload((byte[])payload, typeOfPayload); } //main process logic if (await ValidateDataAsync(actionName, payload, cancellationToken)) { var result = await InternalProcessAsync(actionName, payload, cancellationToken); await OnSuccessAsync(actionName, payload, result, cancellationToken); } else { await OnFailedAsync(actionName, payload, new ActorMessageValidationException( $"{CurrentActor} failed to validate its message of request {currentRequestContextId}"), cancellationToken); } } catch (Exception e) { await OnFailedAsync(actionName, payload, e, cancellationToken); throw; } finally { await ActorRequestPersistence.RemoveStateDataForRequestIdAsync(actionName, CurrentRequestContext.RequestId, cancellationToken); //unregister reminder after process await UnregisterReminderAsync(GetReminder(reminderName)); } } }