Skip to content

Commit 1e30e97

Browse files
authored
Add opt-in Timeout to PurgeInstancesFilter for partial purge (#680)
1 parent 727670f commit 1e30e97

5 files changed

Lines changed: 73 additions & 2 deletions

File tree

src/Client/Core/PurgeInstancesFilter.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,16 @@ public record PurgeInstancesFilter(
1414
DateTimeOffset? CreatedTo = null,
1515
IEnumerable<OrchestrationRuntimeStatus>? Statuses = null)
1616
{
17+
/// <summary>
18+
/// Gets or sets the maximum amount of time to spend purging instances in a single call.
19+
/// If <c>null</c> (default), all matching instances are purged with no time limit.
20+
/// When set, the purge operation stops deleting additional instances after this duration elapses
21+
/// and returns a partial result. Callers can check <see cref="PurgeResult.IsComplete"/> and
22+
/// re-invoke the purge to continue where it left off.
23+
/// The value of <see cref="PurgeResult.IsComplete"/> depends on the backend implementation:
24+
/// it may be <c>false</c> if the purge timed out, <c>true</c> if all instances were purged,
25+
/// or <c>null</c> if the backend does not support reporting completion status.
26+
/// Not all backends support this property; those that do not will ignore it.
27+
/// </summary>
28+
public TimeSpan? Timeout { get; init; }
1729
}

src/Client/Grpc/GrpcDurableTaskClient.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,19 @@ public override Task<PurgeResult> PurgeAllInstancesAsync(
488488
request.PurgeInstanceFilter.RuntimeStatus.AddRange(filter.Statuses.Select(x => x.ToGrpcStatus()));
489489
}
490490

491+
if (filter?.Timeout is not null)
492+
{
493+
if (filter.Timeout.Value <= TimeSpan.Zero)
494+
{
495+
throw new ArgumentOutOfRangeException(
496+
nameof(filter),
497+
filter.Timeout.Value,
498+
"PurgeInstancesFilter.Timeout must be a positive TimeSpan.");
499+
}
500+
501+
request.PurgeInstanceFilter.Timeout = Google.Protobuf.WellKnownTypes.Duration.FromTimeSpan(filter.Timeout.Value);
502+
}
503+
491504
return this.PurgeInstancesCoreAsync(request, cancellation);
492505
}
493506

src/Grpc/orchestrator_service.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ message ActivityRequest {
2525
OrchestrationInstance orchestrationInstance = 4;
2626
int32 taskId = 5;
2727
TraceContext parentTraceContext = 6;
28+
map<string, string> tags = 7;
2829
}
2930

3031
message ActivityResponse {
@@ -320,6 +321,10 @@ message SendEntityMessageAction {
320321
}
321322
}
322323

324+
message RewindOrchestrationAction {
325+
repeated HistoryEvent newHistory = 1;
326+
}
327+
323328
message OrchestratorAction {
324329
int32 id = 1;
325330
oneof orchestratorActionType {
@@ -330,6 +335,7 @@ message OrchestratorAction {
330335
CompleteOrchestrationAction completeOrchestration = 6;
331336
TerminateOrchestrationAction terminateOrchestration = 7;
332337
SendEntityMessageAction sendEntityMessage = 8;
338+
RewindOrchestrationAction rewindOrchestration = 9;
333339
}
334340
}
335341

@@ -517,6 +523,7 @@ message PurgeInstanceFilter {
517523
google.protobuf.Timestamp createdTimeFrom = 1;
518524
google.protobuf.Timestamp createdTimeTo = 2;
519525
repeated OrchestrationStatus runtimeStatus = 3;
526+
google.protobuf.Duration timeout = 4;
520527
}
521528

522529
message PurgeInstancesResponse {

src/Grpc/versions.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
# The following files were downloaded from branch main at 2026-02-24 00:01:28 UTC
2-
https://raw.githubusercontent.com/microsoft/durabletask-protobuf/1caadbd7ecfdf5f2309acbeac28a3e36d16aa156/protos/orchestrator_service.proto
1+
# The following files were downloaded from branch main at 2026-04-06 16:10:08 UTC
2+
https://raw.githubusercontent.com/microsoft/durabletask-protobuf/bcf5af6a22caa70601bfc909918ba5937484279f/protos/orchestrator_service.proto

test/Client/Grpc.Tests/GrpcDurableTaskClientTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,44 @@ public async Task ScheduleNewOrchestrationInstanceAsync_ValidDedupeStatus_DoesNo
127127
var exception = await act.Should().ThrowAsync<Exception>();
128128
exception.Which.Should().NotBeOfType<ArgumentException>();
129129
}
130+
131+
[Fact]
132+
public async Task PurgeAllInstancesAsync_NegativeTimeout_ThrowsArgumentOutOfRangeException()
133+
{
134+
// Arrange
135+
var client = this.CreateClient();
136+
var filter = new PurgeInstancesFilter { Timeout = TimeSpan.FromSeconds(-1) };
137+
138+
// Act & Assert
139+
Func<Task> act = async () => await client.PurgeAllInstancesAsync(filter);
140+
var exception = await act.Should().ThrowAsync<ArgumentOutOfRangeException>();
141+
exception.Which.Message.Should().Contain("Timeout must be a positive TimeSpan.");
142+
}
143+
144+
[Fact]
145+
public async Task PurgeAllInstancesAsync_ZeroTimeout_ThrowsArgumentOutOfRangeException()
146+
{
147+
// Arrange
148+
var client = this.CreateClient();
149+
var filter = new PurgeInstancesFilter { Timeout = TimeSpan.Zero };
150+
151+
// Act & Assert
152+
Func<Task> act = async () => await client.PurgeAllInstancesAsync(filter);
153+
var exception = await act.Should().ThrowAsync<ArgumentOutOfRangeException>();
154+
exception.Which.Message.Should().Contain("Timeout must be a positive TimeSpan.");
155+
}
156+
157+
[Fact]
158+
public async Task PurgeAllInstancesAsync_PositiveTimeout_DoesNotThrowValidationError()
159+
{
160+
// Arrange
161+
var client = this.CreateClient();
162+
var filter = new PurgeInstancesFilter { Timeout = TimeSpan.FromSeconds(30) };
163+
164+
// Act & Assert - validation should pass; the call will fail at gRPC level, not validation
165+
Func<Task> act = async () => await client.PurgeAllInstancesAsync(filter);
166+
var exception = await act.Should().ThrowAsync<Exception>();
167+
exception.Which.Should().NotBeOfType<ArgumentOutOfRangeException>();
168+
}
130169
}
131170

0 commit comments

Comments
 (0)