Skip to content

Commit

Permalink
Workflow start delay (#146)
Browse files Browse the repository at this point in the history
  • Loading branch information
cretz authored Oct 24, 2023
1 parent 1cde5c9 commit bd986d7
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/Temporalio/Client/TemporalClient.Workflow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ private async Task<WorkflowHandle<TWorkflow, TResult>> StartWorkflowInternalAsyn
{
req.SearchAttributes = input.Options.TypedSearchAttributes.ToProto();
}
if (input.Options.StartDelay is { } startDelay)
{
req.WorkflowStartDelay = Duration.FromTimeSpan(startDelay);
}
if (input.Headers != null)
{
req.Header = new();
Expand Down Expand Up @@ -489,6 +493,7 @@ private async Task<WorkflowHandle<TWorkflow, TResult>> StartWorkflowInternalAsyn
Memo = req.Memo,
SearchAttributes = req.SearchAttributes,
Header = req.Header,
WorkflowStartDelay = req.WorkflowStartDelay,
SignalName = input.Options.StartSignal,
};
if (input.Options.StartSignalArgs != null && input.Options.StartSignalArgs.Count > 0)
Expand Down
7 changes: 7 additions & 0 deletions src/Temporalio/Client/WorkflowOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ public WorkflowOptions(string id, string taskQueue)
/// </summary>
public SearchAttributeCollection? TypedSearchAttributes { get; set; }

/// <summary>
/// Gets or sets the amount of time to wait before starting the workflow.
/// </summary>
/// <remarks>Start delay does not work with <see cref="CronSchedule" />.</remarks>
/// <remarks>WARNING: Start delay is experimental.</remarks>
public TimeSpan? StartDelay { get; set; }

/// <summary>
/// Gets or sets the start signal for the workflow. If this is non-null, a signal-with-start
/// is used instead of a traditional workflow start. This means the workflow will only be
Expand Down
39 changes: 39 additions & 0 deletions tests/Temporalio.Tests/Client/TemporalClientWorkflowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,45 @@ await Client.StartWorkflowAsync(
});
}

[Fact]
public async Task StartWorkflowAsync_StartDelay_WaitsProperly()
{
var arg = new KSWorkflowParams(new KSAction(Result: new(Value: "Some String")));
var handle = await Client.StartWorkflowAsync(
(IKitchenSinkWorkflowWithUnknownReturn wf) => wf.RunAsync(arg),
new(id: $"workflow-{Guid.NewGuid()}", taskQueue: Env.KitchenSinkWorkerTaskQueue)
{
StartDelay = TimeSpan.FromMinutes(45),
});
// Check that first event has start delay
await using var enumerator = handle.FetchHistoryEventsAsync().GetAsyncEnumerator();
Assert.True(await enumerator.MoveNextAsync());
var attrs = enumerator.Current.WorkflowExecutionStartedEventAttributes;
Assert.Equal(
TimeSpan.FromMinutes(45),
enumerator.Current.WorkflowExecutionStartedEventAttributes.FirstWorkflowTaskBackoff.ToTimeSpan());
}

[Fact]
public async Task StartWorkflowAsync_SignalWithStartDelay_WaitsProperly()
{
var arg = new KSWorkflowParams(new KSAction(Result: new(Value: "Some String")));
var handle = await Client.StartWorkflowAsync(
(IKitchenSinkWorkflowWithUnknownReturn wf) => wf.RunAsync(arg),
new(id: $"workflow-{Guid.NewGuid()}", taskQueue: Env.KitchenSinkWorkerTaskQueue)
{
StartDelay = TimeSpan.FromMinutes(45),
StartSignal = "some-signal",
});
// Check that first event has start delay
await using var enumerator = handle.FetchHistoryEventsAsync().GetAsyncEnumerator();
Assert.True(await enumerator.MoveNextAsync());
var attrs = enumerator.Current.WorkflowExecutionStartedEventAttributes;
Assert.Equal(
TimeSpan.FromMinutes(45),
enumerator.Current.WorkflowExecutionStartedEventAttributes.FirstWorkflowTaskBackoff.ToTimeSpan());
}

[Fact]
public async Task StartWorkflowAsync_StartSignal_Succeeds()
{
Expand Down
5 changes: 2 additions & 3 deletions tests/Temporalio.Tests/Testing/WorkflowEnvironmentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,8 @@ await worker.ExecuteAsync(async () =>
new(id: $"workflow-{Guid.NewGuid()}", taskQueue: worker.Options.TaskQueue!)));
// Check causes too
var actExc = Assert.IsType<ActivityFailureException>(exc.InnerException);
var toExc1 = Assert.IsType<TimeoutFailureException>(actExc.InnerException);
var toExc2 = Assert.IsType<TimeoutFailureException>(toExc1.InnerException);
Assert.Equal(TimeoutType.Heartbeat, toExc2.TimeoutType);
var toExc = Assert.IsType<TimeoutFailureException>(actExc.InnerException);
Assert.Equal(TimeoutType.Heartbeat, toExc.TimeoutType);
});
}

Expand Down

0 comments on commit bd986d7

Please sign in to comment.