Skip to content

Commit bb993ca

Browse files
committed
Move Version to TaskVersion and use as TaskOption
Signed-off-by: halspang <[email protected]>
1 parent ec03d94 commit bb993ca

File tree

10 files changed

+245
-47
lines changed

10 files changed

+245
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +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-
12-
>>>>>>> 766fa69 (Add version to TaskName)
11+
- Add TaskVersion and utilize it for version overrides when starting orchestrations ([#416](https://github.com/microsoft/durabletask-dotnet/pull/416))
1312

1413
## v1.10.0
1514

src/Abstractions/TaskName.cs

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
using System;
5-
64
namespace Microsoft.DurableTask;
75

86
/// <summary>
@@ -27,31 +25,7 @@ public TaskName(string name)
2725
else
2826
{
2927
this.Name = name;
30-
this.Version = string.Empty;
31-
}
32-
}
33-
34-
/// <summary>
35-
/// Initializes a new instance of the <see cref="TaskName"/> struct.
36-
/// </summary>
37-
/// <remarks>
38-
/// This <c>TaskName</c> is explicitly versioned, as compared to the default value.
39-
/// </remarks>
40-
/// <param name="name">The name of the task. Providing <c>null</c> will yield the default struct, ignoring the version.</param>
41-
/// <param name="version">Optional. The version of the task. Providing <c>null</c> will yield the default value.</param>
42-
public TaskName(string name, string version)
43-
{
44-
if (name is null)
45-
{
46-
// Force the default struct when null is passed in.
47-
this.Name = null!;
48-
this.Version = null!;
49-
}
50-
else
51-
{
52-
this.IsVersioned = true;
53-
this.Name = name;
54-
this.Version = version == null ? null! : version;
28+
this.Version = string.Empty; // expose setting Version only when we actually consume it.
5529
}
5630
}
5731

@@ -70,16 +44,9 @@ public TaskName(string name, string version)
7044
/// Task versions is currently locked to <see cref="string.Empty" /> as it is not yet integrated into task
7145
/// identification. This is being left here as we intend to support it soon.
7246
/// </remarks>
47+
[Obsolete("Refer to TaskVersion instead.")]
7348
public string Version { get; }
7449

75-
/// <summary>
76-
/// Gets the flag denoting if this TaskName is versioned.
77-
/// </summary>
78-
/// <remarks>
79-
/// This flag is used to distinguish between the default (empty version) and a Task with an explicit empty version.
80-
/// </remarks>
81-
public bool IsVersioned { get; } = false;
82-
8350
/// <summary>
8451
/// Implicitly converts a <see cref="TaskName"/> into a <see cref="string"/> of the <see cref="Name"/> property value.
8552
/// </summary>

src/Abstractions/TaskOptions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ public SubOrchestrationOptions(TaskOptions options, string? instanceId = null)
9090
/// Gets the orchestration instance ID.
9191
/// </summary>
9292
public string? InstanceId { get; init; }
93+
94+
/// <summary>
95+
/// Gets the version to associate with the sub-orchestration instance.
96+
/// </summary>
97+
public TaskVersion Version { get; init; } = default!;
9398
}
9499

95100
/// <summary>
@@ -108,4 +113,9 @@ public record StartOrchestrationOptions(string? InstanceId = null, DateTimeOffse
108113
/// Gets the tags to associate with the orchestration instance.
109114
/// </summary>
110115
public IReadOnlyDictionary<string, string> Tags { get; init; } = ImmutableDictionary.Create<string, string>();
116+
117+
/// <summary>
118+
/// Gets the version to associate with the orchestration instance.
119+
/// </summary>
120+
public TaskVersion? Version { get; init; }
111121
}

src/Abstractions/TaskOrchestrationContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ public abstract class TaskOrchestrationContext
6161
public abstract bool IsReplaying { get; }
6262

6363
/// <summary>
64-
/// Gets or sets the version of the current orchestration instance, which was set when the instance was created.
64+
/// Gets the version of the current orchestration instance, which was set when the instance was created.
6565
/// </summary>
66-
public virtual string Version { get; protected internal set; } = string.Empty;
66+
public virtual string Version => string.Empty;
6767

6868
/// <summary>
6969
/// Gets the configuration settings for the orchestration context.

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 != null && options.Version != null)
8384
{
84-
version = orchestratorName.Version;
85+
version = options.Version;
8586
}
8687
else if (!string.IsNullOrEmpty(this.options.DefaultVersion))
8788
{

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/TaskOrchestrationContextWrapper.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,14 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
177177
static string? GetInstanceId(TaskOptions? options)
178178
=> options is SubOrchestrationOptions derived ? derived.InstanceId : null;
179179
string instanceId = GetInstanceId(options) ?? this.NewGuid().ToString("N");
180+
string defaultVersion = this.invocationContext.Options != null && this.invocationContext.Options.Versioning != null
181+
? this.invocationContext.Options.Versioning.DefaultVersion
182+
: string.Empty;
183+
string version = options != null && options is SubOrchestrationOptions subOptions ? subOptions.Version : defaultVersion;
180184

181185
Check.NotEntity(this.invocationContext.Options.EnableEntitySupport, instanceId);
182186

183-
// if this orchestration uses entities, first validate that the suborchsestration call is allowed in the current context
187+
// if this orchestration uses entities, first validate that the suborchestration call is allowed in the current context
184188
if (this.entityFeature != null && !this.entityFeature.EntityContext.ValidateSuborchestrationTransition(out string? errorMsg))
185189
{
186190
throw new InvalidOperationException(errorMsg);
@@ -192,7 +196,7 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
192196
{
193197
return await this.innerContext.CreateSubOrchestrationInstanceWithRetry<TResult>(
194198
orchestratorName.Name,
195-
orchestratorName.Version,
199+
version,
196200
instanceId,
197201
policy.ToDurableTaskCoreRetryOptions(),
198202
input);
@@ -202,7 +206,7 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
202206
return await this.InvokeWithCustomRetryHandler(
203207
() => this.innerContext.CreateSubOrchestrationInstance<TResult>(
204208
orchestratorName.Name,
205-
orchestratorName.Version,
209+
version,
206210
instanceId,
207211
input),
208212
orchestratorName.Name,
@@ -213,7 +217,7 @@ public override async Task<TResult> CallSubOrchestratorAsync<TResult>(
213217
{
214218
return await this.innerContext.CreateSubOrchestrationInstance<TResult>(
215219
orchestratorName.Name,
216-
orchestratorName.Version,
220+
version,
217221
instanceId,
218222
input);
219223
}

0 commit comments

Comments
 (0)