/// <summary> /// Logs that an event is being raised to an orchestration. /// </summary> /// <param name="target">The target orchestration instance.</param> /// <param name="raisedEvent">The event-raised event.</param> internal void RaisingEvent(OrchestrationInstance target, EventRaisedEvent raisedEvent) { if (this.IsStructuredLoggingEnabled) { this.WriteStructuredLog(new LogEvents.RaisingEvent(target, raisedEvent)); } }
private async Task EntityMiddleware(DispatchMiddlewareContext dispatchContext, Func <Task> next) { var entityShim = dispatchContext.GetProperty <TaskOrchestration>() as TaskEntityShim; if (entityShim == null) { // This is not an entity - skip. await next(); return; } OrchestrationRuntimeState runtimeState = dispatchContext.GetProperty <OrchestrationRuntimeState>(); DurableEntityContext entityContext = (DurableEntityContext)entityShim.Context; entityContext.InstanceId = runtimeState.OrchestrationInstance.InstanceId; entityContext.ExecutionId = runtimeState.OrchestrationInstance.ExecutionId; entityContext.History = runtimeState.Events; entityContext.RawInput = runtimeState.Input; try { // 1. First time through the history // we count events, add any under-lock op to the batch, and process lock releases foreach (HistoryEvent e in runtimeState.Events) { switch (e.EventType) { case EventType.ExecutionStarted: entityShim.Rehydrate(runtimeState.Input); break; case EventType.EventRaised: EventRaisedEvent eventRaisedEvent = (EventRaisedEvent)e; this.TraceHelper.DeliveringEntityMessage( entityContext.InstanceId, entityContext.ExecutionId, e.EventId, eventRaisedEvent.Name, eventRaisedEvent.Input); entityShim.NumberEventsToReceive++; if (eventRaisedEvent.Name == "op") { // we are receiving an operation request or a lock request var requestMessage = JsonConvert.DeserializeObject <RequestMessage>(eventRaisedEvent.Input); // run this through the message sorter to help with reordering and duplicate filtering var deliverNow = entityContext.State.MessageSorter.ReceiveInOrder(requestMessage, entityContext.EntityMessageReorderWindow); foreach (var message in deliverNow) { if (entityContext.State.LockedBy == message.ParentInstanceId) { // operation requests from the lock holder are processed immediately entityShim.AddOperationToBatch(message); } else { // others go to the back of the queue entityContext.State.Enqueue(message); } } } else { // we are receiving a lock release var message = JsonConvert.DeserializeObject <ReleaseMessage>(eventRaisedEvent.Input); if (entityContext.State.LockedBy == message.ParentInstanceId) { this.TraceHelper.EntityLockReleased( entityContext.HubName, entityContext.Name, entityContext.InstanceId, message.ParentInstanceId, message.LockRequestId, isReplay: false); entityContext.State.LockedBy = null; } } break; } } // 2. We add as many requests from the queue to the batch as possible (stopping at lock requests) while (entityContext.State.LockedBy == null && entityContext.State.TryDequeue(out var request)) { if (request.IsLockRequest) { entityShim.AddLockRequestToBatch(request); entityContext.State.LockedBy = request.ParentInstanceId; } else { entityShim.AddOperationToBatch(request); } } } catch (Exception e) { entityContext.CaptureInternalError(e); } // 3. Start the functions invocation pipeline (billing, logging, bindings, and timeout tracking). FunctionResult result = await entityShim.GetFunctionInfo().Executor.TryExecuteAsync( new TriggeredFunctionData { TriggerValue = entityShim.Context, #pragma warning disable CS0618 // Approved for use by this extension InvokeHandler = async userCodeInvoker => { entityShim.SetFunctionInvocationCallback(userCodeInvoker); // 3. Run all the operations in the batch if (entityContext.InternalError == null) { try { await entityShim.ExecuteBatch(); } catch (Exception e) { entityContext.CaptureInternalError(e); } } // 4. Run the DTFx orchestration to persist the effects, // send the outbox, and continue as new await next(); // 5. If there were internal or application errors, indicate to the functions host entityContext.ThrowInternalExceptionIfAny(); entityContext.ThrowApplicationExceptionsIfAny(); }, #pragma warning restore CS0618 }, CancellationToken.None); await entityContext.RunDeferredTasks(); // If there were internal errors, do not commit the batch, but instead rethrow // here so DTFx can abort the batch and back off the work item entityContext.ThrowInternalExceptionIfAny(); }
async Task <OrchestrationInstance> InternalCreateOrchestrationInstanceWithRaisedEventAsync( string orchestrationName, string orchestrationVersion, string orchestrationInstanceId, object orchestrationInput, IDictionary <string, string> orchestrationTags, OrchestrationStatus[] dedupeStatuses, string eventName, object eventData, DateTime?startAt = null) { if (string.IsNullOrWhiteSpace(orchestrationInstanceId)) { orchestrationInstanceId = Guid.NewGuid().ToString("N"); } var orchestrationInstance = new OrchestrationInstance { InstanceId = orchestrationInstanceId, ExecutionId = Guid.NewGuid().ToString("N"), }; string serializedOrchestrationData = this.defaultConverter.Serialize(orchestrationInput); var startedEvent = new ExecutionStartedEvent(-1, serializedOrchestrationData) { Tags = orchestrationTags, Name = orchestrationName, Version = orchestrationVersion, OrchestrationInstance = orchestrationInstance, ScheduledStartTime = startAt }; var startMessage = new TaskMessage { OrchestrationInstance = orchestrationInstance, Event = startedEvent }; this.logHelper.SchedulingOrchestration(startedEvent); // Raised events and create orchestration calls use different methods so get handled separately await this.ServiceClient.CreateTaskOrchestrationAsync(startMessage, dedupeStatuses); if (eventData != null) { string serializedEventData = this.defaultConverter.Serialize(eventData); var eventRaisedEvent = new EventRaisedEvent(-1, serializedEventData) { Name = eventName }; this.logHelper.RaisingEvent(orchestrationInstance, eventRaisedEvent); var eventMessage = new TaskMessage { OrchestrationInstance = new OrchestrationInstance { InstanceId = orchestrationInstanceId, // to ensure that the event gets raised on the running // orchestration instance, null the execution id // so that it will find out which execution // it should use for processing ExecutionId = null }, Event = eventRaisedEvent, }; await this.ServiceClient.SendTaskOrchestrationMessageAsync(eventMessage); } return(orchestrationInstance); }
public static HistoryEvent GetHistoryEvent(this DbDataReader reader, bool isOrchestrationHistory = false) { string eventTypeString = (string)reader["EventType"]; if (!Enum.TryParse(eventTypeString, out EventType eventType)) { throw new InvalidOperationException($"Unknown event type '{eventTypeString}'."); } int eventId = GetTaskId(reader); HistoryEvent historyEvent; switch (eventType) { case EventType.ContinueAsNew: historyEvent = new ContinueAsNewEvent(eventId, GetPayloadText(reader)); break; case EventType.EventRaised: historyEvent = new EventRaisedEvent(eventId, GetPayloadText(reader)) { Name = GetName(reader), }; break; case EventType.EventSent: historyEvent = new EventSentEvent(eventId) { Input = GetPayloadText(reader), Name = GetName(reader), InstanceId = GetInstanceId(reader), }; break; case EventType.ExecutionCompleted: historyEvent = new ExecutionCompletedEvent( eventId, result: GetPayloadText(reader), orchestrationStatus: OrchestrationStatus.Completed); break; case EventType.ExecutionFailed: historyEvent = new ExecutionCompletedEvent( eventId, result: GetPayloadText(reader), orchestrationStatus: OrchestrationStatus.Failed); break; case EventType.ExecutionStarted: historyEvent = new ExecutionStartedEvent(eventId, GetPayloadText(reader)) { Name = GetName(reader), OrchestrationInstance = new OrchestrationInstance { InstanceId = GetInstanceId(reader), ExecutionId = GetExecutionId(reader), }, Tags = null, // TODO Version = GetVersion(reader), }; string?parentInstanceId = GetParentInstanceId(reader); if (parentInstanceId != null) { ((ExecutionStartedEvent)historyEvent).ParentInstance = new ParentInstance { OrchestrationInstance = new OrchestrationInstance { InstanceId = parentInstanceId }, TaskScheduleId = GetTaskId(reader) }; } break; case EventType.ExecutionTerminated: historyEvent = new ExecutionTerminatedEvent(eventId, GetPayloadText(reader)); break; case EventType.GenericEvent: historyEvent = new GenericEvent(eventId, GetPayloadText(reader)); break; case EventType.OrchestratorCompleted: historyEvent = new OrchestratorCompletedEvent(eventId); break; case EventType.OrchestratorStarted: historyEvent = new OrchestratorStartedEvent(eventId); break; case EventType.SubOrchestrationInstanceCompleted: historyEvent = new SubOrchestrationInstanceCompletedEvent(eventId, GetTaskId(reader), GetPayloadText(reader)); break; case EventType.SubOrchestrationInstanceCreated: historyEvent = new SubOrchestrationInstanceCreatedEvent(eventId) { Input = GetPayloadText(reader), InstanceId = null, // TODO Name = GetName(reader), Version = null, }; break; case EventType.SubOrchestrationInstanceFailed: historyEvent = new SubOrchestrationInstanceFailedEvent( eventId, taskScheduledId: GetTaskId(reader), reason: GetReason(reader), details: GetPayloadText(reader)); break; case EventType.TaskCompleted: historyEvent = new TaskCompletedEvent( eventId, taskScheduledId: GetTaskId(reader), result: GetPayloadText(reader)); break; case EventType.TaskFailed: historyEvent = new TaskFailedEvent( eventId, taskScheduledId: GetTaskId(reader), reason: GetReason(reader), details: GetPayloadText(reader)); break; case EventType.TaskScheduled: historyEvent = new TaskScheduledEvent(eventId) { Input = GetPayloadText(reader), Name = GetName(reader), Version = GetVersion(reader), }; break; case EventType.TimerCreated: historyEvent = new TimerCreatedEvent(eventId) { FireAt = GetVisibleTime(reader), }; break; case EventType.TimerFired: historyEvent = new TimerFiredEvent(eventId) { FireAt = GetVisibleTime(reader), TimerId = GetTaskId(reader), }; break; default: throw new InvalidOperationException($"Don't know how to interpret '{eventType}'."); } historyEvent.Timestamp = GetTimestamp(reader); historyEvent.IsPlayed = isOrchestrationHistory && (bool)reader["IsPlayed"]; return(historyEvent); }