Skip to content

Commit c8cb34c

Browse files
authored
Update to latest protobuf definitions (#359)
Summary of changes: - Updates to latest proto definitions in microsoft/durabletask-proto - Implements new work item completion token APIs - Updates System.Text.Json dependency to 6.0.10 to address CVEs - Adds work item concurrency configuration - Bump the version from 1.4.0 to 1.5.0 since there is new public surface area (concurrency configuration)
1 parent 52e18fc commit c8cb34c

File tree

10 files changed

+93
-23
lines changed

10 files changed

+93
-23
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v1.5.0
4+
5+
- Implement work item completion tokens for standalone worker scenarios ([#359](https://github.com/microsoft/durabletask-dotnet/pull/359))
6+
- Support for worker concurrency configuration ([#359](https://github.com/microsoft/durabletask-dotnet/pull/359))
7+
- Bump System.Text.Json to 6.0.10
8+
39
## v1.4.0
410

511
- Microsoft.Azure.DurableTask.Core dependency increased to `3.0.0`

eng/targets/Release.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</PropertyGroup>
1818

1919
<PropertyGroup>
20-
<VersionPrefix>1.4.0</VersionPrefix>
20+
<VersionPrefix>1.5.0</VersionPrefix>
2121
<VersionSuffix></VersionSuffix>
2222
</PropertyGroup>
2323

src/Abstractions/Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
1212
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
1313
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
14-
<PackageReference Include="System.Text.Json" Version="6.0.0" />
14+
<PackageReference Include="System.Text.Json" Version="6.0.10" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

src/Shared/Grpc/ProtoUtils.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,18 +223,24 @@ internal static Timestamp ToTimestamp(this DateTime dateTime)
223223
/// <param name="instanceId">The orchestrator instance ID.</param>
224224
/// <param name="customStatus">The orchestrator customer status or <c>null</c> if no custom status.</param>
225225
/// <param name="actions">The orchestrator actions.</param>
226+
/// <param name="completionToken">
227+
/// The completion token for the work item. It must be the exact same <see cref="P.WorkItem.CompletionToken" />
228+
/// value that was provided by the corresponding <see cref="P.WorkItem"/> that triggered the orchestrator execution.
229+
/// </param>
226230
/// <returns>The orchestrator response.</returns>
227231
/// <exception cref="NotSupportedException">When an orchestrator action is unknown.</exception>
228232
internal static P.OrchestratorResponse ConstructOrchestratorResponse(
229233
string instanceId,
230234
string? customStatus,
231-
IEnumerable<OrchestratorAction> actions)
235+
IEnumerable<OrchestratorAction> actions,
236+
string completionToken)
232237
{
233238
Check.NotNull(actions);
234239
var response = new P.OrchestratorResponse
235240
{
236241
InstanceId = instanceId,
237242
CustomStatus = customStatus,
243+
CompletionToken = completionToken,
238244
};
239245

240246
foreach (OrchestratorAction action in actions)

src/Worker/Core/DurableTaskWorkerOptions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ public DataConverter DataConverter
8383
/// </remarks>
8484
public TimeSpan MaximumTimerInterval { get; set; } = TimeSpan.FromDays(3);
8585

86+
/// <summary>
87+
/// Gets options for the Durable Task worker concurrency.
88+
/// </summary>
89+
/// <remarks>
90+
/// Worker concurrency options control how many work items of a particular type (e.g., orchestration, activity,
91+
/// or entity) can be processed concurrently by the worker. It is recommended to set these values based on the
92+
/// expected workload and the resources available on the machine running the worker.
93+
/// </remarks>
94+
public ConcurrencyOptions Concurrency { get; } = new();
95+
8696
/// <summary>
8797
/// Gets a value indicating whether <see cref="DataConverter" /> was explicitly set or not.
8898
/// </summary>
@@ -108,4 +118,25 @@ internal void ApplyTo(DurableTaskWorkerOptions other)
108118
other.EnableEntitySupport = this.EnableEntitySupport;
109119
}
110120
}
121+
122+
/// <summary>
123+
/// Options for the Durable Task worker concurrency.
124+
/// </summary>
125+
public class ConcurrencyOptions
126+
{
127+
/// <summary>
128+
/// Gets or sets the maximum number of concurrent activity work items that can be processed by the worker.
129+
/// </summary>
130+
public int MaximumConcurrentActivityWorkItems { get; set; } = 100 * Environment.ProcessorCount;
131+
132+
/// <summary>
133+
/// Gets or sets the maximum number of concurrent orchestration work items that can be processed by the worker.
134+
/// </summary>
135+
public int MaximumConcurrentOrchestrationWorkItems { get; set; } = 100 * Environment.ProcessorCount;
136+
137+
/// <summary>
138+
/// Gets or sets the maximum number of concurrent entity work items that can be processed by the worker.
139+
/// </summary>
140+
public int MaximumConcurrentEntityWorkItems { get; set; } = 100 * Environment.ProcessorCount;
141+
}
111142
}

src/Worker/Core/Worker.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The worker is responsible for processing durable task work items.</PackageDescri
1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
1313
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
14-
<PackageReference Include="System.Text.Json" Version="6.0.0" />
14+
<PackageReference Include="System.Text.Json" Version="6.0.10" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

src/Worker/Grpc/GrpcDurableTaskWorker.Processor.cs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public Processor(GrpcDurableTaskWorker worker, TaskHubSidecarServiceClient sidec
3434
{
3535
this.worker = worker;
3636
this.sidecar = sidecar;
37-
this.shimFactory = new DurableTaskShimFactory(this.worker.options, this.worker.loggerFactory);
37+
this.shimFactory = new DurableTaskShimFactory(this.worker.grpcOptions, this.worker.loggerFactory);
3838
}
3939

4040
ILogger Logger => this.worker.logger;
@@ -102,7 +102,7 @@ static OrchestrationRuntimeState BuildRuntimeState(P.OrchestratorRequest request
102102

103103
if (runtimeState.ExecutionStartedEvent == null)
104104
{
105-
// TODO: What's the right way to handle this? Callback to the sidecar with a retryable error request?
105+
// TODO: What's the right way to handle this? Callback to the sidecar with a retriable error request?
106106
throw new InvalidOperationException("The provided orchestration history was incomplete");
107107
}
108108

@@ -133,8 +133,18 @@ static string GetActionsListForLogging(IReadOnlyList<P.OrchestratorAction> actio
133133
await this.sidecar!.HelloAsync(EmptyMessage, cancellationToken: cancellation);
134134
this.Logger.EstablishedWorkItemConnection();
135135

136+
DurableTaskWorkerOptions workerOptions = this.worker.workerOptions;
137+
136138
// Get the stream for receiving work-items
137-
return this.sidecar!.GetWorkItems(new P.GetWorkItemsRequest(), cancellationToken: cancellation);
139+
return this.sidecar!.GetWorkItems(
140+
new P.GetWorkItemsRequest
141+
{
142+
MaxConcurrentActivityWorkItems =
143+
workerOptions.Concurrency.MaximumConcurrentActivityWorkItems,
144+
MaxConcurrentOrchestrationWorkItems =
145+
workerOptions.Concurrency.MaximumConcurrentOrchestrationWorkItems,
146+
},
147+
cancellationToken: cancellation);
138148
}
139149

140150
async Task ProcessWorkItemsAsync(AsyncServerStreamingCall<P.WorkItem> stream, CancellationToken cancellation)
@@ -145,16 +155,25 @@ async Task ProcessWorkItemsAsync(AsyncServerStreamingCall<P.WorkItem> stream, Ca
145155
{
146156
if (workItem.RequestCase == P.WorkItem.RequestOneofCase.OrchestratorRequest)
147157
{
148-
this.RunBackgroundTask(workItem, () => this.OnRunOrchestratorAsync(
149-
workItem.OrchestratorRequest));
158+
this.RunBackgroundTask(
159+
workItem,
160+
() => this.OnRunOrchestratorAsync(workItem.OrchestratorRequest, workItem.CompletionToken));
150161
}
151162
else if (workItem.RequestCase == P.WorkItem.RequestOneofCase.ActivityRequest)
152163
{
153-
this.RunBackgroundTask(workItem, () => this.OnRunActivityAsync(workItem.ActivityRequest));
164+
this.RunBackgroundTask(
165+
workItem,
166+
() => this.OnRunActivityAsync(workItem.ActivityRequest, workItem.CompletionToken));
154167
}
155168
else if (workItem.RequestCase == P.WorkItem.RequestOneofCase.EntityRequest)
156169
{
157-
this.RunBackgroundTask(workItem, () => this.OnRunEntityBatchAsync(workItem.EntityRequest));
170+
this.RunBackgroundTask(
171+
workItem,
172+
() => this.OnRunEntityBatchAsync(workItem.EntityRequest));
173+
}
174+
else if (workItem.RequestCase == P.WorkItem.RequestOneofCase.HealthPing)
175+
{
176+
// No-op
158177
}
159178
else
160179
{
@@ -188,7 +207,7 @@ void RunBackgroundTask(P.WorkItem? workItem, Func<Task> handler)
188207
});
189208
}
190209

191-
async Task OnRunOrchestratorAsync(P.OrchestratorRequest request)
210+
async Task OnRunOrchestratorAsync(P.OrchestratorRequest request, string completionToken)
192211
{
193212
OrchestratorExecutionResult? result = null;
194213
P.TaskFailureDetails? failureDetails = null;
@@ -248,14 +267,16 @@ async Task OnRunOrchestratorAsync(P.OrchestratorRequest request)
248267
response = ProtoUtils.ConstructOrchestratorResponse(
249268
request.InstanceId,
250269
result.CustomStatus,
251-
result.Actions);
270+
result.Actions,
271+
completionToken);
252272
}
253273
else
254274
{
255275
// This is the case for failures that happened *outside* the orchestrator executor
256276
response = new P.OrchestratorResponse
257277
{
258278
InstanceId = request.InstanceId,
279+
CompletionToken = completionToken,
259280
Actions =
260281
{
261282
new P.OrchestratorAction
@@ -279,7 +300,7 @@ async Task OnRunOrchestratorAsync(P.OrchestratorRequest request)
279300
await this.sidecar.CompleteOrchestratorTaskAsync(response);
280301
}
281302

282-
async Task OnRunActivityAsync(P.ActivityRequest request)
303+
async Task OnRunActivityAsync(P.ActivityRequest request, string completionToken)
283304
{
284305
OrchestrationInstance instance = request.OrchestrationInstance.ToCore();
285306
string rawInput = request.Input;
@@ -336,6 +357,7 @@ async Task OnRunActivityAsync(P.ActivityRequest request)
336357
TaskId = request.TaskId,
337358
Result = output,
338359
FailureDetails = failureDetails,
360+
CompletionToken = completionToken,
339361
};
340362

341363
await this.sidecar.CompleteActivityTaskAsync(response);

src/Worker/Grpc/GrpcDurableTaskWorker.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace Microsoft.DurableTask.Worker.Grpc;
1212
/// </summary>
1313
sealed partial class GrpcDurableTaskWorker : DurableTaskWorker
1414
{
15-
readonly GrpcDurableTaskWorkerOptions options;
15+
readonly GrpcDurableTaskWorkerOptions grpcOptions;
16+
readonly DurableTaskWorkerOptions workerOptions;
1617
readonly IServiceProvider services;
1718
readonly ILoggerFactory loggerFactory;
1819
readonly ILogger logger;
@@ -22,18 +23,21 @@ sealed partial class GrpcDurableTaskWorker : DurableTaskWorker
2223
/// </summary>
2324
/// <param name="name">The name of the worker.</param>
2425
/// <param name="factory">The task factory.</param>
25-
/// <param name="options">The gRPC worker options.</param>
26+
/// <param name="grpcOptions">The gRPC-specific worker options.</param>
27+
/// <param name="workerOptions">The generic worker options.</param>
2628
/// <param name="services">The service provider.</param>
2729
/// <param name="loggerFactory">The logger.</param>
2830
public GrpcDurableTaskWorker(
2931
string name,
3032
IDurableTaskFactory factory,
31-
IOptionsMonitor<GrpcDurableTaskWorkerOptions> options,
33+
IOptionsMonitor<GrpcDurableTaskWorkerOptions> grpcOptions,
34+
IOptionsMonitor<DurableTaskWorkerOptions> workerOptions,
3235
IServiceProvider services,
3336
ILoggerFactory loggerFactory)
3437
: base(name, factory)
3538
{
36-
this.options = Check.NotNull(options).Get(name);
39+
this.grpcOptions = Check.NotNull(grpcOptions).Get(name);
40+
this.workerOptions = Check.NotNull(workerOptions).Get(name);
3741
this.services = Check.NotNull(services);
3842
this.loggerFactory = Check.NotNull(loggerFactory);
3943
this.logger = loggerFactory.CreateLogger("Microsoft.DurableTask"); // TODO: use better category name.
@@ -73,19 +77,19 @@ static GrpcChannel GetChannel(string? address)
7377

7478
AsyncDisposable GetCallInvoker(out CallInvoker callInvoker)
7579
{
76-
if (this.options.Channel is GrpcChannel c)
80+
if (this.grpcOptions.Channel is GrpcChannel c)
7781
{
7882
callInvoker = c.CreateCallInvoker();
7983
return default;
8084
}
8185

82-
if (this.options.CallInvoker is CallInvoker invoker)
86+
if (this.grpcOptions.CallInvoker is CallInvoker invoker)
8387
{
8488
callInvoker = invoker;
8589
return default;
8690
}
8791

88-
c = GetChannel(this.options.Address);
92+
c = GetChannel(this.grpcOptions.Address);
8993
callInvoker = c.CreateCallInvoker();
9094
return new AsyncDisposable(() => new(c.ShutdownAsync()));
9195
}

src/Worker/Grpc/GrpcOrchestrationRunner.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ public static string LoadAndRun(
115115
P.OrchestratorResponse response = ProtoUtils.ConstructOrchestratorResponse(
116116
request.InstanceId,
117117
result.CustomStatus,
118-
result.Actions);
118+
result.Actions,
119+
completionToken: string.Empty /* doesn't apply */);
119120
byte[] responseBytes = response.ToByteArray();
120121
return Convert.ToBase64String(responseBytes);
121122
}

0 commit comments

Comments
 (0)