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); }
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); }
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); }
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; } } } }
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)); }
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 }); } }