From 3fbb68e2df89a71ed7ce2df3c3003a206a997e40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:40:33 +0000 Subject: [PATCH 01/11] Initial plan From 41c1deb5753897d27c35ebc69113f96f76ca0e66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:44:52 +0000 Subject: [PATCH 02/11] Initial plan Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .../protos/orchestrator_service.proto | 105 ++++++++++++++++-- 1 file changed, 96 insertions(+), 9 deletions(-) diff --git a/internal/durabletask-protobuf/protos/orchestrator_service.proto b/internal/durabletask-protobuf/protos/orchestrator_service.proto index 88928c3b..f58cf37b 100644 --- a/internal/durabletask-protobuf/protos/orchestrator_service.proto +++ b/internal/durabletask-protobuf/protos/orchestrator_service.proto @@ -41,6 +41,7 @@ message TaskFailureDetails { google.protobuf.StringValue stackTrace = 3; TaskFailureDetails innerFailure = 4; bool isNonRetriable = 5; + map properties = 6; } enum OrchestrationStatus { @@ -95,6 +96,7 @@ message TaskScheduledEvent { google.protobuf.StringValue version = 2; google.protobuf.StringValue input = 3; TraceContext parentTraceContext = 4; + map tags = 5; } message TaskCompletedEvent { @@ -192,7 +194,7 @@ message EntityOperationCalledEvent { } message EntityLockRequestedEvent { - string criticalSectionId = 1; + string criticalSectionId = 1; repeated string lockSet = 2; int32 position = 3; google.protobuf.StringValue parentInstanceId = 4; // used only within messages, null in histories @@ -217,7 +219,14 @@ message EntityUnlockSentEvent { message EntityLockGrantedEvent { string criticalSectionId = 1; } - + +message ExecutionRewoundEvent { + google.protobuf.StringValue reason = 1; + google.protobuf.StringValue parentExecutionId = 2; // used only for rewinding suborchestrations, null otherwise + google.protobuf.StringValue instanceId = 3; // used only for rewinding suborchestrations, null otherwise + TraceContext parentTraceContext = 4; // used only for rewinding suborchestrations, null otherwise +} + message HistoryEvent { int32 eventId = 1; google.protobuf.Timestamp timestamp = 2; @@ -244,11 +253,12 @@ message HistoryEvent { ExecutionResumedEvent executionResumed = 22; EntityOperationSignaledEvent entityOperationSignaled = 23; EntityOperationCalledEvent entityOperationCalled = 24; - EntityOperationCompletedEvent entityOperationCompleted = 25; - EntityOperationFailedEvent entityOperationFailed = 26; + EntityOperationCompletedEvent entityOperationCompleted = 25; + EntityOperationFailedEvent entityOperationFailed = 26; EntityLockRequestedEvent entityLockRequested = 27; EntityLockGrantedEvent entityLockGranted = 28; EntityUnlockSentEvent entityUnlockSent = 29; + ExecutionRewoundEvent executionRewound = 30; } } @@ -256,6 +266,8 @@ message ScheduleTaskAction { string name = 1; google.protobuf.StringValue version = 2; google.protobuf.StringValue input = 3; + map tags = 4; + TraceContext parentTraceContext = 5; } message CreateSubOrchestrationAction { @@ -263,6 +275,7 @@ message CreateSubOrchestrationAction { string name = 2; google.protobuf.StringValue version = 3; google.protobuf.StringValue input = 4; + TraceContext parentTraceContext = 5; } message CreateTimerAction { @@ -282,6 +295,7 @@ message CompleteOrchestrationAction { google.protobuf.StringValue newVersion = 4; repeated HistoryEvent carryoverEvents = 5; TaskFailureDetails failureDetails = 6; + map tags = 7; } message TerminateOrchestrationAction { @@ -312,6 +326,11 @@ message OrchestratorAction { } } +message OrchestrationTraceContext { + google.protobuf.StringValue spanID = 1; + google.protobuf.Timestamp spanStartTime = 2; +} + message OrchestratorRequest { string instanceId = 1; google.protobuf.StringValue executionId = 2; @@ -320,6 +339,8 @@ message OrchestratorRequest { OrchestratorEntityParameters entityParameters = 5; bool requiresHistoryStreaming = 6; map properties = 7; + + OrchestrationTraceContext orchestrationTraceContext = 8; } message OrchestratorResponse { @@ -331,6 +352,17 @@ message OrchestratorResponse { // The number of work item events that were processed by the orchestrator. // This field is optional. If not set, the service should assume that the orchestrator processed all events. google.protobuf.Int32Value numEventsProcessed = 5; + OrchestrationTraceContext orchestrationTraceContext = 6; + + // Whether or not a history is required to complete the original OrchestratorRequest and none was provided. + bool requiresHistory = 7; + + // True if this is a partial (chunked) completion. The backend must keep the work item open until the final chunk (isPartial=false). + bool isPartial = 8; + + // Zero-based position of the current chunk within a chunked completion sequence. + // This field is omitted for non-chunked completions. + google.protobuf.Int32Value chunkIndex = 9; } message CreateInstanceRequest { @@ -343,6 +375,7 @@ message CreateInstanceRequest { google.protobuf.StringValue executionId = 7; map tags = 8; TraceContext parentTraceContext = 9; + google.protobuf.Timestamp requestTime = 10; } message OrchestrationIdReusePolicy { @@ -453,6 +486,7 @@ message PurgeInstancesRequest { oneof request { string instanceId = 1; PurgeInstanceFilter purgeInstanceFilter = 2; + InstanceBatch instanceBatch = 4; } bool recursive = 3; } @@ -468,6 +502,15 @@ message PurgeInstancesResponse { google.protobuf.BoolValue isComplete = 2; } +message RestartInstanceRequest { + string instanceId = 1; + bool restartWithNewInstanceId = 2; +} + +message RestartInstanceResponse { + string instanceId = 1; +} + message CreateTaskHubRequest { bool recreateIfExists = 1; } @@ -490,10 +533,12 @@ message SignalEntityRequest { google.protobuf.StringValue input = 3; string requestId = 4; google.protobuf.Timestamp scheduledTime = 5; + TraceContext parentTraceContext = 6; + google.protobuf.Timestamp requestTime = 7; } message SignalEntityResponse { - // no payload + // no payload } message GetEntityRequest { @@ -575,6 +620,7 @@ message OperationRequest { string operation = 1; string requestId = 2; google.protobuf.StringValue input = 3; + TraceContext traceContext = 4; } message OperationResult { @@ -591,10 +637,14 @@ message OperationInfo { message OperationResultSuccess { google.protobuf.StringValue result = 1; + google.protobuf.Timestamp startTimeUtc = 2; + google.protobuf.Timestamp endTimeUtc = 3; } message OperationResultFailure { TaskFailureDetails failureDetails = 1; + google.protobuf.Timestamp startTimeUtc = 2; + google.protobuf.Timestamp endTimeUtc = 3; } message OperationAction { @@ -610,6 +660,8 @@ message SendSignalAction { string name = 2; google.protobuf.StringValue input = 3; google.protobuf.Timestamp scheduledTime = 4; + google.protobuf.Timestamp requestTime = 5; + TraceContext parentTraceContext = 6; } message StartNewOrchestrationAction { @@ -618,6 +670,8 @@ message StartNewOrchestrationAction { google.protobuf.StringValue version = 3; google.protobuf.StringValue input = 4; google.protobuf.Timestamp scheduledTime = 5; + google.protobuf.Timestamp requestTime = 6; + TraceContext parentTraceContext = 7; } message AbandonActivityTaskRequest { @@ -644,6 +698,17 @@ message AbandonEntityTaskResponse { // Empty. } +message SkipGracefulOrchestrationTerminationsRequest { + InstanceBatch instanceBatch = 1; + google.protobuf.StringValue reason = 2; +} + +message SkipGracefulOrchestrationTerminationsResponse { + // Those instances which could not be terminated because they had locked entities at the time of this termination call, + // are already in a terminal state (completed, failed, terminated, etc.), are not orchestrations, or do not exist (i.e. have been purged) + repeated string unterminatedInstanceIds = 1; +} + service TaskHubSidecarService { // Sends a hello request to the sidecar service. rpc Hello(google.protobuf.Empty) returns (google.protobuf.Empty); @@ -657,18 +722,21 @@ service TaskHubSidecarService { // Rewinds an orchestration instance to last known good state and replays from there. rpc RewindInstance(RewindInstanceRequest) returns (RewindInstanceResponse); + // Restarts an orchestration instance. + rpc RestartInstance(RestartInstanceRequest) returns (RestartInstanceResponse); + // Waits for an orchestration instance to reach a running or completion state. rpc WaitForInstanceStart(GetInstanceRequest) returns (GetInstanceResponse); - + // Waits for an orchestration instance to reach a completion state (completed, failed, terminated, etc.). rpc WaitForInstanceCompletion(GetInstanceRequest) returns (GetInstanceResponse); // Raises an event to a running orchestration instance. rpc RaiseEvent(RaiseEventRequest) returns (RaiseEventResponse); - + // Terminates a running orchestration instance. rpc TerminateInstance(TerminateRequest) returns (TerminateResponse); - + // Suspends a running orchestration instance. rpc SuspendInstance(SuspendRequest) returns (SuspendResponse); @@ -714,6 +782,10 @@ service TaskHubSidecarService { // Abandon an entity work item rpc AbandonTaskEntityWorkItem(AbandonEntityTaskRequest) returns (AbandonEntityTaskResponse); + + // "Skip" graceful termination of orchestrations by immediately changing their status in storage to "terminated". + // Note that a maximum of 500 orchestrations can be terminated at a time using this method. + rpc SkipGracefulOrchestrationTerminations(SkipGracefulOrchestrationTerminationsRequest) returns (SkipGracefulOrchestrationTerminationsResponse); } message GetWorkItemsRequest { @@ -732,6 +804,16 @@ enum WorkerCapability { // When set, the service may return work items without any history events as an optimization. // It is strongly recommended that all SDKs support this capability. WORKER_CAPABILITY_HISTORY_STREAMING = 1; + + // Indicates that the worker supports scheduled tasks. + // The service may send schedule-triggered orchestration work items, + // and the worker must handle them, including the scheduledTime field. + WORKER_CAPABILITY_SCHEDULED_TASKS = 2; + + // Signals that the worker can handle large payloads stored externally (e.g., Blob Storage). + // Work items may contain URI references instead of inline data, and the worker must fetch them. + // This avoids message size limits and reduces network overhead. + WORKER_CAPABILITY_LARGE_PAYLOADS = 3; } message WorkItem { @@ -750,7 +832,7 @@ message CompleteTaskResponse { } message HealthPing { - // No payload + // No payload } message StreamInstanceHistoryRequest { @@ -764,3 +846,8 @@ message StreamInstanceHistoryRequest { message HistoryChunk { repeated HistoryEvent events = 1; } + +message InstanceBatch { + // A maximum of 500 instance IDs can be provided in this list. + repeated string instanceIds = 1; +} \ No newline at end of file From 090c941e49129753ea546b3a5f20627c0a1b26c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:51:17 +0000 Subject: [PATCH 03/11] Fix 'default messager has already been registered' warning - Remove duplicate plugin application in samples-azure-functions/build.gradle - Remove duplicate plugin application in endtoendtests/build.gradle - Move 'java' plugin to plugins block - Upgrade azure-functions plugin from 1.11.1 to 1.16.1 Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- endtoendtests/build.gradle | 5 ++--- samples-azure-functions/build.gradle | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/endtoendtests/build.gradle b/endtoendtests/build.gradle index 658e777f..f208d324 100644 --- a/endtoendtests/build.gradle +++ b/endtoendtests/build.gradle @@ -1,8 +1,7 @@ plugins { - id "com.microsoft.azure.azurefunctions" version "1.11.1" + id 'java' + id "com.microsoft.azure.azurefunctions" version "1.16.1" } -apply plugin: 'java' -apply plugin: "com.microsoft.azure.azurefunctions" group 'com.durabletask.endtoend' version '0.0.0-SNAPSHOT' diff --git a/samples-azure-functions/build.gradle b/samples-azure-functions/build.gradle index 646ab7c8..3a017cf4 100644 --- a/samples-azure-functions/build.gradle +++ b/samples-azure-functions/build.gradle @@ -1,8 +1,7 @@ plugins { - id "com.microsoft.azure.azurefunctions" version "1.11.1" + id 'java' + id "com.microsoft.azure.azurefunctions" version "1.16.1" } -apply plugin: 'java' -apply plugin: "com.microsoft.azure.azurefunctions" group 'com.functions' version '0.1.0-SNAPSHOT' From 3e26e32db4f8d9385c5737f8ae37b54c72bf7b31 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:02:42 +0000 Subject: [PATCH 04/11] Revert unintended changes to orchestrator_service.proto The proto file was accidentally modified by the downloadProtoFiles task during the build. This reverts it to the original state. Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .../protos/orchestrator_service.proto | 105 ++---------------- 1 file changed, 9 insertions(+), 96 deletions(-) diff --git a/internal/durabletask-protobuf/protos/orchestrator_service.proto b/internal/durabletask-protobuf/protos/orchestrator_service.proto index f58cf37b..88928c3b 100644 --- a/internal/durabletask-protobuf/protos/orchestrator_service.proto +++ b/internal/durabletask-protobuf/protos/orchestrator_service.proto @@ -41,7 +41,6 @@ message TaskFailureDetails { google.protobuf.StringValue stackTrace = 3; TaskFailureDetails innerFailure = 4; bool isNonRetriable = 5; - map properties = 6; } enum OrchestrationStatus { @@ -96,7 +95,6 @@ message TaskScheduledEvent { google.protobuf.StringValue version = 2; google.protobuf.StringValue input = 3; TraceContext parentTraceContext = 4; - map tags = 5; } message TaskCompletedEvent { @@ -194,7 +192,7 @@ message EntityOperationCalledEvent { } message EntityLockRequestedEvent { - string criticalSectionId = 1; + string criticalSectionId = 1; repeated string lockSet = 2; int32 position = 3; google.protobuf.StringValue parentInstanceId = 4; // used only within messages, null in histories @@ -219,14 +217,7 @@ message EntityUnlockSentEvent { message EntityLockGrantedEvent { string criticalSectionId = 1; } - -message ExecutionRewoundEvent { - google.protobuf.StringValue reason = 1; - google.protobuf.StringValue parentExecutionId = 2; // used only for rewinding suborchestrations, null otherwise - google.protobuf.StringValue instanceId = 3; // used only for rewinding suborchestrations, null otherwise - TraceContext parentTraceContext = 4; // used only for rewinding suborchestrations, null otherwise -} - + message HistoryEvent { int32 eventId = 1; google.protobuf.Timestamp timestamp = 2; @@ -253,12 +244,11 @@ message HistoryEvent { ExecutionResumedEvent executionResumed = 22; EntityOperationSignaledEvent entityOperationSignaled = 23; EntityOperationCalledEvent entityOperationCalled = 24; - EntityOperationCompletedEvent entityOperationCompleted = 25; - EntityOperationFailedEvent entityOperationFailed = 26; + EntityOperationCompletedEvent entityOperationCompleted = 25; + EntityOperationFailedEvent entityOperationFailed = 26; EntityLockRequestedEvent entityLockRequested = 27; EntityLockGrantedEvent entityLockGranted = 28; EntityUnlockSentEvent entityUnlockSent = 29; - ExecutionRewoundEvent executionRewound = 30; } } @@ -266,8 +256,6 @@ message ScheduleTaskAction { string name = 1; google.protobuf.StringValue version = 2; google.protobuf.StringValue input = 3; - map tags = 4; - TraceContext parentTraceContext = 5; } message CreateSubOrchestrationAction { @@ -275,7 +263,6 @@ message CreateSubOrchestrationAction { string name = 2; google.protobuf.StringValue version = 3; google.protobuf.StringValue input = 4; - TraceContext parentTraceContext = 5; } message CreateTimerAction { @@ -295,7 +282,6 @@ message CompleteOrchestrationAction { google.protobuf.StringValue newVersion = 4; repeated HistoryEvent carryoverEvents = 5; TaskFailureDetails failureDetails = 6; - map tags = 7; } message TerminateOrchestrationAction { @@ -326,11 +312,6 @@ message OrchestratorAction { } } -message OrchestrationTraceContext { - google.protobuf.StringValue spanID = 1; - google.protobuf.Timestamp spanStartTime = 2; -} - message OrchestratorRequest { string instanceId = 1; google.protobuf.StringValue executionId = 2; @@ -339,8 +320,6 @@ message OrchestratorRequest { OrchestratorEntityParameters entityParameters = 5; bool requiresHistoryStreaming = 6; map properties = 7; - - OrchestrationTraceContext orchestrationTraceContext = 8; } message OrchestratorResponse { @@ -352,17 +331,6 @@ message OrchestratorResponse { // The number of work item events that were processed by the orchestrator. // This field is optional. If not set, the service should assume that the orchestrator processed all events. google.protobuf.Int32Value numEventsProcessed = 5; - OrchestrationTraceContext orchestrationTraceContext = 6; - - // Whether or not a history is required to complete the original OrchestratorRequest and none was provided. - bool requiresHistory = 7; - - // True if this is a partial (chunked) completion. The backend must keep the work item open until the final chunk (isPartial=false). - bool isPartial = 8; - - // Zero-based position of the current chunk within a chunked completion sequence. - // This field is omitted for non-chunked completions. - google.protobuf.Int32Value chunkIndex = 9; } message CreateInstanceRequest { @@ -375,7 +343,6 @@ message CreateInstanceRequest { google.protobuf.StringValue executionId = 7; map tags = 8; TraceContext parentTraceContext = 9; - google.protobuf.Timestamp requestTime = 10; } message OrchestrationIdReusePolicy { @@ -486,7 +453,6 @@ message PurgeInstancesRequest { oneof request { string instanceId = 1; PurgeInstanceFilter purgeInstanceFilter = 2; - InstanceBatch instanceBatch = 4; } bool recursive = 3; } @@ -502,15 +468,6 @@ message PurgeInstancesResponse { google.protobuf.BoolValue isComplete = 2; } -message RestartInstanceRequest { - string instanceId = 1; - bool restartWithNewInstanceId = 2; -} - -message RestartInstanceResponse { - string instanceId = 1; -} - message CreateTaskHubRequest { bool recreateIfExists = 1; } @@ -533,12 +490,10 @@ message SignalEntityRequest { google.protobuf.StringValue input = 3; string requestId = 4; google.protobuf.Timestamp scheduledTime = 5; - TraceContext parentTraceContext = 6; - google.protobuf.Timestamp requestTime = 7; } message SignalEntityResponse { - // no payload + // no payload } message GetEntityRequest { @@ -620,7 +575,6 @@ message OperationRequest { string operation = 1; string requestId = 2; google.protobuf.StringValue input = 3; - TraceContext traceContext = 4; } message OperationResult { @@ -637,14 +591,10 @@ message OperationInfo { message OperationResultSuccess { google.protobuf.StringValue result = 1; - google.protobuf.Timestamp startTimeUtc = 2; - google.protobuf.Timestamp endTimeUtc = 3; } message OperationResultFailure { TaskFailureDetails failureDetails = 1; - google.protobuf.Timestamp startTimeUtc = 2; - google.protobuf.Timestamp endTimeUtc = 3; } message OperationAction { @@ -660,8 +610,6 @@ message SendSignalAction { string name = 2; google.protobuf.StringValue input = 3; google.protobuf.Timestamp scheduledTime = 4; - google.protobuf.Timestamp requestTime = 5; - TraceContext parentTraceContext = 6; } message StartNewOrchestrationAction { @@ -670,8 +618,6 @@ message StartNewOrchestrationAction { google.protobuf.StringValue version = 3; google.protobuf.StringValue input = 4; google.protobuf.Timestamp scheduledTime = 5; - google.protobuf.Timestamp requestTime = 6; - TraceContext parentTraceContext = 7; } message AbandonActivityTaskRequest { @@ -698,17 +644,6 @@ message AbandonEntityTaskResponse { // Empty. } -message SkipGracefulOrchestrationTerminationsRequest { - InstanceBatch instanceBatch = 1; - google.protobuf.StringValue reason = 2; -} - -message SkipGracefulOrchestrationTerminationsResponse { - // Those instances which could not be terminated because they had locked entities at the time of this termination call, - // are already in a terminal state (completed, failed, terminated, etc.), are not orchestrations, or do not exist (i.e. have been purged) - repeated string unterminatedInstanceIds = 1; -} - service TaskHubSidecarService { // Sends a hello request to the sidecar service. rpc Hello(google.protobuf.Empty) returns (google.protobuf.Empty); @@ -722,21 +657,18 @@ service TaskHubSidecarService { // Rewinds an orchestration instance to last known good state and replays from there. rpc RewindInstance(RewindInstanceRequest) returns (RewindInstanceResponse); - // Restarts an orchestration instance. - rpc RestartInstance(RestartInstanceRequest) returns (RestartInstanceResponse); - // Waits for an orchestration instance to reach a running or completion state. rpc WaitForInstanceStart(GetInstanceRequest) returns (GetInstanceResponse); - + // Waits for an orchestration instance to reach a completion state (completed, failed, terminated, etc.). rpc WaitForInstanceCompletion(GetInstanceRequest) returns (GetInstanceResponse); // Raises an event to a running orchestration instance. rpc RaiseEvent(RaiseEventRequest) returns (RaiseEventResponse); - + // Terminates a running orchestration instance. rpc TerminateInstance(TerminateRequest) returns (TerminateResponse); - + // Suspends a running orchestration instance. rpc SuspendInstance(SuspendRequest) returns (SuspendResponse); @@ -782,10 +714,6 @@ service TaskHubSidecarService { // Abandon an entity work item rpc AbandonTaskEntityWorkItem(AbandonEntityTaskRequest) returns (AbandonEntityTaskResponse); - - // "Skip" graceful termination of orchestrations by immediately changing their status in storage to "terminated". - // Note that a maximum of 500 orchestrations can be terminated at a time using this method. - rpc SkipGracefulOrchestrationTerminations(SkipGracefulOrchestrationTerminationsRequest) returns (SkipGracefulOrchestrationTerminationsResponse); } message GetWorkItemsRequest { @@ -804,16 +732,6 @@ enum WorkerCapability { // When set, the service may return work items without any history events as an optimization. // It is strongly recommended that all SDKs support this capability. WORKER_CAPABILITY_HISTORY_STREAMING = 1; - - // Indicates that the worker supports scheduled tasks. - // The service may send schedule-triggered orchestration work items, - // and the worker must handle them, including the scheduledTime field. - WORKER_CAPABILITY_SCHEDULED_TASKS = 2; - - // Signals that the worker can handle large payloads stored externally (e.g., Blob Storage). - // Work items may contain URI references instead of inline data, and the worker must fetch them. - // This avoids message size limits and reduces network overhead. - WORKER_CAPABILITY_LARGE_PAYLOADS = 3; } message WorkItem { @@ -832,7 +750,7 @@ message CompleteTaskResponse { } message HealthPing { - // No payload + // No payload } message StreamInstanceHistoryRequest { @@ -846,8 +764,3 @@ message StreamInstanceHistoryRequest { message HistoryChunk { repeated HistoryEvent events = 1; } - -message InstanceBatch { - // A maximum of 500 instance IDs can be provided in this list. - repeated string instanceIds = 1; -} \ No newline at end of file From 5472f1b438a4fa7adc5c6d7c7a4bb0fd4e388787 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:05:04 +0000 Subject: [PATCH 05/11] Revert plugin version to 1.11.1 to fix Gradle 7.4 compatibility The azure-functions plugin 1.16.1 brings in jackson-core 2.17.0 which contains Java 21 class files that Gradle 7.4's ASM cannot handle. Keep the duplicate plugin application removal fix which helps reduce the "default messager" warning. Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- endtoendtests/build.gradle | 2 +- samples-azure-functions/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/endtoendtests/build.gradle b/endtoendtests/build.gradle index f208d324..9aad765b 100644 --- a/endtoendtests/build.gradle +++ b/endtoendtests/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id "com.microsoft.azure.azurefunctions" version "1.16.1" + id "com.microsoft.azure.azurefunctions" version "1.11.1" } group 'com.durabletask.endtoend' diff --git a/samples-azure-functions/build.gradle b/samples-azure-functions/build.gradle index 3a017cf4..4f283c57 100644 --- a/samples-azure-functions/build.gradle +++ b/samples-azure-functions/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id "com.microsoft.azure.azurefunctions" version "1.16.1" + id "com.microsoft.azure.azurefunctions" version "1.11.1" } group 'com.functions' From a6d684ff2025035f274611acc15b6f180b0bef69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:13:59 +0000 Subject: [PATCH 06/11] Fix SpotBugs Java version - run with JDK 11 SpotBugs 4.9.2 requires Java 11+ (class file version 55.0) but the workflow was running it with Java 8. Updated workflow to use JDK_11 environment variable for SpotBugs tasks. Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .github/workflows/build-validation.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index db7f298f..4cb57f1b 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -45,7 +45,9 @@ jobs: run: ./gradlew build -x test - name: Run SpotBugs - run: ./gradlew spotbugsMain spotbugsTest + run: | + export JAVA_HOME=$JDK_11 + ./gradlew spotbugsMain spotbugsTest continue-on-error: false - name: Upload SpotBugs reports From 2cae306a96c1e62b2442aca7a9699e7354f7d817 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:19:30 +0000 Subject: [PATCH 07/11] Fix SpotBugs - run build task with JDK 11 too SpotBugs runs as part of the 'build' task, not just in the explicit SpotBugs step. Updated the build step to also use JDK_11 since SpotBugs 4.9.2 requires Java 11+. Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .github/workflows/build-validation.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index 4cb57f1b..12850904 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -42,7 +42,9 @@ jobs: uses: gradle/gradle-build-action@v2 - name: Build with Gradle - run: ./gradlew build -x test + run: | + export JAVA_HOME=$JDK_11 + ./gradlew build -x test - name: Run SpotBugs run: | From 50803370f29358ae3612b3ded9c069d536ac1a5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:35:36 +0000 Subject: [PATCH 08/11] Update all CI jobs to use JDK 11 - Updated Integration Tests to use JDK_11 - Updated functions-e2e-tests job to use JDK 11 instead of JDK 8 - Updated functions-sample-tests job to use JDK 11 instead of JDK 8 Regarding Gradle 8.0: Upgrading would require also updating the protobuf plugin from 0.8.16 to 0.9.x for full compatibility, which is a larger change that could be done in a separate PR. Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .github/workflows/build-validation.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index 12850904..da126de1 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -88,7 +88,9 @@ jobs: run: sleep 10 - name: Integration Tests with Gradle - run: ./gradlew integrationTest || echo "TEST_FAILED=true" >> $GITHUB_ENV + run: | + export JAVA_HOME=$JDK_11 + ./gradlew integrationTest || echo "TEST_FAILED=true" >> $GITHUB_ENV continue-on-error: true - name: Kill Durable Task Emulator @@ -124,11 +126,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v2 with: - java-version: '8' - distribution: 'temurin' + java-version: '11' + distribution: 'microsoft' - name: Setup Gradle uses: gradle/gradle-build-action@v2 @@ -163,11 +165,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v2 with: - java-version: '8' - distribution: 'temurin' + java-version: '11' + distribution: 'microsoft' - name: Setup Gradle uses: gradle/gradle-build-action@v2 From 19df1cd1cf963e7da08b4efa91b0bf5856ec51a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:15:54 +0000 Subject: [PATCH 09/11] Fix DTS emulator connectivity with proper health check loop Replace fixed 10-second sleep with a robust health check that: - Actively polls port 4001 using nc until the emulator is ready - Allows up to 60 seconds for the emulator to start - Provides better error diagnostics if startup fails - Adds 5 extra seconds after port is open for gRPC initialization Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .github/workflows/build-validation.yml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index da126de1..ae6330e9 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -81,11 +81,29 @@ jobs: run: docker run --name durabletask-emulator -p 4001:8080 -d mcr.microsoft.com/dts/dts-emulator:latest - name: Display Durable Task Emulator Logs - run: nohup docker logs --since=0 durabletask-emulator > durabletask-emulator.log 2>&1 & + run: nohup docker logs -f durabletask-emulator > durabletask-emulator.log 2>&1 & - # wait for 10 seconds, so sidecar container can be fully up, this will avoid intermittent failing issues for integration tests causing by failed to connect to sidecar - - name: Wait for 10 seconds - run: sleep 10 + - name: Wait for Durable Task Emulator to be ready + run: | + echo "Waiting for Durable Task Emulator to be ready on port 4001..." + max_attempts=60 + attempt=0 + while ! nc -z localhost 4001 2>/dev/null; do + attempt=$((attempt + 1)) + if [ $attempt -ge $max_attempts ]; then + echo "ERROR: Emulator not ready after $max_attempts seconds" + echo "Docker container status:" + docker ps -a + echo "Container logs:" + docker logs durabletask-emulator 2>&1 | tail -50 + exit 1 + fi + echo "Attempt $attempt/$max_attempts - waiting for emulator..." + sleep 1 + done + echo "Durable Task Emulator is ready on port 4001" + # Give additional time for gRPC service to fully initialize after port is open + sleep 5 - name: Integration Tests with Gradle run: | From 570d6305910d05e7e65afa189bb849d15b1afc13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:18:01 +0000 Subject: [PATCH 10/11] Use bash /dev/tcp for more portable health check Address code review feedback: - Replace nc with bash's /dev/tcp for portability - Rename max_attempts to MAX_WAIT_SECONDS with comment - Add comment explaining the TCP check mechanism Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .github/workflows/build-validation.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index ae6330e9..21573fb8 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -86,19 +86,21 @@ jobs: - name: Wait for Durable Task Emulator to be ready run: | echo "Waiting for Durable Task Emulator to be ready on port 4001..." - max_attempts=60 + # Maximum wait time of 60 seconds for emulator startup + MAX_WAIT_SECONDS=60 attempt=0 - while ! nc -z localhost 4001 2>/dev/null; do + # Use bash's /dev/tcp for portability instead of nc + while ! (echo > /dev/tcp/localhost/4001) 2>/dev/null; do attempt=$((attempt + 1)) - if [ $attempt -ge $max_attempts ]; then - echo "ERROR: Emulator not ready after $max_attempts seconds" + if [ $attempt -ge $MAX_WAIT_SECONDS ]; then + echo "ERROR: Emulator not ready after $MAX_WAIT_SECONDS seconds" echo "Docker container status:" docker ps -a echo "Container logs:" docker logs durabletask-emulator 2>&1 | tail -50 exit 1 fi - echo "Attempt $attempt/$max_attempts - waiting for emulator..." + echo "Attempt $attempt/$MAX_WAIT_SECONDS - waiting for emulator..." sleep 1 done echo "Durable Task Emulator is ready on port 4001" From 45316b1daad553367745ecc6f773b90ff177b56f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 20:06:27 +0000 Subject: [PATCH 11/11] Simplify Java setup: remove JDK 8, use only JDK 11 - Remove JDK 8 setup since it's not needed - Remove JDK_11 environment variable workaround - Remove all `export JAVA_HOME=$JDK_11` lines - Upgrade setup-java from v2 to v4 in all jobs - Workflow now uses only JDK 11 throughout Co-authored-by: torosent <17064840+torosent@users.noreply.github.com> --- .github/workflows/build-validation.yml | 33 ++++++-------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index 21573fb8..8fd6d57c 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -22,34 +22,19 @@ jobs: - uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'microsoft' - - name: set JDK_11 environment variable test compiling and running - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: true - run: echo ::set-env name=JDK_11::$(echo $JAVA_HOME) - - - name: Set up JDK 8 - uses: actions/setup-java@v2 - with: - java-version: '8' - distribution: 'temurin' - - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Build with Gradle - run: | - export JAVA_HOME=$JDK_11 - ./gradlew build -x test + run: ./gradlew build -x test - name: Run SpotBugs - run: | - export JAVA_HOME=$JDK_11 - ./gradlew spotbugsMain spotbugsTest + run: ./gradlew spotbugsMain spotbugsTest continue-on-error: false - name: Upload SpotBugs reports @@ -60,9 +45,7 @@ jobs: if-no-files-found: ignore - name: Run Unit Tests with Gradle - run: | - export JAVA_HOME=$JDK_11 - ./gradlew clean test || echo "UNIT_TEST_FAILED=true" >> $GITHUB_ENV + run: ./gradlew clean test || echo "UNIT_TEST_FAILED=true" >> $GITHUB_ENV continue-on-error: true - name: Upload test reports if tests failed @@ -108,9 +91,7 @@ jobs: sleep 5 - name: Integration Tests with Gradle - run: | - export JAVA_HOME=$JDK_11 - ./gradlew integrationTest || echo "TEST_FAILED=true" >> $GITHUB_ENV + run: ./gradlew integrationTest || echo "TEST_FAILED=true" >> $GITHUB_ENV continue-on-error: true - name: Kill Durable Task Emulator @@ -147,7 +128,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'microsoft' @@ -186,7 +167,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'microsoft'