From be473e9b9e0a15bdc023ad5052bf1e91f532302a Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Fri, 31 Jul 2026 09:38:07 -0600 Subject: [PATCH 1/3] Target .NET 8 and .NET 10 Modernize production, test, sample, and tooling projects using the durable extension repository's multi-targeting convention. Update CI SDK provisioning and serialize shared-database test runs across target frameworks. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c4a998a9-4194-49f7-b180-7400759b38aa --- .github/workflows/build-and-test.yml | 6 +++--- .github/workflows/publish-release.yml | 2 +- DurableTask.SqlServer.sln | 1 + azure-pipelines.yml | 4 ++-- eng/templates/build.yml | 4 ++-- global.json | 6 ++++++ .../DurableTask.SqlServer.AzureFunctions.csproj | 5 +---- src/DurableTask.SqlServer/DurableTask.SqlServer.csproj | 2 +- ...Functions.Worker.Extensions.DurableTask.SqlServer.csproj | 2 +- .../DurableTask.SqlServer.AzureFunctions.Tests.csproj | 2 +- .../DurableTask.SqlServer.Tests.csproj | 2 +- test/PerformanceTests/PerformanceTests.csproj | 2 +- tools/TestDBGenerator/TestDBGenerator.csproj | 3 +-- 13 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 global.json diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index ed97358..7990aac 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -24,8 +24,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.0.x 8.0.x + 10.0.x env: NUGET_AUTH_TOKEN: RequiredButNotUsed @@ -40,6 +40,6 @@ jobs: shell: pwsh - name: Durable framework tests - run: dotnet test --no-build --verbosity normal --filter Category!=Stress ./test/DurableTask.SqlServer.Tests/DurableTask.SqlServer.Tests.csproj + run: dotnet test --no-build --verbosity normal --filter Category!=Stress -p:TestTfmsInParallel=false ./test/DurableTask.SqlServer.Tests/DurableTask.SqlServer.Tests.csproj - name: Functions runtime tests - run: dotnet test --no-build --verbosity normal ./test/DurableTask.SqlServer.AzureFunctions.Tests/DurableTask.SqlServer.AzureFunctions.Tests.csproj + run: dotnet test --no-build --verbosity normal -p:TestTfmsInParallel=false ./test/DurableTask.SqlServer.AzureFunctions.Tests/DurableTask.SqlServer.AzureFunctions.Tests.csproj diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 0734c2f..6fc463a 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -20,8 +20,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.0.x 8.0.x + 10.0.x - name: Install dependencies run: dotnet restore - name: Build diff --git a/DurableTask.SqlServer.sln b/DurableTask.SqlServer.sln index 69645c3..016d058 100644 --- a/DurableTask.SqlServer.sln +++ b/DurableTask.SqlServer.sln @@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution src\common.props = src\common.props Directory.Build.targets = Directory.Build.targets Directory.Packages.props = Directory.Packages.props + global.json = global.json nuget.config = nuget.config README.md = README.md sign.snk = sign.snk diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0bcba83..b70f6d3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,10 +15,10 @@ steps: version: '2.1.x' - task: UseDotNet@2 - displayName: 'Use the .NET 6 SDK' + displayName: 'Use the .NET 10 SDK' inputs: packageType: 'sdk' - version: '6.0.x' + version: '10.0.x' - task: UseDotNet@2 displayName: 'Use the .NET 8 SDK' diff --git a/eng/templates/build.yml b/eng/templates/build.yml index 04de820..cb57b07 100644 --- a/eng/templates/build.yml +++ b/eng/templates/build.yml @@ -11,10 +11,10 @@ jobs: steps: - task: UseDotNet@2 - displayName: 'Use the .NET 6 SDK' + displayName: 'Use the .NET 10 SDK' inputs: packageType: 'sdk' - version: '6.0.x' + version: '10.0.x' - task: UseDotNet@2 displayName: 'Use the .NET 8 SDK' diff --git a/global.json b/global.json new file mode 100644 index 0000000..545c92c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.302", + "rollForward": "latestFeature" + } +} diff --git a/src/DurableTask.SqlServer.AzureFunctions/DurableTask.SqlServer.AzureFunctions.csproj b/src/DurableTask.SqlServer.AzureFunctions/DurableTask.SqlServer.AzureFunctions.csproj index 18b4003..761e45f 100644 --- a/src/DurableTask.SqlServer.AzureFunctions/DurableTask.SqlServer.AzureFunctions.csproj +++ b/src/DurableTask.SqlServer.AzureFunctions/DurableTask.SqlServer.AzureFunctions.csproj @@ -4,10 +4,7 @@ - net8.0 - - - + net8.0;net10.0 $(DefineConstants);FUNCTIONS_V4 diff --git a/src/DurableTask.SqlServer/DurableTask.SqlServer.csproj b/src/DurableTask.SqlServer/DurableTask.SqlServer.csproj index e8b3e59..8828cd8 100644 --- a/src/DurableTask.SqlServer/DurableTask.SqlServer.csproj +++ b/src/DurableTask.SqlServer/DurableTask.SqlServer.csproj @@ -4,7 +4,7 @@ - netstandard2.0 + netstandard2.0;net8.0;net10.0 true diff --git a/src/Functions.Worker.Extensions.DurableTask.SqlServer/Functions.Worker.Extensions.DurableTask.SqlServer.csproj b/src/Functions.Worker.Extensions.DurableTask.SqlServer/Functions.Worker.Extensions.DurableTask.SqlServer.csproj index 507326b..70fb4ae 100644 --- a/src/Functions.Worker.Extensions.DurableTask.SqlServer/Functions.Worker.Extensions.DurableTask.SqlServer.csproj +++ b/src/Functions.Worker.Extensions.DurableTask.SqlServer/Functions.Worker.Extensions.DurableTask.SqlServer.csproj @@ -4,7 +4,7 @@ - netstandard2.0 + netstandard2.0;net8.0;net10.0 Microsoft.Azure.Functions.Worker.Extensions.DurableTask.SqlServer diff --git a/test/DurableTask.SqlServer.AzureFunctions.Tests/DurableTask.SqlServer.AzureFunctions.Tests.csproj b/test/DurableTask.SqlServer.AzureFunctions.Tests/DurableTask.SqlServer.AzureFunctions.Tests.csproj index e5e00b3..02aa180 100644 --- a/test/DurableTask.SqlServer.AzureFunctions.Tests/DurableTask.SqlServer.AzureFunctions.Tests.csproj +++ b/test/DurableTask.SqlServer.AzureFunctions.Tests/DurableTask.SqlServer.AzureFunctions.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net10.0 false true 9.0 diff --git a/test/DurableTask.SqlServer.Tests/DurableTask.SqlServer.Tests.csproj b/test/DurableTask.SqlServer.Tests/DurableTask.SqlServer.Tests.csproj index a021dd6..9c0f819 100644 --- a/test/DurableTask.SqlServer.Tests/DurableTask.SqlServer.Tests.csproj +++ b/test/DurableTask.SqlServer.Tests/DurableTask.SqlServer.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0;net10.0 false true 9.0 diff --git a/test/PerformanceTests/PerformanceTests.csproj b/test/PerformanceTests/PerformanceTests.csproj index 9570a29..74ce038 100644 --- a/test/PerformanceTests/PerformanceTests.csproj +++ b/test/PerformanceTests/PerformanceTests.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net10.0 v4 diff --git a/tools/TestDBGenerator/TestDBGenerator.csproj b/tools/TestDBGenerator/TestDBGenerator.csproj index ff3f925..79809f1 100644 --- a/tools/TestDBGenerator/TestDBGenerator.csproj +++ b/tools/TestDBGenerator/TestDBGenerator.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0;net10.0 enable enable @@ -20,7 +20,6 @@ - From 52e44bd1b5e71185c066a699eea3856c21e8f044 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Fri, 31 Jul 2026 10:03:48 -0600 Subject: [PATCH 2/3] Allow bulk purge test more time The 1,100-instance regression test can exceed one minute under the new net10.0 CI pass. Keep its behavioral assertions intact while allowing enough time for all orchestrations to complete. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c4a998a9-4194-49f7-b180-7400759b38aa --- .../Integration/DataRetentionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/DurableTask.SqlServer.Tests/Integration/DataRetentionTests.cs b/test/DurableTask.SqlServer.Tests/Integration/DataRetentionTests.cs index 710bd07..d261c67 100644 --- a/test/DurableTask.SqlServer.Tests/Integration/DataRetentionTests.cs +++ b/test/DurableTask.SqlServer.Tests/Integration/DataRetentionTests.cs @@ -143,7 +143,7 @@ public async Task PurgesMoreThanOneThousandInstances() version: string.Empty, implementation: (ctx, input) => Task.FromResult(input)); - TimeSpan timeout = TimeSpan.FromSeconds(60); + TimeSpan timeout = TimeSpan.FromMinutes(2); await Task.WhenAll(instances.Select(instance => instance.WaitForCompletion(timeout))); var filter = new PurgeInstanceFilter( From 139f7d8bfa579593cf4e4639748bb41a06b649c2 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Fri, 31 Jul 2026 10:16:49 -0600 Subject: [PATCH 3/3] Normalize canceled SQL operations SqlClient can surface a canceled command as SqlException on modern runtimes. Translate that provider-specific result to OperationCanceledException when the operation token is canceled so callers observe stable cancellation semantics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c4a998a9-4194-49f7-b180-7400759b38aa --- src/DurableTask.SqlServer/SqlUtils.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/DurableTask.SqlServer/SqlUtils.cs b/src/DurableTask.SqlServer/SqlUtils.cs index 88bfd85..435f786 100644 --- a/src/DurableTask.SqlServer/SqlUtils.cs +++ b/src/DurableTask.SqlServer/SqlUtils.cs @@ -910,7 +910,8 @@ public static Task ExecuteReaderAsync( command, traceHelper, instanceId, - cmd => cmd.ExecuteReaderAsync(cancellationToken)); + cmd => cmd.ExecuteReaderAsync(cancellationToken), + cancellationToken); } public static Task ExecuteNonQueryAsync( @@ -923,7 +924,8 @@ public static Task ExecuteNonQueryAsync( command, traceHelper, instanceId, - cmd => cmd.ExecuteNonQueryAsync(cancellationToken)); + cmd => cmd.ExecuteNonQueryAsync(cancellationToken), + cancellationToken); } public static Task ExecuteScalarAsync( @@ -936,20 +938,26 @@ public static Task ExecuteScalarAsync( command, traceHelper, instanceId, - cmd => cmd.ExecuteScalarAsync(cancellationToken)); + cmd => cmd.ExecuteScalarAsync(cancellationToken), + cancellationToken); } static async Task ExecuteSprocAndTraceAsync( DbCommand command, LogHelper traceHelper, string? instanceId, - Func> executor) + Func> executor, + CancellationToken cancellationToken) { var context = new SprocExecutionContext(); try { return await WithRetry(command, executor, context, traceHelper, instanceId); } + catch (SqlException e) when (cancellationToken.IsCancellationRequested) + { + throw new OperationCanceledException("The SQL operation was canceled.", e, cancellationToken); + } finally { context.LatencyStopwatch.Stop();