From ab590723a665d875eefc9f0223c521b00404377a Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Sun, 1 Mar 2026 08:22:13 -0800 Subject: [PATCH 01/10] Propagate trace context for entity operations Entity operations triggered from orchestrations don't propagate distributed trace context because SendEntityMessageAction lacks a parentTraceContext field. This causes entity operations to appear disconnected from their parent orchestration traces. Changes: - Vendor updated proto with parentTraceContext field on SendEntityMessageAction (see https://github.com/microsoft/durabletask-protobuf/pull/64) - Set ParentTraceContext in ProtoUtils.ConstructOrchestratorResponse for entity message actions (matching existing behavior for tasks and sub-orchestrations) - Add unit tests for entity trace context propagation Note: A corresponding server-side change is also needed in the Durable Task Scheduler backend to propagate SendEntityMessageAction.ParentTraceContext to OperationRequest.traceContext on entity work items. Fixes #653 --- src/Grpc/orchestrator_service.proto | 2 + src/Shared/Grpc/ProtoUtils.cs | 6 +- .../Grpc.Tests/ProtoUtilsTraceContextTests.cs | 271 ++++++++++++++++++ 3 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs diff --git a/src/Grpc/orchestrator_service.proto b/src/Grpc/orchestrator_service.proto index 0c34d986d..2ba4bc5be 100644 --- a/src/Grpc/orchestrator_service.proto +++ b/src/Grpc/orchestrator_service.proto @@ -318,6 +318,8 @@ message SendEntityMessageAction { EntityLockRequestedEvent entityLockRequested = 3; EntityUnlockSentEvent entityUnlockSent = 4; } + + TraceContext parentTraceContext = 5; } message OrchestratorAction { diff --git a/src/Shared/Grpc/ProtoUtils.cs b/src/Shared/Grpc/ProtoUtils.cs index 2412fac37..c7fe00f35 100644 --- a/src/Shared/Grpc/ProtoUtils.cs +++ b/src/Shared/Grpc/ProtoUtils.cs @@ -414,6 +414,8 @@ internal static P.OrchestratorResponse ConstructOrchestratorResponse( sendAction, out string requestId); + sendAction.ParentTraceContext = CreateTraceContext(); + entityConversionState.EntityRequestIds.Add(requestId); switch (sendAction.EntityMessageTypeCase) @@ -1054,7 +1056,7 @@ internal static T Base64Decode(this MessageParser parser, string encodedMessa case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StringValue: string stringValue = value.StringValue; - // If the value starts with the 'dt:' prefix, it may represent a DateTime value — attempt to parse it. + // If the value starts with the 'dt:' prefix, it may represent a DateTime value � attempt to parse it. if (stringValue.StartsWith("dt:", StringComparison.Ordinal)) { if (DateTime.TryParse(stringValue[3..], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTime date)) @@ -1063,7 +1065,7 @@ internal static T Base64Decode(this MessageParser parser, string encodedMessa } } - // If the value starts with the 'dto:' prefix, it may represent a DateTime value — attempt to parse it. + // If the value starts with the 'dto:' prefix, it may represent a DateTime value � attempt to parse it. if (stringValue.StartsWith("dto:", StringComparison.Ordinal)) { if (DateTimeOffset.TryParse(stringValue[4..], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset date)) diff --git a/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs b/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs new file mode 100644 index 000000000..a4c49ffac --- /dev/null +++ b/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs @@ -0,0 +1,271 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics; +using DurableTask.Core; +using DurableTask.Core.Command; +using Newtonsoft.Json; +using P = Microsoft.DurableTask.Protobuf; + +namespace Microsoft.DurableTask.Worker.Grpc.Tests; + +public class ProtoUtilsTraceContextTests +{ + static readonly ActivitySource TestSource = new(nameof(ProtoUtilsTraceContextTests)); + + [Fact] + public void SendEntityMessage_SignalEntity_SetsParentTraceContext() + { + // Arrange + using ActivityListener listener = CreateListener(); + using Activity? orchestrationActivity = TestSource.StartActivity("TestOrchestration"); + orchestrationActivity.Should().NotBeNull(); + + string requestId = Guid.NewGuid().ToString(); + string entityInstanceId = "@counter@myKey"; + string eventData = JsonConvert.SerializeObject(new + { + op = "increment", + signal = true, + id = requestId, + }); + + SendEventOrchestratorAction sendEventAction = new() + { + Id = 1, + Instance = new OrchestrationInstance { InstanceId = entityInstanceId }, + EventName = "op", + EventData = eventData, + }; + + ProtoUtils.EntityConversionState entityConversionState = new(insertMissingEntityUnlocks: false); + + // Act + P.OrchestratorResponse response = ProtoUtils.ConstructOrchestratorResponse( + instanceId: "test-orchestration", + executionId: "exec-1", + customStatus: null, + actions: [sendEventAction], + completionToken: "token", + entityConversionState: entityConversionState, + orchestrationActivity: orchestrationActivity); + + // Assert + response.Actions.Should().ContainSingle(); + P.OrchestratorAction action = response.Actions[0]; + action.SendEntityMessage.Should().NotBeNull(); + action.SendEntityMessage.EntityOperationSignaled.Should().NotBeNull(); + action.SendEntityMessage.ParentTraceContext.Should().NotBeNull(); + action.SendEntityMessage.ParentTraceContext.TraceParent.Should().NotBeNullOrEmpty(); + action.SendEntityMessage.ParentTraceContext.TraceParent.Should().Contain( + orchestrationActivity!.TraceId.ToString()); + } + + [Fact] + public void SendEntityMessage_CallEntity_SetsParentTraceContext() + { + // Arrange + using ActivityListener listener = CreateListener(); + using Activity? orchestrationActivity = TestSource.StartActivity("TestOrchestration"); + orchestrationActivity.Should().NotBeNull(); + + string requestId = Guid.NewGuid().ToString(); + string entityInstanceId = "@counter@myKey"; + string eventData = JsonConvert.SerializeObject(new + { + op = "get", + signal = false, + id = requestId, + parent = "parent-instance", + }); + + SendEventOrchestratorAction sendEventAction = new() + { + Id = 1, + Instance = new OrchestrationInstance { InstanceId = entityInstanceId }, + EventName = "op", + EventData = eventData, + }; + + ProtoUtils.EntityConversionState entityConversionState = new(insertMissingEntityUnlocks: false); + + // Act + P.OrchestratorResponse response = ProtoUtils.ConstructOrchestratorResponse( + instanceId: "test-orchestration", + executionId: "exec-1", + customStatus: null, + actions: [sendEventAction], + completionToken: "token", + entityConversionState: entityConversionState, + orchestrationActivity: orchestrationActivity); + + // Assert + response.Actions.Should().ContainSingle(); + P.OrchestratorAction action = response.Actions[0]; + action.SendEntityMessage.Should().NotBeNull(); + action.SendEntityMessage.EntityOperationCalled.Should().NotBeNull(); + action.SendEntityMessage.ParentTraceContext.Should().NotBeNull(); + action.SendEntityMessage.ParentTraceContext.TraceParent.Should().NotBeNullOrEmpty(); + action.SendEntityMessage.ParentTraceContext.TraceParent.Should().Contain( + orchestrationActivity!.TraceId.ToString()); + } + + [Fact] + public void SendEntityMessage_NoOrchestrationActivity_DoesNotSetParentTraceContext() + { + // Arrange + string requestId = Guid.NewGuid().ToString(); + string entityInstanceId = "@counter@myKey"; + string eventData = JsonConvert.SerializeObject(new + { + op = "increment", + signal = true, + id = requestId, + }); + + SendEventOrchestratorAction sendEventAction = new() + { + Id = 1, + Instance = new OrchestrationInstance { InstanceId = entityInstanceId }, + EventName = "op", + EventData = eventData, + }; + + ProtoUtils.EntityConversionState entityConversionState = new(insertMissingEntityUnlocks: false); + + // Act + P.OrchestratorResponse response = ProtoUtils.ConstructOrchestratorResponse( + instanceId: "test-orchestration", + executionId: "exec-1", + customStatus: null, + actions: [sendEventAction], + completionToken: "token", + entityConversionState: entityConversionState, + orchestrationActivity: null); + + // Assert + response.Actions.Should().ContainSingle(); + P.OrchestratorAction action = response.Actions[0]; + action.SendEntityMessage.Should().NotBeNull(); + action.SendEntityMessage.ParentTraceContext.Should().BeNull(); + } + + [Fact] + public void SendEntityMessage_NoEntityConversionState_SendsAsSendEvent() + { + // Arrange + using ActivityListener listener = CreateListener(); + using Activity? orchestrationActivity = TestSource.StartActivity("TestOrchestration"); + + string requestId = Guid.NewGuid().ToString(); + string entityInstanceId = "@counter@myKey"; + string eventData = JsonConvert.SerializeObject(new + { + op = "increment", + signal = true, + id = requestId, + }); + + SendEventOrchestratorAction sendEventAction = new() + { + Id = 1, + Instance = new OrchestrationInstance { InstanceId = entityInstanceId }, + EventName = "op", + EventData = eventData, + }; + + // Act - no entityConversionState means entity events are NOT converted + P.OrchestratorResponse response = ProtoUtils.ConstructOrchestratorResponse( + instanceId: "test-orchestration", + executionId: "exec-1", + customStatus: null, + actions: [sendEventAction], + completionToken: "token", + entityConversionState: null, + orchestrationActivity: orchestrationActivity); + + // Assert - should be a SendEvent, not SendEntityMessage + response.Actions.Should().ContainSingle(); + P.OrchestratorAction action = response.Actions[0]; + action.SendEvent.Should().NotBeNull(); + action.SendEntityMessage.Should().BeNull(); + } + + [Fact] + public void SendEntityMessage_TraceContextHasUniqueSpanId() + { + // Arrange + using ActivityListener listener = CreateListener(); + using Activity? orchestrationActivity = TestSource.StartActivity("TestOrchestration"); + orchestrationActivity.Should().NotBeNull(); + + string entityInstanceId = "@counter@myKey"; + string eventData1 = JsonConvert.SerializeObject(new + { + op = "increment", + signal = true, + id = Guid.NewGuid().ToString(), + }); + + string eventData2 = JsonConvert.SerializeObject(new + { + op = "increment", + signal = true, + id = Guid.NewGuid().ToString(), + }); + + SendEventOrchestratorAction action1 = new() + { + Id = 1, + Instance = new OrchestrationInstance { InstanceId = entityInstanceId }, + EventName = "op", + EventData = eventData1, + }; + + SendEventOrchestratorAction action2 = new() + { + Id = 2, + Instance = new OrchestrationInstance { InstanceId = entityInstanceId }, + EventName = "op", + EventData = eventData2, + }; + + ProtoUtils.EntityConversionState entityConversionState = new(insertMissingEntityUnlocks: false); + + // Act + P.OrchestratorResponse response = ProtoUtils.ConstructOrchestratorResponse( + instanceId: "test-orchestration", + executionId: "exec-1", + customStatus: null, + actions: [action1, action2], + completionToken: "token", + entityConversionState: entityConversionState, + orchestrationActivity: orchestrationActivity); + + // Assert - each entity message should get a unique span ID + response.Actions.Should().HaveCount(2); + string traceParent1 = response.Actions[0].SendEntityMessage.ParentTraceContext.TraceParent; + string traceParent2 = response.Actions[1].SendEntityMessage.ParentTraceContext.TraceParent; + traceParent1.Should().NotBeNullOrEmpty(); + traceParent2.Should().NotBeNullOrEmpty(); + + // Same trace ID (from orchestration activity) + traceParent1.Should().Contain(orchestrationActivity!.TraceId.ToString()); + traceParent2.Should().Contain(orchestrationActivity.TraceId.ToString()); + + // Different span IDs + traceParent1.Should().NotBe(traceParent2); + } + + static ActivityListener CreateListener() + { + ActivityListener listener = new() + { + ShouldListenTo = _ => true, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + }; + + ActivitySource.AddActivityListener(listener); + return listener; + } +} From d2057e0199051d238695de9b701f99e8a2512ff8 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Sun, 1 Mar 2026 22:05:40 -0800 Subject: [PATCH 02/10] Extract trace context from entity operation events in ToEntityBatchRequest Add parentTraceContext fields to EntityOperationSignaledEvent and EntityOperationCalledEvent proto messages, and extract them in ToEntityBatchRequest to populate OperationRequest.TraceContext. This is the receiving side of the trace context propagation: once the DTS backend populates parentTraceContext on entity operation events (from SendEntityMessageAction.ParentTraceContext), the SDK will correctly extract and use the trace context for entity operations. --- src/Grpc/orchestrator_service.proto | 2 + src/Shared/Grpc/ProtoUtils.cs | 10 ++ .../Grpc.Tests/ProtoUtilsTraceContextTests.cs | 107 ++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/src/Grpc/orchestrator_service.proto b/src/Grpc/orchestrator_service.proto index 2ba4bc5be..cbcd648d2 100644 --- a/src/Grpc/orchestrator_service.proto +++ b/src/Grpc/orchestrator_service.proto @@ -182,6 +182,7 @@ message EntityOperationSignaledEvent { google.protobuf.Timestamp scheduledTime = 3; google.protobuf.StringValue input = 4; google.protobuf.StringValue targetInstanceId = 5; // used only within histories, null in messages + TraceContext parentTraceContext = 6; } message EntityOperationCalledEvent { @@ -192,6 +193,7 @@ message EntityOperationCalledEvent { google.protobuf.StringValue parentInstanceId = 5; // used only within messages, null in histories google.protobuf.StringValue parentExecutionId = 6; // used only within messages, null in histories google.protobuf.StringValue targetInstanceId = 7; // used only within histories, null in messages + TraceContext parentTraceContext = 8; } message EntityLockRequestedEvent { diff --git a/src/Shared/Grpc/ProtoUtils.cs b/src/Shared/Grpc/ProtoUtils.cs index c7fe00f35..2e229d026 100644 --- a/src/Shared/Grpc/ProtoUtils.cs +++ b/src/Shared/Grpc/ProtoUtils.cs @@ -638,6 +638,11 @@ internal static void ToEntityBatchRequest( Id = Guid.Parse(op.EntityOperationSignaled.RequestId), Operation = op.EntityOperationSignaled.Operation, Input = op.EntityOperationSignaled.Input, + TraceContext = op.EntityOperationSignaled.ParentTraceContext != null + ? new DistributedTraceContext( + op.EntityOperationSignaled.ParentTraceContext.TraceParent, + op.EntityOperationSignaled.ParentTraceContext.TraceState) + : null, }); operationInfos.Add(new P.OperationInfo { @@ -652,6 +657,11 @@ internal static void ToEntityBatchRequest( Id = Guid.Parse(op.EntityOperationCalled.RequestId), Operation = op.EntityOperationCalled.Operation, Input = op.EntityOperationCalled.Input, + TraceContext = op.EntityOperationCalled.ParentTraceContext != null + ? new DistributedTraceContext( + op.EntityOperationCalled.ParentTraceContext.TraceParent, + op.EntityOperationCalled.ParentTraceContext.TraceState) + : null, }); operationInfos.Add(new P.OperationInfo { diff --git a/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs b/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs index a4c49ffac..d130c54ac 100644 --- a/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs +++ b/test/Worker/Grpc.Tests/ProtoUtilsTraceContextTests.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using DurableTask.Core; using DurableTask.Core.Command; +using DurableTask.Core.Entities.OperationFormat; using Newtonsoft.Json; using P = Microsoft.DurableTask.Protobuf; @@ -268,4 +269,110 @@ static ActivityListener CreateListener() ActivitySource.AddActivityListener(listener); return listener; } + + [Fact] + public void ToEntityBatchRequest_SignalEntity_ExtractsTraceContext() + { + // Arrange + string traceParent = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"; + string traceState = "vendor=value"; + + P.EntityRequest entityRequest = new() + { + InstanceId = "@counter@myKey", + OperationRequests = + { + new P.HistoryEvent + { + EntityOperationSignaled = new P.EntityOperationSignaledEvent + { + RequestId = Guid.NewGuid().ToString(), + Operation = "increment", + ParentTraceContext = new P.TraceContext + { + TraceParent = traceParent, + TraceState = traceState, + }, + }, + }, + }, + }; + + // Act + entityRequest.ToEntityBatchRequest(out EntityBatchRequest batchRequest, out _); + + // Assert + batchRequest.Operations.Should().ContainSingle(); + batchRequest.Operations[0].TraceContext.Should().NotBeNull(); + batchRequest.Operations[0].TraceContext!.TraceParent.Should().Be(traceParent); + batchRequest.Operations[0].TraceContext!.TraceState.Should().Be(traceState); + } + + [Fact] + public void ToEntityBatchRequest_CallEntity_ExtractsTraceContext() + { + // Arrange + string traceParent = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"; + string traceState = "vendor=value"; + + P.EntityRequest entityRequest = new() + { + InstanceId = "@counter@myKey", + OperationRequests = + { + new P.HistoryEvent + { + EntityOperationCalled = new P.EntityOperationCalledEvent + { + RequestId = Guid.NewGuid().ToString(), + Operation = "get", + ParentInstanceId = "parent-instance", + ParentExecutionId = "parent-exec", + ParentTraceContext = new P.TraceContext + { + TraceParent = traceParent, + TraceState = traceState, + }, + }, + }, + }, + }; + + // Act + entityRequest.ToEntityBatchRequest(out EntityBatchRequest batchRequest, out _); + + // Assert + batchRequest.Operations.Should().ContainSingle(); + batchRequest.Operations[0].TraceContext.Should().NotBeNull(); + batchRequest.Operations[0].TraceContext!.TraceParent.Should().Be(traceParent); + batchRequest.Operations[0].TraceContext!.TraceState.Should().Be(traceState); + } + + [Fact] + public void ToEntityBatchRequest_NoTraceContext_LeavesTraceContextNull() + { + // Arrange + P.EntityRequest entityRequest = new() + { + InstanceId = "@counter@myKey", + OperationRequests = + { + new P.HistoryEvent + { + EntityOperationSignaled = new P.EntityOperationSignaledEvent + { + RequestId = Guid.NewGuid().ToString(), + Operation = "increment", + }, + }, + }, + }; + + // Act + entityRequest.ToEntityBatchRequest(out EntityBatchRequest batchRequest, out _); + + // Assert + batchRequest.Operations.Should().ContainSingle(); + batchRequest.Operations[0].TraceContext.Should().BeNull(); + } } From 2df4cdd59adbcab677439677f4449ffc51c4d85c Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Tue, 3 Mar 2026 14:39:45 -0800 Subject: [PATCH 03/10] Fix unrelated character encoding issue --- src/Shared/Grpc/ProtoUtils.cs | 2506 ++++++++++++++++----------------- 1 file changed, 1253 insertions(+), 1253 deletions(-) diff --git a/src/Shared/Grpc/ProtoUtils.cs b/src/Shared/Grpc/ProtoUtils.cs index 2e229d026..bd0ccf2ab 100644 --- a/src/Shared/Grpc/ProtoUtils.cs +++ b/src/Shared/Grpc/ProtoUtils.cs @@ -1,1072 +1,1072 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Buffers; -using System.Buffers.Text; -using System.Collections; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; -using System.Text.Json; -using DurableTask.Core; -using DurableTask.Core.Command; -using DurableTask.Core.Entities; -using DurableTask.Core.Entities.OperationFormat; -using DurableTask.Core.History; -using DurableTask.Core.Tracing; -using Google.Protobuf; -using Google.Protobuf.Collections; -using Google.Protobuf.WellKnownTypes; -using DTCore = DurableTask.Core; -using P = Microsoft.DurableTask.Protobuf; -using TraceHelper = Microsoft.DurableTask.Tracing.TraceHelper; - -namespace Microsoft.DurableTask; - -/// -/// Protobuf utilities and helpers. -/// -static class ProtoUtils -{ - /// - /// Converts a history event from to . - /// - /// The proto history event to converter. - /// The converted history event. - /// When the provided history event type is not supported. - internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto) - { - return ConvertHistoryEvent(proto, conversionState: null); - } - - /// - /// Converts a history event from to , and performs - /// stateful conversions of entity-related events. - /// - /// The proto history event to converter. - /// State needed for converting entity-related history entries and actions. - /// The converted history event. - /// When the provided history event type is not supported. - internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto, EntityConversionState? conversionState) - { - Check.NotNull(proto); - HistoryEvent historyEvent; - switch (proto.EventTypeCase) - { - case P.HistoryEvent.EventTypeOneofCase.ContinueAsNew: - historyEvent = new ContinueAsNewEvent(proto.EventId, proto.ContinueAsNew.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionStarted: - OrchestrationInstance instance = proto.ExecutionStarted.OrchestrationInstance.ToCore(); - conversionState?.SetOrchestrationInstance(instance); - historyEvent = new ExecutionStartedEvent(proto.EventId, proto.ExecutionStarted.Input) - { - Name = proto.ExecutionStarted.Name, - Version = proto.ExecutionStarted.Version, - OrchestrationInstance = instance, - Tags = proto.ExecutionStarted.Tags, - ParentInstance = proto.ExecutionStarted.ParentInstance == null ? null : new ParentInstance - { - Name = proto.ExecutionStarted.ParentInstance.Name, - Version = proto.ExecutionStarted.ParentInstance.Version, - OrchestrationInstance = proto.ExecutionStarted.ParentInstance.OrchestrationInstance.ToCore(), - TaskScheduleId = proto.ExecutionStarted.ParentInstance.TaskScheduledId, - }, - ScheduledStartTime = proto.ExecutionStarted.ScheduledStartTimestamp?.ToDateTime(), - }; - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionCompleted: - historyEvent = new ExecutionCompletedEvent( - proto.EventId, - proto.ExecutionCompleted.Result, +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Buffers; +using System.Buffers.Text; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using System.Text.Json; +using DurableTask.Core; +using DurableTask.Core.Command; +using DurableTask.Core.Entities; +using DurableTask.Core.Entities.OperationFormat; +using DurableTask.Core.History; +using DurableTask.Core.Tracing; +using Google.Protobuf; +using Google.Protobuf.Collections; +using Google.Protobuf.WellKnownTypes; +using DTCore = DurableTask.Core; +using P = Microsoft.DurableTask.Protobuf; +using TraceHelper = Microsoft.DurableTask.Tracing.TraceHelper; + +namespace Microsoft.DurableTask; + +/// +/// Protobuf utilities and helpers. +/// +static class ProtoUtils +{ + /// + /// Converts a history event from to . + /// + /// The proto history event to converter. + /// The converted history event. + /// When the provided history event type is not supported. + internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto) + { + return ConvertHistoryEvent(proto, conversionState: null); + } + + /// + /// Converts a history event from to , and performs + /// stateful conversions of entity-related events. + /// + /// The proto history event to converter. + /// State needed for converting entity-related history entries and actions. + /// The converted history event. + /// When the provided history event type is not supported. + internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto, EntityConversionState? conversionState) + { + Check.NotNull(proto); + HistoryEvent historyEvent; + switch (proto.EventTypeCase) + { + case P.HistoryEvent.EventTypeOneofCase.ContinueAsNew: + historyEvent = new ContinueAsNewEvent(proto.EventId, proto.ContinueAsNew.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionStarted: + OrchestrationInstance instance = proto.ExecutionStarted.OrchestrationInstance.ToCore(); + conversionState?.SetOrchestrationInstance(instance); + historyEvent = new ExecutionStartedEvent(proto.EventId, proto.ExecutionStarted.Input) + { + Name = proto.ExecutionStarted.Name, + Version = proto.ExecutionStarted.Version, + OrchestrationInstance = instance, + Tags = proto.ExecutionStarted.Tags, + ParentInstance = proto.ExecutionStarted.ParentInstance == null ? null : new ParentInstance + { + Name = proto.ExecutionStarted.ParentInstance.Name, + Version = proto.ExecutionStarted.ParentInstance.Version, + OrchestrationInstance = proto.ExecutionStarted.ParentInstance.OrchestrationInstance.ToCore(), + TaskScheduleId = proto.ExecutionStarted.ParentInstance.TaskScheduledId, + }, + ScheduledStartTime = proto.ExecutionStarted.ScheduledStartTimestamp?.ToDateTime(), + }; + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionCompleted: + historyEvent = new ExecutionCompletedEvent( + proto.EventId, + proto.ExecutionCompleted.Result, proto.ExecutionCompleted.OrchestrationStatus.ToCore(), - proto.ExecutionCompleted.FailureDetails.ToCore()); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionTerminated: - historyEvent = new ExecutionTerminatedEvent(proto.EventId, proto.ExecutionTerminated.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionSuspended: - historyEvent = new ExecutionSuspendedEvent(proto.EventId, proto.ExecutionSuspended.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionResumed: - historyEvent = new ExecutionResumedEvent(proto.EventId, proto.ExecutionResumed.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.TaskScheduled: - historyEvent = new TaskScheduledEvent( - proto.EventId, - proto.TaskScheduled.Name, - proto.TaskScheduled.Version, - proto.TaskScheduled.Input) - { - Tags = proto.TaskScheduled.Tags, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.TaskCompleted: - historyEvent = new TaskCompletedEvent( - proto.EventId, - proto.TaskCompleted.TaskScheduledId, - proto.TaskCompleted.Result); - break; - case P.HistoryEvent.EventTypeOneofCase.TaskFailed: - historyEvent = new TaskFailedEvent( - proto.EventId, - proto.TaskFailed.TaskScheduledId, - reason: null, /* not supported */ - details: null, /* not supported */ - proto.TaskFailed.FailureDetails.ToCore()); - break; - case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCreated: - historyEvent = new SubOrchestrationInstanceCreatedEvent(proto.EventId) - { - Input = proto.SubOrchestrationInstanceCreated.Input, - InstanceId = proto.SubOrchestrationInstanceCreated.InstanceId, - Name = proto.SubOrchestrationInstanceCreated.Name, - Version = proto.SubOrchestrationInstanceCreated.Version, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCompleted: - historyEvent = new SubOrchestrationInstanceCompletedEvent( - proto.EventId, - proto.SubOrchestrationInstanceCompleted.TaskScheduledId, - proto.SubOrchestrationInstanceCompleted.Result); - break; - case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceFailed: - historyEvent = new SubOrchestrationInstanceFailedEvent( - proto.EventId, - proto.SubOrchestrationInstanceFailed.TaskScheduledId, - reason: null /* not supported */, - details: null /* not supported */, - proto.SubOrchestrationInstanceFailed.FailureDetails.ToCore()); - break; - case P.HistoryEvent.EventTypeOneofCase.TimerCreated: - historyEvent = new TimerCreatedEvent( - proto.EventId, - proto.TimerCreated.FireAt.ToDateTime()); - break; - case P.HistoryEvent.EventTypeOneofCase.TimerFired: - historyEvent = new TimerFiredEvent( - eventId: -1, - proto.TimerFired.FireAt.ToDateTime()) - { - TimerId = proto.TimerFired.TimerId, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.OrchestratorStarted: - historyEvent = new OrchestratorStartedEvent(proto.EventId); - break; - case P.HistoryEvent.EventTypeOneofCase.OrchestratorCompleted: - historyEvent = new OrchestratorCompletedEvent(proto.EventId); - break; - case P.HistoryEvent.EventTypeOneofCase.EventSent: - historyEvent = new EventSentEvent(proto.EventId) - { - InstanceId = proto.EventSent.InstanceId, - Name = proto.EventSent.Name, - Input = proto.EventSent.Input, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.EventRaised: - historyEvent = new EventRaisedEvent(proto.EventId, proto.EventRaised.Input) - { - Name = proto.EventRaised.Name, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationCalled: - historyEvent = EntityConversions.EncodeOperationCalled(proto, conversionState!.CurrentInstance); - conversionState?.EntityRequestIds.Add(proto.EntityOperationCalled.RequestId); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationSignaled: - historyEvent = EntityConversions.EncodeOperationSignaled(proto); - conversionState?.EntityRequestIds.Add(proto.EntityOperationSignaled.RequestId); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityLockRequested: - historyEvent = EntityConversions.EncodeLockRequested(proto, conversionState!.CurrentInstance); - conversionState?.AddUnlockObligations(proto.EntityLockRequested); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityUnlockSent: - historyEvent = EntityConversions.EncodeUnlockSent(proto, conversionState!.CurrentInstance); - conversionState?.RemoveUnlockObligation(proto.EntityUnlockSent.TargetInstanceId); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityLockGranted: - historyEvent = EntityConversions.EncodeLockGranted(proto); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationCompleted: - historyEvent = EntityConversions.EncodeOperationCompleted(proto); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationFailed: - historyEvent = EntityConversions.EncodeOperationFailed(proto); - break; - case P.HistoryEvent.EventTypeOneofCase.GenericEvent: - historyEvent = new GenericEvent(proto.EventId, proto.GenericEvent.Data); - break; - case P.HistoryEvent.EventTypeOneofCase.HistoryState: - historyEvent = new HistoryStateEvent( - proto.EventId, - new OrchestrationState - { - OrchestrationInstance = new OrchestrationInstance - { - InstanceId = proto.HistoryState.OrchestrationState.InstanceId, - }, - Name = proto.HistoryState.OrchestrationState.Name, - Version = proto.HistoryState.OrchestrationState.Version, - ScheduledStartTime = proto.HistoryState.OrchestrationState.ScheduledStartTimestamp.ToDateTime(), - CreatedTime = proto.HistoryState.OrchestrationState.CreatedTimestamp.ToDateTime(), - LastUpdatedTime = proto.HistoryState.OrchestrationState.LastUpdatedTimestamp.ToDateTime(), - Input = proto.HistoryState.OrchestrationState.Input, - Output = proto.HistoryState.OrchestrationState.Output, - Status = proto.HistoryState.OrchestrationState.CustomStatus, - Tags = proto.HistoryState.OrchestrationState.Tags, - }); - break; + proto.ExecutionCompleted.FailureDetails.ToCore()); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionTerminated: + historyEvent = new ExecutionTerminatedEvent(proto.EventId, proto.ExecutionTerminated.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionSuspended: + historyEvent = new ExecutionSuspendedEvent(proto.EventId, proto.ExecutionSuspended.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionResumed: + historyEvent = new ExecutionResumedEvent(proto.EventId, proto.ExecutionResumed.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.TaskScheduled: + historyEvent = new TaskScheduledEvent( + proto.EventId, + proto.TaskScheduled.Name, + proto.TaskScheduled.Version, + proto.TaskScheduled.Input) + { + Tags = proto.TaskScheduled.Tags, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.TaskCompleted: + historyEvent = new TaskCompletedEvent( + proto.EventId, + proto.TaskCompleted.TaskScheduledId, + proto.TaskCompleted.Result); + break; + case P.HistoryEvent.EventTypeOneofCase.TaskFailed: + historyEvent = new TaskFailedEvent( + proto.EventId, + proto.TaskFailed.TaskScheduledId, + reason: null, /* not supported */ + details: null, /* not supported */ + proto.TaskFailed.FailureDetails.ToCore()); + break; + case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCreated: + historyEvent = new SubOrchestrationInstanceCreatedEvent(proto.EventId) + { + Input = proto.SubOrchestrationInstanceCreated.Input, + InstanceId = proto.SubOrchestrationInstanceCreated.InstanceId, + Name = proto.SubOrchestrationInstanceCreated.Name, + Version = proto.SubOrchestrationInstanceCreated.Version, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCompleted: + historyEvent = new SubOrchestrationInstanceCompletedEvent( + proto.EventId, + proto.SubOrchestrationInstanceCompleted.TaskScheduledId, + proto.SubOrchestrationInstanceCompleted.Result); + break; + case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceFailed: + historyEvent = new SubOrchestrationInstanceFailedEvent( + proto.EventId, + proto.SubOrchestrationInstanceFailed.TaskScheduledId, + reason: null /* not supported */, + details: null /* not supported */, + proto.SubOrchestrationInstanceFailed.FailureDetails.ToCore()); + break; + case P.HistoryEvent.EventTypeOneofCase.TimerCreated: + historyEvent = new TimerCreatedEvent( + proto.EventId, + proto.TimerCreated.FireAt.ToDateTime()); + break; + case P.HistoryEvent.EventTypeOneofCase.TimerFired: + historyEvent = new TimerFiredEvent( + eventId: -1, + proto.TimerFired.FireAt.ToDateTime()) + { + TimerId = proto.TimerFired.TimerId, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.OrchestratorStarted: + historyEvent = new OrchestratorStartedEvent(proto.EventId); + break; + case P.HistoryEvent.EventTypeOneofCase.OrchestratorCompleted: + historyEvent = new OrchestratorCompletedEvent(proto.EventId); + break; + case P.HistoryEvent.EventTypeOneofCase.EventSent: + historyEvent = new EventSentEvent(proto.EventId) + { + InstanceId = proto.EventSent.InstanceId, + Name = proto.EventSent.Name, + Input = proto.EventSent.Input, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.EventRaised: + historyEvent = new EventRaisedEvent(proto.EventId, proto.EventRaised.Input) + { + Name = proto.EventRaised.Name, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationCalled: + historyEvent = EntityConversions.EncodeOperationCalled(proto, conversionState!.CurrentInstance); + conversionState?.EntityRequestIds.Add(proto.EntityOperationCalled.RequestId); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationSignaled: + historyEvent = EntityConversions.EncodeOperationSignaled(proto); + conversionState?.EntityRequestIds.Add(proto.EntityOperationSignaled.RequestId); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityLockRequested: + historyEvent = EntityConversions.EncodeLockRequested(proto, conversionState!.CurrentInstance); + conversionState?.AddUnlockObligations(proto.EntityLockRequested); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityUnlockSent: + historyEvent = EntityConversions.EncodeUnlockSent(proto, conversionState!.CurrentInstance); + conversionState?.RemoveUnlockObligation(proto.EntityUnlockSent.TargetInstanceId); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityLockGranted: + historyEvent = EntityConversions.EncodeLockGranted(proto); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationCompleted: + historyEvent = EntityConversions.EncodeOperationCompleted(proto); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationFailed: + historyEvent = EntityConversions.EncodeOperationFailed(proto); + break; + case P.HistoryEvent.EventTypeOneofCase.GenericEvent: + historyEvent = new GenericEvent(proto.EventId, proto.GenericEvent.Data); + break; + case P.HistoryEvent.EventTypeOneofCase.HistoryState: + historyEvent = new HistoryStateEvent( + proto.EventId, + new OrchestrationState + { + OrchestrationInstance = new OrchestrationInstance + { + InstanceId = proto.HistoryState.OrchestrationState.InstanceId, + }, + Name = proto.HistoryState.OrchestrationState.Name, + Version = proto.HistoryState.OrchestrationState.Version, + ScheduledStartTime = proto.HistoryState.OrchestrationState.ScheduledStartTimestamp.ToDateTime(), + CreatedTime = proto.HistoryState.OrchestrationState.CreatedTimestamp.ToDateTime(), + LastUpdatedTime = proto.HistoryState.OrchestrationState.LastUpdatedTimestamp.ToDateTime(), + Input = proto.HistoryState.OrchestrationState.Input, + Output = proto.HistoryState.OrchestrationState.Output, + Status = proto.HistoryState.OrchestrationState.CustomStatus, + Tags = proto.HistoryState.OrchestrationState.Tags, + }); + break; case P.HistoryEvent.EventTypeOneofCase.ExecutionRewound: historyEvent = new ExecutionRewoundEvent(proto.EventId); break; - default: - throw new NotSupportedException($"Deserialization of {proto.EventTypeCase} is not supported."); - } - - historyEvent.Timestamp = proto.Timestamp.ToDateTime(); - return historyEvent; - } - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp ToTimestamp(this DateTime dateTime) - { - // The protobuf libraries require timestamps to be in UTC - if (dateTime.Kind == DateTimeKind.Unspecified) - { - dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); - } - else if (dateTime.Kind == DateTimeKind.Local) - { - dateTime = dateTime.ToUniversalTime(); - } - - return Timestamp.FromDateTime(dateTime); - } - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp? ToTimestamp(this DateTime? dateTime) - => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp ToTimestamp(this DateTimeOffset dateTime) => Timestamp.FromDateTimeOffset(dateTime); - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp? ToTimestamp(this DateTimeOffset? dateTime) - => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; - - /// - /// Constructs a . - /// - /// The orchestrator instance ID. - /// The orchestrator execution ID. - /// The orchestrator customer status or null if no custom status. - /// The orchestrator actions. - /// - /// The completion token for the work item. It must be the exact same - /// value that was provided by the corresponding that triggered the orchestrator execution. - /// - /// The entity conversion state, or null if no conversion is required. - /// The that represents orchestration execution. - /// Whether or not a history is required to complete the orchestration request and none was provided. - /// The orchestrator response. - /// When an orchestrator action is unknown. - internal static P.OrchestratorResponse ConstructOrchestratorResponse( - string instanceId, - string executionId, - string? customStatus, - IEnumerable? actions, - string completionToken, - EntityConversionState? entityConversionState, - Activity? orchestrationActivity, - bool requiresHistory = false) - { - var response = new P.OrchestratorResponse - { - InstanceId = instanceId, - CustomStatus = customStatus, - CompletionToken = completionToken, - OrchestrationTraceContext = - new() - { - SpanID = orchestrationActivity?.SpanId.ToString(), - SpanStartTime = orchestrationActivity?.StartTimeUtc.ToTimestamp(), - }, - RequiresHistory = requiresHistory, - }; - - // If a history is required and the orchestration request was not completed, then there is no list of actions. - if (requiresHistory) - { - return response; - } - - Check.NotNull(actions); - foreach (OrchestratorAction action in actions) - { - var protoAction = new P.OrchestratorAction { Id = action.Id }; - - P.TraceContext? CreateTraceContext() - { - if (orchestrationActivity is null) - { - return null; - } - - ActivitySpanId clientSpanId = ActivitySpanId.CreateRandom(); - ActivityContext clientActivityContext = new(orchestrationActivity.TraceId, clientSpanId, orchestrationActivity.ActivityTraceFlags, orchestrationActivity.TraceStateString); - - return new P.TraceContext - { - TraceParent = $"00-{clientActivityContext.TraceId}-{clientActivityContext.SpanId}-0{clientActivityContext.TraceFlags:d}", - TraceState = clientActivityContext.TraceState, - }; - } - - switch (action.OrchestratorActionType) - { - case OrchestratorActionType.ScheduleOrchestrator: - var scheduleTaskAction = (ScheduleTaskOrchestratorAction)action; - - protoAction.ScheduleTask = new P.ScheduleTaskAction - { - Name = scheduleTaskAction.Name, - Version = scheduleTaskAction.Version, - Input = scheduleTaskAction.Input, - ParentTraceContext = CreateTraceContext(), - }; - - if (scheduleTaskAction.Tags != null) - { - foreach (KeyValuePair tag in scheduleTaskAction.Tags) - { - protoAction.ScheduleTask.Tags[tag.Key] = tag.Value; - } - } - - break; - case OrchestratorActionType.CreateSubOrchestration: - var subOrchestrationAction = (CreateSubOrchestrationAction)action; - protoAction.CreateSubOrchestration = new P.CreateSubOrchestrationAction - { - Input = subOrchestrationAction.Input, - InstanceId = subOrchestrationAction.InstanceId, - Name = subOrchestrationAction.Name, - Version = subOrchestrationAction.Version, - ParentTraceContext = CreateTraceContext(), + default: + throw new NotSupportedException($"Deserialization of {proto.EventTypeCase} is not supported."); + } + + historyEvent.Timestamp = proto.Timestamp.ToDateTime(); + return historyEvent; + } + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp ToTimestamp(this DateTime dateTime) + { + // The protobuf libraries require timestamps to be in UTC + if (dateTime.Kind == DateTimeKind.Unspecified) + { + dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); + } + else if (dateTime.Kind == DateTimeKind.Local) + { + dateTime = dateTime.ToUniversalTime(); + } + + return Timestamp.FromDateTime(dateTime); + } + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp? ToTimestamp(this DateTime? dateTime) + => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp ToTimestamp(this DateTimeOffset dateTime) => Timestamp.FromDateTimeOffset(dateTime); + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp? ToTimestamp(this DateTimeOffset? dateTime) + => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; + + /// + /// Constructs a . + /// + /// The orchestrator instance ID. + /// The orchestrator execution ID. + /// The orchestrator customer status or null if no custom status. + /// The orchestrator actions. + /// + /// The completion token for the work item. It must be the exact same + /// value that was provided by the corresponding that triggered the orchestrator execution. + /// + /// The entity conversion state, or null if no conversion is required. + /// The that represents orchestration execution. + /// Whether or not a history is required to complete the orchestration request and none was provided. + /// The orchestrator response. + /// When an orchestrator action is unknown. + internal static P.OrchestratorResponse ConstructOrchestratorResponse( + string instanceId, + string executionId, + string? customStatus, + IEnumerable? actions, + string completionToken, + EntityConversionState? entityConversionState, + Activity? orchestrationActivity, + bool requiresHistory = false) + { + var response = new P.OrchestratorResponse + { + InstanceId = instanceId, + CustomStatus = customStatus, + CompletionToken = completionToken, + OrchestrationTraceContext = + new() + { + SpanID = orchestrationActivity?.SpanId.ToString(), + SpanStartTime = orchestrationActivity?.StartTimeUtc.ToTimestamp(), + }, + RequiresHistory = requiresHistory, + }; + + // If a history is required and the orchestration request was not completed, then there is no list of actions. + if (requiresHistory) + { + return response; + } + + Check.NotNull(actions); + foreach (OrchestratorAction action in actions) + { + var protoAction = new P.OrchestratorAction { Id = action.Id }; + + P.TraceContext? CreateTraceContext() + { + if (orchestrationActivity is null) + { + return null; + } + + ActivitySpanId clientSpanId = ActivitySpanId.CreateRandom(); + ActivityContext clientActivityContext = new(orchestrationActivity.TraceId, clientSpanId, orchestrationActivity.ActivityTraceFlags, orchestrationActivity.TraceStateString); + + return new P.TraceContext + { + TraceParent = $"00-{clientActivityContext.TraceId}-{clientActivityContext.SpanId}-0{clientActivityContext.TraceFlags:d}", + TraceState = clientActivityContext.TraceState, + }; + } + + switch (action.OrchestratorActionType) + { + case OrchestratorActionType.ScheduleOrchestrator: + var scheduleTaskAction = (ScheduleTaskOrchestratorAction)action; + + protoAction.ScheduleTask = new P.ScheduleTaskAction + { + Name = scheduleTaskAction.Name, + Version = scheduleTaskAction.Version, + Input = scheduleTaskAction.Input, + ParentTraceContext = CreateTraceContext(), }; - if (subOrchestrationAction.Tags != null) - { - foreach (KeyValuePair tag in subOrchestrationAction.Tags) - { - protoAction.CreateSubOrchestration.Tags[tag.Key] = tag.Value; - } + if (scheduleTaskAction.Tags != null) + { + foreach (KeyValuePair tag in scheduleTaskAction.Tags) + { + protoAction.ScheduleTask.Tags[tag.Key] = tag.Value; + } } - - break; - case OrchestratorActionType.CreateTimer: - var createTimerAction = (CreateTimerOrchestratorAction)action; - protoAction.CreateTimer = new P.CreateTimerAction - { - FireAt = createTimerAction.FireAt.ToTimestamp(), - }; - break; - case OrchestratorActionType.SendEvent: - var sendEventAction = (SendEventOrchestratorAction)action; - if (sendEventAction.Instance == null) - { - throw new ArgumentException( - $"{nameof(SendEventOrchestratorAction)} cannot have a null Instance property!"); - } - - if (entityConversionState is not null - && DTCore.Common.Entities.IsEntityInstance(sendEventAction.Instance.InstanceId) - && sendEventAction.EventName is not null - && sendEventAction.EventData is not null) - { - P.SendEntityMessageAction sendAction = new P.SendEntityMessageAction(); - protoAction.SendEntityMessage = sendAction; - - EntityConversions.DecodeEntityMessageAction( - sendEventAction.EventName, - sendEventAction.EventData, - sendEventAction.Instance.InstanceId, - sendAction, - out string requestId); - - sendAction.ParentTraceContext = CreateTraceContext(); - - entityConversionState.EntityRequestIds.Add(requestId); - - switch (sendAction.EntityMessageTypeCase) - { - case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityLockRequested: - entityConversionState.AddUnlockObligations(sendAction.EntityLockRequested); - break; - case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityUnlockSent: - entityConversionState.RemoveUnlockObligation(sendAction.EntityUnlockSent.TargetInstanceId); - break; - default: - break; - } - } - else - { - protoAction.SendEvent = new P.SendEventAction - { - Instance = sendEventAction.Instance.ToProtobuf(), - Name = sendEventAction.EventName, - Data = sendEventAction.EventData, - }; - - // Distributed Tracing: start a new trace activity derived from the orchestration - // for an EventRaisedEvent (external event) - using Activity? traceActivity = TraceHelper.StartTraceActivityForEventRaisedFromWorker(sendEventAction, instanceId, executionId); - - traceActivity?.Stop(); - } - - break; - case OrchestratorActionType.OrchestrationComplete: - - if (entityConversionState is not null) - { - // as a precaution, unlock any entities that were not unlocked for some reason, before - // completing the orchestration. - foreach ((string target, string criticalSectionId) in entityConversionState.ResetObligations()) - { - response.Actions.Add(new P.OrchestratorAction - { - Id = action.Id, - SendEntityMessage = new P.SendEntityMessageAction - { - EntityUnlockSent = new P.EntityUnlockSentEvent - { - CriticalSectionId = criticalSectionId, - TargetInstanceId = target, - ParentInstanceId = entityConversionState.CurrentInstance?.InstanceId, - }, - }, - }); - } - } - - var completeAction = (OrchestrationCompleteOrchestratorAction)action; - protoAction.CompleteOrchestration = new P.CompleteOrchestrationAction + + break; + case OrchestratorActionType.CreateSubOrchestration: + var subOrchestrationAction = (CreateSubOrchestrationAction)action; + protoAction.CreateSubOrchestration = new P.CreateSubOrchestrationAction { - CarryoverEvents = { completeAction.CarryoverEvents.Select(ToProtobuf) }, - Details = completeAction.Details, - NewVersion = completeAction.NewVersion, - OrchestrationStatus = completeAction.OrchestrationStatus.ToProtobuf(), - Result = completeAction.Result, + Input = subOrchestrationAction.Input, + InstanceId = subOrchestrationAction.InstanceId, + Name = subOrchestrationAction.Name, + Version = subOrchestrationAction.Version, + ParentTraceContext = CreateTraceContext(), + }; + + if (subOrchestrationAction.Tags != null) + { + foreach (KeyValuePair tag in subOrchestrationAction.Tags) + { + protoAction.CreateSubOrchestration.Tags[tag.Key] = tag.Value; + } + } + + break; + case OrchestratorActionType.CreateTimer: + var createTimerAction = (CreateTimerOrchestratorAction)action; + protoAction.CreateTimer = new P.CreateTimerAction + { + FireAt = createTimerAction.FireAt.ToTimestamp(), + }; + break; + case OrchestratorActionType.SendEvent: + var sendEventAction = (SendEventOrchestratorAction)action; + if (sendEventAction.Instance == null) + { + throw new ArgumentException( + $"{nameof(SendEventOrchestratorAction)} cannot have a null Instance property!"); + } + + if (entityConversionState is not null + && DTCore.Common.Entities.IsEntityInstance(sendEventAction.Instance.InstanceId) + && sendEventAction.EventName is not null + && sendEventAction.EventData is not null) + { + P.SendEntityMessageAction sendAction = new P.SendEntityMessageAction(); + protoAction.SendEntityMessage = sendAction; + + EntityConversions.DecodeEntityMessageAction( + sendEventAction.EventName, + sendEventAction.EventData, + sendEventAction.Instance.InstanceId, + sendAction, + out string requestId); + + sendAction.ParentTraceContext = CreateTraceContext(); + + entityConversionState.EntityRequestIds.Add(requestId); + + switch (sendAction.EntityMessageTypeCase) + { + case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityLockRequested: + entityConversionState.AddUnlockObligations(sendAction.EntityLockRequested); + break; + case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityUnlockSent: + entityConversionState.RemoveUnlockObligation(sendAction.EntityUnlockSent.TargetInstanceId); + break; + default: + break; + } + } + else + { + protoAction.SendEvent = new P.SendEventAction + { + Instance = sendEventAction.Instance.ToProtobuf(), + Name = sendEventAction.EventName, + Data = sendEventAction.EventData, + }; + + // Distributed Tracing: start a new trace activity derived from the orchestration + // for an EventRaisedEvent (external event) + using Activity? traceActivity = TraceHelper.StartTraceActivityForEventRaisedFromWorker(sendEventAction, instanceId, executionId); + + traceActivity?.Stop(); + } + + break; + case OrchestratorActionType.OrchestrationComplete: + + if (entityConversionState is not null) + { + // as a precaution, unlock any entities that were not unlocked for some reason, before + // completing the orchestration. + foreach ((string target, string criticalSectionId) in entityConversionState.ResetObligations()) + { + response.Actions.Add(new P.OrchestratorAction + { + Id = action.Id, + SendEntityMessage = new P.SendEntityMessageAction + { + EntityUnlockSent = new P.EntityUnlockSentEvent + { + CriticalSectionId = criticalSectionId, + TargetInstanceId = target, + ParentInstanceId = entityConversionState.CurrentInstance?.InstanceId, + }, + }, + }); + } + } + + var completeAction = (OrchestrationCompleteOrchestratorAction)action; + protoAction.CompleteOrchestration = new P.CompleteOrchestrationAction + { + CarryoverEvents = { completeAction.CarryoverEvents.Select(ToProtobuf) }, + Details = completeAction.Details, + NewVersion = completeAction.NewVersion, + OrchestrationStatus = completeAction.OrchestrationStatus.ToProtobuf(), + Result = completeAction.Result, }; foreach (KeyValuePair tag in completeAction.Tags) { protoAction.CompleteOrchestration.Tags[tag.Key] = tag.Value; - } - - if (completeAction.OrchestrationStatus == OrchestrationStatus.Failed) - { - protoAction.CompleteOrchestration.FailureDetails = completeAction.FailureDetails.ToProtobuf(); - } - - break; - default: - throw new NotSupportedException($"Unknown orchestrator action: {action.OrchestratorActionType}"); - } - - response.Actions.Add(protoAction); - } - - return response; - } - - /// - /// Converts a to a . - /// - /// The status to convert. - /// The converted status. - internal static OrchestrationStatus ToCore(this P.OrchestrationStatus status) - { - return (OrchestrationStatus)status; - } - - /// - /// Converts a to a . - /// - /// The status to convert. - /// The converted status. - [return: NotNullIfNotNull(nameof(status))] - internal static OrchestrationInstance? ToCore(this P.OrchestrationInstance? status) - { - if (status == null) - { - return null; - } - - return new OrchestrationInstance - { - InstanceId = status.InstanceId, - ExecutionId = status.ExecutionId, - }; - } - - /// - /// Converts a to a . - /// - /// The failure details to convert. - /// The converted failure details. - [return: NotNullIfNotNull(nameof(failureDetails))] - internal static TaskFailureDetails? ToTaskFailureDetails(this P.TaskFailureDetails? failureDetails) - { - if (failureDetails == null) - { - return null; - } - - return new TaskFailureDetails( - failureDetails.ErrorType, - failureDetails.ErrorMessage, - failureDetails.StackTrace, - failureDetails.InnerFailure.ToTaskFailureDetails(), - ConvertProperties(failureDetails.Properties)); - } - - /// - /// Converts a to . - /// - /// The exception to convert. - /// Optional exception properties provider. - /// The task failure details. - [return: NotNullIfNotNull(nameof(e))] - internal static P.TaskFailureDetails? ToTaskFailureDetails(this Exception? e, DTCore.IExceptionPropertiesProvider? exceptionPropertiesProvider = null) - { - if (e == null) - { - return null; - } - - IDictionary? properties = exceptionPropertiesProvider?.GetExceptionProperties(e); - - var taskFailureDetails = new P.TaskFailureDetails - { - ErrorType = e.GetType().FullName, - ErrorMessage = e.Message, - StackTrace = e.StackTrace, - InnerFailure = e.InnerException.ToTaskFailureDetails(exceptionPropertiesProvider), - }; - - if (properties != null) - { - foreach (var kvp in properties) - { - taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); - } - } - - return taskFailureDetails; - } - - /// - /// Converts a to a . - /// - /// The entity batch request to convert. - /// The converted entity batch request. - [return: NotNullIfNotNull(nameof(entityBatchRequest))] - internal static EntityBatchRequest? ToEntityBatchRequest(this P.EntityBatchRequest? entityBatchRequest) - { - if (entityBatchRequest == null) - { - return null; - } - - return new EntityBatchRequest() - { - EntityState = entityBatchRequest.EntityState, - InstanceId = entityBatchRequest.InstanceId, - Operations = entityBatchRequest.Operations.Select(r => r.ToOperationRequest()).ToList(), - }; - } - - /// - /// Converts a to a . - /// - /// The entity request to convert. - /// The converted request. - /// Additional info about each operation, required by DTS. - internal static void ToEntityBatchRequest( - this P.EntityRequest entityRequest, - out EntityBatchRequest batchRequest, - out List operationInfos) - { - batchRequest = new EntityBatchRequest() - { - EntityState = entityRequest.EntityState, - InstanceId = entityRequest.InstanceId, - Operations = [], // operations are added to this collection below - }; - - operationInfos = new(entityRequest.OperationRequests.Count); - - foreach (P.HistoryEvent? op in entityRequest.OperationRequests) - { - if (op.EntityOperationSignaled is not null) - { - batchRequest.Operations.Add(new OperationRequest - { - Id = Guid.Parse(op.EntityOperationSignaled.RequestId), - Operation = op.EntityOperationSignaled.Operation, - Input = op.EntityOperationSignaled.Input, - TraceContext = op.EntityOperationSignaled.ParentTraceContext != null - ? new DistributedTraceContext( - op.EntityOperationSignaled.ParentTraceContext.TraceParent, - op.EntityOperationSignaled.ParentTraceContext.TraceState) - : null, - }); - operationInfos.Add(new P.OperationInfo - { - RequestId = op.EntityOperationSignaled.RequestId, - ResponseDestination = null, // means we don't send back a response to the caller - }); - } - else if (op.EntityOperationCalled is not null) - { - batchRequest.Operations.Add(new OperationRequest - { - Id = Guid.Parse(op.EntityOperationCalled.RequestId), - Operation = op.EntityOperationCalled.Operation, - Input = op.EntityOperationCalled.Input, - TraceContext = op.EntityOperationCalled.ParentTraceContext != null - ? new DistributedTraceContext( - op.EntityOperationCalled.ParentTraceContext.TraceParent, - op.EntityOperationCalled.ParentTraceContext.TraceState) - : null, - }); - operationInfos.Add(new P.OperationInfo - { - RequestId = op.EntityOperationCalled.RequestId, - ResponseDestination = new P.OrchestrationInstance - { - InstanceId = op.EntityOperationCalled.ParentInstanceId, - ExecutionId = op.EntityOperationCalled.ParentExecutionId, - }, - }); - } - } - } - - /// - /// Converts a to a . - /// - /// The operation request to convert. - /// The converted operation request. - [return: NotNullIfNotNull(nameof(operationRequest))] - internal static OperationRequest? ToOperationRequest(this P.OperationRequest? operationRequest) - { - if (operationRequest == null) - { - return null; - } - - return new OperationRequest() - { - Operation = operationRequest.Operation, - Input = operationRequest.Input, - Id = Guid.Parse(operationRequest.RequestId), - TraceContext = operationRequest.TraceContext != null ? - new DistributedTraceContext( - operationRequest.TraceContext.TraceParent, - operationRequest.TraceContext.TraceState) : null, - }; - } - - /// - /// Converts a to a . - /// - /// The operation result to convert. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(operationResult))] - internal static OperationResult? ToOperationResult(this P.OperationResult? operationResult) - { - if (operationResult == null) - { - return null; - } - - switch (operationResult.ResultTypeCase) - { - case P.OperationResult.ResultTypeOneofCase.Success: - return new OperationResult() - { - Result = operationResult.Success.Result, - StartTimeUtc = operationResult.Success.StartTimeUtc?.ToDateTime(), - EndTimeUtc = operationResult.Success.EndTimeUtc?.ToDateTime(), - }; - - case P.OperationResult.ResultTypeOneofCase.Failure: - return new OperationResult() - { - FailureDetails = operationResult.Failure.FailureDetails.ToCore(), - StartTimeUtc = operationResult.Failure.StartTimeUtc?.ToDateTime(), - EndTimeUtc = operationResult.Failure.EndTimeUtc?.ToDateTime(), - }; - - default: - throw new NotSupportedException($"Deserialization of {operationResult.ResultTypeCase} is not supported."); - } - } - - /// - /// Converts a to . - /// - /// The operation result to convert. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(operationResult))] - internal static P.OperationResult? ToOperationResult(this OperationResult? operationResult) - { - if (operationResult == null) - { - return null; - } - - if (operationResult.FailureDetails == null) - { - return new P.OperationResult() - { - Success = new P.OperationResultSuccess() - { - Result = operationResult.Result, - StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), - EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), - }, - }; - } - else - { - return new P.OperationResult() - { - Failure = new P.OperationResultFailure() - { - FailureDetails = ToProtobuf(operationResult.FailureDetails), - StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), - EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), - }, - }; - } - } - - /// - /// Converts a to a . - /// - /// The operation action to convert. - /// The converted operation action. - [return: NotNullIfNotNull(nameof(operationAction))] - internal static OperationAction? ToOperationAction(this P.OperationAction? operationAction) - { - if (operationAction == null) - { - return null; - } - - switch (operationAction.OperationActionTypeCase) - { - case P.OperationAction.OperationActionTypeOneofCase.SendSignal: - - return new SendSignalOperationAction() - { - Name = operationAction.SendSignal.Name, - Input = operationAction.SendSignal.Input, - InstanceId = operationAction.SendSignal.InstanceId, - ScheduledTime = operationAction.SendSignal.ScheduledTime?.ToDateTime(), - RequestTime = operationAction.SendSignal.RequestTime?.ToDateTimeOffset(), - ParentTraceContext = operationAction.SendSignal.ParentTraceContext != null ? - new DistributedTraceContext( - operationAction.SendSignal.ParentTraceContext.TraceParent, - operationAction.SendSignal.ParentTraceContext.TraceState) : null, - }; - - case P.OperationAction.OperationActionTypeOneofCase.StartNewOrchestration: - - return new StartNewOrchestrationOperationAction() - { - Name = operationAction.StartNewOrchestration.Name, - Input = operationAction.StartNewOrchestration.Input, - InstanceId = operationAction.StartNewOrchestration.InstanceId, - Version = operationAction.StartNewOrchestration.Version, - ScheduledStartTime = operationAction.StartNewOrchestration.ScheduledTime?.ToDateTime(), - RequestTime = operationAction.StartNewOrchestration.RequestTime?.ToDateTimeOffset(), - ParentTraceContext = operationAction.StartNewOrchestration.ParentTraceContext != null ? - new DistributedTraceContext( - operationAction.StartNewOrchestration.ParentTraceContext.TraceParent, - operationAction.StartNewOrchestration.ParentTraceContext.TraceState) : null, - }; - default: - throw new NotSupportedException($"Deserialization of {operationAction.OperationActionTypeCase} is not supported."); - } - } - - /// - /// Converts a to . - /// - /// The operation action to convert. - /// The converted operation action. - [return: NotNullIfNotNull(nameof(operationAction))] - internal static P.OperationAction? ToOperationAction(this OperationAction? operationAction) - { - if (operationAction == null) - { - return null; - } - - var action = new P.OperationAction(); - - switch (operationAction) - { - case SendSignalOperationAction sendSignalAction: - - action.SendSignal = new P.SendSignalAction() - { - Name = sendSignalAction.Name, - Input = sendSignalAction.Input, - InstanceId = sendSignalAction.InstanceId, - ScheduledTime = sendSignalAction.ScheduledTime?.ToTimestamp(), - RequestTime = sendSignalAction.RequestTime?.ToTimestamp(), - ParentTraceContext = sendSignalAction.ParentTraceContext != null ? - new P.TraceContext - { - TraceParent = sendSignalAction.ParentTraceContext.TraceParent, - TraceState = sendSignalAction.ParentTraceContext.TraceState, - } - : null, - }; - break; - - case StartNewOrchestrationOperationAction startNewOrchestrationAction: - - action.StartNewOrchestration = new P.StartNewOrchestrationAction() - { - Name = startNewOrchestrationAction.Name, - Input = startNewOrchestrationAction.Input, - Version = startNewOrchestrationAction.Version, - InstanceId = startNewOrchestrationAction.InstanceId, - ScheduledTime = startNewOrchestrationAction.ScheduledStartTime?.ToTimestamp(), - RequestTime = startNewOrchestrationAction.RequestTime?.ToTimestamp(), - ParentTraceContext = startNewOrchestrationAction.ParentTraceContext != null ? - new P.TraceContext - { - TraceParent = startNewOrchestrationAction.ParentTraceContext.TraceParent, - TraceState = startNewOrchestrationAction.ParentTraceContext.TraceState, - } - : null, - }; - break; - } - - return action; - } - - /// - /// Converts a to a . - /// - /// The operation result to convert. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(entityBatchResult))] - internal static EntityBatchResult? ToEntityBatchResult(this P.EntityBatchResult? entityBatchResult) - { - if (entityBatchResult == null) - { - return null; - } - - return new EntityBatchResult() - { - Actions = entityBatchResult.Actions.Select(operationAction => operationAction!.ToOperationAction()).ToList(), - EntityState = entityBatchResult.EntityState, - Results = entityBatchResult.Results.Select(operationResult => operationResult!.ToOperationResult()).ToList(), - FailureDetails = entityBatchResult.FailureDetails.ToCore(), - }; - } - - /// - /// Converts a to . - /// - /// The operation result to convert. - /// The completion token, or null for the older protocol. - /// Additional information about each operation, required by DTS. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(entityBatchResult))] - internal static P.EntityBatchResult? ToEntityBatchResult( - this EntityBatchResult? entityBatchResult, - string? completionToken = null, - IEnumerable? operationInfos = null) - { - if (entityBatchResult == null) - { - return null; - } - - return new P.EntityBatchResult() - { - EntityState = entityBatchResult.EntityState, - FailureDetails = entityBatchResult.FailureDetails.ToProtobuf(), - Actions = { entityBatchResult.Actions?.Select(a => a.ToOperationAction()) ?? [] }, - Results = { entityBatchResult.Results?.Select(a => a.ToOperationResult()) ?? [] }, - CompletionToken = completionToken ?? string.Empty, - OperationInfos = { operationInfos ?? [] }, - }; - } - - /// - /// Converts the gRPC representation of orchestrator entity parameters to the DT.Core representation. - /// - /// The DT.Core representation. - /// The gRPC representation. - [return: NotNullIfNotNull(nameof(parameters))] - internal static TaskOrchestrationEntityParameters? ToCore(this P.OrchestratorEntityParameters? parameters) - { - if (parameters == null) - { - return null; - } - - return new TaskOrchestrationEntityParameters() - { - EntityMessageReorderWindow = parameters.EntityMessageReorderWindow.ToTimeSpan(), - }; - } - - /// - /// Gets the approximate byte count for a . - /// - /// The failure details. - /// The approximate byte count. - internal static int GetApproximateByteCount(this P.TaskFailureDetails failureDetails) - { - // Protobuf strings are always UTF-8: https://developers.google.com/protocol-buffers/docs/proto3#scalar - Encoding encoding = Encoding.UTF8; - - int byteCount = 0; - if (failureDetails.ErrorType != null) - { - byteCount += encoding.GetByteCount(failureDetails.ErrorType); - } - - if (failureDetails.ErrorMessage != null) - { - byteCount += encoding.GetByteCount(failureDetails.ErrorMessage); - } - - if (failureDetails.StackTrace != null) - { - byteCount += encoding.GetByteCount(failureDetails.StackTrace); - } - - if (failureDetails.InnerFailure != null) - { - byteCount += failureDetails.InnerFailure.GetApproximateByteCount(); - } - - return byteCount; - } - - /// - /// Decode a protobuf message from a base64 string. - /// - /// The type to decode to. - /// The message parser. - /// The base64 encoded message. - /// The decoded message. - /// If decoding fails. - internal static T Base64Decode(this MessageParser parser, string encodedMessage) where T : IMessage - { - // Decode the base64 in a way that doesn't allocate a byte[] on each request - int encodedByteCount = Encoding.UTF8.GetByteCount(encodedMessage); - byte[] buffer = ArrayPool.Shared.Rent(encodedByteCount); - try - { - // The Base64 APIs require first converting the string into UTF-8 bytes. We then - // do an in-place conversion from base64 UTF-8 bytes to protobuf bytes so that - // we can finally decode the protobuf request. - Encoding.UTF8.GetBytes(encodedMessage, 0, encodedMessage.Length, buffer, 0); - OperationStatus status = Base64.DecodeFromUtf8InPlace( - buffer.AsSpan(0, encodedByteCount), - out int bytesWritten); - if (status != OperationStatus.Done) - { - throw new ArgumentException( - $"Failed to base64-decode the '{typeof(T).Name}' payload: {status}", nameof(encodedMessage)); - } - - return (T)parser.ParseFrom(buffer, 0, bytesWritten); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - /// - /// Converts a grpc to a . - /// - /// The failure details to convert. - /// The converted failure details. - internal static FailureDetails? ToCore(this P.TaskFailureDetails? failureDetails) - { - if (failureDetails == null) - { - return null; - } - - return new FailureDetails( - failureDetails.ErrorType, - failureDetails.ErrorMessage, - failureDetails.StackTrace, - failureDetails.InnerFailure.ToCore(), - failureDetails.IsNonRetriable, - ConvertProperties(failureDetails.Properties)); - } - - /// - /// Converts a instance to a corresponding C# object. - /// - /// The Protobuf Value to convert. - /// The corresponding C# object. - /// - /// Thrown when the Protobuf Value.KindCase is not one of the supported types. - /// - internal static object? ConvertValueToObject(Google.Protobuf.WellKnownTypes.Value value) - { - switch (value.KindCase) - { - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NullValue: - return null; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NumberValue: - return value.NumberValue; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StringValue: + } + + if (completeAction.OrchestrationStatus == OrchestrationStatus.Failed) + { + protoAction.CompleteOrchestration.FailureDetails = completeAction.FailureDetails.ToProtobuf(); + } + + break; + default: + throw new NotSupportedException($"Unknown orchestrator action: {action.OrchestratorActionType}"); + } + + response.Actions.Add(protoAction); + } + + return response; + } + + /// + /// Converts a to a . + /// + /// The status to convert. + /// The converted status. + internal static OrchestrationStatus ToCore(this P.OrchestrationStatus status) + { + return (OrchestrationStatus)status; + } + + /// + /// Converts a to a . + /// + /// The status to convert. + /// The converted status. + [return: NotNullIfNotNull(nameof(status))] + internal static OrchestrationInstance? ToCore(this P.OrchestrationInstance? status) + { + if (status == null) + { + return null; + } + + return new OrchestrationInstance + { + InstanceId = status.InstanceId, + ExecutionId = status.ExecutionId, + }; + } + + /// + /// Converts a to a . + /// + /// The failure details to convert. + /// The converted failure details. + [return: NotNullIfNotNull(nameof(failureDetails))] + internal static TaskFailureDetails? ToTaskFailureDetails(this P.TaskFailureDetails? failureDetails) + { + if (failureDetails == null) + { + return null; + } + + return new TaskFailureDetails( + failureDetails.ErrorType, + failureDetails.ErrorMessage, + failureDetails.StackTrace, + failureDetails.InnerFailure.ToTaskFailureDetails(), + ConvertProperties(failureDetails.Properties)); + } + + /// + /// Converts a to . + /// + /// The exception to convert. + /// Optional exception properties provider. + /// The task failure details. + [return: NotNullIfNotNull(nameof(e))] + internal static P.TaskFailureDetails? ToTaskFailureDetails(this Exception? e, DTCore.IExceptionPropertiesProvider? exceptionPropertiesProvider = null) + { + if (e == null) + { + return null; + } + + IDictionary? properties = exceptionPropertiesProvider?.GetExceptionProperties(e); + + var taskFailureDetails = new P.TaskFailureDetails + { + ErrorType = e.GetType().FullName, + ErrorMessage = e.Message, + StackTrace = e.StackTrace, + InnerFailure = e.InnerException.ToTaskFailureDetails(exceptionPropertiesProvider), + }; + + if (properties != null) + { + foreach (var kvp in properties) + { + taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); + } + } + + return taskFailureDetails; + } + + /// + /// Converts a to a . + /// + /// The entity batch request to convert. + /// The converted entity batch request. + [return: NotNullIfNotNull(nameof(entityBatchRequest))] + internal static EntityBatchRequest? ToEntityBatchRequest(this P.EntityBatchRequest? entityBatchRequest) + { + if (entityBatchRequest == null) + { + return null; + } + + return new EntityBatchRequest() + { + EntityState = entityBatchRequest.EntityState, + InstanceId = entityBatchRequest.InstanceId, + Operations = entityBatchRequest.Operations.Select(r => r.ToOperationRequest()).ToList(), + }; + } + + /// + /// Converts a to a . + /// + /// The entity request to convert. + /// The converted request. + /// Additional info about each operation, required by DTS. + internal static void ToEntityBatchRequest( + this P.EntityRequest entityRequest, + out EntityBatchRequest batchRequest, + out List operationInfos) + { + batchRequest = new EntityBatchRequest() + { + EntityState = entityRequest.EntityState, + InstanceId = entityRequest.InstanceId, + Operations = [], // operations are added to this collection below + }; + + operationInfos = new(entityRequest.OperationRequests.Count); + + foreach (P.HistoryEvent? op in entityRequest.OperationRequests) + { + if (op.EntityOperationSignaled is not null) + { + batchRequest.Operations.Add(new OperationRequest + { + Id = Guid.Parse(op.EntityOperationSignaled.RequestId), + Operation = op.EntityOperationSignaled.Operation, + Input = op.EntityOperationSignaled.Input, + TraceContext = op.EntityOperationSignaled.ParentTraceContext != null + ? new DistributedTraceContext( + op.EntityOperationSignaled.ParentTraceContext.TraceParent, + op.EntityOperationSignaled.ParentTraceContext.TraceState) + : null, + }); + operationInfos.Add(new P.OperationInfo + { + RequestId = op.EntityOperationSignaled.RequestId, + ResponseDestination = null, // means we don't send back a response to the caller + }); + } + else if (op.EntityOperationCalled is not null) + { + batchRequest.Operations.Add(new OperationRequest + { + Id = Guid.Parse(op.EntityOperationCalled.RequestId), + Operation = op.EntityOperationCalled.Operation, + Input = op.EntityOperationCalled.Input, + TraceContext = op.EntityOperationCalled.ParentTraceContext != null + ? new DistributedTraceContext( + op.EntityOperationCalled.ParentTraceContext.TraceParent, + op.EntityOperationCalled.ParentTraceContext.TraceState) + : null, + }); + operationInfos.Add(new P.OperationInfo + { + RequestId = op.EntityOperationCalled.RequestId, + ResponseDestination = new P.OrchestrationInstance + { + InstanceId = op.EntityOperationCalled.ParentInstanceId, + ExecutionId = op.EntityOperationCalled.ParentExecutionId, + }, + }); + } + } + } + + /// + /// Converts a to a . + /// + /// The operation request to convert. + /// The converted operation request. + [return: NotNullIfNotNull(nameof(operationRequest))] + internal static OperationRequest? ToOperationRequest(this P.OperationRequest? operationRequest) + { + if (operationRequest == null) + { + return null; + } + + return new OperationRequest() + { + Operation = operationRequest.Operation, + Input = operationRequest.Input, + Id = Guid.Parse(operationRequest.RequestId), + TraceContext = operationRequest.TraceContext != null ? + new DistributedTraceContext( + operationRequest.TraceContext.TraceParent, + operationRequest.TraceContext.TraceState) : null, + }; + } + + /// + /// Converts a to a . + /// + /// The operation result to convert. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(operationResult))] + internal static OperationResult? ToOperationResult(this P.OperationResult? operationResult) + { + if (operationResult == null) + { + return null; + } + + switch (operationResult.ResultTypeCase) + { + case P.OperationResult.ResultTypeOneofCase.Success: + return new OperationResult() + { + Result = operationResult.Success.Result, + StartTimeUtc = operationResult.Success.StartTimeUtc?.ToDateTime(), + EndTimeUtc = operationResult.Success.EndTimeUtc?.ToDateTime(), + }; + + case P.OperationResult.ResultTypeOneofCase.Failure: + return new OperationResult() + { + FailureDetails = operationResult.Failure.FailureDetails.ToCore(), + StartTimeUtc = operationResult.Failure.StartTimeUtc?.ToDateTime(), + EndTimeUtc = operationResult.Failure.EndTimeUtc?.ToDateTime(), + }; + + default: + throw new NotSupportedException($"Deserialization of {operationResult.ResultTypeCase} is not supported."); + } + } + + /// + /// Converts a to . + /// + /// The operation result to convert. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(operationResult))] + internal static P.OperationResult? ToOperationResult(this OperationResult? operationResult) + { + if (operationResult == null) + { + return null; + } + + if (operationResult.FailureDetails == null) + { + return new P.OperationResult() + { + Success = new P.OperationResultSuccess() + { + Result = operationResult.Result, + StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), + EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), + }, + }; + } + else + { + return new P.OperationResult() + { + Failure = new P.OperationResultFailure() + { + FailureDetails = ToProtobuf(operationResult.FailureDetails), + StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), + EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), + }, + }; + } + } + + /// + /// Converts a to a . + /// + /// The operation action to convert. + /// The converted operation action. + [return: NotNullIfNotNull(nameof(operationAction))] + internal static OperationAction? ToOperationAction(this P.OperationAction? operationAction) + { + if (operationAction == null) + { + return null; + } + + switch (operationAction.OperationActionTypeCase) + { + case P.OperationAction.OperationActionTypeOneofCase.SendSignal: + + return new SendSignalOperationAction() + { + Name = operationAction.SendSignal.Name, + Input = operationAction.SendSignal.Input, + InstanceId = operationAction.SendSignal.InstanceId, + ScheduledTime = operationAction.SendSignal.ScheduledTime?.ToDateTime(), + RequestTime = operationAction.SendSignal.RequestTime?.ToDateTimeOffset(), + ParentTraceContext = operationAction.SendSignal.ParentTraceContext != null ? + new DistributedTraceContext( + operationAction.SendSignal.ParentTraceContext.TraceParent, + operationAction.SendSignal.ParentTraceContext.TraceState) : null, + }; + + case P.OperationAction.OperationActionTypeOneofCase.StartNewOrchestration: + + return new StartNewOrchestrationOperationAction() + { + Name = operationAction.StartNewOrchestration.Name, + Input = operationAction.StartNewOrchestration.Input, + InstanceId = operationAction.StartNewOrchestration.InstanceId, + Version = operationAction.StartNewOrchestration.Version, + ScheduledStartTime = operationAction.StartNewOrchestration.ScheduledTime?.ToDateTime(), + RequestTime = operationAction.StartNewOrchestration.RequestTime?.ToDateTimeOffset(), + ParentTraceContext = operationAction.StartNewOrchestration.ParentTraceContext != null ? + new DistributedTraceContext( + operationAction.StartNewOrchestration.ParentTraceContext.TraceParent, + operationAction.StartNewOrchestration.ParentTraceContext.TraceState) : null, + }; + default: + throw new NotSupportedException($"Deserialization of {operationAction.OperationActionTypeCase} is not supported."); + } + } + + /// + /// Converts a to . + /// + /// The operation action to convert. + /// The converted operation action. + [return: NotNullIfNotNull(nameof(operationAction))] + internal static P.OperationAction? ToOperationAction(this OperationAction? operationAction) + { + if (operationAction == null) + { + return null; + } + + var action = new P.OperationAction(); + + switch (operationAction) + { + case SendSignalOperationAction sendSignalAction: + + action.SendSignal = new P.SendSignalAction() + { + Name = sendSignalAction.Name, + Input = sendSignalAction.Input, + InstanceId = sendSignalAction.InstanceId, + ScheduledTime = sendSignalAction.ScheduledTime?.ToTimestamp(), + RequestTime = sendSignalAction.RequestTime?.ToTimestamp(), + ParentTraceContext = sendSignalAction.ParentTraceContext != null ? + new P.TraceContext + { + TraceParent = sendSignalAction.ParentTraceContext.TraceParent, + TraceState = sendSignalAction.ParentTraceContext.TraceState, + } + : null, + }; + break; + + case StartNewOrchestrationOperationAction startNewOrchestrationAction: + + action.StartNewOrchestration = new P.StartNewOrchestrationAction() + { + Name = startNewOrchestrationAction.Name, + Input = startNewOrchestrationAction.Input, + Version = startNewOrchestrationAction.Version, + InstanceId = startNewOrchestrationAction.InstanceId, + ScheduledTime = startNewOrchestrationAction.ScheduledStartTime?.ToTimestamp(), + RequestTime = startNewOrchestrationAction.RequestTime?.ToTimestamp(), + ParentTraceContext = startNewOrchestrationAction.ParentTraceContext != null ? + new P.TraceContext + { + TraceParent = startNewOrchestrationAction.ParentTraceContext.TraceParent, + TraceState = startNewOrchestrationAction.ParentTraceContext.TraceState, + } + : null, + }; + break; + } + + return action; + } + + /// + /// Converts a to a . + /// + /// The operation result to convert. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(entityBatchResult))] + internal static EntityBatchResult? ToEntityBatchResult(this P.EntityBatchResult? entityBatchResult) + { + if (entityBatchResult == null) + { + return null; + } + + return new EntityBatchResult() + { + Actions = entityBatchResult.Actions.Select(operationAction => operationAction!.ToOperationAction()).ToList(), + EntityState = entityBatchResult.EntityState, + Results = entityBatchResult.Results.Select(operationResult => operationResult!.ToOperationResult()).ToList(), + FailureDetails = entityBatchResult.FailureDetails.ToCore(), + }; + } + + /// + /// Converts a to . + /// + /// The operation result to convert. + /// The completion token, or null for the older protocol. + /// Additional information about each operation, required by DTS. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(entityBatchResult))] + internal static P.EntityBatchResult? ToEntityBatchResult( + this EntityBatchResult? entityBatchResult, + string? completionToken = null, + IEnumerable? operationInfos = null) + { + if (entityBatchResult == null) + { + return null; + } + + return new P.EntityBatchResult() + { + EntityState = entityBatchResult.EntityState, + FailureDetails = entityBatchResult.FailureDetails.ToProtobuf(), + Actions = { entityBatchResult.Actions?.Select(a => a.ToOperationAction()) ?? [] }, + Results = { entityBatchResult.Results?.Select(a => a.ToOperationResult()) ?? [] }, + CompletionToken = completionToken ?? string.Empty, + OperationInfos = { operationInfos ?? [] }, + }; + } + + /// + /// Converts the gRPC representation of orchestrator entity parameters to the DT.Core representation. + /// + /// The DT.Core representation. + /// The gRPC representation. + [return: NotNullIfNotNull(nameof(parameters))] + internal static TaskOrchestrationEntityParameters? ToCore(this P.OrchestratorEntityParameters? parameters) + { + if (parameters == null) + { + return null; + } + + return new TaskOrchestrationEntityParameters() + { + EntityMessageReorderWindow = parameters.EntityMessageReorderWindow.ToTimeSpan(), + }; + } + + /// + /// Gets the approximate byte count for a . + /// + /// The failure details. + /// The approximate byte count. + internal static int GetApproximateByteCount(this P.TaskFailureDetails failureDetails) + { + // Protobuf strings are always UTF-8: https://developers.google.com/protocol-buffers/docs/proto3#scalar + Encoding encoding = Encoding.UTF8; + + int byteCount = 0; + if (failureDetails.ErrorType != null) + { + byteCount += encoding.GetByteCount(failureDetails.ErrorType); + } + + if (failureDetails.ErrorMessage != null) + { + byteCount += encoding.GetByteCount(failureDetails.ErrorMessage); + } + + if (failureDetails.StackTrace != null) + { + byteCount += encoding.GetByteCount(failureDetails.StackTrace); + } + + if (failureDetails.InnerFailure != null) + { + byteCount += failureDetails.InnerFailure.GetApproximateByteCount(); + } + + return byteCount; + } + + /// + /// Decode a protobuf message from a base64 string. + /// + /// The type to decode to. + /// The message parser. + /// The base64 encoded message. + /// The decoded message. + /// If decoding fails. + internal static T Base64Decode(this MessageParser parser, string encodedMessage) where T : IMessage + { + // Decode the base64 in a way that doesn't allocate a byte[] on each request + int encodedByteCount = Encoding.UTF8.GetByteCount(encodedMessage); + byte[] buffer = ArrayPool.Shared.Rent(encodedByteCount); + try + { + // The Base64 APIs require first converting the string into UTF-8 bytes. We then + // do an in-place conversion from base64 UTF-8 bytes to protobuf bytes so that + // we can finally decode the protobuf request. + Encoding.UTF8.GetBytes(encodedMessage, 0, encodedMessage.Length, buffer, 0); + OperationStatus status = Base64.DecodeFromUtf8InPlace( + buffer.AsSpan(0, encodedByteCount), + out int bytesWritten); + if (status != OperationStatus.Done) + { + throw new ArgumentException( + $"Failed to base64-decode the '{typeof(T).Name}' payload: {status}", nameof(encodedMessage)); + } + + return (T)parser.ParseFrom(buffer, 0, bytesWritten); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Converts a grpc to a . + /// + /// The failure details to convert. + /// The converted failure details. + internal static FailureDetails? ToCore(this P.TaskFailureDetails? failureDetails) + { + if (failureDetails == null) + { + return null; + } + + return new FailureDetails( + failureDetails.ErrorType, + failureDetails.ErrorMessage, + failureDetails.StackTrace, + failureDetails.InnerFailure.ToCore(), + failureDetails.IsNonRetriable, + ConvertProperties(failureDetails.Properties)); + } + + /// + /// Converts a instance to a corresponding C# object. + /// + /// The Protobuf Value to convert. + /// The corresponding C# object. + /// + /// Thrown when the Protobuf Value.KindCase is not one of the supported types. + /// + internal static object? ConvertValueToObject(Google.Protobuf.WellKnownTypes.Value value) + { + switch (value.KindCase) + { + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NullValue: + return null; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NumberValue: + return value.NumberValue; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StringValue: string stringValue = value.StringValue; - // If the value starts with the 'dt:' prefix, it may represent a DateTime value � attempt to parse it. + // If the value starts with the 'dt:' prefix, it may represent a DateTime value - attempt to parse it. if (stringValue.StartsWith("dt:", StringComparison.Ordinal)) { if (DateTime.TryParse(stringValue[3..], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTime date)) @@ -1075,7 +1075,7 @@ internal static T Base64Decode(this MessageParser parser, string encodedMessa } } - // If the value starts with the 'dto:' prefix, it may represent a DateTime value � attempt to parse it. + // If the value starts with the 'dto:' prefix, it may represent a DateTime value - attempt to parse it. if (stringValue.StartsWith("dto:", StringComparison.Ordinal)) { if (DateTimeOffset.TryParse(stringValue[4..], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset date)) @@ -1085,110 +1085,110 @@ internal static T Base64Decode(this MessageParser parser, string encodedMessa } // Otherwise just return as string - return stringValue; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.BoolValue: - return value.BoolValue; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StructValue: - return value.StructValue.Fields.ToDictionary( - pair => pair.Key, - pair => ConvertValueToObject(pair.Value)); - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.ListValue: - return value.ListValue.Values.Select(ConvertValueToObject).ToList(); + return stringValue; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.BoolValue: + return value.BoolValue; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StructValue: + return value.StructValue.Fields.ToDictionary( + pair => pair.Key, + pair => ConvertValueToObject(pair.Value)); + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.ListValue: + return value.ListValue.Values.Select(ConvertValueToObject).ToList(); default: // Fallback: serialize the whole value to JSON string - return JsonSerializer.Serialize(value); - } + return JsonSerializer.Serialize(value); + } } - /// - /// Converts a MapFieldinto a IDictionary. - /// + /// + /// Converts a MapFieldinto a IDictionary. + /// /// The map to convert. - /// Dictionary contains the converted obejct. - internal static IDictionary ConvertProperties(MapField properties) - { - return properties.ToDictionary( - kvp => kvp.Key, - kvp => ConvertValueToObject(kvp.Value)); - } - - /// - /// Converts a C# object to a protobuf Value. - /// - /// The object to convert. - /// The converted protobuf Value. - internal static Value ConvertObjectToValue(object? obj) - { - return obj switch - { - null => Value.ForNull(), - string str => Value.ForString(str), - bool b => Value.ForBool(b), - int i => Value.ForNumber(i), - long l => Value.ForNumber(l), - float f => Value.ForNumber(f), - double d => Value.ForNumber(d), + /// Dictionary contains the converted obejct. + internal static IDictionary ConvertProperties(MapField properties) + { + return properties.ToDictionary( + kvp => kvp.Key, + kvp => ConvertValueToObject(kvp.Value)); + } + + /// + /// Converts a C# object to a protobuf Value. + /// + /// The object to convert. + /// The converted protobuf Value. + internal static Value ConvertObjectToValue(object? obj) + { + return obj switch + { + null => Value.ForNull(), + string str => Value.ForString(str), + bool b => Value.ForBool(b), + int i => Value.ForNumber(i), + long l => Value.ForNumber(l), + float f => Value.ForNumber(f), + double d => Value.ForNumber(d), decimal dec => Value.ForNumber((double)dec), - // For DateTime and DateTimeOffset, add prefix to distinguish from normal string. - DateTime dt => Value.ForString($"dt:{dt.ToString("O")}"), - DateTimeOffset dto => Value.ForString($"dto:{dto.ToString("O")}"), - IDictionary dict => Value.ForStruct(new Struct - { - Fields = { dict.ToDictionary(kvp => kvp.Key, kvp => ConvertObjectToValue(kvp.Value)) }, - }), + // For DateTime and DateTimeOffset, add prefix to distinguish from normal string. + DateTime dt => Value.ForString($"dt:{dt.ToString("O")}"), + DateTimeOffset dto => Value.ForString($"dto:{dto.ToString("O")}"), + IDictionary dict => Value.ForStruct(new Struct + { + Fields = { dict.ToDictionary(kvp => kvp.Key, kvp => ConvertObjectToValue(kvp.Value)) }, + }), IEnumerable e => Value.ForList(e.Cast().Select(ConvertObjectToValue).ToArray()), // Fallback: convert unlisted type to string. - _ => Value.ForString(obj.ToString() ?? string.Empty), - }; - } - - /// - /// Converts a to a grpc . - /// - /// The failure details to convert. - /// The converted failure details. - static P.TaskFailureDetails? ToProtobuf(this FailureDetails? failureDetails) - { - if (failureDetails == null) - { - return null; - } - - var taskFailureDetails = new P.TaskFailureDetails - { - ErrorType = failureDetails.ErrorType ?? "(unknown)", - ErrorMessage = failureDetails.ErrorMessage ?? "(unknown)", - StackTrace = failureDetails.StackTrace, - IsNonRetriable = failureDetails.IsNonRetriable, - InnerFailure = failureDetails.InnerFailure.ToProtobuf(), - }; - - // Properly populate the MapField - if (failureDetails.Properties != null) - { - foreach (var kvp in failureDetails.Properties) - { - taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); - } - } - - return taskFailureDetails; - } - - static P.OrchestrationStatus ToProtobuf(this OrchestrationStatus status) - { - return (P.OrchestrationStatus)status; - } - - static P.OrchestrationInstance ToProtobuf(this OrchestrationInstance instance) - { - return new P.OrchestrationInstance - { - InstanceId = instance.InstanceId, - ExecutionId = instance.ExecutionId, - }; + _ => Value.ForString(obj.ToString() ?? string.Empty), + }; + } + + /// + /// Converts a to a grpc . + /// + /// The failure details to convert. + /// The converted failure details. + static P.TaskFailureDetails? ToProtobuf(this FailureDetails? failureDetails) + { + if (failureDetails == null) + { + return null; + } + + var taskFailureDetails = new P.TaskFailureDetails + { + ErrorType = failureDetails.ErrorType ?? "(unknown)", + ErrorMessage = failureDetails.ErrorMessage ?? "(unknown)", + StackTrace = failureDetails.StackTrace, + IsNonRetriable = failureDetails.IsNonRetriable, + InnerFailure = failureDetails.InnerFailure.ToProtobuf(), + }; + + // Properly populate the MapField + if (failureDetails.Properties != null) + { + foreach (var kvp in failureDetails.Properties) + { + taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); + } + } + + return taskFailureDetails; + } + + static P.OrchestrationStatus ToProtobuf(this OrchestrationStatus status) + { + return (P.OrchestrationStatus)status; + } + + static P.OrchestrationInstance ToProtobuf(this OrchestrationInstance instance) + { + return new P.OrchestrationInstance + { + InstanceId = instance.InstanceId, + ExecutionId = instance.ExecutionId, + }; } static P.HistoryEvent ToProtobuf(HistoryEvent e) @@ -1211,107 +1211,107 @@ static P.HistoryEvent ToProtobuf(HistoryEvent e) } throw new ArgumentException("Unsupported event type"); - } - - /// - /// Tracks state required for converting orchestration histories containing entity-related events. - /// - internal class EntityConversionState - { - readonly bool insertMissingEntityUnlocks; - - OrchestrationInstance? instance; - HashSet? entityRequestIds; - Dictionary? unlockObligations; - - /// - /// Initializes a new instance of the class. - /// - /// Whether to insert missing unlock events in to the history - /// when the orchestration completes. - public EntityConversionState(bool insertMissingEntityUnlocks) - { - this.ConvertFromProto = (P.HistoryEvent e) => ProtoUtils.ConvertHistoryEvent(e, this); - this.insertMissingEntityUnlocks = insertMissingEntityUnlocks; - } - - /// - /// Gets a function that converts a history event in protobuf format to a core history event. - /// - public Func ConvertFromProto { get; } - - /// - /// Gets the orchestration instance of this history. - /// - public OrchestrationInstance? CurrentInstance => this.instance; - - /// - /// Gets the set of guids that have been used as entity request ids in this history. - /// - public HashSet EntityRequestIds => this.entityRequestIds ??= new(); - - /// - /// Records the orchestration instance, which may be needed for some conversions. - /// - /// The orchestration instance. - public void SetOrchestrationInstance(OrchestrationInstance instance) - { - this.instance = instance; - } - - /// - /// Adds unlock obligations for all entities that are being locked by this request. - /// - /// The lock request. - public void AddUnlockObligations(P.EntityLockRequestedEvent request) - { - if (!this.insertMissingEntityUnlocks) - { - return; - } - - this.unlockObligations ??= new(); - - foreach (string target in request.LockSet) - { - this.unlockObligations[target] = request.CriticalSectionId; - } - } - - /// - /// Removes an unlock obligation. - /// - /// The target entity. - public void RemoveUnlockObligation(string target) - { - if (!this.insertMissingEntityUnlocks) - { - return; - } - - this.unlockObligations?.Remove(target); - } - - /// - /// Returns the remaining unlock obligations, and clears the list. - /// - /// The unlock obligations. - public IEnumerable<(string Target, string CriticalSectionId)> ResetObligations() - { - if (!this.insertMissingEntityUnlocks) - { - yield break; - } - - if (this.unlockObligations is not null) - { - foreach (var kvp in this.unlockObligations) - { - yield return (kvp.Key, kvp.Value); - } - - this.unlockObligations = null; - } - } - } -} + } + + /// + /// Tracks state required for converting orchestration histories containing entity-related events. + /// + internal class EntityConversionState + { + readonly bool insertMissingEntityUnlocks; + + OrchestrationInstance? instance; + HashSet? entityRequestIds; + Dictionary? unlockObligations; + + /// + /// Initializes a new instance of the class. + /// + /// Whether to insert missing unlock events in to the history + /// when the orchestration completes. + public EntityConversionState(bool insertMissingEntityUnlocks) + { + this.ConvertFromProto = (P.HistoryEvent e) => ProtoUtils.ConvertHistoryEvent(e, this); + this.insertMissingEntityUnlocks = insertMissingEntityUnlocks; + } + + /// + /// Gets a function that converts a history event in protobuf format to a core history event. + /// + public Func ConvertFromProto { get; } + + /// + /// Gets the orchestration instance of this history. + /// + public OrchestrationInstance? CurrentInstance => this.instance; + + /// + /// Gets the set of guids that have been used as entity request ids in this history. + /// + public HashSet EntityRequestIds => this.entityRequestIds ??= new(); + + /// + /// Records the orchestration instance, which may be needed for some conversions. + /// + /// The orchestration instance. + public void SetOrchestrationInstance(OrchestrationInstance instance) + { + this.instance = instance; + } + + /// + /// Adds unlock obligations for all entities that are being locked by this request. + /// + /// The lock request. + public void AddUnlockObligations(P.EntityLockRequestedEvent request) + { + if (!this.insertMissingEntityUnlocks) + { + return; + } + + this.unlockObligations ??= new(); + + foreach (string target in request.LockSet) + { + this.unlockObligations[target] = request.CriticalSectionId; + } + } + + /// + /// Removes an unlock obligation. + /// + /// The target entity. + public void RemoveUnlockObligation(string target) + { + if (!this.insertMissingEntityUnlocks) + { + return; + } + + this.unlockObligations?.Remove(target); + } + + /// + /// Returns the remaining unlock obligations, and clears the list. + /// + /// The unlock obligations. + public IEnumerable<(string Target, string CriticalSectionId)> ResetObligations() + { + if (!this.insertMissingEntityUnlocks) + { + yield break; + } + + if (this.unlockObligations is not null) + { + foreach (var kvp in this.unlockObligations) + { + yield return (kvp.Key, kvp.Value); + } + + this.unlockObligations = null; + } + } + } +} From 6fade3c5dc573acd1c98d4e421d420a2699e46dc Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Tue, 3 Mar 2026 15:18:06 -0800 Subject: [PATCH 04/10] Fix line ending normalization in ProtoUtils.cs Restore original LF line endings that were inadvertently converted to CRLF when editing on Windows. Only the actual code changes remain in the diff. --- src/Shared/Grpc/ProtoUtils.cs | 2501 ++++++++++++++++----------------- 1 file changed, 1248 insertions(+), 1253 deletions(-) diff --git a/src/Shared/Grpc/ProtoUtils.cs b/src/Shared/Grpc/ProtoUtils.cs index bd0ccf2ab..6bac27c52 100644 --- a/src/Shared/Grpc/ProtoUtils.cs +++ b/src/Shared/Grpc/ProtoUtils.cs @@ -1,1072 +1,1067 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Buffers; -using System.Buffers.Text; -using System.Collections; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; -using System.Text.Json; -using DurableTask.Core; -using DurableTask.Core.Command; -using DurableTask.Core.Entities; -using DurableTask.Core.Entities.OperationFormat; -using DurableTask.Core.History; -using DurableTask.Core.Tracing; -using Google.Protobuf; -using Google.Protobuf.Collections; -using Google.Protobuf.WellKnownTypes; -using DTCore = DurableTask.Core; -using P = Microsoft.DurableTask.Protobuf; -using TraceHelper = Microsoft.DurableTask.Tracing.TraceHelper; - -namespace Microsoft.DurableTask; - -/// -/// Protobuf utilities and helpers. -/// -static class ProtoUtils -{ - /// - /// Converts a history event from to . - /// - /// The proto history event to converter. - /// The converted history event. - /// When the provided history event type is not supported. - internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto) - { - return ConvertHistoryEvent(proto, conversionState: null); - } - - /// - /// Converts a history event from to , and performs - /// stateful conversions of entity-related events. - /// - /// The proto history event to converter. - /// State needed for converting entity-related history entries and actions. - /// The converted history event. - /// When the provided history event type is not supported. - internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto, EntityConversionState? conversionState) - { - Check.NotNull(proto); - HistoryEvent historyEvent; - switch (proto.EventTypeCase) - { - case P.HistoryEvent.EventTypeOneofCase.ContinueAsNew: - historyEvent = new ContinueAsNewEvent(proto.EventId, proto.ContinueAsNew.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionStarted: - OrchestrationInstance instance = proto.ExecutionStarted.OrchestrationInstance.ToCore(); - conversionState?.SetOrchestrationInstance(instance); - historyEvent = new ExecutionStartedEvent(proto.EventId, proto.ExecutionStarted.Input) - { - Name = proto.ExecutionStarted.Name, - Version = proto.ExecutionStarted.Version, - OrchestrationInstance = instance, - Tags = proto.ExecutionStarted.Tags, - ParentInstance = proto.ExecutionStarted.ParentInstance == null ? null : new ParentInstance - { - Name = proto.ExecutionStarted.ParentInstance.Name, - Version = proto.ExecutionStarted.ParentInstance.Version, - OrchestrationInstance = proto.ExecutionStarted.ParentInstance.OrchestrationInstance.ToCore(), - TaskScheduleId = proto.ExecutionStarted.ParentInstance.TaskScheduledId, - }, - ScheduledStartTime = proto.ExecutionStarted.ScheduledStartTimestamp?.ToDateTime(), - }; - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionCompleted: - historyEvent = new ExecutionCompletedEvent( - proto.EventId, - proto.ExecutionCompleted.Result, +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Buffers; +using System.Buffers.Text; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using System.Text.Json; +using DurableTask.Core; +using DurableTask.Core.Command; +using DurableTask.Core.Entities; +using DurableTask.Core.Entities.OperationFormat; +using DurableTask.Core.History; +using DurableTask.Core.Tracing; +using Google.Protobuf; +using Google.Protobuf.Collections; +using Google.Protobuf.WellKnownTypes; +using DTCore = DurableTask.Core; +using P = Microsoft.DurableTask.Protobuf; +using TraceHelper = Microsoft.DurableTask.Tracing.TraceHelper; + +namespace Microsoft.DurableTask; + +/// +/// Protobuf utilities and helpers. +/// +static class ProtoUtils +{ + /// + /// Converts a history event from to . + /// + /// The proto history event to converter. + /// The converted history event. + /// When the provided history event type is not supported. + internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto) + { + return ConvertHistoryEvent(proto, conversionState: null); + } + + /// + /// Converts a history event from to , and performs + /// stateful conversions of entity-related events. + /// + /// The proto history event to converter. + /// State needed for converting entity-related history entries and actions. + /// The converted history event. + /// When the provided history event type is not supported. + internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto, EntityConversionState? conversionState) + { + Check.NotNull(proto); + HistoryEvent historyEvent; + switch (proto.EventTypeCase) + { + case P.HistoryEvent.EventTypeOneofCase.ContinueAsNew: + historyEvent = new ContinueAsNewEvent(proto.EventId, proto.ContinueAsNew.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionStarted: + OrchestrationInstance instance = proto.ExecutionStarted.OrchestrationInstance.ToCore(); + conversionState?.SetOrchestrationInstance(instance); + historyEvent = new ExecutionStartedEvent(proto.EventId, proto.ExecutionStarted.Input) + { + Name = proto.ExecutionStarted.Name, + Version = proto.ExecutionStarted.Version, + OrchestrationInstance = instance, + Tags = proto.ExecutionStarted.Tags, + ParentInstance = proto.ExecutionStarted.ParentInstance == null ? null : new ParentInstance + { + Name = proto.ExecutionStarted.ParentInstance.Name, + Version = proto.ExecutionStarted.ParentInstance.Version, + OrchestrationInstance = proto.ExecutionStarted.ParentInstance.OrchestrationInstance.ToCore(), + TaskScheduleId = proto.ExecutionStarted.ParentInstance.TaskScheduledId, + }, + ScheduledStartTime = proto.ExecutionStarted.ScheduledStartTimestamp?.ToDateTime(), + }; + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionCompleted: + historyEvent = new ExecutionCompletedEvent( + proto.EventId, + proto.ExecutionCompleted.Result, proto.ExecutionCompleted.OrchestrationStatus.ToCore(), - proto.ExecutionCompleted.FailureDetails.ToCore()); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionTerminated: - historyEvent = new ExecutionTerminatedEvent(proto.EventId, proto.ExecutionTerminated.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionSuspended: - historyEvent = new ExecutionSuspendedEvent(proto.EventId, proto.ExecutionSuspended.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.ExecutionResumed: - historyEvent = new ExecutionResumedEvent(proto.EventId, proto.ExecutionResumed.Input); - break; - case P.HistoryEvent.EventTypeOneofCase.TaskScheduled: - historyEvent = new TaskScheduledEvent( - proto.EventId, - proto.TaskScheduled.Name, - proto.TaskScheduled.Version, - proto.TaskScheduled.Input) - { - Tags = proto.TaskScheduled.Tags, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.TaskCompleted: - historyEvent = new TaskCompletedEvent( - proto.EventId, - proto.TaskCompleted.TaskScheduledId, - proto.TaskCompleted.Result); - break; - case P.HistoryEvent.EventTypeOneofCase.TaskFailed: - historyEvent = new TaskFailedEvent( - proto.EventId, - proto.TaskFailed.TaskScheduledId, - reason: null, /* not supported */ - details: null, /* not supported */ - proto.TaskFailed.FailureDetails.ToCore()); - break; - case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCreated: - historyEvent = new SubOrchestrationInstanceCreatedEvent(proto.EventId) - { - Input = proto.SubOrchestrationInstanceCreated.Input, - InstanceId = proto.SubOrchestrationInstanceCreated.InstanceId, - Name = proto.SubOrchestrationInstanceCreated.Name, - Version = proto.SubOrchestrationInstanceCreated.Version, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCompleted: - historyEvent = new SubOrchestrationInstanceCompletedEvent( - proto.EventId, - proto.SubOrchestrationInstanceCompleted.TaskScheduledId, - proto.SubOrchestrationInstanceCompleted.Result); - break; - case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceFailed: - historyEvent = new SubOrchestrationInstanceFailedEvent( - proto.EventId, - proto.SubOrchestrationInstanceFailed.TaskScheduledId, - reason: null /* not supported */, - details: null /* not supported */, - proto.SubOrchestrationInstanceFailed.FailureDetails.ToCore()); - break; - case P.HistoryEvent.EventTypeOneofCase.TimerCreated: - historyEvent = new TimerCreatedEvent( - proto.EventId, - proto.TimerCreated.FireAt.ToDateTime()); - break; - case P.HistoryEvent.EventTypeOneofCase.TimerFired: - historyEvent = new TimerFiredEvent( - eventId: -1, - proto.TimerFired.FireAt.ToDateTime()) - { - TimerId = proto.TimerFired.TimerId, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.OrchestratorStarted: - historyEvent = new OrchestratorStartedEvent(proto.EventId); - break; - case P.HistoryEvent.EventTypeOneofCase.OrchestratorCompleted: - historyEvent = new OrchestratorCompletedEvent(proto.EventId); - break; - case P.HistoryEvent.EventTypeOneofCase.EventSent: - historyEvent = new EventSentEvent(proto.EventId) - { - InstanceId = proto.EventSent.InstanceId, - Name = proto.EventSent.Name, - Input = proto.EventSent.Input, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.EventRaised: - historyEvent = new EventRaisedEvent(proto.EventId, proto.EventRaised.Input) - { - Name = proto.EventRaised.Name, - }; - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationCalled: - historyEvent = EntityConversions.EncodeOperationCalled(proto, conversionState!.CurrentInstance); - conversionState?.EntityRequestIds.Add(proto.EntityOperationCalled.RequestId); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationSignaled: - historyEvent = EntityConversions.EncodeOperationSignaled(proto); - conversionState?.EntityRequestIds.Add(proto.EntityOperationSignaled.RequestId); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityLockRequested: - historyEvent = EntityConversions.EncodeLockRequested(proto, conversionState!.CurrentInstance); - conversionState?.AddUnlockObligations(proto.EntityLockRequested); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityUnlockSent: - historyEvent = EntityConversions.EncodeUnlockSent(proto, conversionState!.CurrentInstance); - conversionState?.RemoveUnlockObligation(proto.EntityUnlockSent.TargetInstanceId); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityLockGranted: - historyEvent = EntityConversions.EncodeLockGranted(proto); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationCompleted: - historyEvent = EntityConversions.EncodeOperationCompleted(proto); - break; - case P.HistoryEvent.EventTypeOneofCase.EntityOperationFailed: - historyEvent = EntityConversions.EncodeOperationFailed(proto); - break; - case P.HistoryEvent.EventTypeOneofCase.GenericEvent: - historyEvent = new GenericEvent(proto.EventId, proto.GenericEvent.Data); - break; - case P.HistoryEvent.EventTypeOneofCase.HistoryState: - historyEvent = new HistoryStateEvent( - proto.EventId, - new OrchestrationState - { - OrchestrationInstance = new OrchestrationInstance - { - InstanceId = proto.HistoryState.OrchestrationState.InstanceId, - }, - Name = proto.HistoryState.OrchestrationState.Name, - Version = proto.HistoryState.OrchestrationState.Version, - ScheduledStartTime = proto.HistoryState.OrchestrationState.ScheduledStartTimestamp.ToDateTime(), - CreatedTime = proto.HistoryState.OrchestrationState.CreatedTimestamp.ToDateTime(), - LastUpdatedTime = proto.HistoryState.OrchestrationState.LastUpdatedTimestamp.ToDateTime(), - Input = proto.HistoryState.OrchestrationState.Input, - Output = proto.HistoryState.OrchestrationState.Output, - Status = proto.HistoryState.OrchestrationState.CustomStatus, - Tags = proto.HistoryState.OrchestrationState.Tags, - }); - break; + proto.ExecutionCompleted.FailureDetails.ToCore()); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionTerminated: + historyEvent = new ExecutionTerminatedEvent(proto.EventId, proto.ExecutionTerminated.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionSuspended: + historyEvent = new ExecutionSuspendedEvent(proto.EventId, proto.ExecutionSuspended.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.ExecutionResumed: + historyEvent = new ExecutionResumedEvent(proto.EventId, proto.ExecutionResumed.Input); + break; + case P.HistoryEvent.EventTypeOneofCase.TaskScheduled: + historyEvent = new TaskScheduledEvent( + proto.EventId, + proto.TaskScheduled.Name, + proto.TaskScheduled.Version, + proto.TaskScheduled.Input) + { + Tags = proto.TaskScheduled.Tags, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.TaskCompleted: + historyEvent = new TaskCompletedEvent( + proto.EventId, + proto.TaskCompleted.TaskScheduledId, + proto.TaskCompleted.Result); + break; + case P.HistoryEvent.EventTypeOneofCase.TaskFailed: + historyEvent = new TaskFailedEvent( + proto.EventId, + proto.TaskFailed.TaskScheduledId, + reason: null, /* not supported */ + details: null, /* not supported */ + proto.TaskFailed.FailureDetails.ToCore()); + break; + case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCreated: + historyEvent = new SubOrchestrationInstanceCreatedEvent(proto.EventId) + { + Input = proto.SubOrchestrationInstanceCreated.Input, + InstanceId = proto.SubOrchestrationInstanceCreated.InstanceId, + Name = proto.SubOrchestrationInstanceCreated.Name, + Version = proto.SubOrchestrationInstanceCreated.Version, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceCompleted: + historyEvent = new SubOrchestrationInstanceCompletedEvent( + proto.EventId, + proto.SubOrchestrationInstanceCompleted.TaskScheduledId, + proto.SubOrchestrationInstanceCompleted.Result); + break; + case P.HistoryEvent.EventTypeOneofCase.SubOrchestrationInstanceFailed: + historyEvent = new SubOrchestrationInstanceFailedEvent( + proto.EventId, + proto.SubOrchestrationInstanceFailed.TaskScheduledId, + reason: null /* not supported */, + details: null /* not supported */, + proto.SubOrchestrationInstanceFailed.FailureDetails.ToCore()); + break; + case P.HistoryEvent.EventTypeOneofCase.TimerCreated: + historyEvent = new TimerCreatedEvent( + proto.EventId, + proto.TimerCreated.FireAt.ToDateTime()); + break; + case P.HistoryEvent.EventTypeOneofCase.TimerFired: + historyEvent = new TimerFiredEvent( + eventId: -1, + proto.TimerFired.FireAt.ToDateTime()) + { + TimerId = proto.TimerFired.TimerId, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.OrchestratorStarted: + historyEvent = new OrchestratorStartedEvent(proto.EventId); + break; + case P.HistoryEvent.EventTypeOneofCase.OrchestratorCompleted: + historyEvent = new OrchestratorCompletedEvent(proto.EventId); + break; + case P.HistoryEvent.EventTypeOneofCase.EventSent: + historyEvent = new EventSentEvent(proto.EventId) + { + InstanceId = proto.EventSent.InstanceId, + Name = proto.EventSent.Name, + Input = proto.EventSent.Input, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.EventRaised: + historyEvent = new EventRaisedEvent(proto.EventId, proto.EventRaised.Input) + { + Name = proto.EventRaised.Name, + }; + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationCalled: + historyEvent = EntityConversions.EncodeOperationCalled(proto, conversionState!.CurrentInstance); + conversionState?.EntityRequestIds.Add(proto.EntityOperationCalled.RequestId); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationSignaled: + historyEvent = EntityConversions.EncodeOperationSignaled(proto); + conversionState?.EntityRequestIds.Add(proto.EntityOperationSignaled.RequestId); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityLockRequested: + historyEvent = EntityConversions.EncodeLockRequested(proto, conversionState!.CurrentInstance); + conversionState?.AddUnlockObligations(proto.EntityLockRequested); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityUnlockSent: + historyEvent = EntityConversions.EncodeUnlockSent(proto, conversionState!.CurrentInstance); + conversionState?.RemoveUnlockObligation(proto.EntityUnlockSent.TargetInstanceId); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityLockGranted: + historyEvent = EntityConversions.EncodeLockGranted(proto); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationCompleted: + historyEvent = EntityConversions.EncodeOperationCompleted(proto); + break; + case P.HistoryEvent.EventTypeOneofCase.EntityOperationFailed: + historyEvent = EntityConversions.EncodeOperationFailed(proto); + break; + case P.HistoryEvent.EventTypeOneofCase.GenericEvent: + historyEvent = new GenericEvent(proto.EventId, proto.GenericEvent.Data); + break; + case P.HistoryEvent.EventTypeOneofCase.HistoryState: + historyEvent = new HistoryStateEvent( + proto.EventId, + new OrchestrationState + { + OrchestrationInstance = new OrchestrationInstance + { + InstanceId = proto.HistoryState.OrchestrationState.InstanceId, + }, + Name = proto.HistoryState.OrchestrationState.Name, + Version = proto.HistoryState.OrchestrationState.Version, + ScheduledStartTime = proto.HistoryState.OrchestrationState.ScheduledStartTimestamp.ToDateTime(), + CreatedTime = proto.HistoryState.OrchestrationState.CreatedTimestamp.ToDateTime(), + LastUpdatedTime = proto.HistoryState.OrchestrationState.LastUpdatedTimestamp.ToDateTime(), + Input = proto.HistoryState.OrchestrationState.Input, + Output = proto.HistoryState.OrchestrationState.Output, + Status = proto.HistoryState.OrchestrationState.CustomStatus, + Tags = proto.HistoryState.OrchestrationState.Tags, + }); + break; case P.HistoryEvent.EventTypeOneofCase.ExecutionRewound: historyEvent = new ExecutionRewoundEvent(proto.EventId); break; - default: - throw new NotSupportedException($"Deserialization of {proto.EventTypeCase} is not supported."); - } - - historyEvent.Timestamp = proto.Timestamp.ToDateTime(); - return historyEvent; - } - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp ToTimestamp(this DateTime dateTime) - { - // The protobuf libraries require timestamps to be in UTC - if (dateTime.Kind == DateTimeKind.Unspecified) - { - dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); - } - else if (dateTime.Kind == DateTimeKind.Local) - { - dateTime = dateTime.ToUniversalTime(); - } - - return Timestamp.FromDateTime(dateTime); - } - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp? ToTimestamp(this DateTime? dateTime) - => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp ToTimestamp(this DateTimeOffset dateTime) => Timestamp.FromDateTimeOffset(dateTime); - - /// - /// Converts a to a gRPC . - /// - /// The date-time to convert. - /// The gRPC timestamp. - internal static Timestamp? ToTimestamp(this DateTimeOffset? dateTime) - => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; - - /// - /// Constructs a . - /// - /// The orchestrator instance ID. - /// The orchestrator execution ID. - /// The orchestrator customer status or null if no custom status. - /// The orchestrator actions. - /// - /// The completion token for the work item. It must be the exact same - /// value that was provided by the corresponding that triggered the orchestrator execution. - /// - /// The entity conversion state, or null if no conversion is required. - /// The that represents orchestration execution. - /// Whether or not a history is required to complete the orchestration request and none was provided. - /// The orchestrator response. - /// When an orchestrator action is unknown. - internal static P.OrchestratorResponse ConstructOrchestratorResponse( - string instanceId, - string executionId, - string? customStatus, - IEnumerable? actions, - string completionToken, - EntityConversionState? entityConversionState, - Activity? orchestrationActivity, - bool requiresHistory = false) - { - var response = new P.OrchestratorResponse - { - InstanceId = instanceId, - CustomStatus = customStatus, - CompletionToken = completionToken, - OrchestrationTraceContext = - new() - { - SpanID = orchestrationActivity?.SpanId.ToString(), - SpanStartTime = orchestrationActivity?.StartTimeUtc.ToTimestamp(), - }, - RequiresHistory = requiresHistory, - }; - - // If a history is required and the orchestration request was not completed, then there is no list of actions. - if (requiresHistory) - { - return response; - } - - Check.NotNull(actions); - foreach (OrchestratorAction action in actions) - { - var protoAction = new P.OrchestratorAction { Id = action.Id }; - - P.TraceContext? CreateTraceContext() - { - if (orchestrationActivity is null) - { - return null; - } - - ActivitySpanId clientSpanId = ActivitySpanId.CreateRandom(); - ActivityContext clientActivityContext = new(orchestrationActivity.TraceId, clientSpanId, orchestrationActivity.ActivityTraceFlags, orchestrationActivity.TraceStateString); - - return new P.TraceContext - { - TraceParent = $"00-{clientActivityContext.TraceId}-{clientActivityContext.SpanId}-0{clientActivityContext.TraceFlags:d}", - TraceState = clientActivityContext.TraceState, - }; - } - - switch (action.OrchestratorActionType) - { - case OrchestratorActionType.ScheduleOrchestrator: - var scheduleTaskAction = (ScheduleTaskOrchestratorAction)action; - - protoAction.ScheduleTask = new P.ScheduleTaskAction - { - Name = scheduleTaskAction.Name, - Version = scheduleTaskAction.Version, - Input = scheduleTaskAction.Input, - ParentTraceContext = CreateTraceContext(), + default: + throw new NotSupportedException($"Deserialization of {proto.EventTypeCase} is not supported."); + } + + historyEvent.Timestamp = proto.Timestamp.ToDateTime(); + return historyEvent; + } + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp ToTimestamp(this DateTime dateTime) + { + // The protobuf libraries require timestamps to be in UTC + if (dateTime.Kind == DateTimeKind.Unspecified) + { + dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); + } + else if (dateTime.Kind == DateTimeKind.Local) + { + dateTime = dateTime.ToUniversalTime(); + } + + return Timestamp.FromDateTime(dateTime); + } + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp? ToTimestamp(this DateTime? dateTime) + => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp ToTimestamp(this DateTimeOffset dateTime) => Timestamp.FromDateTimeOffset(dateTime); + + /// + /// Converts a to a gRPC . + /// + /// The date-time to convert. + /// The gRPC timestamp. + internal static Timestamp? ToTimestamp(this DateTimeOffset? dateTime) + => dateTime.HasValue ? dateTime.Value.ToTimestamp() : null; + + /// + /// Constructs a . + /// + /// The orchestrator instance ID. + /// The orchestrator execution ID. + /// The orchestrator customer status or null if no custom status. + /// The orchestrator actions. + /// + /// The completion token for the work item. It must be the exact same + /// value that was provided by the corresponding that triggered the orchestrator execution. + /// + /// The entity conversion state, or null if no conversion is required. + /// The that represents orchestration execution. + /// Whether or not a history is required to complete the orchestration request and none was provided. + /// The orchestrator response. + /// When an orchestrator action is unknown. + internal static P.OrchestratorResponse ConstructOrchestratorResponse( + string instanceId, + string executionId, + string? customStatus, + IEnumerable? actions, + string completionToken, + EntityConversionState? entityConversionState, + Activity? orchestrationActivity, + bool requiresHistory = false) + { + var response = new P.OrchestratorResponse + { + InstanceId = instanceId, + CustomStatus = customStatus, + CompletionToken = completionToken, + OrchestrationTraceContext = + new() + { + SpanID = orchestrationActivity?.SpanId.ToString(), + SpanStartTime = orchestrationActivity?.StartTimeUtc.ToTimestamp(), + }, + RequiresHistory = requiresHistory, + }; + + // If a history is required and the orchestration request was not completed, then there is no list of actions. + if (requiresHistory) + { + return response; + } + + Check.NotNull(actions); + foreach (OrchestratorAction action in actions) + { + var protoAction = new P.OrchestratorAction { Id = action.Id }; + + P.TraceContext? CreateTraceContext() + { + if (orchestrationActivity is null) + { + return null; + } + + ActivitySpanId clientSpanId = ActivitySpanId.CreateRandom(); + ActivityContext clientActivityContext = new(orchestrationActivity.TraceId, clientSpanId, orchestrationActivity.ActivityTraceFlags, orchestrationActivity.TraceStateString); + + return new P.TraceContext + { + TraceParent = $"00-{clientActivityContext.TraceId}-{clientActivityContext.SpanId}-0{clientActivityContext.TraceFlags:d}", + TraceState = clientActivityContext.TraceState, + }; + } + + switch (action.OrchestratorActionType) + { + case OrchestratorActionType.ScheduleOrchestrator: + var scheduleTaskAction = (ScheduleTaskOrchestratorAction)action; + + protoAction.ScheduleTask = new P.ScheduleTaskAction + { + Name = scheduleTaskAction.Name, + Version = scheduleTaskAction.Version, + Input = scheduleTaskAction.Input, + ParentTraceContext = CreateTraceContext(), + }; + + if (scheduleTaskAction.Tags != null) + { + foreach (KeyValuePair tag in scheduleTaskAction.Tags) + { + protoAction.ScheduleTask.Tags[tag.Key] = tag.Value; + } + } + + break; + case OrchestratorActionType.CreateSubOrchestration: + var subOrchestrationAction = (CreateSubOrchestrationAction)action; + protoAction.CreateSubOrchestration = new P.CreateSubOrchestrationAction + { + Input = subOrchestrationAction.Input, + InstanceId = subOrchestrationAction.InstanceId, + Name = subOrchestrationAction.Name, + Version = subOrchestrationAction.Version, + ParentTraceContext = CreateTraceContext(), }; - if (scheduleTaskAction.Tags != null) - { - foreach (KeyValuePair tag in scheduleTaskAction.Tags) - { - protoAction.ScheduleTask.Tags[tag.Key] = tag.Value; - } + if (subOrchestrationAction.Tags != null) + { + foreach (KeyValuePair tag in subOrchestrationAction.Tags) + { + protoAction.CreateSubOrchestration.Tags[tag.Key] = tag.Value; + } } - - break; - case OrchestratorActionType.CreateSubOrchestration: - var subOrchestrationAction = (CreateSubOrchestrationAction)action; - protoAction.CreateSubOrchestration = new P.CreateSubOrchestrationAction + + break; + case OrchestratorActionType.CreateTimer: + var createTimerAction = (CreateTimerOrchestratorAction)action; + protoAction.CreateTimer = new P.CreateTimerAction + { + FireAt = createTimerAction.FireAt.ToTimestamp(), + }; + break; + case OrchestratorActionType.SendEvent: + var sendEventAction = (SendEventOrchestratorAction)action; + if (sendEventAction.Instance == null) + { + throw new ArgumentException( + $"{nameof(SendEventOrchestratorAction)} cannot have a null Instance property!"); + } + + if (entityConversionState is not null + && DTCore.Common.Entities.IsEntityInstance(sendEventAction.Instance.InstanceId) + && sendEventAction.EventName is not null + && sendEventAction.EventData is not null) + { + P.SendEntityMessageAction sendAction = new P.SendEntityMessageAction(); + protoAction.SendEntityMessage = sendAction; + + EntityConversions.DecodeEntityMessageAction( + sendEventAction.EventName, + sendEventAction.EventData, + sendEventAction.Instance.InstanceId, + sendAction, + out string requestId); + + entityConversionState.EntityRequestIds.Add(requestId); + sendAction.ParentTraceContext = CreateTraceContext(); + + switch (sendAction.EntityMessageTypeCase) + { + case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityLockRequested: + entityConversionState.AddUnlockObligations(sendAction.EntityLockRequested); + break; + case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityUnlockSent: + entityConversionState.RemoveUnlockObligation(sendAction.EntityUnlockSent.TargetInstanceId); + break; + default: + break; + } + } + else + { + protoAction.SendEvent = new P.SendEventAction + { + Instance = sendEventAction.Instance.ToProtobuf(), + Name = sendEventAction.EventName, + Data = sendEventAction.EventData, + }; + + // Distributed Tracing: start a new trace activity derived from the orchestration + // for an EventRaisedEvent (external event) + using Activity? traceActivity = TraceHelper.StartTraceActivityForEventRaisedFromWorker(sendEventAction, instanceId, executionId); + + traceActivity?.Stop(); + } + + break; + case OrchestratorActionType.OrchestrationComplete: + + if (entityConversionState is not null) + { + // as a precaution, unlock any entities that were not unlocked for some reason, before + // completing the orchestration. + foreach ((string target, string criticalSectionId) in entityConversionState.ResetObligations()) + { + response.Actions.Add(new P.OrchestratorAction + { + Id = action.Id, + SendEntityMessage = new P.SendEntityMessageAction + { + EntityUnlockSent = new P.EntityUnlockSentEvent + { + CriticalSectionId = criticalSectionId, + TargetInstanceId = target, + ParentInstanceId = entityConversionState.CurrentInstance?.InstanceId, + }, + }, + }); + } + } + + var completeAction = (OrchestrationCompleteOrchestratorAction)action; + protoAction.CompleteOrchestration = new P.CompleteOrchestrationAction { - Input = subOrchestrationAction.Input, - InstanceId = subOrchestrationAction.InstanceId, - Name = subOrchestrationAction.Name, - Version = subOrchestrationAction.Version, - ParentTraceContext = CreateTraceContext(), - }; - - if (subOrchestrationAction.Tags != null) - { - foreach (KeyValuePair tag in subOrchestrationAction.Tags) - { - protoAction.CreateSubOrchestration.Tags[tag.Key] = tag.Value; - } - } - - break; - case OrchestratorActionType.CreateTimer: - var createTimerAction = (CreateTimerOrchestratorAction)action; - protoAction.CreateTimer = new P.CreateTimerAction - { - FireAt = createTimerAction.FireAt.ToTimestamp(), - }; - break; - case OrchestratorActionType.SendEvent: - var sendEventAction = (SendEventOrchestratorAction)action; - if (sendEventAction.Instance == null) - { - throw new ArgumentException( - $"{nameof(SendEventOrchestratorAction)} cannot have a null Instance property!"); - } - - if (entityConversionState is not null - && DTCore.Common.Entities.IsEntityInstance(sendEventAction.Instance.InstanceId) - && sendEventAction.EventName is not null - && sendEventAction.EventData is not null) - { - P.SendEntityMessageAction sendAction = new P.SendEntityMessageAction(); - protoAction.SendEntityMessage = sendAction; - - EntityConversions.DecodeEntityMessageAction( - sendEventAction.EventName, - sendEventAction.EventData, - sendEventAction.Instance.InstanceId, - sendAction, - out string requestId); - - sendAction.ParentTraceContext = CreateTraceContext(); - - entityConversionState.EntityRequestIds.Add(requestId); - - switch (sendAction.EntityMessageTypeCase) - { - case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityLockRequested: - entityConversionState.AddUnlockObligations(sendAction.EntityLockRequested); - break; - case P.SendEntityMessageAction.EntityMessageTypeOneofCase.EntityUnlockSent: - entityConversionState.RemoveUnlockObligation(sendAction.EntityUnlockSent.TargetInstanceId); - break; - default: - break; - } - } - else - { - protoAction.SendEvent = new P.SendEventAction - { - Instance = sendEventAction.Instance.ToProtobuf(), - Name = sendEventAction.EventName, - Data = sendEventAction.EventData, - }; - - // Distributed Tracing: start a new trace activity derived from the orchestration - // for an EventRaisedEvent (external event) - using Activity? traceActivity = TraceHelper.StartTraceActivityForEventRaisedFromWorker(sendEventAction, instanceId, executionId); - - traceActivity?.Stop(); - } - - break; - case OrchestratorActionType.OrchestrationComplete: - - if (entityConversionState is not null) - { - // as a precaution, unlock any entities that were not unlocked for some reason, before - // completing the orchestration. - foreach ((string target, string criticalSectionId) in entityConversionState.ResetObligations()) - { - response.Actions.Add(new P.OrchestratorAction - { - Id = action.Id, - SendEntityMessage = new P.SendEntityMessageAction - { - EntityUnlockSent = new P.EntityUnlockSentEvent - { - CriticalSectionId = criticalSectionId, - TargetInstanceId = target, - ParentInstanceId = entityConversionState.CurrentInstance?.InstanceId, - }, - }, - }); - } - } - - var completeAction = (OrchestrationCompleteOrchestratorAction)action; - protoAction.CompleteOrchestration = new P.CompleteOrchestrationAction - { - CarryoverEvents = { completeAction.CarryoverEvents.Select(ToProtobuf) }, - Details = completeAction.Details, - NewVersion = completeAction.NewVersion, - OrchestrationStatus = completeAction.OrchestrationStatus.ToProtobuf(), - Result = completeAction.Result, + CarryoverEvents = { completeAction.CarryoverEvents.Select(ToProtobuf) }, + Details = completeAction.Details, + NewVersion = completeAction.NewVersion, + OrchestrationStatus = completeAction.OrchestrationStatus.ToProtobuf(), + Result = completeAction.Result, }; foreach (KeyValuePair tag in completeAction.Tags) { protoAction.CompleteOrchestration.Tags[tag.Key] = tag.Value; - } - - if (completeAction.OrchestrationStatus == OrchestrationStatus.Failed) - { - protoAction.CompleteOrchestration.FailureDetails = completeAction.FailureDetails.ToProtobuf(); - } - - break; - default: - throw new NotSupportedException($"Unknown orchestrator action: {action.OrchestratorActionType}"); - } - - response.Actions.Add(protoAction); - } - - return response; - } - - /// - /// Converts a to a . - /// - /// The status to convert. - /// The converted status. - internal static OrchestrationStatus ToCore(this P.OrchestrationStatus status) - { - return (OrchestrationStatus)status; - } - - /// - /// Converts a to a . - /// - /// The status to convert. - /// The converted status. - [return: NotNullIfNotNull(nameof(status))] - internal static OrchestrationInstance? ToCore(this P.OrchestrationInstance? status) - { - if (status == null) - { - return null; - } - - return new OrchestrationInstance - { - InstanceId = status.InstanceId, - ExecutionId = status.ExecutionId, - }; - } - - /// - /// Converts a to a . - /// - /// The failure details to convert. - /// The converted failure details. - [return: NotNullIfNotNull(nameof(failureDetails))] - internal static TaskFailureDetails? ToTaskFailureDetails(this P.TaskFailureDetails? failureDetails) - { - if (failureDetails == null) - { - return null; - } - - return new TaskFailureDetails( - failureDetails.ErrorType, - failureDetails.ErrorMessage, - failureDetails.StackTrace, - failureDetails.InnerFailure.ToTaskFailureDetails(), - ConvertProperties(failureDetails.Properties)); - } - - /// - /// Converts a to . - /// - /// The exception to convert. - /// Optional exception properties provider. - /// The task failure details. - [return: NotNullIfNotNull(nameof(e))] - internal static P.TaskFailureDetails? ToTaskFailureDetails(this Exception? e, DTCore.IExceptionPropertiesProvider? exceptionPropertiesProvider = null) - { - if (e == null) - { - return null; - } - - IDictionary? properties = exceptionPropertiesProvider?.GetExceptionProperties(e); - - var taskFailureDetails = new P.TaskFailureDetails - { - ErrorType = e.GetType().FullName, - ErrorMessage = e.Message, - StackTrace = e.StackTrace, - InnerFailure = e.InnerException.ToTaskFailureDetails(exceptionPropertiesProvider), - }; - - if (properties != null) - { - foreach (var kvp in properties) - { - taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); - } - } - - return taskFailureDetails; - } - - /// - /// Converts a to a . - /// - /// The entity batch request to convert. - /// The converted entity batch request. - [return: NotNullIfNotNull(nameof(entityBatchRequest))] - internal static EntityBatchRequest? ToEntityBatchRequest(this P.EntityBatchRequest? entityBatchRequest) - { - if (entityBatchRequest == null) - { - return null; - } - - return new EntityBatchRequest() - { - EntityState = entityBatchRequest.EntityState, - InstanceId = entityBatchRequest.InstanceId, - Operations = entityBatchRequest.Operations.Select(r => r.ToOperationRequest()).ToList(), - }; - } - - /// - /// Converts a to a . - /// - /// The entity request to convert. - /// The converted request. - /// Additional info about each operation, required by DTS. - internal static void ToEntityBatchRequest( - this P.EntityRequest entityRequest, - out EntityBatchRequest batchRequest, - out List operationInfos) - { - batchRequest = new EntityBatchRequest() - { - EntityState = entityRequest.EntityState, - InstanceId = entityRequest.InstanceId, - Operations = [], // operations are added to this collection below - }; - - operationInfos = new(entityRequest.OperationRequests.Count); - - foreach (P.HistoryEvent? op in entityRequest.OperationRequests) - { - if (op.EntityOperationSignaled is not null) - { - batchRequest.Operations.Add(new OperationRequest - { - Id = Guid.Parse(op.EntityOperationSignaled.RequestId), - Operation = op.EntityOperationSignaled.Operation, - Input = op.EntityOperationSignaled.Input, - TraceContext = op.EntityOperationSignaled.ParentTraceContext != null - ? new DistributedTraceContext( - op.EntityOperationSignaled.ParentTraceContext.TraceParent, - op.EntityOperationSignaled.ParentTraceContext.TraceState) - : null, - }); - operationInfos.Add(new P.OperationInfo - { - RequestId = op.EntityOperationSignaled.RequestId, - ResponseDestination = null, // means we don't send back a response to the caller - }); - } - else if (op.EntityOperationCalled is not null) - { - batchRequest.Operations.Add(new OperationRequest - { - Id = Guid.Parse(op.EntityOperationCalled.RequestId), - Operation = op.EntityOperationCalled.Operation, - Input = op.EntityOperationCalled.Input, - TraceContext = op.EntityOperationCalled.ParentTraceContext != null - ? new DistributedTraceContext( - op.EntityOperationCalled.ParentTraceContext.TraceParent, - op.EntityOperationCalled.ParentTraceContext.TraceState) - : null, - }); - operationInfos.Add(new P.OperationInfo - { - RequestId = op.EntityOperationCalled.RequestId, - ResponseDestination = new P.OrchestrationInstance - { - InstanceId = op.EntityOperationCalled.ParentInstanceId, - ExecutionId = op.EntityOperationCalled.ParentExecutionId, - }, - }); - } - } - } - - /// - /// Converts a to a . - /// - /// The operation request to convert. - /// The converted operation request. - [return: NotNullIfNotNull(nameof(operationRequest))] - internal static OperationRequest? ToOperationRequest(this P.OperationRequest? operationRequest) - { - if (operationRequest == null) - { - return null; - } - - return new OperationRequest() - { - Operation = operationRequest.Operation, - Input = operationRequest.Input, - Id = Guid.Parse(operationRequest.RequestId), - TraceContext = operationRequest.TraceContext != null ? - new DistributedTraceContext( - operationRequest.TraceContext.TraceParent, - operationRequest.TraceContext.TraceState) : null, - }; - } - - /// - /// Converts a to a . - /// - /// The operation result to convert. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(operationResult))] - internal static OperationResult? ToOperationResult(this P.OperationResult? operationResult) - { - if (operationResult == null) - { - return null; - } - - switch (operationResult.ResultTypeCase) - { - case P.OperationResult.ResultTypeOneofCase.Success: - return new OperationResult() - { - Result = operationResult.Success.Result, - StartTimeUtc = operationResult.Success.StartTimeUtc?.ToDateTime(), - EndTimeUtc = operationResult.Success.EndTimeUtc?.ToDateTime(), - }; - - case P.OperationResult.ResultTypeOneofCase.Failure: - return new OperationResult() - { - FailureDetails = operationResult.Failure.FailureDetails.ToCore(), - StartTimeUtc = operationResult.Failure.StartTimeUtc?.ToDateTime(), - EndTimeUtc = operationResult.Failure.EndTimeUtc?.ToDateTime(), - }; - - default: - throw new NotSupportedException($"Deserialization of {operationResult.ResultTypeCase} is not supported."); - } - } - - /// - /// Converts a to . - /// - /// The operation result to convert. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(operationResult))] - internal static P.OperationResult? ToOperationResult(this OperationResult? operationResult) - { - if (operationResult == null) - { - return null; - } - - if (operationResult.FailureDetails == null) - { - return new P.OperationResult() - { - Success = new P.OperationResultSuccess() - { - Result = operationResult.Result, - StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), - EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), - }, - }; - } - else - { - return new P.OperationResult() - { - Failure = new P.OperationResultFailure() - { - FailureDetails = ToProtobuf(operationResult.FailureDetails), - StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), - EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), - }, - }; - } - } - - /// - /// Converts a to a . - /// - /// The operation action to convert. - /// The converted operation action. - [return: NotNullIfNotNull(nameof(operationAction))] - internal static OperationAction? ToOperationAction(this P.OperationAction? operationAction) - { - if (operationAction == null) - { - return null; - } - - switch (operationAction.OperationActionTypeCase) - { - case P.OperationAction.OperationActionTypeOneofCase.SendSignal: - - return new SendSignalOperationAction() - { - Name = operationAction.SendSignal.Name, - Input = operationAction.SendSignal.Input, - InstanceId = operationAction.SendSignal.InstanceId, - ScheduledTime = operationAction.SendSignal.ScheduledTime?.ToDateTime(), - RequestTime = operationAction.SendSignal.RequestTime?.ToDateTimeOffset(), - ParentTraceContext = operationAction.SendSignal.ParentTraceContext != null ? - new DistributedTraceContext( - operationAction.SendSignal.ParentTraceContext.TraceParent, - operationAction.SendSignal.ParentTraceContext.TraceState) : null, - }; - - case P.OperationAction.OperationActionTypeOneofCase.StartNewOrchestration: - - return new StartNewOrchestrationOperationAction() - { - Name = operationAction.StartNewOrchestration.Name, - Input = operationAction.StartNewOrchestration.Input, - InstanceId = operationAction.StartNewOrchestration.InstanceId, - Version = operationAction.StartNewOrchestration.Version, - ScheduledStartTime = operationAction.StartNewOrchestration.ScheduledTime?.ToDateTime(), - RequestTime = operationAction.StartNewOrchestration.RequestTime?.ToDateTimeOffset(), - ParentTraceContext = operationAction.StartNewOrchestration.ParentTraceContext != null ? - new DistributedTraceContext( - operationAction.StartNewOrchestration.ParentTraceContext.TraceParent, - operationAction.StartNewOrchestration.ParentTraceContext.TraceState) : null, - }; - default: - throw new NotSupportedException($"Deserialization of {operationAction.OperationActionTypeCase} is not supported."); - } - } - - /// - /// Converts a to . - /// - /// The operation action to convert. - /// The converted operation action. - [return: NotNullIfNotNull(nameof(operationAction))] - internal static P.OperationAction? ToOperationAction(this OperationAction? operationAction) - { - if (operationAction == null) - { - return null; - } - - var action = new P.OperationAction(); - - switch (operationAction) - { - case SendSignalOperationAction sendSignalAction: - - action.SendSignal = new P.SendSignalAction() - { - Name = sendSignalAction.Name, - Input = sendSignalAction.Input, - InstanceId = sendSignalAction.InstanceId, - ScheduledTime = sendSignalAction.ScheduledTime?.ToTimestamp(), - RequestTime = sendSignalAction.RequestTime?.ToTimestamp(), - ParentTraceContext = sendSignalAction.ParentTraceContext != null ? - new P.TraceContext - { - TraceParent = sendSignalAction.ParentTraceContext.TraceParent, - TraceState = sendSignalAction.ParentTraceContext.TraceState, - } - : null, - }; - break; - - case StartNewOrchestrationOperationAction startNewOrchestrationAction: - - action.StartNewOrchestration = new P.StartNewOrchestrationAction() - { - Name = startNewOrchestrationAction.Name, - Input = startNewOrchestrationAction.Input, - Version = startNewOrchestrationAction.Version, - InstanceId = startNewOrchestrationAction.InstanceId, - ScheduledTime = startNewOrchestrationAction.ScheduledStartTime?.ToTimestamp(), - RequestTime = startNewOrchestrationAction.RequestTime?.ToTimestamp(), - ParentTraceContext = startNewOrchestrationAction.ParentTraceContext != null ? - new P.TraceContext - { - TraceParent = startNewOrchestrationAction.ParentTraceContext.TraceParent, - TraceState = startNewOrchestrationAction.ParentTraceContext.TraceState, - } - : null, - }; - break; - } - - return action; - } - - /// - /// Converts a to a . - /// - /// The operation result to convert. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(entityBatchResult))] - internal static EntityBatchResult? ToEntityBatchResult(this P.EntityBatchResult? entityBatchResult) - { - if (entityBatchResult == null) - { - return null; - } - - return new EntityBatchResult() - { - Actions = entityBatchResult.Actions.Select(operationAction => operationAction!.ToOperationAction()).ToList(), - EntityState = entityBatchResult.EntityState, - Results = entityBatchResult.Results.Select(operationResult => operationResult!.ToOperationResult()).ToList(), - FailureDetails = entityBatchResult.FailureDetails.ToCore(), - }; - } - - /// - /// Converts a to . - /// - /// The operation result to convert. - /// The completion token, or null for the older protocol. - /// Additional information about each operation, required by DTS. - /// The converted operation result. - [return: NotNullIfNotNull(nameof(entityBatchResult))] - internal static P.EntityBatchResult? ToEntityBatchResult( - this EntityBatchResult? entityBatchResult, - string? completionToken = null, - IEnumerable? operationInfos = null) - { - if (entityBatchResult == null) - { - return null; - } - - return new P.EntityBatchResult() - { - EntityState = entityBatchResult.EntityState, - FailureDetails = entityBatchResult.FailureDetails.ToProtobuf(), - Actions = { entityBatchResult.Actions?.Select(a => a.ToOperationAction()) ?? [] }, - Results = { entityBatchResult.Results?.Select(a => a.ToOperationResult()) ?? [] }, - CompletionToken = completionToken ?? string.Empty, - OperationInfos = { operationInfos ?? [] }, - }; - } - - /// - /// Converts the gRPC representation of orchestrator entity parameters to the DT.Core representation. - /// - /// The DT.Core representation. - /// The gRPC representation. - [return: NotNullIfNotNull(nameof(parameters))] - internal static TaskOrchestrationEntityParameters? ToCore(this P.OrchestratorEntityParameters? parameters) - { - if (parameters == null) - { - return null; - } - - return new TaskOrchestrationEntityParameters() - { - EntityMessageReorderWindow = parameters.EntityMessageReorderWindow.ToTimeSpan(), - }; - } - - /// - /// Gets the approximate byte count for a . - /// - /// The failure details. - /// The approximate byte count. - internal static int GetApproximateByteCount(this P.TaskFailureDetails failureDetails) - { - // Protobuf strings are always UTF-8: https://developers.google.com/protocol-buffers/docs/proto3#scalar - Encoding encoding = Encoding.UTF8; - - int byteCount = 0; - if (failureDetails.ErrorType != null) - { - byteCount += encoding.GetByteCount(failureDetails.ErrorType); - } - - if (failureDetails.ErrorMessage != null) - { - byteCount += encoding.GetByteCount(failureDetails.ErrorMessage); - } - - if (failureDetails.StackTrace != null) - { - byteCount += encoding.GetByteCount(failureDetails.StackTrace); - } - - if (failureDetails.InnerFailure != null) - { - byteCount += failureDetails.InnerFailure.GetApproximateByteCount(); - } - - return byteCount; - } - - /// - /// Decode a protobuf message from a base64 string. - /// - /// The type to decode to. - /// The message parser. - /// The base64 encoded message. - /// The decoded message. - /// If decoding fails. - internal static T Base64Decode(this MessageParser parser, string encodedMessage) where T : IMessage - { - // Decode the base64 in a way that doesn't allocate a byte[] on each request - int encodedByteCount = Encoding.UTF8.GetByteCount(encodedMessage); - byte[] buffer = ArrayPool.Shared.Rent(encodedByteCount); - try - { - // The Base64 APIs require first converting the string into UTF-8 bytes. We then - // do an in-place conversion from base64 UTF-8 bytes to protobuf bytes so that - // we can finally decode the protobuf request. - Encoding.UTF8.GetBytes(encodedMessage, 0, encodedMessage.Length, buffer, 0); - OperationStatus status = Base64.DecodeFromUtf8InPlace( - buffer.AsSpan(0, encodedByteCount), - out int bytesWritten); - if (status != OperationStatus.Done) - { - throw new ArgumentException( - $"Failed to base64-decode the '{typeof(T).Name}' payload: {status}", nameof(encodedMessage)); - } - - return (T)parser.ParseFrom(buffer, 0, bytesWritten); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - /// - /// Converts a grpc to a . - /// - /// The failure details to convert. - /// The converted failure details. - internal static FailureDetails? ToCore(this P.TaskFailureDetails? failureDetails) - { - if (failureDetails == null) - { - return null; - } - - return new FailureDetails( - failureDetails.ErrorType, - failureDetails.ErrorMessage, - failureDetails.StackTrace, - failureDetails.InnerFailure.ToCore(), - failureDetails.IsNonRetriable, - ConvertProperties(failureDetails.Properties)); - } - - /// - /// Converts a instance to a corresponding C# object. - /// - /// The Protobuf Value to convert. - /// The corresponding C# object. - /// - /// Thrown when the Protobuf Value.KindCase is not one of the supported types. - /// - internal static object? ConvertValueToObject(Google.Protobuf.WellKnownTypes.Value value) - { - switch (value.KindCase) - { - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NullValue: - return null; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NumberValue: - return value.NumberValue; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StringValue: + } + + if (completeAction.OrchestrationStatus == OrchestrationStatus.Failed) + { + protoAction.CompleteOrchestration.FailureDetails = completeAction.FailureDetails.ToProtobuf(); + } + + break; + default: + throw new NotSupportedException($"Unknown orchestrator action: {action.OrchestratorActionType}"); + } + + response.Actions.Add(protoAction); + } + + return response; + } + + /// + /// Converts a to a . + /// + /// The status to convert. + /// The converted status. + internal static OrchestrationStatus ToCore(this P.OrchestrationStatus status) + { + return (OrchestrationStatus)status; + } + + /// + /// Converts a to a . + /// + /// The status to convert. + /// The converted status. + [return: NotNullIfNotNull(nameof(status))] + internal static OrchestrationInstance? ToCore(this P.OrchestrationInstance? status) + { + if (status == null) + { + return null; + } + + return new OrchestrationInstance + { + InstanceId = status.InstanceId, + ExecutionId = status.ExecutionId, + }; + } + + /// + /// Converts a to a . + /// + /// The failure details to convert. + /// The converted failure details. + [return: NotNullIfNotNull(nameof(failureDetails))] + internal static TaskFailureDetails? ToTaskFailureDetails(this P.TaskFailureDetails? failureDetails) + { + if (failureDetails == null) + { + return null; + } + + return new TaskFailureDetails( + failureDetails.ErrorType, + failureDetails.ErrorMessage, + failureDetails.StackTrace, + failureDetails.InnerFailure.ToTaskFailureDetails(), + ConvertProperties(failureDetails.Properties)); + } + + /// + /// Converts a to . + /// + /// The exception to convert. + /// Optional exception properties provider. + /// The task failure details. + [return: NotNullIfNotNull(nameof(e))] + internal static P.TaskFailureDetails? ToTaskFailureDetails(this Exception? e, DTCore.IExceptionPropertiesProvider? exceptionPropertiesProvider = null) + { + if (e == null) + { + return null; + } + + IDictionary? properties = exceptionPropertiesProvider?.GetExceptionProperties(e); + + var taskFailureDetails = new P.TaskFailureDetails + { + ErrorType = e.GetType().FullName, + ErrorMessage = e.Message, + StackTrace = e.StackTrace, + InnerFailure = e.InnerException.ToTaskFailureDetails(exceptionPropertiesProvider), + }; + + if (properties != null) + { + foreach (var kvp in properties) + { + taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); + } + } + + return taskFailureDetails; + } + + /// + /// Converts a to a . + /// + /// The entity batch request to convert. + /// The converted entity batch request. + [return: NotNullIfNotNull(nameof(entityBatchRequest))] + internal static EntityBatchRequest? ToEntityBatchRequest(this P.EntityBatchRequest? entityBatchRequest) + { + if (entityBatchRequest == null) + { + return null; + } + + return new EntityBatchRequest() + { + EntityState = entityBatchRequest.EntityState, + InstanceId = entityBatchRequest.InstanceId, + Operations = entityBatchRequest.Operations.Select(r => r.ToOperationRequest()).ToList(), + }; + } + + /// + /// Converts a to a . + /// + /// The entity request to convert. + /// The converted request. + /// Additional info about each operation, required by DTS. + internal static void ToEntityBatchRequest( + this P.EntityRequest entityRequest, + out EntityBatchRequest batchRequest, + out List operationInfos) + { + batchRequest = new EntityBatchRequest() + { + EntityState = entityRequest.EntityState, + InstanceId = entityRequest.InstanceId, + Operations = [], // operations are added to this collection below + }; + + operationInfos = new(entityRequest.OperationRequests.Count); + + foreach (P.HistoryEvent? op in entityRequest.OperationRequests) + { + if (op.EntityOperationSignaled is not null) + { + batchRequest.Operations.Add(new OperationRequest + { + Id = Guid.Parse(op.EntityOperationSignaled.RequestId), + Operation = op.EntityOperationSignaled.Operation, + Input = op.EntityOperationSignaled.Input, + TraceContext = op.EntityOperationSignaled.ParentTraceContext is { } signalTc + ? new DistributedTraceContext(signalTc.TraceParent, signalTc.TraceState) + : null, + }); + operationInfos.Add(new P.OperationInfo + { + RequestId = op.EntityOperationSignaled.RequestId, + ResponseDestination = null, // means we don't send back a response to the caller + }); + } + else if (op.EntityOperationCalled is not null) + { + batchRequest.Operations.Add(new OperationRequest + { + Id = Guid.Parse(op.EntityOperationCalled.RequestId), + Operation = op.EntityOperationCalled.Operation, + Input = op.EntityOperationCalled.Input, + TraceContext = op.EntityOperationCalled.ParentTraceContext is { } calledTc + ? new DistributedTraceContext(calledTc.TraceParent, calledTc.TraceState) + : null, + }); + operationInfos.Add(new P.OperationInfo + { + RequestId = op.EntityOperationCalled.RequestId, + ResponseDestination = new P.OrchestrationInstance + { + InstanceId = op.EntityOperationCalled.ParentInstanceId, + ExecutionId = op.EntityOperationCalled.ParentExecutionId, + }, + }); + } + } + } + + /// + /// Converts a to a . + /// + /// The operation request to convert. + /// The converted operation request. + [return: NotNullIfNotNull(nameof(operationRequest))] + internal static OperationRequest? ToOperationRequest(this P.OperationRequest? operationRequest) + { + if (operationRequest == null) + { + return null; + } + + return new OperationRequest() + { + Operation = operationRequest.Operation, + Input = operationRequest.Input, + Id = Guid.Parse(operationRequest.RequestId), + TraceContext = operationRequest.TraceContext != null ? + new DistributedTraceContext( + operationRequest.TraceContext.TraceParent, + operationRequest.TraceContext.TraceState) : null, + }; + } + + /// + /// Converts a to a . + /// + /// The operation result to convert. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(operationResult))] + internal static OperationResult? ToOperationResult(this P.OperationResult? operationResult) + { + if (operationResult == null) + { + return null; + } + + switch (operationResult.ResultTypeCase) + { + case P.OperationResult.ResultTypeOneofCase.Success: + return new OperationResult() + { + Result = operationResult.Success.Result, + StartTimeUtc = operationResult.Success.StartTimeUtc?.ToDateTime(), + EndTimeUtc = operationResult.Success.EndTimeUtc?.ToDateTime(), + }; + + case P.OperationResult.ResultTypeOneofCase.Failure: + return new OperationResult() + { + FailureDetails = operationResult.Failure.FailureDetails.ToCore(), + StartTimeUtc = operationResult.Failure.StartTimeUtc?.ToDateTime(), + EndTimeUtc = operationResult.Failure.EndTimeUtc?.ToDateTime(), + }; + + default: + throw new NotSupportedException($"Deserialization of {operationResult.ResultTypeCase} is not supported."); + } + } + + /// + /// Converts a to . + /// + /// The operation result to convert. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(operationResult))] + internal static P.OperationResult? ToOperationResult(this OperationResult? operationResult) + { + if (operationResult == null) + { + return null; + } + + if (operationResult.FailureDetails == null) + { + return new P.OperationResult() + { + Success = new P.OperationResultSuccess() + { + Result = operationResult.Result, + StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), + EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), + }, + }; + } + else + { + return new P.OperationResult() + { + Failure = new P.OperationResultFailure() + { + FailureDetails = ToProtobuf(operationResult.FailureDetails), + StartTimeUtc = operationResult.StartTimeUtc?.ToTimestamp(), + EndTimeUtc = operationResult.EndTimeUtc?.ToTimestamp(), + }, + }; + } + } + + /// + /// Converts a to a . + /// + /// The operation action to convert. + /// The converted operation action. + [return: NotNullIfNotNull(nameof(operationAction))] + internal static OperationAction? ToOperationAction(this P.OperationAction? operationAction) + { + if (operationAction == null) + { + return null; + } + + switch (operationAction.OperationActionTypeCase) + { + case P.OperationAction.OperationActionTypeOneofCase.SendSignal: + + return new SendSignalOperationAction() + { + Name = operationAction.SendSignal.Name, + Input = operationAction.SendSignal.Input, + InstanceId = operationAction.SendSignal.InstanceId, + ScheduledTime = operationAction.SendSignal.ScheduledTime?.ToDateTime(), + RequestTime = operationAction.SendSignal.RequestTime?.ToDateTimeOffset(), + ParentTraceContext = operationAction.SendSignal.ParentTraceContext != null ? + new DistributedTraceContext( + operationAction.SendSignal.ParentTraceContext.TraceParent, + operationAction.SendSignal.ParentTraceContext.TraceState) : null, + }; + + case P.OperationAction.OperationActionTypeOneofCase.StartNewOrchestration: + + return new StartNewOrchestrationOperationAction() + { + Name = operationAction.StartNewOrchestration.Name, + Input = operationAction.StartNewOrchestration.Input, + InstanceId = operationAction.StartNewOrchestration.InstanceId, + Version = operationAction.StartNewOrchestration.Version, + ScheduledStartTime = operationAction.StartNewOrchestration.ScheduledTime?.ToDateTime(), + RequestTime = operationAction.StartNewOrchestration.RequestTime?.ToDateTimeOffset(), + ParentTraceContext = operationAction.StartNewOrchestration.ParentTraceContext != null ? + new DistributedTraceContext( + operationAction.StartNewOrchestration.ParentTraceContext.TraceParent, + operationAction.StartNewOrchestration.ParentTraceContext.TraceState) : null, + }; + default: + throw new NotSupportedException($"Deserialization of {operationAction.OperationActionTypeCase} is not supported."); + } + } + + /// + /// Converts a to . + /// + /// The operation action to convert. + /// The converted operation action. + [return: NotNullIfNotNull(nameof(operationAction))] + internal static P.OperationAction? ToOperationAction(this OperationAction? operationAction) + { + if (operationAction == null) + { + return null; + } + + var action = new P.OperationAction(); + + switch (operationAction) + { + case SendSignalOperationAction sendSignalAction: + + action.SendSignal = new P.SendSignalAction() + { + Name = sendSignalAction.Name, + Input = sendSignalAction.Input, + InstanceId = sendSignalAction.InstanceId, + ScheduledTime = sendSignalAction.ScheduledTime?.ToTimestamp(), + RequestTime = sendSignalAction.RequestTime?.ToTimestamp(), + ParentTraceContext = sendSignalAction.ParentTraceContext != null ? + new P.TraceContext + { + TraceParent = sendSignalAction.ParentTraceContext.TraceParent, + TraceState = sendSignalAction.ParentTraceContext.TraceState, + } + : null, + }; + break; + + case StartNewOrchestrationOperationAction startNewOrchestrationAction: + + action.StartNewOrchestration = new P.StartNewOrchestrationAction() + { + Name = startNewOrchestrationAction.Name, + Input = startNewOrchestrationAction.Input, + Version = startNewOrchestrationAction.Version, + InstanceId = startNewOrchestrationAction.InstanceId, + ScheduledTime = startNewOrchestrationAction.ScheduledStartTime?.ToTimestamp(), + RequestTime = startNewOrchestrationAction.RequestTime?.ToTimestamp(), + ParentTraceContext = startNewOrchestrationAction.ParentTraceContext != null ? + new P.TraceContext + { + TraceParent = startNewOrchestrationAction.ParentTraceContext.TraceParent, + TraceState = startNewOrchestrationAction.ParentTraceContext.TraceState, + } + : null, + }; + break; + } + + return action; + } + + /// + /// Converts a to a . + /// + /// The operation result to convert. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(entityBatchResult))] + internal static EntityBatchResult? ToEntityBatchResult(this P.EntityBatchResult? entityBatchResult) + { + if (entityBatchResult == null) + { + return null; + } + + return new EntityBatchResult() + { + Actions = entityBatchResult.Actions.Select(operationAction => operationAction!.ToOperationAction()).ToList(), + EntityState = entityBatchResult.EntityState, + Results = entityBatchResult.Results.Select(operationResult => operationResult!.ToOperationResult()).ToList(), + FailureDetails = entityBatchResult.FailureDetails.ToCore(), + }; + } + + /// + /// Converts a to . + /// + /// The operation result to convert. + /// The completion token, or null for the older protocol. + /// Additional information about each operation, required by DTS. + /// The converted operation result. + [return: NotNullIfNotNull(nameof(entityBatchResult))] + internal static P.EntityBatchResult? ToEntityBatchResult( + this EntityBatchResult? entityBatchResult, + string? completionToken = null, + IEnumerable? operationInfos = null) + { + if (entityBatchResult == null) + { + return null; + } + + return new P.EntityBatchResult() + { + EntityState = entityBatchResult.EntityState, + FailureDetails = entityBatchResult.FailureDetails.ToProtobuf(), + Actions = { entityBatchResult.Actions?.Select(a => a.ToOperationAction()) ?? [] }, + Results = { entityBatchResult.Results?.Select(a => a.ToOperationResult()) ?? [] }, + CompletionToken = completionToken ?? string.Empty, + OperationInfos = { operationInfos ?? [] }, + }; + } + + /// + /// Converts the gRPC representation of orchestrator entity parameters to the DT.Core representation. + /// + /// The DT.Core representation. + /// The gRPC representation. + [return: NotNullIfNotNull(nameof(parameters))] + internal static TaskOrchestrationEntityParameters? ToCore(this P.OrchestratorEntityParameters? parameters) + { + if (parameters == null) + { + return null; + } + + return new TaskOrchestrationEntityParameters() + { + EntityMessageReorderWindow = parameters.EntityMessageReorderWindow.ToTimeSpan(), + }; + } + + /// + /// Gets the approximate byte count for a . + /// + /// The failure details. + /// The approximate byte count. + internal static int GetApproximateByteCount(this P.TaskFailureDetails failureDetails) + { + // Protobuf strings are always UTF-8: https://developers.google.com/protocol-buffers/docs/proto3#scalar + Encoding encoding = Encoding.UTF8; + + int byteCount = 0; + if (failureDetails.ErrorType != null) + { + byteCount += encoding.GetByteCount(failureDetails.ErrorType); + } + + if (failureDetails.ErrorMessage != null) + { + byteCount += encoding.GetByteCount(failureDetails.ErrorMessage); + } + + if (failureDetails.StackTrace != null) + { + byteCount += encoding.GetByteCount(failureDetails.StackTrace); + } + + if (failureDetails.InnerFailure != null) + { + byteCount += failureDetails.InnerFailure.GetApproximateByteCount(); + } + + return byteCount; + } + + /// + /// Decode a protobuf message from a base64 string. + /// + /// The type to decode to. + /// The message parser. + /// The base64 encoded message. + /// The decoded message. + /// If decoding fails. + internal static T Base64Decode(this MessageParser parser, string encodedMessage) where T : IMessage + { + // Decode the base64 in a way that doesn't allocate a byte[] on each request + int encodedByteCount = Encoding.UTF8.GetByteCount(encodedMessage); + byte[] buffer = ArrayPool.Shared.Rent(encodedByteCount); + try + { + // The Base64 APIs require first converting the string into UTF-8 bytes. We then + // do an in-place conversion from base64 UTF-8 bytes to protobuf bytes so that + // we can finally decode the protobuf request. + Encoding.UTF8.GetBytes(encodedMessage, 0, encodedMessage.Length, buffer, 0); + OperationStatus status = Base64.DecodeFromUtf8InPlace( + buffer.AsSpan(0, encodedByteCount), + out int bytesWritten); + if (status != OperationStatus.Done) + { + throw new ArgumentException( + $"Failed to base64-decode the '{typeof(T).Name}' payload: {status}", nameof(encodedMessage)); + } + + return (T)parser.ParseFrom(buffer, 0, bytesWritten); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Converts a grpc to a . + /// + /// The failure details to convert. + /// The converted failure details. + internal static FailureDetails? ToCore(this P.TaskFailureDetails? failureDetails) + { + if (failureDetails == null) + { + return null; + } + + return new FailureDetails( + failureDetails.ErrorType, + failureDetails.ErrorMessage, + failureDetails.StackTrace, + failureDetails.InnerFailure.ToCore(), + failureDetails.IsNonRetriable, + ConvertProperties(failureDetails.Properties)); + } + + /// + /// Converts a instance to a corresponding C# object. + /// + /// The Protobuf Value to convert. + /// The corresponding C# object. + /// + /// Thrown when the Protobuf Value.KindCase is not one of the supported types. + /// + internal static object? ConvertValueToObject(Google.Protobuf.WellKnownTypes.Value value) + { + switch (value.KindCase) + { + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NullValue: + return null; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NumberValue: + return value.NumberValue; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StringValue: string stringValue = value.StringValue; - // If the value starts with the 'dt:' prefix, it may represent a DateTime value - attempt to parse it. + // If the value starts with the 'dt:' prefix, it may represent a DateTime value — attempt to parse it. if (stringValue.StartsWith("dt:", StringComparison.Ordinal)) { if (DateTime.TryParse(stringValue[3..], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTime date)) @@ -1075,7 +1070,7 @@ internal static T Base64Decode(this MessageParser parser, string encodedMessa } } - // If the value starts with the 'dto:' prefix, it may represent a DateTime value - attempt to parse it. + // If the value starts with the 'dto:' prefix, it may represent a DateTime value — attempt to parse it. if (stringValue.StartsWith("dto:", StringComparison.Ordinal)) { if (DateTimeOffset.TryParse(stringValue[4..], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset date)) @@ -1085,110 +1080,110 @@ internal static T Base64Decode(this MessageParser parser, string encodedMessa } // Otherwise just return as string - return stringValue; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.BoolValue: - return value.BoolValue; - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StructValue: - return value.StructValue.Fields.ToDictionary( - pair => pair.Key, - pair => ConvertValueToObject(pair.Value)); - case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.ListValue: - return value.ListValue.Values.Select(ConvertValueToObject).ToList(); + return stringValue; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.BoolValue: + return value.BoolValue; + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StructValue: + return value.StructValue.Fields.ToDictionary( + pair => pair.Key, + pair => ConvertValueToObject(pair.Value)); + case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.ListValue: + return value.ListValue.Values.Select(ConvertValueToObject).ToList(); default: // Fallback: serialize the whole value to JSON string - return JsonSerializer.Serialize(value); - } + return JsonSerializer.Serialize(value); + } } - /// - /// Converts a MapFieldinto a IDictionary. - /// + /// + /// Converts a MapFieldinto a IDictionary. + /// /// The map to convert. - /// Dictionary contains the converted obejct. - internal static IDictionary ConvertProperties(MapField properties) - { - return properties.ToDictionary( - kvp => kvp.Key, - kvp => ConvertValueToObject(kvp.Value)); - } - - /// - /// Converts a C# object to a protobuf Value. - /// - /// The object to convert. - /// The converted protobuf Value. - internal static Value ConvertObjectToValue(object? obj) - { - return obj switch - { - null => Value.ForNull(), - string str => Value.ForString(str), - bool b => Value.ForBool(b), - int i => Value.ForNumber(i), - long l => Value.ForNumber(l), - float f => Value.ForNumber(f), - double d => Value.ForNumber(d), + /// Dictionary contains the converted obejct. + internal static IDictionary ConvertProperties(MapField properties) + { + return properties.ToDictionary( + kvp => kvp.Key, + kvp => ConvertValueToObject(kvp.Value)); + } + + /// + /// Converts a C# object to a protobuf Value. + /// + /// The object to convert. + /// The converted protobuf Value. + internal static Value ConvertObjectToValue(object? obj) + { + return obj switch + { + null => Value.ForNull(), + string str => Value.ForString(str), + bool b => Value.ForBool(b), + int i => Value.ForNumber(i), + long l => Value.ForNumber(l), + float f => Value.ForNumber(f), + double d => Value.ForNumber(d), decimal dec => Value.ForNumber((double)dec), - // For DateTime and DateTimeOffset, add prefix to distinguish from normal string. - DateTime dt => Value.ForString($"dt:{dt.ToString("O")}"), - DateTimeOffset dto => Value.ForString($"dto:{dto.ToString("O")}"), - IDictionary dict => Value.ForStruct(new Struct - { - Fields = { dict.ToDictionary(kvp => kvp.Key, kvp => ConvertObjectToValue(kvp.Value)) }, - }), + // For DateTime and DateTimeOffset, add prefix to distinguish from normal string. + DateTime dt => Value.ForString($"dt:{dt.ToString("O")}"), + DateTimeOffset dto => Value.ForString($"dto:{dto.ToString("O")}"), + IDictionary dict => Value.ForStruct(new Struct + { + Fields = { dict.ToDictionary(kvp => kvp.Key, kvp => ConvertObjectToValue(kvp.Value)) }, + }), IEnumerable e => Value.ForList(e.Cast().Select(ConvertObjectToValue).ToArray()), // Fallback: convert unlisted type to string. - _ => Value.ForString(obj.ToString() ?? string.Empty), - }; - } - - /// - /// Converts a to a grpc . - /// - /// The failure details to convert. - /// The converted failure details. - static P.TaskFailureDetails? ToProtobuf(this FailureDetails? failureDetails) - { - if (failureDetails == null) - { - return null; - } - - var taskFailureDetails = new P.TaskFailureDetails - { - ErrorType = failureDetails.ErrorType ?? "(unknown)", - ErrorMessage = failureDetails.ErrorMessage ?? "(unknown)", - StackTrace = failureDetails.StackTrace, - IsNonRetriable = failureDetails.IsNonRetriable, - InnerFailure = failureDetails.InnerFailure.ToProtobuf(), - }; - - // Properly populate the MapField - if (failureDetails.Properties != null) - { - foreach (var kvp in failureDetails.Properties) - { - taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); - } - } - - return taskFailureDetails; - } - - static P.OrchestrationStatus ToProtobuf(this OrchestrationStatus status) - { - return (P.OrchestrationStatus)status; - } - - static P.OrchestrationInstance ToProtobuf(this OrchestrationInstance instance) - { - return new P.OrchestrationInstance - { - InstanceId = instance.InstanceId, - ExecutionId = instance.ExecutionId, - }; + _ => Value.ForString(obj.ToString() ?? string.Empty), + }; + } + + /// + /// Converts a to a grpc . + /// + /// The failure details to convert. + /// The converted failure details. + static P.TaskFailureDetails? ToProtobuf(this FailureDetails? failureDetails) + { + if (failureDetails == null) + { + return null; + } + + var taskFailureDetails = new P.TaskFailureDetails + { + ErrorType = failureDetails.ErrorType ?? "(unknown)", + ErrorMessage = failureDetails.ErrorMessage ?? "(unknown)", + StackTrace = failureDetails.StackTrace, + IsNonRetriable = failureDetails.IsNonRetriable, + InnerFailure = failureDetails.InnerFailure.ToProtobuf(), + }; + + // Properly populate the MapField + if (failureDetails.Properties != null) + { + foreach (var kvp in failureDetails.Properties) + { + taskFailureDetails.Properties[kvp.Key] = ConvertObjectToValue(kvp.Value); + } + } + + return taskFailureDetails; + } + + static P.OrchestrationStatus ToProtobuf(this OrchestrationStatus status) + { + return (P.OrchestrationStatus)status; + } + + static P.OrchestrationInstance ToProtobuf(this OrchestrationInstance instance) + { + return new P.OrchestrationInstance + { + InstanceId = instance.InstanceId, + ExecutionId = instance.ExecutionId, + }; } static P.HistoryEvent ToProtobuf(HistoryEvent e) @@ -1211,107 +1206,107 @@ static P.HistoryEvent ToProtobuf(HistoryEvent e) } throw new ArgumentException("Unsupported event type"); - } - - /// - /// Tracks state required for converting orchestration histories containing entity-related events. - /// - internal class EntityConversionState - { - readonly bool insertMissingEntityUnlocks; - - OrchestrationInstance? instance; - HashSet? entityRequestIds; - Dictionary? unlockObligations; - - /// - /// Initializes a new instance of the class. - /// - /// Whether to insert missing unlock events in to the history - /// when the orchestration completes. - public EntityConversionState(bool insertMissingEntityUnlocks) - { - this.ConvertFromProto = (P.HistoryEvent e) => ProtoUtils.ConvertHistoryEvent(e, this); - this.insertMissingEntityUnlocks = insertMissingEntityUnlocks; - } - - /// - /// Gets a function that converts a history event in protobuf format to a core history event. - /// - public Func ConvertFromProto { get; } - - /// - /// Gets the orchestration instance of this history. - /// - public OrchestrationInstance? CurrentInstance => this.instance; - - /// - /// Gets the set of guids that have been used as entity request ids in this history. - /// - public HashSet EntityRequestIds => this.entityRequestIds ??= new(); - - /// - /// Records the orchestration instance, which may be needed for some conversions. - /// - /// The orchestration instance. - public void SetOrchestrationInstance(OrchestrationInstance instance) - { - this.instance = instance; - } - - /// - /// Adds unlock obligations for all entities that are being locked by this request. - /// - /// The lock request. - public void AddUnlockObligations(P.EntityLockRequestedEvent request) - { - if (!this.insertMissingEntityUnlocks) - { - return; - } - - this.unlockObligations ??= new(); - - foreach (string target in request.LockSet) - { - this.unlockObligations[target] = request.CriticalSectionId; - } - } - - /// - /// Removes an unlock obligation. - /// - /// The target entity. - public void RemoveUnlockObligation(string target) - { - if (!this.insertMissingEntityUnlocks) - { - return; - } - - this.unlockObligations?.Remove(target); - } - - /// - /// Returns the remaining unlock obligations, and clears the list. - /// - /// The unlock obligations. - public IEnumerable<(string Target, string CriticalSectionId)> ResetObligations() - { - if (!this.insertMissingEntityUnlocks) - { - yield break; - } - - if (this.unlockObligations is not null) - { - foreach (var kvp in this.unlockObligations) - { - yield return (kvp.Key, kvp.Value); - } - - this.unlockObligations = null; - } - } - } -} + } + + /// + /// Tracks state required for converting orchestration histories containing entity-related events. + /// + internal class EntityConversionState + { + readonly bool insertMissingEntityUnlocks; + + OrchestrationInstance? instance; + HashSet? entityRequestIds; + Dictionary? unlockObligations; + + /// + /// Initializes a new instance of the class. + /// + /// Whether to insert missing unlock events in to the history + /// when the orchestration completes. + public EntityConversionState(bool insertMissingEntityUnlocks) + { + this.ConvertFromProto = (P.HistoryEvent e) => ProtoUtils.ConvertHistoryEvent(e, this); + this.insertMissingEntityUnlocks = insertMissingEntityUnlocks; + } + + /// + /// Gets a function that converts a history event in protobuf format to a core history event. + /// + public Func ConvertFromProto { get; } + + /// + /// Gets the orchestration instance of this history. + /// + public OrchestrationInstance? CurrentInstance => this.instance; + + /// + /// Gets the set of guids that have been used as entity request ids in this history. + /// + public HashSet EntityRequestIds => this.entityRequestIds ??= new(); + + /// + /// Records the orchestration instance, which may be needed for some conversions. + /// + /// The orchestration instance. + public void SetOrchestrationInstance(OrchestrationInstance instance) + { + this.instance = instance; + } + + /// + /// Adds unlock obligations for all entities that are being locked by this request. + /// + /// The lock request. + public void AddUnlockObligations(P.EntityLockRequestedEvent request) + { + if (!this.insertMissingEntityUnlocks) + { + return; + } + + this.unlockObligations ??= new(); + + foreach (string target in request.LockSet) + { + this.unlockObligations[target] = request.CriticalSectionId; + } + } + + /// + /// Removes an unlock obligation. + /// + /// The target entity. + public void RemoveUnlockObligation(string target) + { + if (!this.insertMissingEntityUnlocks) + { + return; + } + + this.unlockObligations?.Remove(target); + } + + /// + /// Returns the remaining unlock obligations, and clears the list. + /// + /// The unlock obligations. + public IEnumerable<(string Target, string CriticalSectionId)> ResetObligations() + { + if (!this.insertMissingEntityUnlocks) + { + yield break; + } + + if (this.unlockObligations is not null) + { + foreach (var kvp in this.unlockObligations) + { + yield return (kvp.Key, kvp.Value); + } + + this.unlockObligations = null; + } + } + } +} From ee1b67a49dc29c3d413fcd8330eac320626d5483 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Wed, 4 Mar 2026 08:40:00 -0800 Subject: [PATCH 05/10] Add entity operation trace span creation Create trace spans for entity operation execution in OnRunEntityBatchAsync, using the trace context propagated from the parent orchestration. This completes the distributed tracing story for entities by making entity operations visible in trace viewers. - Add EntityOperation constant to TraceActivityConstants - Add StartTraceActivityForEntityOperation to TraceHelper - Create span with entity metadata tags in GrpcDurableTaskWorker.Processor --- .../Grpc/Tracing/TraceActivityConstants.cs | 5 ++ src/Shared/Grpc/Tracing/TraceHelper.cs | 52 +++++++++++++++++++ .../Grpc/GrpcDurableTaskWorker.Processor.cs | 11 ++++ 3 files changed, 68 insertions(+) diff --git a/src/Shared/Grpc/Tracing/TraceActivityConstants.cs b/src/Shared/Grpc/Tracing/TraceActivityConstants.cs index cba4bf524..60472b1a1 100644 --- a/src/Shared/Grpc/Tracing/TraceActivityConstants.cs +++ b/src/Shared/Grpc/Tracing/TraceActivityConstants.cs @@ -24,6 +24,11 @@ static class TraceActivityConstants /// public const string Event = "event"; + /// + /// The name of the activity that represents entity operation execution. + /// + public const string EntityOperation = "entity_operation"; + /// /// The name of the activity that represents timer operations. /// diff --git a/src/Shared/Grpc/Tracing/TraceHelper.cs b/src/Shared/Grpc/Tracing/TraceHelper.cs index 1283ff126..7b245f932 100644 --- a/src/Shared/Grpc/Tracing/TraceHelper.cs +++ b/src/Shared/Grpc/Tracing/TraceHelper.cs @@ -156,6 +156,58 @@ static class TraceHelper return newActivity; } + /// + /// Starts a new trace activity for executing an entity operation. + /// + /// The name of the entity. + /// The name of the operation being executed. + /// The instance ID of the entity. + /// The W3C traceparent header value from the parent orchestration. + /// The W3C tracestate header value from the parent orchestration. + /// + /// Returns a newly started with entity operation metadata, or null if tracing is not enabled. + /// + public static Activity? StartTraceActivityForEntityOperation( + string entityName, + string? operationName, + string instanceId, + string? traceParent, + string? traceState) + { + if (traceParent is null || !ActivityContext.TryParse( + traceParent, + traceState, + out ActivityContext activityContext)) + { + return null; + } + + string spanName = string.IsNullOrEmpty(operationName) + ? $"{TraceActivityConstants.EntityOperation}:{entityName}" + : $"{TraceActivityConstants.EntityOperation}:{entityName}:{operationName}"; + + Activity? newActivity = ActivityTraceSource.StartActivity( + spanName, + kind: ActivityKind.Server, + parentContext: activityContext); + + if (newActivity == null) + { + return null; + } + + newActivity.SetTag(Schema.Task.Type, TraceActivityConstants.EntityOperation); + newActivity.SetTag(Schema.Task.Name, entityName); + newActivity.SetTag(Schema.Task.InstanceId, instanceId); + + if (!string.IsNullOrEmpty(operationName)) + { + newActivity.SetTag("durabletask.entity.operation", operationName); + } + + return newActivity; + } + /// /// Emits a new trace activity for a (task) activity that successfully completes. /// diff --git a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs index a3aa3dab0..26a9306bf 100644 --- a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs +++ b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs @@ -865,6 +865,15 @@ async Task OnRunEntityBatchAsync( var coreEntityId = DTCore.Entities.EntityId.FromString(batchRequest.InstanceId!); EntityId entityId = new(coreEntityId.Name, coreEntityId.Key); + // Start a trace span for entity operation execution using the first operation's trace context. + OperationRequest? firstOp = batchRequest.Operations?.FirstOrDefault(); + using Activity? traceActivity = TraceHelper.StartTraceActivityForEntityOperation( + entityId.Name, + firstOp?.Operation, + batchRequest.InstanceId!, + firstOp?.TraceContext?.TraceParent, + firstOp?.TraceContext?.TraceState); + TaskName name = new(entityId.Name); EntityBatchResult? batchResult; @@ -913,6 +922,8 @@ async Task OnRunEntityBatchAsync( { FailureDetails = new FailureDetails(frameworkException), }; + + traceActivity?.SetStatus(ActivityStatusCode.Error, frameworkException.Message); } P.EntityBatchResult response = batchResult.ToEntityBatchResult( From b6d5ae631034cbaef2c6b31be64e3e9244965511 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Wed, 4 Mar 2026 14:50:59 -0800 Subject: [PATCH 06/10] Add entity operation Client/Server trace spans Add Client spans (emitted during orchestration replay) and Server spans (emitted during entity execution) for entity operations. This creates proper Client->Server span nesting in trace viewers, matching the existing pattern used by activities and sub-orchestrations. - TraceHelper: Add EmitTraceActivityForEntityOperationCompleted/Failed and StartTraceActivityForSchedulingEntityOperation (Client spans) - Processor: Handle EntityOperationCompleted/Failed in newEvents switch with GetEntityOperationCalledEvent helper for request correlation - Include before/after screenshots showing entity traces in Aspire Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- after-entity-traces.png | Bin 0 -> 69402 bytes before-entity-traces.png | Bin 0 -> 53761 bytes src/Shared/Grpc/Tracing/TraceHelper.cs | 104 ++++++++++++++++++ .../Grpc/GrpcDurableTaskWorker.Processor.cs | 35 ++++++ 4 files changed, 139 insertions(+) create mode 100644 after-entity-traces.png create mode 100644 before-entity-traces.png diff --git a/after-entity-traces.png b/after-entity-traces.png new file mode 100644 index 0000000000000000000000000000000000000000..2988b2cac03d6c6844339027802251f3729d6efa GIT binary patch literal 69402 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 z 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 literal 0 HcmV?d00001 diff --git a/src/Shared/Grpc/Tracing/TraceHelper.cs b/src/Shared/Grpc/Tracing/TraceHelper.cs index 7b245f932..434ce0b8e 100644 --- a/src/Shared/Grpc/Tracing/TraceHelper.cs +++ b/src/Shared/Grpc/Tracing/TraceHelper.cs @@ -254,6 +254,51 @@ public static void EmitTraceActivityForTaskFailed( activity?.Dispose(); } + /// + /// Emits a new trace activity for an entity operation that successfully completes. + /// + /// The ID of the associated orchestration. + /// The associated . + /// The associated . + public static void EmitTraceActivityForEntityOperationCompleted( + string? instanceId, + P.HistoryEvent? historyEvent, + P.EntityOperationCalledEvent? calledEvent) + { + Activity? activity = StartTraceActivityForSchedulingEntityOperation(instanceId, historyEvent, calledEvent); + + activity?.Dispose(); + } + + /// + /// Emits a new trace activity for an entity operation that fails. + /// + /// The ID of the associated orchestration. + /// The associated . + /// The associated . + /// The associated . + public static void EmitTraceActivityForEntityOperationFailed( + string? instanceId, + P.HistoryEvent? historyEvent, + P.EntityOperationCalledEvent? calledEvent, + P.EntityOperationFailedEvent? failedEvent) + { + Activity? activity = StartTraceActivityForSchedulingEntityOperation(instanceId, historyEvent, calledEvent); + + if (activity is null) + { + return; + } + + if (failedEvent != null) + { + string statusDescription = failedEvent.FailureDetails?.ErrorMessage ?? "Unspecified entity operation failure"; + activity.SetStatus(ActivityStatusCode.Error, statusDescription); + } + + activity.Dispose(); + } + /// /// Emits a new trace activity for sub-orchestration execution when the sub-orchestration /// completes successfully. @@ -461,6 +506,65 @@ static string CreateSpanName(string spanDescription, string? taskName, string? t return newActivity; } + /// + /// Starts a new trace activity for scheduling an entity operation. Represents the time between + /// enqueuing the entity operation message and it completing. + /// + /// The ID of the associated orchestration. + /// The associated . + /// The associated . + /// + /// Returns a newly started with entity operation metadata. + /// + static Activity? StartTraceActivityForSchedulingEntityOperation( + string? instanceId, + P.HistoryEvent? historyEvent, + P.EntityOperationCalledEvent? calledEvent) + { + if (calledEvent == null) + { + return null; + } + + string entityName = historyEvent?.EntityOperationCalled?.TargetInstanceId ?? string.Empty; + string spanName = string.IsNullOrEmpty(calledEvent.Operation) + ? $"{TraceActivityConstants.EntityOperation}:{entityName}" + : $"{TraceActivityConstants.EntityOperation}:{entityName}:{calledEvent.Operation}"; + + Activity? newActivity = ActivityTraceSource.StartActivity( + spanName, + kind: ActivityKind.Client, + startTime: historyEvent?.Timestamp?.ToDateTimeOffset() ?? default, + parentContext: Activity.Current?.Context ?? default); + + if (newActivity == null) + { + return null; + } + + if (calledEvent.ParentTraceContext != null) + { + if (ActivityContext.TryParse( + calledEvent.ParentTraceContext.TraceParent, + calledEvent.ParentTraceContext?.TraceState, + out ActivityContext parentContext)) + { + newActivity.SetSpanId(parentContext.SpanId.ToString()); + } + } + + newActivity.AddTag(Schema.Task.Type, TraceActivityConstants.EntityOperation); + newActivity.AddTag(Schema.Task.Name, entityName); + newActivity.AddTag(Schema.Task.InstanceId, instanceId); + + if (!string.IsNullOrEmpty(calledEvent.Operation)) + { + newActivity.AddTag("durabletask.entity.operation", calledEvent.Operation); + } + + return newActivity; + } + /// /// Starts a new trace activity for sub-orchestrations. Represents the time between enqueuing /// the sub-orchestration message and it completing. diff --git a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs index 26a9306bf..d9a2e690c 100644 --- a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs +++ b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs @@ -502,6 +502,14 @@ async Task OnRunOrchestratorAsync( return taskScheduledEvent; } + P.HistoryEvent? GetEntityOperationCalledEvent(string requestId) + { + return request + .PastEvents + .Where(x => x.EventTypeCase == P.HistoryEvent.EventTypeOneofCase.EntityOperationCalled) + .FirstOrDefault(x => x.EntityOperationCalled.RequestId == requestId); + } + foreach (var newEvent in request.NewEvents) { switch (newEvent.EventTypeCase) @@ -565,6 +573,33 @@ async Task OnRunOrchestratorAsync( newEvent.Timestamp.ToDateTime(), newEvent.TimerFired); break; + + case P.HistoryEvent.EventTypeOneofCase.EntityOperationCompleted: + { + P.HistoryEvent? entityCalledEvent = + GetEntityOperationCalledEvent( + newEvent.EntityOperationCompleted.RequestId); + + TraceHelper.EmitTraceActivityForEntityOperationCompleted( + request.InstanceId, + entityCalledEvent, + entityCalledEvent?.EntityOperationCalled); + break; + } + + case P.HistoryEvent.EventTypeOneofCase.EntityOperationFailed: + { + P.HistoryEvent? entityCalledEvent = + GetEntityOperationCalledEvent( + newEvent.EntityOperationFailed.RequestId); + + TraceHelper.EmitTraceActivityForEntityOperationFailed( + request.InstanceId, + entityCalledEvent, + entityCalledEvent?.EntityOperationCalled, + newEvent.EntityOperationFailed); + break; + } } } } From 1d199d5ae23f3552643d5dfc9845ca2fde0b96c1 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Wed, 4 Mar 2026 14:54:37 -0800 Subject: [PATCH 07/10] Remove screenshots from tracked files Screenshots are provided separately in the PR description. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- after-entity-traces.png | Bin 69402 -> 0 bytes before-entity-traces.png | Bin 53761 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 after-entity-traces.png delete mode 100644 before-entity-traces.png diff --git a/after-entity-traces.png b/after-entity-traces.png deleted file mode 100644 index 2988b2cac03d6c6844339027802251f3729d6efa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69402 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 z 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 From 764c94a61e5263562c930479f9605a6067b710e6 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Wed, 4 Mar 2026 15:10:40 -0800 Subject: [PATCH 08/10] Address PR review feedback - Add Schema.Task.EntityOperationName constant for entity operation tag - Replace magic string 'durabletask.entity.operation' with constant - Combine nested if statements for ParentTraceContext null check - Extract entity name from TargetInstanceId (format '@name@key') - Add comment explaining Server span trace context for entity batches Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Shared/Grpc/Tracing/Schema.cs | 5 +++ src/Shared/Grpc/Tracing/TraceHelper.cs | 31 ++++++++++++------- .../Grpc/GrpcDurableTaskWorker.Processor.cs | 5 ++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Shared/Grpc/Tracing/Schema.cs b/src/Shared/Grpc/Tracing/Schema.cs index 377778172..65741114d 100644 --- a/src/Shared/Grpc/Tracing/Schema.cs +++ b/src/Shared/Grpc/Tracing/Schema.cs @@ -60,5 +60,10 @@ public static class Task /// The time at which the timer is scheduled to fire. /// public const string FireAt = "durabletask.fire_at"; + + /// + /// The name of the entity operation being executed. + /// + public const string EntityOperationName = "durabletask.entity.operation"; } } diff --git a/src/Shared/Grpc/Tracing/TraceHelper.cs b/src/Shared/Grpc/Tracing/TraceHelper.cs index 434ce0b8e..9886b46b5 100644 --- a/src/Shared/Grpc/Tracing/TraceHelper.cs +++ b/src/Shared/Grpc/Tracing/TraceHelper.cs @@ -202,7 +202,7 @@ static class TraceHelper if (!string.IsNullOrEmpty(operationName)) { - newActivity.SetTag("durabletask.entity.operation", operationName); + newActivity.SetTag(Schema.Task.EntityOperationName, operationName); } return newActivity; @@ -526,7 +526,18 @@ static string CreateSpanName(string spanDescription, string? taskName, string? t return null; } - string entityName = historyEvent?.EntityOperationCalled?.TargetInstanceId ?? string.Empty; + string targetInstanceId = historyEvent?.EntityOperationCalled?.TargetInstanceId ?? string.Empty; + + // Extract entity name from instance ID (format: "@name@key") + string entityName = targetInstanceId; + if (targetInstanceId.Length > 1 && targetInstanceId[0] == '@') + { + int secondAt = targetInstanceId.IndexOf('@', 1); + if (secondAt > 1) + { + entityName = targetInstanceId.Substring(1, secondAt - 1); + } + } string spanName = string.IsNullOrEmpty(calledEvent.Operation) ? $"{TraceActivityConstants.EntityOperation}:{entityName}" : $"{TraceActivityConstants.EntityOperation}:{entityName}:{calledEvent.Operation}"; @@ -542,15 +553,13 @@ static string CreateSpanName(string spanDescription, string? taskName, string? t return null; } - if (calledEvent.ParentTraceContext != null) + if (calledEvent.ParentTraceContext != null + && ActivityContext.TryParse( + calledEvent.ParentTraceContext.TraceParent, + calledEvent.ParentTraceContext?.TraceState, + out ActivityContext parentContext)) { - if (ActivityContext.TryParse( - calledEvent.ParentTraceContext.TraceParent, - calledEvent.ParentTraceContext?.TraceState, - out ActivityContext parentContext)) - { - newActivity.SetSpanId(parentContext.SpanId.ToString()); - } + newActivity.SetSpanId(parentContext.SpanId.ToString()); } newActivity.AddTag(Schema.Task.Type, TraceActivityConstants.EntityOperation); @@ -559,7 +568,7 @@ static string CreateSpanName(string spanDescription, string? taskName, string? t if (!string.IsNullOrEmpty(calledEvent.Operation)) { - newActivity.AddTag("durabletask.entity.operation", calledEvent.Operation); + newActivity.AddTag(Schema.Task.EntityOperationName, calledEvent.Operation); } return newActivity; diff --git a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs index d9a2e690c..2e0c352c1 100644 --- a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs +++ b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs @@ -900,7 +900,10 @@ async Task OnRunEntityBatchAsync( var coreEntityId = DTCore.Entities.EntityId.FromString(batchRequest.InstanceId!); EntityId entityId = new(coreEntityId.Name, coreEntityId.Key); - // Start a trace span for entity operation execution using the first operation's trace context. + // Start a Server trace span for entity operation execution using the first operation's + // trace context. In multi-operation batches, only the first operation's trace context is + // used as the parent. This is acceptable because entity batches are typically single-operation, + // and individual Client spans are emitted per-operation in the orchestrator replay path. OperationRequest? firstOp = batchRequest.Operations?.FirstOrDefault(); using Activity? traceActivity = TraceHelper.StartTraceActivityForEntityOperation( entityId.Name, From f78f80bed4535d9c37d08373a49385c65dd57204 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Wed, 4 Mar 2026 15:47:04 -0800 Subject: [PATCH 09/10] Create per-operation Server spans for entity batches Each entity operation in a batch may come from a different orchestration with its own trace context. Create individual Server spans per operation instead of a single span for the whole batch, so each operation's span is correctly parented under its corresponding Client span. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Grpc/GrpcDurableTaskWorker.Processor.cs | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs index 2e0c352c1..150d97c35 100644 --- a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs +++ b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs @@ -900,17 +900,28 @@ async Task OnRunEntityBatchAsync( var coreEntityId = DTCore.Entities.EntityId.FromString(batchRequest.InstanceId!); EntityId entityId = new(coreEntityId.Name, coreEntityId.Key); - // Start a Server trace span for entity operation execution using the first operation's - // trace context. In multi-operation batches, only the first operation's trace context is - // used as the parent. This is acceptable because entity batches are typically single-operation, - // and individual Client spans are emitted per-operation in the orchestrator replay path. - OperationRequest? firstOp = batchRequest.Operations?.FirstOrDefault(); - using Activity? traceActivity = TraceHelper.StartTraceActivityForEntityOperation( - entityId.Name, - firstOp?.Operation, - batchRequest.InstanceId!, - firstOp?.TraceContext?.TraceParent, - firstOp?.TraceContext?.TraceState); + // Start a Server trace span for each entity operation in the batch. + // Each operation may come from a different orchestration with its own trace context, + // so we create individual spans to preserve correct parent-child relationships. + List? traceActivities = null; + if (batchRequest.Operations is { Count: > 0 }) + { + foreach (OperationRequest op in batchRequest.Operations) + { + Activity? activity = TraceHelper.StartTraceActivityForEntityOperation( + entityId.Name, + op.Operation, + batchRequest.InstanceId!, + op.TraceContext?.TraceParent, + op.TraceContext?.TraceState); + + if (activity != null) + { + traceActivities ??= []; + traceActivities.Add(activity); + } + } + } TaskName name = new(entityId.Name); @@ -961,7 +972,11 @@ async Task OnRunEntityBatchAsync( FailureDetails = new FailureDetails(frameworkException), }; - traceActivity?.SetStatus(ActivityStatusCode.Error, frameworkException.Message); + traceActivities?.ForEach(a => a.SetStatus(ActivityStatusCode.Error, frameworkException.Message)); + } + finally + { + traceActivities?.ForEach(a => a.Dispose()); } P.EntityBatchResult response = batchResult.ToEntityBatchResult( From 0e4deefae02ba33bc917082006bd3dfc88eb4e7e Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Wed, 4 Mar 2026 18:17:55 -0800 Subject: [PATCH 10/10] Address PR feedback: LINQ Select, entity-not-found error status - Replace foreach loop with LINQ Select/OfType/ToList for trace activities - Mark trace activities with Error status in entity-not-found path - Use non-nullable List initialized via LINQ expression Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Grpc/GrpcDurableTaskWorker.Processor.cs | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs index 150d97c35..1e1d9e05a 100644 --- a/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs +++ b/src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs @@ -903,25 +903,16 @@ async Task OnRunEntityBatchAsync( // Start a Server trace span for each entity operation in the batch. // Each operation may come from a different orchestration with its own trace context, // so we create individual spans to preserve correct parent-child relationships. - List? traceActivities = null; - if (batchRequest.Operations is { Count: > 0 }) - { - foreach (OperationRequest op in batchRequest.Operations) - { - Activity? activity = TraceHelper.StartTraceActivityForEntityOperation( - entityId.Name, - op.Operation, - batchRequest.InstanceId!, - op.TraceContext?.TraceParent, - op.TraceContext?.TraceState); - - if (activity != null) - { - traceActivities ??= []; - traceActivities.Add(activity); - } - } - } + List traceActivities = batchRequest.Operations? + .Select(op => TraceHelper.StartTraceActivityForEntityOperation( + entityId.Name, + op.Operation, + batchRequest.InstanceId!, + op.TraceContext?.TraceParent, + op.TraceContext?.TraceState)) + .OfType() + .ToList() + ?? []; TaskName name = new(entityId.Name); @@ -943,6 +934,9 @@ async Task OnRunEntityBatchAsync( { // we could not find the entity. This is considered an application error, // so we return a non-retriable error-OperationResult for each operation in the batch. + string errorMessage = $"No entity task named '{name}' was found."; + traceActivities.ForEach(a => a.SetStatus(ActivityStatusCode.Error, errorMessage)); + batchResult = new EntityBatchResult() { Actions = [], // no actions @@ -952,7 +946,7 @@ async Task OnRunEntityBatchAsync( { FailureDetails = new FailureDetails( errorType: "EntityTaskNotFound", - errorMessage: $"No entity task named '{name}' was found.", + errorMessage: errorMessage, stackTrace: null, innerFailure: null, isNonRetriable: true), @@ -972,11 +966,11 @@ async Task OnRunEntityBatchAsync( FailureDetails = new FailureDetails(frameworkException), }; - traceActivities?.ForEach(a => a.SetStatus(ActivityStatusCode.Error, frameworkException.Message)); + traceActivities.ForEach(a => a.SetStatus(ActivityStatusCode.Error, frameworkException.Message)); } finally { - traceActivities?.ForEach(a => a.Dispose()); + traceActivities.ForEach(a => a.Dispose()); } P.EntityBatchResult response = batchResult.ToEntityBatchResult(