Example #1
0
        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;
            }
        }
Example #2
0
        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;
            }
        }
Example #3
0
        public async Task <InvokeRoutineResult> InvokeAsync(ExecuteRoutineIntent intent)
        {
            var serviceRef = _serviceResolver.Resolve(intent.Service);
            var methodRef  = _methodResolver.Resolve(serviceRef.Definition, intent.Method);

            var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodRef.Definition);

            ICommunicator communicator = _communicatorProvider.GetCommunicator(serviceRef.Id, methodRef.Id);

            bool preferToRunInPlace = behaviorSettings.RunInPlace && serviceRef.Definition.Type != ServiceType.External;
            bool runInPlace         = preferToRunInPlace && (!behaviorSettings.Persistent ||
                                                             communicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish));

            var invocationData = InvocationDataUtils.CreateMethodInvocationData(intent,
                                                                                _transitionScope.IsActive ? _transitionScope.CurrentMonitor.Context : null);

            var resultValueType = methodRef.Definition.MethodInfo.ReturnType;

            if (resultValueType != typeof(void))
            {
                resultValueType = TaskAccessor.GetTaskResultType(resultValueType);
                if (resultValueType == TaskAccessor.VoidTaskResultType)
                {
                    resultValueType = typeof(void);
                }
            }

            if (runInPlace)
            {
                IMessageHandle messageHandle = null;
                if (behaviorSettings.Persistent)
                {
                    var preferences = new InvocationPreferences
                    {
                        LockMessage     = true,
                        ResultValueType = resultValueType
                    };

                    var invocationResult = await communicator.InvokeAsync(invocationData, preferences);

                    if (invocationResult.Outcome == InvocationOutcome.Complete)
                    {
                        return(invocationResult);
                    }
                    messageHandle = invocationResult.MessageHandle;
                }

                try
                {
                    var result = await _localMethodRunner.RunAsync(
                        invocationData,
                        RuntimeCommunicatorMessage.Instance);

                    if (messageHandle != null)
                    {
                        await messageHandle.Complete();
                    }

                    return(result);
                }
                catch (Exception ex) // TODO: infra exceptions? should not be there, right?
                {
                    if (messageHandle != null)
                    {
                        messageHandle.ReleaseLock();
                    }

                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Scheduled,
                        MessageHandle = messageHandle
                    });
                }
            }
            else
            {
                var preferences = new InvocationPreferences
                {
                    // TODO: check this option
                    //LockMessage = behaviorSettings.Resilient && communicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish),

                    Synchronous     = communicator.Traits.HasFlag(CommunicationTraits.SyncReplies),
                    ResultValueType = resultValueType
                };

                return(await communicator.InvokeAsync(invocationData, preferences));
            }
        }