Skip to content

Commit 572edf9

Browse files
authored
Merge pull request #301 from microsoft/stevosyan/fix-terminating-suspended-orchestrations
Add logic to terminate suspended instances
2 parents d48539e + a4b5151 commit 572edf9

5 files changed

Lines changed: 57 additions & 18 deletions

File tree

src/DurableTask.SqlServer/Scripts/logic.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ BEGIN
487487

488488
DECLARE @now datetime2(7) = SYSUTCDATETIME()
489489

490-
IF @existingStatus IN ('Running', 'Pending')
490+
IF @existingStatus IN ('Running', 'Pending', 'Suspended')
491491
BEGIN
492492
-- Create a payload to store the reason, if any
493493
DECLARE @PayloadID uniqueidentifier = NULL

src/DurableTask.SqlServer/SqlUtils.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,24 +75,12 @@ public static HistoryEvent GetHistoryEvent(this DbDataReader reader, bool isOrch
7575
InstanceId = GetInstanceId(reader),
7676
};
7777
break;
78-
case EventType.ExecutionCompleted:
78+
case EventType.ExecutionCompleted:
79+
TryGetFailureDetails(reader, out FailureDetails? executionFailedDetails);
7980
historyEvent = new ExecutionCompletedEvent(
8081
eventId,
8182
result: GetPayloadText(reader),
82-
orchestrationStatus: OrchestrationStatus.Completed);
83-
break;
84-
case EventType.ExecutionFailed:
85-
string? executionFailedResult = null;
86-
if (!TryGetFailureDetails(reader, out FailureDetails? executionFailedDetails))
87-
{
88-
// Fall back to the old behavior
89-
executionFailedResult = GetPayloadText(reader);
90-
}
91-
92-
historyEvent = new ExecutionCompletedEvent(
93-
eventId,
94-
result: executionFailedResult,
95-
orchestrationStatus: OrchestrationStatus.Failed,
83+
orchestrationStatus: GetRuntimeStatus(reader),
9684
failureDetails: executionFailedDetails);
9785
break;
9886
case EventType.ExecutionStarted:

src/common.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<PropertyGroup>
1818
<MajorVersion>1</MajorVersion>
1919
<MinorVersion>5</MinorVersion>
20-
<PatchVersion>2</PatchVersion>
20+
<PatchVersion>3</PatchVersion>
2121
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
2222
<VersionSuffix></VersionSuffix>
2323
<AssemblyVersion>$(MajorVersion).$(MinorVersion).0.0</AssemblyVersion>

test/DurableTask.SqlServer.Tests/Integration/DatabaseManagement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ async Task ValidateDatabaseSchemaAsync(TestDatabase database, string schemaName
505505
schemaName);
506506
Assert.Equal(1, currentSchemaVersion.Major);
507507
Assert.Equal(5, currentSchemaVersion.Minor);
508-
Assert.Equal(2, currentSchemaVersion.Patch);
508+
Assert.Equal(3, currentSchemaVersion.Patch);
509509
}
510510

511511
sealed class TestDatabase : IDisposable

test/DurableTask.SqlServer.Tests/Integration/Orchestrations.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,57 @@ await instance.WaitForCompletion(
891891
expectedOutput: "Bye!");
892892

893893
LogAssert.NoWarningsOrErrors(this.testService.LogProvider);
894+
}
895+
896+
[Fact]
897+
public async Task TerminateSuspendedOrchestration()
898+
{
899+
string input = $"Hello {DateTime.UtcNow:o}";
900+
string orchestrationName = "OrchestrationWithTimer";
901+
TimeSpan delay = TimeSpan.FromSeconds(30);
902+
903+
// Performs a delay and then returns the input
904+
TestInstance<string> instance = await this.testService.RunOrchestration(
905+
input,
906+
orchestrationName,
907+
implementation: (ctx, input) => ctx.CreateTimer(ctx.CurrentUtcDateTime.Add(delay), input));
908+
909+
// Wait for the orchestration to finish starting
910+
await instance.WaitForStart();
911+
912+
// Suspend the orchestration so that it won't process any new events
913+
await instance.SuspendAsync();
914+
915+
// Wait for the orchestration to become suspended
916+
OrchestrationState state = await instance.GetStateAsync();
917+
TimeSpan waitForSuspendTimeout = TimeSpan.FromSeconds(5);
918+
using CancellationTokenSource cts = new(waitForSuspendTimeout);
919+
while (!cts.IsCancellationRequested && state.OrchestrationStatus != OrchestrationStatus.Suspended)
920+
{
921+
state = await instance.GetStateAsync();
922+
}
923+
Assert.Equal(OrchestrationStatus.Suspended, state.OrchestrationStatus);
924+
925+
// Now terminate the orchestration
926+
await instance.TerminateAsync("Bye!");
927+
928+
TimeSpan waitForTerminationTimeout = TimeSpan.FromSeconds(5);
929+
state = await instance.WaitForCompletion(
930+
waitForTerminationTimeout,
931+
expectedStatus: OrchestrationStatus.Terminated,
932+
expectedOutput: "Bye!");
933+
934+
// Validate logs
935+
LogAssert.NoWarningsOrErrors(this.testService.LogProvider);
936+
LogAssert.Sequence(
937+
this.testService.LogProvider,
938+
LogAssert.AcquiredAppLock(),
939+
LogAssert.CheckpointStarting(orchestrationName),
940+
LogAssert.CheckpointCompleted(orchestrationName),
941+
LogAssert.CheckpointStarting(orchestrationName),
942+
LogAssert.CheckpointCompleted(orchestrationName),
943+
LogAssert.CheckpointStarting(orchestrationName),
944+
LogAssert.CheckpointCompleted(orchestrationName));
894945
}
895946
}
896947
}

0 commit comments

Comments
 (0)