private async Task <InvokeRoutineResult> RunRoutineAsync( ITransitionCarrier transitionCarrier, TransitionDescriptor transitionDescriptor, CancellationToken ct) { var invocationResult = new InvokeRoutineResult(); using (_transitionScope.Enter(transitionDescriptor)) { var transitionMonitor = _transitionScope.CurrentMonitor; var serviceId = await transitionCarrier.GetServiceIdAsync(ct); var methodId = await transitionCarrier.GetRoutineDescriptorAsync(ct); var serviceReference = _serviceResolver.Resolve(serviceId); var methodReference = _methodResolver.Resolve(serviceReference.Definition, methodId); object serviceInstance = serviceReference.GetInstance(); //var serviceStateContainer = _serviceStateValueContainerProvider.CreateContainer(serviceInstance); //var isStatefullService = serviceStateContainer.GetCount() > 0; //if (isStatefullService) // await transitionCarrier.ReadServiceStateAsync(serviceStateContainer, ct); Type taskResultType = methodReference.Definition.MethodInfo.ReturnType == typeof(void) ? TaskAccessor.VoidTaskResultType : TaskAccessor.GetTaskResultType(methodReference.Definition.MethodInfo.ReturnType); Task completionTask; IValueContainer asmValueContainer = null; if (TryCreateAsyncStateMachine(methodReference.Definition.MethodInfo, methodId.IntentId, out var asmInstance, out var asmMetadata)) { var isContinuation = transitionDescriptor.Type == TransitionType.ContinueRoutine; asmValueContainer = await LoadRoutineStateAsync(transitionCarrier, asmInstance, asmMetadata, isContinuation, ct); asmMetadata.Owner.FieldInfo?.SetValue(asmInstance, serviceInstance); transitionMonitor.OnRoutineStart( serviceReference, methodReference, methodId, serviceInstance, asmInstance, (transitionCarrier as TransitionCarrier)?.Caller); try { asmInstance.MoveNext(); completionTask = GetCompletionTask(asmInstance, asmMetadata); } catch (Exception ex) { // The MoveNext() must not throw, but instead complete the task with an error. // try-catch is added just in case for a non-compiler-generated state machine. completionTask = TaskAccessor.FromException(taskResultType, ex); } }
public Task <TransitionDescriptor> GetTransitionDescriptorAsync(CancellationToken ct) { TransitionDescriptor result; if (_eventEnvelope.EventType == DasyncCloudEventsTypes.InvokeRoutine.Name) { result = new TransitionDescriptor { Type = TransitionType.InvokeRoutine, ETag = _eventEnvelope.ETag }; } else if (_eventEnvelope.EventType == DasyncCloudEventsTypes.ContinueRoutine.Name) { result = new TransitionDescriptor { Type = TransitionType.ContinueRoutine, ETag = _eventEnvelope.ETag }; } else { throw new InvalidOperationException($"Unknown event type '{_eventEnvelope.EventType}'."); } return(Task.FromResult(result)); }
public Task <ActiveRoutineInfo> ScheduleContinuationAsync( ContinueRoutineIntent intent, CancellationToken ct) { var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.ContinueRoutine, ETag = intent.Continuation.Routine.ETag }; var message = new Message { //["IntentId"] = _serializer.Serialize(intent.Id), [nameof(TransitionDescriptor)] = _serializer.SerializeToString(transitionDescriptor), [nameof(ServiceId)] = _serializer.SerializeToString(intent.Continuation.ServiceId), [nameof(RoutineDescriptor)] = _serializer.SerializeToString(intent.Continuation.Routine), [nameof(RoutineResultDescriptor)] = _serializer.SerializeToString(intent.Result), DeliverAt = intent.Continuation.ContinueAt?.ToUniversalTime() }; _dataStore.ScheduleMessage(message); var info = new ActiveRoutineInfo { RoutineId = intent.Continuation.Routine.RoutineId }; return(Task.FromResult(info)); }
public IDisposable Enter(TransitionDescriptor transitionDescriptor) { if (IsActive) { throw new InvalidOperationException( "Cannot start transition of while another one is still running."); } var context = new TransitionContext { TransitionDescriptor = transitionDescriptor }; var monitor = _transitionMonitorFactory.Create(context); var scopeData = new ScopeData { Context = context, Monitor = monitor }; _currentScope.Value = scopeData; return(new DisposeTarget(this, scopeData)); }
public Task SubscribeToTriggerAsync(SubscribeToTriggerIntent intent, CancellationToken ct) { _dataStore.SubscribeToTrigger( intent.TriggerId, taskResult => { var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.ContinueRoutine, ETag = intent.Continuation.Routine.ETag }; var resultDescriptor = new ResultDescriptor { CorrelationId = intent.TriggerId, Result = taskResult }; var message = new Message { [nameof(TransitionDescriptor)] = _serializer.SerializeToString(transitionDescriptor), [nameof(ServiceId)] = _serializer.SerializeToString(intent.Continuation.ServiceId), [nameof(RoutineDescriptor)] = _serializer.SerializeToString(intent.Continuation.Routine), [nameof(ResultDescriptor)] = _serializer.SerializeToString(resultDescriptor), DeliverAt = intent.Continuation.ContinueAt?.ToUniversalTime() }; _dataStore.ScheduleMessage(message); }); return(Task.FromResult(0)); }
/// <summary> /// Reinforces the model from the new item and reward. /// </summary> /// <param name="state">Initial State object or features with action label.</param> /// <param name="stateP">New State object or features with reward label.</param> public void Learn(object state, object stateP) { var doubles1 = Descriptor.Convert(state, true); var tuple1 = new double[][] { doubles1.ToArray() }.ToExamples(); var doubles2 = TransitionDescriptor.Convert(stateP, true); var tuple2 = new double[][] { doubles2.ToArray() }.ToExamples(); this.Learn(tuple1.X[0], tuple1.Y[0], tuple2.X[0], tuple2.Y[0]); }
public Task <ActiveRoutineInfo> ScheduleRoutineAsync( ExecuteRoutineIntent intent, CancellationToken ct) { #warning Need to send message first, then create routine record. var routineId = Interlocked.Increment(ref _dataStore.RoutineCounter); var routineRecord = new RoutineStateRecord { ETag = DateTime.UtcNow.Ticks.ToString("X16"), Id = routineId.ToString(), Completion = new TaskCompletionSource <string>(), Continuation = intent.Continuation == null ? null : _serializer.SerializeToString(intent.Continuation) }; lock (_dataStore.Routines) { _dataStore.Routines.Add(routineRecord.Id, routineRecord); } var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.InvokeRoutine, ETag = routineRecord.ETag }; var routineDescriptor = new RoutineDescriptor { MethodId = intent.MethodId, IntentId = intent.Id, RoutineId = routineRecord.Id, ETag = routineRecord.ETag }; var message = new Message { //["IntentId"] = _serializer.Serialize(intent.Id), [nameof(TransitionDescriptor)] = _serializer.SerializeToString(transitionDescriptor), [nameof(ServiceId)] = _serializer.SerializeToString(intent.ServiceId), [nameof(RoutineDescriptor)] = _serializer.SerializeToString(routineDescriptor), ["Parameters"] = _serializer.SerializeToString(intent.Parameters) }; _dataStore.ScheduleMessage(message); var info = new ActiveRoutineInfo { RoutineId = routineRecord.Id }; return(Task.FromResult(info)); }
public IDisposable Enter(TransitionDescriptor transitionDescriptor) { var context = new TransitionContext { TransitionDescriptor = transitionDescriptor }; var monitor = _transitionMonitorFactory.Create(context); var scopeData = new ScopeData { Context = context, Monitor = monitor }; _currentScope.Value = scopeData; return(new DisposeTarget(this, scopeData)); }
public async Task <InvokeRoutineResult> RunAsync(MethodInvocationData data, ICommunicatorMessage message) { var serviceReference = _serviceResolver.Resolve(data.Service); var methodReference = _methodResolver.Resolve(serviceReference.Definition, data.Method); var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodReference.Definition); //------------------------------------------------------------------------------- // MESSGE DE-DUPLICATION //------------------------------------------------------------------------------- bool needsDeduplication = behaviorSettings.Deduplicate; if (methodReference.Definition.IsQuery) { // NOTE: you can run queries using message passing (non-volatile), // or the volatile method uses a callback instead of re-trying. if (message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile) && data.Continuation == null) { needsDeduplication = false; } } if (needsDeduplication && !message.CommunicatorTraits.HasFlag(CommunicationTraits.MessageDeduplication) && (message.IsRetry != false || string.IsNullOrEmpty(message.RequestId))) { // TODO: if has message de-dup'er, check if a dedup // return new InvokeRoutineResult { Outcome = InvocationOutcome.Deduplicated }; } //------------------------------------------------------------------------------- // UNIT OF WORK //------------------------------------------------------------------------------- // TODO: Unit of Work - check if there is cached/stored data if (message.IsRetry != false) { // TODO: If has entities in transitions and they have been already committed, // skip method transition and re-try sending commands and events. } //------------------------------------------------------------------------------- // DELEGATION TO RESILIENT COMMUNICATOR //------------------------------------------------------------------------------- IMessageHandle messageHandle = null; if (behaviorSettings.Persistent && message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile)) { // TODO: check if can poll for result! (think about continuation and sync invocation) var preferredCommunicator = _communicatorProvider.GetCommunicator(data.Service, data.Method); if (message.CommunicatorType != preferredCommunicator.Type && !preferredCommunicator.Traits.HasFlag(CommunicationTraits.Volatile)) { var resultValueType = methodReference.Definition.MethodInfo.ReturnType; if (resultValueType != typeof(void)) { resultValueType = TaskAccessor.GetTaskResultType(resultValueType); if (resultValueType == TaskAccessor.VoidTaskResultType) { resultValueType = typeof(void); } } var preferences = new InvocationPreferences { LockMessage = behaviorSettings.RunInPlace && preferredCommunicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish), ResultValueType = resultValueType }; var invocationResult = await preferredCommunicator.InvokeAsync(data, preferences); if (invocationResult.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.IntentId)) { _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.IntentId, invocationResult.Result); } if (invocationResult.Outcome == InvocationOutcome.Scheduled && invocationResult.MessageHandle != null) { // NOTE: will run synchronously below messageHandle = invocationResult.MessageHandle; } else { return(invocationResult); } } } //------------------------------------------------------------------------------- // RUN METHOD TRANSITION //------------------------------------------------------------------------------- try { var adapter = new TransitionCarrier(data, _valueContainerCopier, message); var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.InvokeRoutine }; var result = await RunRoutineAsync(adapter, transitionDescriptor, default); if (result.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.IntentId)) { _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.IntentId, result.Result); } if (messageHandle != null) { await messageHandle.Complete(); } return(result); } catch { messageHandle?.ReleaseLock(); throw; } }
public async Task <ContinueRoutineResult> ContinueAsync(MethodContinuationData data, ICommunicatorMessage message) { var serviceReference = _serviceResolver.Resolve(data.Service); var methodReference = _methodResolver.Resolve(serviceReference.Definition, data.Method); var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodReference.Definition); //------------------------------------------------------------------------------- // MESSGE DE-DUPLICATION //------------------------------------------------------------------------------- if (behaviorSettings.Deduplicate && !message.CommunicatorTraits.HasFlag(CommunicationTraits.MessageDeduplication) && (message.IsRetry != false || string.IsNullOrEmpty(message.RequestId))) { // TODO: if has message de-dup'er, check if a dedup // return new InvokeRoutineResult { Outcome = InvocationOutcome.Deduplicated }; } //------------------------------------------------------------------------------- // UNIT OF WORK //------------------------------------------------------------------------------- // TODO: Unit of Work - check if there is cached/stored data if (message.IsRetry != false) { // TODO: If has entities in transitions and they have been already committed, // skip method transition and re-try sending commands and events. } //------------------------------------------------------------------------------- // DELEGATION TO RESILIENT COMMUNICATOR //------------------------------------------------------------------------------- IMessageHandle messageHandle = null; if (behaviorSettings.Persistent && message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile)) { // TODO: check if can poll for result! (think about continuation and sync invocation) var preferredCommunicator = _communicatorProvider.GetCommunicator(data.Service, data.Method); if (message.CommunicatorType != preferredCommunicator.Type && !preferredCommunicator.Traits.HasFlag(CommunicationTraits.Volatile)) { var preferences = new InvocationPreferences { LockMessage = behaviorSettings.RunInPlace && preferredCommunicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish) }; var invocationResult = await preferredCommunicator.ContinueAsync(data, preferences); if (!preferences.LockMessage || invocationResult.MessageHandle == null) { return(new ContinueRoutineResult { }); } // NOTE: will run synchronously below messageHandle = invocationResult.MessageHandle; } } //------------------------------------------------------------------------------- // RUN METHOD TRANSITION //------------------------------------------------------------------------------- // A communication method may not support a scheduled message delivery. if (data.ContinueAt.HasValue && data.ContinueAt.Value > DateTimeOffset.Now) { var delay = data.ContinueAt.Value - DateTimeOffset.Now; if (delay > TimeSpan.Zero) { await Task.Delay(delay); } } try { @TryRun: var adapter = new TransitionCarrier(data, message); MethodExecutionState methodState = DecodeContinuationData(data.State); if (methodState == null) { var stateStorage = _methodStateStorageProvider.GetStorage(data.Service, data.Method, returnNullIfNotFound: true); if (stateStorage == null) { throw new InvalidOperationException($"Cannot resume method '{data.Service}'.{data.Method} due to absence of a persistence mechanism."); } // TODO: a method can be already completed or transitioned (duplicate messages?) - discard transition? methodState = await stateStorage.ReadStateAsync(data.Service, data.Method, default); } adapter.SetMethodExecutionState(methodState, _valueContainerCopier); InvokeRoutineResult result; try { var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.ContinueRoutine }; result = await RunRoutineAsync(adapter, transitionDescriptor, default); } catch (ConcurrentTransitionException) { goto TryRun; } if (result.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.Method.IntentId)) { _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.Method.IntentId, result.Result); } if (messageHandle != null) { await messageHandle.Complete(); } return(new ContinueRoutineResult { }); } catch { messageHandle?.ReleaseLock(); throw; } }
private async void RunMessageInBackground(Message message) { if (message.DeliverAt.HasValue && message.DeliverAt > DateTime.UtcNow) { await Task.Delay(message.DeliverAt.Value - DateTime.UtcNow); } else { await Task.Yield(); } var ct = CancellationToken.None; if (message.IsEvent) { var serviceId = Serializer.Deserialize <ServiceId>(message[nameof(ServiceId)]); var eventId = Serializer.Deserialize <EventId>(message[nameof(EventId)]); var eventDesc = new EventDescriptor { EventId = eventId, ServiceId = serviceId }; var subscribers = DataStore.GetEventSubscribers(eventDesc); foreach (var subscriber in subscribers) { var routineId = Interlocked.Increment(ref DataStore.RoutineCounter); var routineRecord = new RoutineStateRecord { ETag = DateTime.UtcNow.Ticks.ToString("X16"), Id = routineId.ToString(), Completion = new TaskCompletionSource <string>() }; lock (DataStore.Routines) { DataStore.Routines.Add(routineRecord.Id, routineRecord); } var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.InvokeRoutine, ETag = routineRecord.ETag }; var routineDescriptor = new RoutineDescriptor { MethodId = subscriber.MethodId, IntentId = _numericIdGenerator.NewId(), RoutineId = routineRecord.Id, ETag = routineRecord.ETag }; var invokeRoutineMessage = new Message { //["IntentId"] = _serializer.Serialize(intent.Id), [nameof(TransitionDescriptor)] = Serializer.SerializeToString(transitionDescriptor), [nameof(ServiceId)] = Serializer.SerializeToString(subscriber.ServiceId), [nameof(RoutineDescriptor)] = Serializer.SerializeToString(routineDescriptor), ["Parameters"] = message["Parameters"] }; DataStore.ScheduleMessage(invokeRoutineMessage); } } else { for (; ;) { var carrier = new TransitionCarrier(this, message); carrier.Initialize(); //var transitionInfo = await data.GetTransitionDescriptorAsync(ct); //if (transitionInfo.Type == TransitionType.InvokeRoutine || // transitionInfo.Type == TransitionType.ContinueRoutine) //{ // var routineDescriptor = await data.GetRoutineDescriptorAsync(ct); // if (!string.IsNullOrEmpty(transitionInfo.ETag) && // transitionInfo.ETag != routineDescriptor.ETag) // { // // Ignore - stale duplicate message // return; // } //} try { await _transitionRunner.RunAsync(carrier, ct); break; } catch (ConcurrentRoutineExecutionException) { // re-try continue; } } } }
private async Task RunRoutineAsync( ITransitionCarrier transitionCarrier, TransitionDescriptor transitionDescriptor, CancellationToken ct) { using (_transitionScope.Enter(transitionDescriptor)) { var transitionMonitor = _transitionScope.CurrentMonitor; var serviceId = await transitionCarrier.GetServiceIdAsync(ct); var routineDescriptor = await transitionCarrier.GetRoutineDescriptorAsync(ct); object serviceInstance; IServiceDefinition serviceDefinition; #warning IntrinsicRoutines must be registered in the service registry, but it needs the engine IoC to resolve. if (serviceId.ProxyName == nameof(IntrinsicRoutines)) { serviceInstance = _intrinsicRoutines; serviceDefinition = IntrinsicCommunicationModel.IntrinsicRoutinesServiceDefinition; } else { serviceInstance = _serviceProxyBuilder.Build(serviceId); serviceDefinition = ((ServiceProxyContext)((IProxy)serviceInstance).Context).Definition; } var routineMethod = _routineMethodResolver.Resolve(serviceDefinition, routineDescriptor.MethodId); //var serviceStateContainer = _serviceStateValueContainerProvider.CreateContainer(serviceInstance); //var isStatefullService = serviceStateContainer.GetCount() > 0; //if (isStatefullService) // await transitionCarrier.ReadServiceStateAsync(serviceStateContainer, ct); Task completionTask; IValueContainer asmValueContainer = null; if (TryCreateAsyncStateMachine(routineMethod, out var asmInstance, out var asmMetadata)) { var isContinuation = transitionDescriptor.Type == TransitionType.ContinueRoutine; asmValueContainer = await LoadRoutineStateAsync(transitionCarrier, asmInstance, asmMetadata, isContinuation, ct); asmMetadata.Owner.FieldInfo?.SetValue(asmInstance, serviceInstance); transitionMonitor.OnRoutineStart( serviceId, routineDescriptor, serviceInstance, routineMethod, asmInstance); try { #warning possibly need to create a proxy? on a sealed ASM class? How to capture Task.Delay if it's not immediate after first MoveNext? asmInstance.MoveNext(); completionTask = GetCompletionTask(asmInstance, asmMetadata); } catch (Exception ex) { // The MoveNext() must not throw, but instead complete the task with an error. // try-catch is added just in case for a non-compiler-generated state machine. var taskResultType = TaskAccessor.GetTaskResultType(routineMethod.ReturnType); completionTask = TaskAccessor.FromException(taskResultType, ex); } }
private async Task RunRoutineAsync( ITransitionCarrier transitionCarrier, ITransitionData transitionData, TransitionDescriptor transitionDescriptor, CancellationToken ct) { using (_transitionScope.Enter(transitionDescriptor)) { var transitionMonitor = _transitionScope.CurrentMonitor; var serviceId = await transitionData.GetServiceIdAsync(ct); var routineDescriptor = await transitionData.GetRoutineDescriptorAsync(ct); var serviceInstance = #warning IntrinsicRoutines must be registered in the service registry, but it needs the engine IoC to resolve. serviceId.ProxyName == nameof(IntrinsicRoutines) ? _intrinsicRoutines : _serviceProxyBuilder.Build(serviceId); #warning check if the serviceInstance proxy is an actual non-abstract class with implementation // Need exact underlying type of the service implementation type to call // the routine method directly without using the virtual method table. var serviceType = (serviceInstance as IProxy)?.ObjectType ?? serviceInstance.GetType(); var routineMethod = _routineMethodResolver.Resolve(serviceType, routineDescriptor.MethodId); var serviceStateContainer = _serviceStateValueContainerProvider.CreateContainer(serviceInstance); var isStatefullService = serviceStateContainer.GetCount() > 0; if (isStatefullService) { await transitionData.ReadServiceStateAsync(serviceStateContainer, ct); } Task completionTask; IValueContainer asmValueContainer = null; if (TryCreateAsyncStateMachine(routineMethod, out var asmInstance, out var asmMetadata, out completionTask)) { var isContinuation = transitionDescriptor.Type == TransitionType.ContinueRoutine; asmValueContainer = await LoadRoutineStateAsync(transitionData, asmInstance, asmMetadata, isContinuation, ct); asmMetadata.Owner.FieldInfo?.SetValue(asmInstance, serviceInstance); transitionMonitor.OnRoutineStart( serviceId, routineDescriptor, serviceInstance, routineMethod, asmInstance); try { #warning possibly need to create a proxy? on a sealed ASM class? How to capture Task.Delay if it's not immediate after first MoveNext? asmInstance.MoveNext(); } catch (Exception ex) { // The MoveNext() must not throw, but instead complete the task with an error. // try-catch is added just in case for a non-compiler-generated state machine. var taskResultType = TaskAccessor.GetTaskResultType(routineMethod.ReturnType); completionTask = TaskAccessor.FromException(taskResultType, ex); } }