Skip to content

Commit 301062a

Browse files
authored
Fix child workflow already exists and minor README updates (#379)
Fixes #371 Fixes #368 Fixes #361
1 parent aac363d commit 301062a

File tree

6 files changed

+85
-9
lines changed

6 files changed

+85
-9
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ CLI:
8888

8989
dotnet add package Temporalio
9090

91-
If you are using .NET Framework or a non-standard target platform, see the
91+
The .NET SDK supports .NET Framework >= 4.6.2, .NET Core >= 3.1 (so includes .NET 5+), and .NET Standard >= 2.0. If you
92+
are using .NET Framework or a non-standard target platform, see the
9293
[Built-in Native Shared Library](#built-in-native-shared-library) section later for additional information.
9394

9495
**NOTE: This README is for the current branch and not necessarily what's released on NuGet.**

src/Temporalio/Client/TemporalClient.Workflow.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ public override async Task<WorkflowHandle<TWorkflow, TResult>> StartWorkflowAsyn
106106
{
107107
throw new WorkflowAlreadyStartedException(
108108
e.Message,
109-
input.Options.Id!,
110-
failure.RunId);
109+
workflowId: input.Options.Id!,
110+
workflowType: input.Workflow,
111+
runId: failure.RunId);
111112
}
112113
}
113114
throw;

src/Temporalio/Exceptions/WorkflowAlreadyStartedException.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
13
namespace Temporalio.Exceptions
24
{
35
/// <summary>
@@ -9,12 +11,27 @@ public class WorkflowAlreadyStartedException : FailureException
911
/// Initializes a new instance of the <see cref="WorkflowAlreadyStartedException"/> class.
1012
/// </summary>
1113
/// <param name="message">Error message.</param>
12-
/// <param name="workflowId">Already started workflow ID.</param>
13-
/// <param name="runId">Already started run ID.</param>
14+
/// <param name="workflowId">See <see cref="WorkflowId"/>.</param>
15+
/// <param name="runId">See <see cref="RunId"/>.</param>
16+
[Obsolete("Use other constructor")]
1417
public WorkflowAlreadyStartedException(string message, string workflowId, string runId)
18+
: this(message, workflowId, "<unknown>", runId)
19+
{
20+
}
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="WorkflowAlreadyStartedException"/> class.
24+
/// </summary>
25+
/// <param name="message">Error message.</param>
26+
/// <param name="workflowId">See <see cref="WorkflowId"/>.</param>
27+
/// <param name="workflowType">See <see cref="WorkflowType"/>.</param>
28+
/// <param name="runId">See <see cref="RunId"/>.</param>
29+
public WorkflowAlreadyStartedException(
30+
string message, string workflowId, string workflowType, string runId)
1531
: base(message)
1632
{
1733
WorkflowId = workflowId;
34+
WorkflowType = workflowType;
1835
RunId = runId;
1936
}
2037

@@ -24,7 +41,13 @@ public WorkflowAlreadyStartedException(string message, string workflowId, string
2441
public string WorkflowId { get; private init; }
2542

2643
/// <summary>
27-
/// Gets the run ID that was already started.
44+
/// Gets the workflow type that was attempted to start.
45+
/// </summary>
46+
public string WorkflowType { get; private init; }
47+
48+
/// <summary>
49+
/// Gets the run ID that was already started. This may be <c>&lt;unknown&gt;</c> when this
50+
/// error is thrown for a child workflow from inside a workflow.
2851
/// </summary>
2952
public string RunId { get; private init; }
3053
}

src/Temporalio/Worker/WorkflowInstance.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,8 +2061,10 @@ public override Task<ChildWorkflowHandle<TWorkflow, TResult>> StartChildWorkflow
20612061
handleSource.SetException(
20622062
new WorkflowAlreadyStartedException(
20632063
"Child workflow already started",
2064-
startRes.Failed.WorkflowId,
2065-
startRes.Failed.WorkflowType));
2064+
workflowId: startRes.Failed.WorkflowId,
2065+
workflowType: startRes.Failed.WorkflowType,
2066+
// Pending https://github.com/temporalio/temporal/issues/6961
2067+
runId: "<unknown>"));
20662068
return;
20672069
default:
20682070
handleSource.SetException(new InvalidOperationException(

src/Temporalio/Worker/WorkflowWorker.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ public WorkflowWorker(
4949
{
5050
if (!defn.Instantiable)
5151
{
52-
throw new ArgumentException($"Workflow named {defn.Name} is not instantiable");
52+
throw new ArgumentException($"Workflow named {defn.Name} is not instantiable. " +
53+
"Note, dependency injection is not supported in workflows. " +
54+
"Workflows must be deterministic and self-contained with a lifetime controlled by Temporal.");
5355
}
5456
if (defn.Name == null)
5557
{

tests/Temporalio.Tests/Worker/WorkflowWorkerTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6011,6 +6011,53 @@ await ExecuteWorkerAsync<DetachedCancellationWorkflow>(
60116011
new TemporalWorkerOptions().AddAllActivities(activities));
60126012
}
60136013

6014+
[Workflow]
6015+
public class ChildWorkflowAlreadyExists1Workflow
6016+
{
6017+
[WorkflowRun]
6018+
public Task RunAsync() => Workflow.WaitConditionAsync(() => false);
6019+
}
6020+
6021+
[Workflow]
6022+
public class ChildWorkflowAlreadyExists2Workflow
6023+
{
6024+
[WorkflowRun]
6025+
public async Task<string> RunAsync(string workflowId)
6026+
{
6027+
try
6028+
{
6029+
await Workflow.StartChildWorkflowAsync(
6030+
(ChildWorkflowAlreadyExists2Workflow wf) => wf.RunAsync("ignored"),
6031+
new() { Id = workflowId });
6032+
throw new ApplicationFailureException("Should not be reached");
6033+
}
6034+
catch (WorkflowAlreadyStartedException e)
6035+
{
6036+
return $"already started, type: {e.WorkflowType}, run ID: {e.RunId}";
6037+
}
6038+
}
6039+
}
6040+
6041+
[Fact]
6042+
public async Task ExecuteWorkflowAsync_ChildWorkflowAlreadyExists_ErrorsProperly()
6043+
{
6044+
await ExecuteWorkerAsync<ChildWorkflowAlreadyExists1Workflow>(
6045+
async worker =>
6046+
{
6047+
// Start workflow
6048+
var handle = await Client.StartWorkflowAsync(
6049+
(ChildWorkflowAlreadyExists1Workflow wf) => wf.RunAsync(),
6050+
new(id: $"workflow-{Guid.NewGuid()}", taskQueue: worker.Options.TaskQueue!));
6051+
6052+
// Try to run other one
6053+
var result = await Client.ExecuteWorkflowAsync(
6054+
(ChildWorkflowAlreadyExists2Workflow wf) => wf.RunAsync(handle.Id),
6055+
new(id: $"workflow-{Guid.NewGuid()}", taskQueue: worker.Options.TaskQueue!));
6056+
Assert.Equal("already started, type: ChildWorkflowAlreadyExists2Workflow, run ID: <unknown>", result);
6057+
},
6058+
new TemporalWorkerOptions().AddWorkflow<ChildWorkflowAlreadyExists2Workflow>());
6059+
}
6060+
60146061
[Workflow]
60156062
public class UserMetadataWorkflow
60166063
{

0 commit comments

Comments
 (0)