Skip to content

Commit 022d567

Browse files
authored
Introduce orchestration versioning into worker (#401)
This commit gives the worker several ways to configure versioning. Included are, setting the version, how to match against incoming versions, and how to react to a failed match. Signed-off-by: halspang <[email protected]>
1 parent 85dbb19 commit 022d567

File tree

8 files changed

+1070
-656
lines changed

8 files changed

+1070
-656
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
- Introduce default version setting to DurableTaskClient and expose to orchestrator ([#393](https://github.com/microsoft/durabletask-dotnet/pull/393))
66
- Add support for local credential types in DTS libraries ([#396](https://github.com/microsoft/durabletask-dotnet/pull/396))
77
- Add utility for easier version comparison in orchestration context ([#394](https://github.com/microsoft/durabletask-dotnet/pull/394))
8-
- Add tags support for orchestrations ([#397])(https://github.com/microsoft/durabletask-dotnet/pull/397)
8+
- Add tags support for orchestrations ([#397](https://github.com/microsoft/durabletask-dotnet/pull/397))
9+
- Add support for versioning in the gRPC worker ([#401](https://github.com/microsoft/durabletask-dotnet/pull/401))
910

1011
## v1.8.1
1112

src/Abstractions/TaskOrchestrationContext.cs

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

4+
using Microsoft.DurableTask.Abstractions;
45
using Microsoft.DurableTask.Entities;
56
using Microsoft.Extensions.Logging;
67

@@ -421,32 +422,7 @@ public virtual ILogger CreateReplaySafeLogger<T>()
421422
/// <returns>True if the orchestration's version is greater than the provided version, false otherwise.</returns>
422423
public virtual int CompareVersionTo(string version)
423424
{
424-
// Both versions are empty, treat as equal.
425-
if (string.IsNullOrWhiteSpace(this.Version) && string.IsNullOrWhiteSpace(version))
426-
{
427-
return 0;
428-
}
429-
430-
// An empty version in the context is always less than a defined version in the parameter.
431-
if (string.IsNullOrWhiteSpace(this.Version))
432-
{
433-
return -1;
434-
}
435-
436-
// An empty version in the parameter is always less than a defined version in the context.
437-
if (string.IsNullOrWhiteSpace(version))
438-
{
439-
return 1;
440-
}
441-
442-
// If both versions use the .NET Version class, return that comparison.
443-
if (System.Version.TryParse(this.Version, out Version contextVersion) && System.Version.TryParse(version, out Version otherVersion))
444-
{
445-
return contextVersion.CompareTo(otherVersion);
446-
}
447-
448-
// If we have gotten to here, we don't know the syntax of the versions we are comparing, use a string comparison as a final check.
449-
return string.Compare(this.Version, version, StringComparison.OrdinalIgnoreCase);
425+
return TaskOrchestrationVersioningUtils.CompareVersions(this.Version, version);
450426
}
451427

452428
class ReplaySafeLogger : ILogger
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.DurableTask.Abstractions;
5+
6+
/// <summary>
7+
/// Utilities for handling Orchestration/Task versioning operations.
8+
/// </summary>
9+
public static class TaskOrchestrationVersioningUtils
10+
{
11+
/// <summary>
12+
/// Compare two versions to each other.
13+
/// </summary>
14+
/// <remarks>
15+
/// This method's comparison is handled in the following order:
16+
/// 1. The versions are checked if they are empty (non-versioned). Both being empty signifies equality.
17+
/// 2. If sourceVersion is empty but otherVersion is defined, this is treated as the source being less than the other.
18+
/// 3. If otherVersion is empty but sourceVersion is defined, this is treated as the source being greater than the other.
19+
/// 4. Both versions are attempted to be parsed into System.Version and compared as such.
20+
/// 5. If all else fails, a direct string comparison is done between the versions.
21+
/// </remarks>
22+
/// <param name="sourceVersion">The source version that will be compared against the other version.</param>
23+
/// <param name="otherVersion">The other version to compare against.</param>
24+
/// <returns>An int representing how sourceVersion compares to otherVersion.</returns>
25+
public static int CompareVersions(string sourceVersion, string otherVersion)
26+
{
27+
// Both versions are empty, treat as equal.
28+
if (string.IsNullOrWhiteSpace(sourceVersion) && string.IsNullOrWhiteSpace(otherVersion))
29+
{
30+
return 0;
31+
}
32+
33+
// An empty version in the context is always less than a defined version in the parameter.
34+
if (string.IsNullOrWhiteSpace(sourceVersion))
35+
{
36+
return -1;
37+
}
38+
39+
// An empty version in the parameter is always less than a defined version in the context.
40+
if (string.IsNullOrWhiteSpace(otherVersion))
41+
{
42+
return 1;
43+
}
44+
45+
// If both versions use the .NET Version class, return that comparison.
46+
if (System.Version.TryParse(sourceVersion, out Version parsedSourceVersion) && System.Version.TryParse(otherVersion, out Version parsedOtherVersion))
47+
{
48+
return parsedSourceVersion.CompareTo(parsedOtherVersion);
49+
}
50+
51+
// If we have gotten to here, we don't know the syntax of the versions we are comparing, use a string comparison as a final check.
52+
return string.Compare(sourceVersion, otherVersion, StringComparison.OrdinalIgnoreCase);
53+
}
54+
}

src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.DurableTask.Worker.Hosting;
55
using Microsoft.Extensions.DependencyInjection;
66
using Microsoft.Extensions.Options;
7+
using static Microsoft.DurableTask.Worker.DurableTaskWorkerOptions;
78

89
namespace Microsoft.DurableTask.Worker;
910

@@ -86,4 +87,25 @@ public static IDurableTaskWorkerBuilder UseBuildTarget<TTarget, TOptions>(this I
8687
});
8788
return builder;
8889
}
90+
91+
/// <summary>
92+
/// Configures the versioning options for this builder.
93+
/// </summary>
94+
/// <param name="builder">The builder to set the builder target for.</param>
95+
/// <param name="versionOptions">The collection of options specified for versioning the worker.</param>
96+
/// <returns>The original builder, for call chaining.</returns>
97+
public static IDurableTaskWorkerBuilder UseVersioning(this IDurableTaskWorkerBuilder builder, VersioningOptions versionOptions)
98+
{
99+
Check.NotNull(builder);
100+
builder.Configure(options =>
101+
{
102+
options.Versioning = new VersioningOptions
103+
{
104+
Version = versionOptions.Version,
105+
MatchStrategy = versionOptions.MatchStrategy,
106+
FailureStrategy = versionOptions.FailureStrategy,
107+
};
108+
});
109+
return builder;
110+
}
89111
}

src/Worker/Core/DurableTaskWorkerOptions.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,43 @@ public class DurableTaskWorkerOptions
1212
{
1313
DataConverter dataConverter = JsonDataConverter.Default;
1414

15+
/// <summary>
16+
/// Defines the version matching strategy for the Durable Task worker.
17+
/// </summary>
18+
public enum VersionMatchStrategy
19+
{
20+
/// <summary>
21+
/// Ignore Orchestration version, all work received is processed.
22+
/// </summary>
23+
None = 0,
24+
25+
/// <summary>
26+
/// Worker will only process Tasks from Orchestrations with the same version as the worker.
27+
/// </summary>
28+
Strict = 1,
29+
30+
/// <summary>
31+
/// Worker will process Tasks from Orchestrations whose version is less than or equal to the worker.
32+
/// </summary>
33+
CurrentOrOlder = 2,
34+
}
35+
36+
/// <summary>
37+
/// Defines the versioning failure strategy for the Durable Task worker.
38+
/// </summary>
39+
public enum VersionFailureStrategy
40+
{
41+
/// <summary>
42+
/// Do not change the orchestration state if the version does not adhere to the matching strategy.
43+
/// </summary>
44+
Reject = 0,
45+
46+
/// <summary>
47+
/// Fail the orchestration if the version does not adhere to the matching strategy.
48+
/// </summary>
49+
Fail = 1,
50+
}
51+
1552
/// <summary>
1653
/// Gets or sets the data converter. Default value is <see cref="JsonDataConverter.Default" />.
1754
/// </summary>
@@ -93,6 +130,21 @@ public DataConverter DataConverter
93130
/// </remarks>
94131
public ConcurrencyOptions Concurrency { get; } = new();
95132

133+
/// <summary>
134+
/// Gets or sets the versioning options for the Durable Task worker.
135+
/// </summary>
136+
/// <remarks>
137+
/// Worker versioning controls how a worker will handle orchestrations of different versions. Defining both the
138+
/// version of the worker, the versions that can be worked on, and what to do in case a version does not comply
139+
/// with the given options.
140+
/// </remarks>
141+
public VersioningOptions? Versioning { get; set; }
142+
143+
/// <summary>
144+
/// Gets a value indicating whether versioning is explicitly set or not.
145+
/// </summary>
146+
public bool IsVersioningSet { get; internal set; }
147+
96148
/// <summary>
97149
/// Gets a value indicating whether <see cref="DataConverter" /> was explicitly set or not.
98150
/// </summary>
@@ -116,6 +168,7 @@ internal void ApplyTo(DurableTaskWorkerOptions other)
116168
other.DataConverter = this.DataConverter;
117169
other.MaximumTimerInterval = this.MaximumTimerInterval;
118170
other.EnableEntitySupport = this.EnableEntitySupport;
171+
other.Versioning = this.Versioning;
119172
}
120173
}
121174

@@ -139,4 +192,28 @@ public class ConcurrencyOptions
139192
/// </summary>
140193
public int MaximumConcurrentEntityWorkItems { get; set; } = 100 * Environment.ProcessorCount;
141194
}
195+
196+
/// <summary>
197+
/// Options for the Durable Task worker versioning.
198+
/// </summary>
199+
public class VersioningOptions
200+
{
201+
/// <summary>
202+
/// Gets or sets the version of orchestrations that the worker can work on.
203+
/// </summary>
204+
public string Version { get; set; } = string.Empty;
205+
206+
/// <summary>
207+
/// Gets or sets the versioning strategy for the Durable Task worker.
208+
/// </summary>
209+
public VersionMatchStrategy MatchStrategy { get; set; } = VersionMatchStrategy.None;
210+
211+
/// <summary>
212+
/// Gets or sets the versioning failure strategy for the Durable Task worker.
213+
/// </summary>
214+
/// <remarks>
215+
/// If the version matching strategy is set to <see cref="VersionMatchStrategy.None"/>, this value has no effect.
216+
/// </remarks>
217+
public VersionFailureStrategy FailureStrategy { get; set; } = VersionFailureStrategy.Reject;
218+
}
142219
}

0 commit comments

Comments
 (0)