Ejemplo n.º 1
0
        public Task <ContinueRoutineResult> ContinueAsync(
            MethodContinuationData data,
            InvocationPreferences preferences)
        {
            var message = new Message
            {
                Type      = MessageType.Response,
                DeliverAt = data.ContinueAt
            };

            MethodContinuationDataTransformer.Write(message, data, _serializer);

            var result = new ContinueRoutineResult();

            if (preferences.LockMessage)
            {
                result.MessageHandle = new MessageHandle(message, _messageHub);
            }
            else
            {
                _messageHub.Schedule(message);
            }

            return(Task.FromResult(result));
        }
Ejemplo n.º 2
0
        public async Task <ContinueRoutineResult> ContinueAsync(
            MethodContinuationData data,
            InvocationPreferences preferences)
        {
            var queueName = _settings.QueueName
                            .Replace("{serviceName}", data.Service.Name)
                            .Replace("{methodName}", data.Method.Name);

            // TODO: declare once? what's the penalty?
            _channel.QueueDeclare(
                queueName,
                durable: true,
                exclusive: false,
                autoDelete: false,
                arguments: null);

            var properties = CreateMessageProperties(_channel);

            properties.Type = MessageTypes.Continue;
            SetIntentId(properties, data.IntentId);
            SetFormat(properties, _serializer);
            properties.Headers.Add("X-Service-Name", data.Service.Name);
            properties.Headers.Add("X-Method-Name", data.Method.Name);

            // This requires the Message Delay plugin.
            if (data.ContinueAt.HasValue && (Traits & CommunicationTraits.ScheduledDelivery) != default)
            {
                var delay = data.ContinueAt.Value - DateTimeOffset.Now;
                if (delay > TimeSpan.Zero)
                {
                    properties.Headers.Add("x-delay", (int)delay.TotalMilliseconds);
                }
            }

            var payload = SerializePayload(data);

            _channel.BasicPublish(
                exchange: "",
                routingKey: queueName,
                basicProperties: properties,
                body: payload);

            _channel.WaitForConfirms(); // no async version :(

            return(new ContinueRoutineResult
            {
            });
        }
Ejemplo n.º 3
0
        public async Task <ContinueRoutineResult> ContinueAsync(
            MethodContinuationData data,
            InvocationPreferences preferences)
        {
            HttpResponseMessage response;

            using (var requestContent = CreateContent(data))
            {
                requestContent.Headers.TryAddWithoutValidation(DasyncHttpHeaders.Envelope, "continue");
                requestContent.Headers.TryAddWithoutValidation(DasyncHttpHeaders.IntentId, data.IntentId);
                AddAsyncHeader(requestContent.Headers, preferAsync: true);
                AddCallerHeaders(requestContent.Headers, data.Caller);
                AddRetryHeader(requestContent.Headers, retryCount: 0);

                var url = _urlTemplate
                          .Replace("{serviceName}", data.Service.Name)
                          .Replace("{methodName}", data.Method.Name)
                          + "/" + data.Method.IntentId;

                if (!string.IsNullOrEmpty(data.Method.ETag))
                {
                    url += "?etag=" + data.Method.ETag;
                }

                response = await _httpClient.PostAsync(url, requestContent);
            }
            using (response)
            {
                if ((int)response.StatusCode == DasyncHttpCodes.Scheduled)
                {
                    return(new ContinueRoutineResult
                    {
                    });
                }

                if ((int)response.StatusCode == DasyncHttpCodes.Deduplicated)
                {
                    return(new ContinueRoutineResult
                    {
                    });
                }
                throw new Exception(); // TODO: add info
            }
        }
Ejemplo n.º 4
0
        public async Task <InvokeRoutineResult> InvokeAsync(
            MethodInvocationData data,
            InvocationPreferences preferences)
        {
            var message = new Message
            {
                Type = MessageType.InvokeMethod,
            };

            MethodInvocationDataTransformer.Write(message, data, _serializer);

            var result = new InvokeRoutineResult
            {
                Outcome = InvocationOutcome.Scheduled
            };

            if (preferences.Synchronous)
            {
                var completionNotification = new TaskCompletionSource <InvokeRoutineResult>();
                message.Data["Notification"] = completionNotification;

                _messageHub.Schedule(message);

                result = await completionNotification.Task;

                if (result.Result != null)
                {
                    result.Result = TaskResult.CreateEmpty(preferences.ResultValueType);
                    _serializer.Populate(_serializer.SerializeToString(result.Result), (IValueContainer)result.Result);
                }
            }
            else if (preferences.LockMessage)
            {
                result.MessageHandle = new MessageHandle(message, _messageHub);
            }
            else
            {
                _messageHub.Schedule(message);
            }

            return(result);
        }
Ejemplo n.º 5
0
        public async Task <InvokeRoutineResult> InvokeAsync(
            MethodInvocationData data,
            InvocationPreferences preferences)
        {
            var queueName = _settings.QueueName
                            .Replace("{serviceName}", data.Service.Name)
                            .Replace("{methodName}", data.Method.Name);

            // TODO: declare once? what's the penalty?
            _channel.QueueDeclare(
                queueName,
                durable: true,
                exclusive: false,
                autoDelete: false,
                arguments: null);

            var properties = CreateMessageProperties(_channel);

            properties.Type = MessageTypes.Invoke;
            SetIntentId(properties, data.IntentId);
            SetFormat(properties, _serializer);
            properties.Headers.Add("X-Service-Name", data.Service.Name);
            properties.Headers.Add("X-Method-Name", data.Method.Name);

            var payload = SerializePayload(data);

            _channel.BasicPublish(
                exchange: "",
                routingKey: queueName,
                basicProperties: properties,
                body: payload);

            _channel.WaitForConfirms(); // no async version :(

            return(new InvokeRoutineResult
            {
                Outcome = InvocationOutcome.Scheduled
            });
        }
Ejemplo n.º 6
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;
            }
        }
Ejemplo n.º 7
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;
            }
        }
Ejemplo n.º 8
0
        public async Task <InvokeRoutineResult> InvokeAsync(
            MethodInvocationData data,
            InvocationPreferences preferences)
        {
            HttpResponseMessage response;

            using (var requestContent = CreateContent(data))
            {
                requestContent.Headers.TryAddWithoutValidation(DasyncHttpHeaders.Envelope, "invoke");
                requestContent.Headers.TryAddWithoutValidation(DasyncHttpHeaders.IntentId, data.IntentId);
                AddAsyncHeader(requestContent.Headers, preferAsync: !preferences.Synchronous);
                AddCallerHeaders(requestContent.Headers, data.Caller);
                AddRetryHeader(requestContent.Headers, retryCount: 0);

                var url = _urlTemplate
                          .Replace("{serviceName}", data.Service.Name)
                          .Replace("{methodName}", data.Method.Name);

                response = await _httpClient.PostAsync(url, requestContent);
            }
            using (response)
            {
                if ((int)response.StatusCode == DasyncHttpCodes.Scheduled)
                {
                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Scheduled
                    });
                }

                if ((int)response.StatusCode == DasyncHttpCodes.Deduplicated)
                {
                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Deduplicated
                    });
                }

                var methodOutcome = response.Headers.GetValue(DasyncHttpHeaders.TaskResult);
                if (string.IsNullOrEmpty(methodOutcome))
                {
                    throw new Exception(); // TODO: add info
                }

                var responseStream = await response.Content.ReadAsStreamAsync();

                var encoding = response.Headers.GetContentEncoding();
                if (!string.IsNullOrEmpty(encoding))
                {
                    if ("gzip".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
                    }
                    else if ("deflate".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
                    }
                    else
                    {
                        throw new Exception($"Unknown content encoding '{encoding}'.");
                    }
                }

                using (responseStream)
                {
                    var taskResult = TaskResult.CreateEmpty(preferences.ResultValueType);
                    _serializer.Populate(responseStream, (IValueContainer)taskResult);

                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Complete,
                        Result = taskResult
                    });
                }
            }
        }
Ejemplo n.º 9
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));
            }
        }