public TaskEntityShim(DurableTaskExtension config, string schedulerId) : base(config) { this.SchedulerId = schedulerId; this.EntityId = EntityId.GetEntityIdFromSchedulerId(schedulerId); this.context = new DurableEntityContext(config, this.EntityId, this); }
public TaskEntityShim(DurableTaskExtension config, DurabilityProvider durabilityProvider, string schedulerId) : base(config) { this.messageDataConverter = config.MessageDataConverter; this.errorDataConverter = config.ErrorDataConverter; this.SchedulerId = schedulerId; this.EntityId = EntityId.GetEntityIdFromSchedulerId(schedulerId); this.context = new DurableEntityContext(config, durabilityProvider, this.EntityId, this); }
private static string EntityContextToString(DurableEntityContext arg) { // assemble the operation batch information var operationBatch = new JArray(); foreach (var operation in arg.OperationBatch) { operationBatch.Add(new JObject( new JProperty("name", operation.Operation), new JProperty("input", operation.Input), new JProperty("isSignal", operation.IsSignal))); } // assemble the entity state information var contextObject = new JObject( new JProperty("self", new JObject( new JProperty("name", arg.Self.EntityName), new JProperty("key", arg.Self.EntityKey))), new JProperty("exists", arg.State.EntityExists), new JProperty("state", arg.State.EntityState), new JProperty("batch", operationBatch)); return(contextObject.ToString()); }
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(); }