public async Task <ActiveRoutineInfo> PollRoutineResultAsync(
            ActiveRoutineInfo info, CancellationToken ct)
        {
            RoutineRecord routineRecord;

            try
            {
                routineRecord = await _routinesTable.TryRetrieveAsync <RoutineRecord>(
                    _serviceId.ServiceName, info.RoutineId, RoutineRecordPropertiesForPolling, ct);
            }
            catch (TableDoesNotExistException)
            {
                routineRecord = null;
            }

            if (routineRecord != null)
            {
                TaskResult routineResult = null;

                if (routineRecord.Status == (int)RoutineStatus.Complete &&
                    !string.IsNullOrEmpty(routineRecord.Result))
                {
                    routineResult = _serializer.Deserialize <TaskResult>(routineRecord.Result);
                }

                info = new ActiveRoutineInfo
                {
                    ETag      = routineRecord.ETag,
                    RoutineId = info.RoutineId,
                    Result    = routineResult
                };
            }

            return(info);
        }
示例#2
0
        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));
        }
示例#3
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));
        }
示例#4
0
        public async Task <ActiveRoutineInfo> PollRoutineResultAsync(
            ActiveRoutineInfo info, CancellationToken ct)
        {
            var routineRecord = _dataStore.GetRoutineRecord(info.RoutineId);
            var resultData    = await routineRecord.Completion.Task;
            var result        = _serializer.Deserialize <TaskResult>(resultData);

            return(new ActiveRoutineInfo
            {
                RoutineId = routineRecord.Id,
                Result = result
            });
        }
示例#5
0
        public Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
            var pregeneratedRoutineId = intent.Id.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intent.Id,
                MethodId  = intent.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId    = intent.ServiceId,
                Routine      = routineDescriptor,
                Caller       = intent.Caller,
                Continuation = intent.Continuation,
                Parameters   = _serializer.SerializeToString(intent.Parameters)
            };

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

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

            File.WriteAllText(filePath, content, Encoding.UTF8);

            var info = new ActiveRoutineInfo
            {
                RoutineId = pregeneratedRoutineId
            };

            return(Task.FromResult(info));
        }
示例#6
0
        private async Task <ActiveRoutineInfo> PollRoutineAsync(
            ActiveRoutineInfo routineInfo,
            IFabricConnector connector,
            DateTimeOffset requestStartTime,
            TimeSpan maxPollTime,
            CancellationToken ct)
        {
            var stopPollingAt = requestStartTime + maxPollTime;

            for (var i = 0; ; i++)
            {
                routineInfo = await connector.PollRoutineResultAsync(routineInfo, ct);

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

                TimeSpan delayInterval;

                if (routineInfo is IRoutinePollInterval routinePollInterval)
                {
                    delayInterval = routinePollInterval.Suggest(i);
                }
                else
                {
                    delayInterval = TimeSpan.FromSeconds(0.5);
                }

                var resumeAt = DateTimeOffset.Now + delayInterval;
                if (resumeAt > stopPollingAt)
                {
                    delayInterval = stopPollingAt - DateTimeOffset.Now;
                }

                if (delayInterval <= TimeSpan.Zero)
                {
                    break;
                }

                await Task.Delay(delayInterval);
            }

            return(routineInfo);
        }
示例#7
0
        public async Task <ActiveRoutineInfo> PollRoutineResultAsync(
            ActiveRoutineInfo info, CancellationToken ct)
        {
            TaskResult routineResult = null;

            if (TryReadRoutineData(_routinesDirectory, info.RoutineId, out var dataEnvelope, out var eTag))
            {
                if (dataEnvelope.Status == RoutineStatus.Complete)
                {
                    routineResult = _serializer.Deserialize <TaskResult>(dataEnvelope.Result);
                }
            }

            return(new ActiveRoutineInfo
            {
                RoutineId = info.RoutineId,
                Result = routineResult,
                ETag = eTag
            });
        }
示例#8
0
        public Task <ActiveRoutineInfo> ScheduleContinuationAsync(
            ContinueRoutineIntent intent, CancellationToken ct)
        {
            var eventData = new RoutineEventData
            {
                ServiceId = intent.Continuation.ServiceId,
                Routine   = intent.Continuation.Routine,
                Callee    = intent.Callee,
                Result    = _serializer.SerializeToString(intent.Result)
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.ContinueRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.ContinueRoutine.Version,
                Source             = "/" + (intent.Callee?.ServiceId.ServiceName ?? ""),
                EventID            = intent.Id.ToString(),
                EventTime          = DateTimeOffset.Now,
                EventDeliveryTime  = intent.Continuation.ContinueAt?.ToUniversalTime(),
                ETag        = intent.Continuation.Routine.ETag,
                ContentType = "application/json",
                Data        = CloudEventsSerialization.Serialize(eventData)
            };

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

            File.WriteAllText(filePath, content, Encoding.UTF8);

            var info = new ActiveRoutineInfo
            {
                RoutineId = intent.Continuation.Routine.RoutineId
            };

            return(Task.FromResult(info));
        }
示例#9
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));
        }