Skip to content

Commit bb9ab1c

Browse files
authored
Merge branch 'main' into wangbill/atag
2 parents 3fa86b8 + 44969e1 commit bb9ab1c

File tree

13 files changed

+251
-11
lines changed

13 files changed

+251
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Add user agent header to gRPC called in ([#417](https://github.com/microsoft/durabletask-dotnet/pull/417))
99
- Enrich User-Agent Header in gRPC Metadata to indicate Client or Worker as caller ([#421](https://github.com/microsoft/durabletask-dotnet/pull/421))
1010
- Add extension methods for registering entities by type ([#427](https://github.com/microsoft/durabletask-dotnet/pull/427))
11+
- Add TaskVersion and utilize it for version overrides when starting orchestrations ([#416](https://github.com/microsoft/durabletask-dotnet/pull/416))
1112

1213
## v1.10.0
1314

src/Abstractions/TaskName.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace Microsoft.DurableTask;
1616
/// <param name="name">The name of the task. Providing <c>null</c> will yield the default struct.</param>
1717
public TaskName(string name)
1818
{
19+
#pragma warning disable 0618
1920
if (name is null)
2021
{
2122
// Force the default struct when null is passed in.
@@ -27,6 +28,7 @@ public TaskName(string name)
2728
this.Name = name;
2829
this.Version = string.Empty; // expose setting Version only when we actually consume it.
2930
}
31+
#pragma warning restore 0618
3032
}
3133

3234
/// <summary>
@@ -44,6 +46,7 @@ public TaskName(string name)
4446
/// Task versions is currently locked to <see cref="string.Empty" /> as it is not yet integrated into task
4547
/// identification. This is being left here as we intend to support it soon.
4648
/// </remarks>
49+
[Obsolete("Refer to TaskVersion instead.")]
4750
public string Version { get; }
4851

4952
/// <summary>
@@ -122,6 +125,7 @@ public override int GetHashCode()
122125
/// <returns>The name and optional version of the current <see cref="TaskName"/> instance.</returns>
123126
public override string ToString()
124127
{
128+
#pragma warning disable 0618
125129
if (string.IsNullOrEmpty(this.Version))
126130
{
127131
return this.Name;
@@ -130,5 +134,6 @@ public override string ToString()
130134
{
131135
return this.Name + ":" + this.Version;
132136
}
137+
#pragma warning restore 0618
133138
}
134139
}

src/Abstractions/TaskOptions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public SubOrchestrationOptions(TaskOptions options, string? instanceId = null)
106106
/// Gets the orchestration instance ID.
107107
/// </summary>
108108
public string? InstanceId { get; init; }
109+
110+
/// <summary>
111+
/// Gets the version to associate with the sub-orchestration instance.
112+
/// </summary>
113+
public TaskVersion Version { get; init; } = default!;
109114
}
110115

111116
/// <summary>
@@ -124,6 +129,11 @@ public record StartOrchestrationOptions(string? InstanceId = null, DateTimeOffse
124129
/// Gets the tags to associate with the orchestration instance.
125130
/// </summary>
126131
public IReadOnlyDictionary<string, string> Tags { get; init; } = ImmutableDictionary.Create<string, string>();
132+
133+
/// <summary>
134+
/// Gets the version to associate with the orchestration instance.
135+
/// </summary>
136+
public TaskVersion? Version { get; init; }
127137
}
128138

129139
/// <summary>

src/Abstractions/TaskVersion.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.DurableTask;
5+
6+
/// <summary>
7+
/// The version of a durable task.
8+
/// </summary>
9+
public readonly struct TaskVersion : IEquatable<TaskVersion>
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of the <see cref="TaskVersion"/> struct.
13+
/// </summary>
14+
/// <param name="version">The version of the task. Providing <c>null</c> will result in the default struct.</param>
15+
public TaskVersion(string version)
16+
{
17+
if (version == null)
18+
{
19+
this.Version = null!;
20+
}
21+
else
22+
{
23+
this.Version = version;
24+
}
25+
}
26+
27+
/// <summary>
28+
/// Gets the version of a task.
29+
/// </summary>
30+
public string Version { get; }
31+
32+
/// <summary>
33+
/// Implicitly converts a <see cref="TaskVersion"/> into a <see cref="string"/> of the <see cref="Version"/> property value.
34+
/// </summary>
35+
/// <param name="value">The <see cref="TaskVersion"/> to be converted into a <see cref="string"/>.</param>
36+
public static implicit operator string(TaskVersion value) => value.Version;
37+
38+
/// <summary>
39+
/// Implicitly converts a <see cref="string"/> into a <see cref="TaskVersion"/>.
40+
/// </summary>
41+
/// <param name="value">The <see cref="string"/> to convert into a <see cref="TaskVersion"/>.</param>
42+
public static implicit operator TaskVersion(string value) => new TaskVersion(value);
43+
44+
/// <summary>
45+
/// Compares two <see cref="TaskVersion"/> structs for equality.
46+
/// </summary>
47+
/// <param name="a">The first <see cref="TaskVersion"/> to compare.</param>
48+
/// <param name="b">The second <see cref="TaskVersion"/> to compare.</param>
49+
/// <returns><c>true</c> if the two <see cref="TaskVersion"/> objects are equal; otherwise <c>false</c>.</returns>
50+
public static bool operator ==(TaskVersion a, TaskVersion b)
51+
{
52+
return a.Equals(b);
53+
}
54+
55+
/// <summary>
56+
/// Compares two <see cref="TaskVersion"/> structs for inequality.
57+
/// </summary>
58+
/// <param name="a">The first <see cref="TaskVersion"/> to compare.</param>
59+
/// <param name="b">The second <see cref="TaskVersion"/> to compare.</param>
60+
/// <returns><c>false</c> if the two <see cref="TaskVersion"/> objects are equal; otherwise <c>true</c>.</returns>
61+
public static bool operator !=(TaskVersion a, TaskVersion b)
62+
{
63+
return !a.Equals(b);
64+
}
65+
66+
/// <summary>
67+
/// Gets a value indicating whether to <see cref="TaskVersion"/> objects
68+
/// are equal using value semantics.
69+
/// </summary>
70+
/// <param name="other">The other <see cref="TaskVersion"/> to compare to.</param>
71+
/// <returns><c>true</c> if the two <see cref="TaskVersion"/> are equal using value semantics; otherwise <c>false</c>.</returns>
72+
public bool Equals(TaskVersion other)
73+
{
74+
return string.Equals(this.Version, other.Version, StringComparison.OrdinalIgnoreCase);
75+
}
76+
77+
/// <summary>
78+
/// Gets a value indicating whether to <see cref="TaskVersion"/> objects
79+
/// are equal using value semantics.
80+
/// </summary>
81+
/// <param name="obj">The other object to compare to.</param>
82+
/// <returns><c>true</c> if the two objects are equal using value semantics; otherwise <c>false</c>.</returns>
83+
public override bool Equals(object? obj)
84+
{
85+
if (obj is not TaskVersion other)
86+
{
87+
return false;
88+
}
89+
90+
return this.Equals(other);
91+
}
92+
93+
/// <summary>
94+
/// Calculates a hash code value for the current <see cref="TaskVersion"/> instance.
95+
/// </summary>
96+
/// <returns>A 32-bit hash code value.</returns>
97+
public override int GetHashCode()
98+
{
99+
return StringComparer.OrdinalIgnoreCase.GetHashCode(this.Version);
100+
}
101+
}

src/Client/Grpc/GrpcDurableTaskClient.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,11 @@ public override async Task<string> ScheduleNewOrchestrationInstanceAsync(
7878
{
7979
Check.NotEntity(this.options.EnableEntitySupport, options?.InstanceId);
8080

81+
// We're explicitly OK with an empty version from the options as that had to be explicitly set. It should take precedence over the default.
8182
string version = string.Empty;
82-
if (!string.IsNullOrEmpty(orchestratorName.Version))
83+
if (options?.Version is { } v)
8384
{
84-
version = orchestratorName.Version;
85+
version = v;
8586
}
8687
else if (!string.IsNullOrEmpty(this.options.DefaultVersion))
8788
{

src/Client/OrchestrationServiceClientShim/ShimDurableTaskClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public override async Task<string> ScheduleNewOrchestrationInstanceAsync(
172172
Event = new ExecutionStartedEvent(-1, serializedInput)
173173
{
174174
Name = orchestratorName.Name,
175-
Version = orchestratorName.Version,
175+
Version = options?.Version ?? string.Empty,
176176
OrchestrationInstance = instance,
177177
ScheduledStartTime = options?.StartAt?.UtcDateTime,
178178
Tags = options?.Tags != null ? options.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) : null,

src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public static IDurableTaskWorkerBuilder UseVersioning(this IDurableTaskWorkerBui
102102
options.Versioning = new VersioningOptions
103103
{
104104
Version = versionOptions.Version,
105+
DefaultVersion = versionOptions.DefaultVersion,
105106
MatchStrategy = versionOptions.MatchStrategy,
106107
FailureStrategy = versionOptions.FailureStrategy,
107108
};

src/Worker/Core/DurableTaskWorkerOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,11 @@ public class VersioningOptions
203203
/// </summary>
204204
public string Version { get; set; } = string.Empty;
205205

206+
/// <summary>
207+
/// Gets or sets the default version that will be used for starting new orchestrations.
208+
/// </summary>
209+
public string DefaultVersion { get; set; } = string.Empty;
210+
206211
/// <summary>
207212
/// Gets or sets the versioning strategy for the Durable Task worker.
208213
/// </summary>

src/Worker/Core/Shims/TaskEntityShim.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public override string ScheduleNewOrchestration(TaskName name, object? input = n
221221
this.operationActions.Add(new StartNewOrchestrationOperationAction()
222222
{
223223
Name = name.Name,
224-
Version = name.Version,
224+
Version = options?.Version ?? string.Empty,
225225
InstanceId = instanceId,
226226
Input = this.dataConverter.Serialize(input),
227227
ScheduledStartTime = options?.StartAt?.UtcDateTime,

src/Worker/Core/Shims/TaskOrchestrationContextWrapper.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public override async Task<T> CallActivityAsync<T>(
148148
}
149149

150150
// TODO: Cancellation (https://github.com/microsoft/durabletask-dotnet/issues/7)
151+
#pragma warning disable 0618
151152
if (options?.Retry?.Policy is RetryPolicy policy)
152153
{
153154
return await this.innerContext.ScheduleTask<T>(
@@ -189,6 +190,7 @@ public override async Task<T> CallActivityAsync<T>(
189190
// Hide the core DTFx types and instead use our own
190191
throw new TaskFailedException(name, e.ScheduleId, e);
191192
}
193+
#pragma warning restore 0618
192194
}
193195

194196
/// <inheritdoc/>
@@ -201,10 +203,12 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
201203
static string? GetInstanceId(TaskOptions? options)
202204
=> options is SubOrchestrationOptions derived ? derived.InstanceId : null;
203205
string instanceId = GetInstanceId(options) ?? this.NewGuid().ToString("N");
206+
string defaultVersion = this.invocationContext.Options?.Versioning?.DefaultVersion ?? string.Empty;
207+
string version = options is SubOrchestrationOptions subOptions ? subOptions.Version : defaultVersion;
204208

205209
Check.NotEntity(this.invocationContext.Options.EnableEntitySupport, instanceId);
206210

207-
// if this orchestration uses entities, first validate that the suborchsestration call is allowed in the current context
211+
// if this orchestration uses entities, first validate that the suborchestration call is allowed in the current context
208212
if (this.entityFeature != null && !this.entityFeature.EntityContext.ValidateSuborchestrationTransition(out string? errorMsg))
209213
{
210214
throw new InvalidOperationException(errorMsg);
@@ -216,7 +220,7 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
216220
{
217221
return await this.innerContext.CreateSubOrchestrationInstanceWithRetry<TResult>(
218222
orchestratorName.Name,
219-
orchestratorName.Version,
223+
version,
220224
instanceId,
221225
policy.ToDurableTaskCoreRetryOptions(),
222226
input);
@@ -226,7 +230,7 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
226230
return await this.InvokeWithCustomRetryHandler(
227231
() => this.innerContext.CreateSubOrchestrationInstance<TResult>(
228232
orchestratorName.Name,
229-
orchestratorName.Version,
233+
version,
230234
instanceId,
231235
input),
232236
orchestratorName.Name,
@@ -237,7 +241,7 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
237241
{
238242
return await this.innerContext.CreateSubOrchestrationInstance<TResult>(
239243
orchestratorName.Name,
240-
orchestratorName.Version,
244+
version,
241245
instanceId,
242246
input);
243247
}

0 commit comments

Comments
 (0)