コード例 #1
0
        public bool TryRegisterNew(object taskCompletionSource, out TriggerReference triggerReference)
        {
            if (taskCompletionSource == null)
            {
                throw new ArgumentNullException(nameof(taskCompletionSource));
            }
            if (!TaskCompletionSourceAccessor.IsTaskCompletionSource(taskCompletionSource))
            {
                throw new ArgumentException($"Input object must be a TaskCompletionSource`1, but got {taskCompletionSource.GetType()}", nameof(taskCompletionSource));
            }

            var task = TaskCompletionSourceAccessor.GetTask(taskCompletionSource);

            triggerReference = task.AsyncState as TriggerReference;
            if (triggerReference != null)
            {
                return(false);
            }

            triggerReference = new TriggerReference
            {
                Id = _numericIdGenerator.NewId()
            };
            task.SetAsyncState(triggerReference);

            return(true);
        }
コード例 #2
0
        public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters)
            where TParameters : IValueContainer
        {
            var serviceProxyContext = (ServiceProxyContext)proxy.Context;

            var intent = new ExecuteRoutineIntent
            {
                Id         = _numericIdGenerator.NewId(),
                ServiceId  = serviceProxyContext.Service.Id,
                MethodId   = _routineMethodIdProvider.GetId(methodInfo),
                Parameters = parameters
            };

            var taskResultType =
                // Dispose() does not return a task, and is the only exception.
#warning check if it's really IDisposable.Dispose
                methodInfo.ReturnType == typeof(void)
                ? TaskAccessor.VoidTaskResultType
                : TaskAccessor.GetTaskResultType(methodInfo.ReturnType);

            var taskState = new RoutineReference
            {
                IntentId = intent.Id
#warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled).
            };

            var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType);

            if (_transitionScope.IsActive)
            {
                _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask);
            }
            else
            {
                ExecuteAndAwaitInBackground(intent, proxyTask);
            }

            return(proxyTask);
        }
コード例 #3
0
        internal void ScheduleRoutineFromEvent(EventSubscriberDescriptor eventSubscriberDescriptor, RoutineEventData raisedEventData)
        {
            var intentId = _numericIdGenerator.NewId();

            var pregeneratedRoutineId = intentId.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intentId,
                MethodId  = eventSubscriberDescriptor.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId = eventSubscriberDescriptor.ServiceId,
                Routine   = routineDescriptor,
                Caller    = new CallerDescriptor
                {
                    ServiceId = raisedEventData.ServiceId
                },
                Parameters = raisedEventData.Parameters
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.InvokeRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.InvokeRoutine.Version,
                Source             = "/" + (raisedEventData.ServiceId?.ServiceName ?? ""),
                EventID            = intentId.ToString(),
                EventTime          = DateTimeOffset.Now,
                ContentType        = "application/json",
                Data = CloudEventsSerialization.Serialize(eventData)
            };

            var fileName = intentId.ToString() + ".json";
            var filePath = Path.Combine(_transitionsDirectory, fileName);
            var content  = CloudEventsSerialization.Serialize(eventEnvelope);

            File.WriteAllText(filePath, content, Encoding.UTF8);
        }
コード例 #4
0
        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;
                    }
                }
            }
        }
コード例 #5
0
        public async Task <HttpResponseMessage> ProcessRequestAsync(
            HttpRequestMessage request,
            FunctionExecutionContext context,
            DateTimeOffset requestStartTime,
            ILogger logger,
            CancellationToken ct)
        {
            _currentFunction.Value = new ExecutingFuncionInfo
            {
                FunctionName = context.FunctionName
            };

            try
            {
#warning Create an HTTP connector
                if (request.Method == HttpMethod.Post)
                {
                    string   serviceName = null;
                    string   routineName = null;
                    long?    intentId    = null;
                    bool     @volatile   = false;
                    TimeSpan pollTime    = TimeSpan.Zero;

                    foreach (var parameter in request.GetQueryNameValuePairs())
                    {
                        switch (parameter.Key)
                        {
                        case "service":
                            serviceName = parameter.Value;
                            break;

                        case "routine":
                            routineName = parameter.Value;
                            break;

                        case "intent":
                            if (long.TryParse(parameter.Value, out var parsedIntentId))
                            {
                                intentId = parsedIntentId;
                            }
                            break;
                        }
                    }

                    foreach (var header in request.Headers)
                    {
                        var firstValue = header.Value.FirstOrDefault();

                        switch (header.Key)
                        {
                        case "Volatile":
                            if (string.IsNullOrEmpty(firstValue) ||
                                !bool.TryParse(firstValue, out @volatile))
                            {
                                @volatile = true;
                            }
                            break;

                        case "Poll-Time":
                            if (!string.IsNullOrEmpty(firstValue))
                            {
                                if (double.TryParse(firstValue, out var pollTimeInSeconds))
                                {
                                    pollTime = TimeSpan.FromSeconds(pollTimeInSeconds);
                                }
                                else if (TimeSpan.TryParse(firstValue, out var pollTimeTimeSpan))
                                {
                                    pollTime = pollTimeTimeSpan;
                                }
                            }
                            break;
                        }
                    }

                    var serviceId = new ServiceId
                    {
                        ServiceName = serviceName
                    };

                    var routineMethodId = new RoutineMethodId
                    {
                        MethodName = routineName
                    };

                    var registration = _serviceRegistry.AllRegistrations
                                       .SingleOrDefault(r => r.ServiceName == serviceId.ServiceName);

                    if (registration == null || registration.IsExternal)
                    {
                        return new HttpResponseMessage(HttpStatusCode.BadRequest)
                               {
                                   Content = new StringContent(
                                       registration == null
                                ? "Service not found"
                                : "Cannot invoke external service")
                               }
                    }
                    ;

                    IValueContainer parameterContainer = null;
                    if (request.Content != null)
                    {
                        var routineMethod = _routineMethodResolver.Resolve(registration.ServiceType, routineMethodId);

                        var methodInvoker = _methodInvokerFactory.Create(routineMethod);
                        parameterContainer = methodInvoker.CreateParametersContainer();

#warning change to use a serializer based on the format
                        var paramsJson = await request.Content.ReadAsStringAsync();

                        if (!string.IsNullOrWhiteSpace(paramsJson))
                        {
                            JsonConvert.PopulateObject(paramsJson, parameterContainer);
                        }
                    }

                    var intent = new ExecuteRoutineIntent
                    {
#warning Generate intent ID from function request ID? any benefit (like on re-try)?
                        Id           = intentId ?? _idGenerator.NewId(),
                        ServiceId    = serviceId,
                        MethodId     = routineMethodId,
                        Parameters   = parameterContainer,
                        Continuation = null
                    };

                    var connector   = GetConnector(serviceId);
                    var routineInfo = await connector.ScheduleRoutineAsync(intent, ct);

                    if (pollTime > TimeSpan.Zero)
                    {
                        routineInfo = await PollRoutineAsync(routineInfo, connector, requestStartTime, pollTime, ct);
                    }

                    if (routineInfo.Result != null)
                    {
                        return(CreateResponseFromRoutineResult(routineInfo.Result));
                    }

                    var response = new HttpResponseMessage(HttpStatusCode.Accepted);

                    var location = $"{request.RequestUri.AbsolutePath}?service={serviceId.ServiceName}&routineId={routineInfo.RoutineId}";
                    response.Headers.Add("Location", location);

#warning ETag must be in certain format
                    //if (!string.IsNullOrEmpty(routineInfo.ETag))
                    //    response.Headers.ETag = new EntityTagHeaderValue(routineInfo.ETag);

                    return(response);
                }
                else if (request.Method == HttpMethod.Get)
                {
                    string   serviceName = null;
                    string   routineId   = null;
                    string   routineETag = null;
                    TimeSpan pollTime    = TimeSpan.Zero;

                    foreach (var parameter in request.GetQueryNameValuePairs())
                    {
                        switch (parameter.Key)
                        {
                        case "service":
                            serviceName = parameter.Value;
                            break;

                        case "routineId":
                            routineId = parameter.Value;
                            break;

                        case "routineTag":
                            routineETag = parameter.Value;
                            break;
                        }
                    }

                    foreach (var header in request.Headers)
                    {
                        var firstValue = header.Value.FirstOrDefault();

                        switch (header.Key)
                        {
                        case "Poll-Time":
                            if (!string.IsNullOrEmpty(firstValue))
                            {
                                if (double.TryParse(firstValue, out var pollTimeInSeconds))
                                {
                                    pollTime = TimeSpan.FromSeconds(pollTimeInSeconds);
                                }
                                else if (TimeSpan.TryParse(firstValue, out var pollTimeTimeSpan))
                                {
                                    pollTime = pollTimeTimeSpan;
                                }
                            }
                            break;
                        }
                    }

                    var serviceId = new ServiceId
                    {
                        ServiceName = serviceName
                    };

                    var routineInfo = new ActiveRoutineInfo
                    {
                        RoutineId = routineId,
                        ETag      = routineETag
                    };

                    var connector = GetConnector(serviceId);

                    routineInfo = await PollRoutineAsync(routineInfo, connector, requestStartTime, pollTime, ct);

                    if (routineInfo.Result != null)
                    {
                        return(CreateResponseFromRoutineResult(routineInfo.Result));
                    }

                    var response = new HttpResponseMessage(HttpStatusCode.NoContent);

#warning ETag must be in certain format
                    //if (!string.IsNullOrEmpty(routineInfo.ETag))
                    //    response.Headers.ETag = new EntityTagHeaderValue(routineInfo.ETag);

                    return(response);
                }
            }
            catch (Exception ex)
            {
                return(CreateInfrastructureErrorResponse(ex));
            }
            finally
            {
                _currentFunction.Value = default;
            }

            return(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
コード例 #6
0
        public void HandleWhenAllAsDetectedContinuation(
            Task whenAllTask, Task[] awaitedTasks,
            ExecuteRoutineIntent awaitedRoutineIntent,
            ITransitionMonitor monitor)
        {
            ExecuteRoutineIntent executeWhenAllIntent;

            lock (whenAllTask)
            {
                var state = whenAllTask.AsyncState as WhenAllProxyTaskState;
                if (state == null)
                {
                    var whenAllIntentId = _numericIdGenerator.NewId();

                    Type itemType   = null;
                    var  resultType = whenAllTask.GetResultType();
                    if (resultType.IsArray)
                    {
                        itemType = resultType.GetElementType();
                    }

                    executeWhenAllIntent = new ExecuteRoutineIntent
                    {
                        Id = whenAllIntentId,

                        ServiceId = new ServiceId
                        {
                            ServiceName = awaitedRoutineIntent.ServiceId.ServiceName,
                            ProxyName   = nameof(IntrinsicRoutines)
                        },

                        MethodId = _routineMethodIdProvider.GetId(
                            IntrinsicRoutines.WhenAllMethodInfo),

                        Parameters = new WhenAllInputParameters
                        {
                            tasks   = awaitedTasks,
                            intents = new ExecuteRoutineIntent[awaitedTasks.Length],
                            // The task result must be an Array, e.g. string[]
                            itemType = itemType
                        }
                    };

                    state = new WhenAllProxyTaskState
                    {
                        IntentId             = whenAllIntentId,
                        ExecuteWhenAllIntent = executeWhenAllIntent
                    };
                    whenAllTask.SetAsyncState(state);

                    monitor.RegisterIntent(executeWhenAllIntent, whenAllTask);
                }
                else
                {
                    executeWhenAllIntent = state.ExecuteWhenAllIntent;
                }
            }

            var index = -1;

            for (var i = 0; i < awaitedTasks.Length; i++)
            {
                if (awaitedTasks[i].AsyncState is ProxyTaskState state &&
                    state.IntentId == awaitedRoutineIntent.Id)
                {
                    index = i;
                    break;
                }
            }
            var parameters = (WhenAllInputParameters)executeWhenAllIntent.Parameters;

            parameters.intents[index] = awaitedRoutineIntent;

            monitor.Context.ScheduledActions.ExecuteRoutineIntents.Remove(awaitedRoutineIntent);

            if (parameters.intents.All(i => i != null))
            {
                whenAllTask.SetAsyncState(new ProxyTaskState
                {
                    IntentId = executeWhenAllIntent.Id
                });
            }
        }