Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVP .NET referential feature & inter-tech how-to documentation #2769

Merged
merged 29 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/develop/dotnet/asynchronous-activity.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
id: asynchronous-activity
title: Asynchronous Activity - .NET SDK feature guide
sidebar_label: Asynchronous Activity
description: Asynchronously complete an Activity using the Temporal .NET SDK by following three simple steps.
toc_max_heading_level: 4
keywords:
- asynchronous activity completion
- temporal
- activity function
- task token
- workflow id
- activity id
- temporal client
- heartbeatasync
- completeasync
- failasync
- reportcancellationasync
- csharp code examples
- external system completion
- activity execution
- completeasyncexception
- getasyncactivityhandle
tags:
- asynchronous-activity
- dotnet-sdk
- overview
---

This page describes how to asynchronously complete an Activity.

[Asynchronous Activity Completion](/activities#asynchronous-activity-completion) enables the Activity Function to return without the Activity Execution completing.

There are three steps to follow:

1. The Activity provides the external system with identifying information needed to complete the Activity Execution.
Identifying information can be a [Task Token](/activities#task-token), or a combination of Namespace, Workflow Id, and Activity Id.
2. The Activity Function completes in a way that identifies it as waiting to be completed by an external system.
3. The Temporal Client is used to Heartbeat and complete the Activity.

To mark an Activity as completing asynchronously, do the following inside the Activity.

```csharp
// Capture token for later completion
capturedToken = ActivityExecutionContext.Current.Info.TaskToken;

// Throw special exception that says an activity will be completed somewhere else
throw new CompleteAsyncException();
```

To update an Activity outside the Activity, use the [GetAsyncActivityHandle()](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClient.html#Temporalio_Client_ITemporalClient_GetAsyncActivityHandle_System_Byte___) method to get the handle of the Activity.

```csharp
var handle = myClient.GetAsyncActivityHandle(capturedToken);
```

Then, on that handle, you can call the results of the Activity, `HeartbeatAsync`, `CompleteAsync`, `FailAsync`, or `ReportCancellationAsync` method to update the Activity.

```csharp
await handle.CompleteAsync("Completion value.");
```
199 changes: 199 additions & 0 deletions docs/develop/dotnet/cancellation.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
---
id: cancellation
title: Interrupt a Workflow - .NET SDK feature guide
sidebar_label: Interrupt a Workflow
description: Cancel and Terminate a Workflow Execution and its Activities using the .NET SDK.
toc_max_heading_level: 4
keywords:
- interrupt workflow execution
- cancel workflow
- terminate workflow
- temporal cancellation
- temporal termination
- workflow execution
- temporal client
- activity heartbeat timeout
- cancellation request
- workflow cancellation token
- cancellation in .net
- terminate async
- cancel async
- workflow handle
- temporal .net
- temporal dotnet
- workflow management
- handle cancellation in workflow
- handle cancellation in activity
- request cancellation
- workflow code examples
tags:
- cancellation
- dotnet-sdk
- overview
---

This page shows how to interrupt a Workflow Execution.

You can interrupt a Workflow Execution in one of the following ways:

- [Cancel](#cancellation): Canceling a Workflow provides a graceful way to stop Workflow Execution.
- [Terminate](#termination): Terminating a Workflow forcefully stops Workflow Execution.
Terminating a Workflow forcefully stops Workflow Execution.
This action resembles killing a process.
- The system records a `WorkflowExecutionTerminated` event in the Workflow History.
- The termination forcefully and immediately stops the Workflow Execution.
- The Workflow code gets no chance to handle termination.
- A Workflow Task doesn't get scheduled.

In most cases, canceling is preferable because it allows the Workflow to finish gracefully.
Terminate only if the Workflow is stuck and cannot be canceled normally.

## Cancellation {#cancellation}

To give a Workflow and its Activities the ability to be cancelled, do the following:

- Handle a Cancellation request within a Workflow.
- Set Activity Heartbeat Timeouts.
- Listen for and handle a Cancellation request within an Activity.
- Send a Cancellation request from a Temporal Client.

### Handle Cancellation in Workflow {#handle-cancellation-in-workflow}

**How to handle a Cancellation in a Workflow in .NET.**

Workflow Definitions can be written to respond to cancellation requests.
It is common for an Activity to be run on Cancellation to perform cleanup.

Cancellation Requests on Workflows cancel the `Workflow.CancellationToken`.
This is the token that is implicitly used for all calls within the workflow as well (e.g. Timers, Activities, etc) and therefore cancellation is propagated to them to be handled and bubble out.

```csharp
[WorkflowRun]
public async Task RunAsync()
{
try
{
// Whether this workflow waits on the activity to handle the cancellation or not is
// dependent upon the CancellationType option. We leave the default here which sends the
// cancellation but does not wait on it to be handled.
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyNormalActivity(),
new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(5) });
}
catch (Exception e) when (TemporalException.IsCanceledException(e))
{
// The "when" clause above is because we only want to apply the logic to cancellation, but
// this kind of cleanup could be done on any/all exceptions too.
Workflow.Logger.LogError(e, "Cancellation occurred, performing cleanup");

// Call cleanup activity. If this throws, it will swallow the original exception which we
// are ok with here. This could be changed to just log a failure and let the original
// cancellation continue. We use a different cancellation token since the default one on
// Workflow.CancellationToken is now marked cancelled.
using var detachedCancelSource = new CancellationTokenSource();
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyCancellationCleanupActivity(),
new()
{
ScheduleToCloseTimeout = TimeSpan.FromMinutes(5),
CancellationToken = detachedCancelSource.Token;
});

// Rethrow the cancellation
throw;
}
}
```

### Handle Cancellation in an Activity {#handle-cancellation-in-an-activity}

**How to handle a Cancellation in an Activity using the Temporal .NET SDK**

Ensure that the Activity is [Heartbeating](/develop/dotnet/failure-detection#activity-heartbeat) to receive the Cancellation request and stop execution.
Also make sure that the [Heartbeat Timeout](/develop/dotnet/failure-detection#heartbeat-timeout) is set on the Activity Options when calling from the Workflow.
An Activity Cancellation Request cancels the `CancellationToken` on the `ActivityExecutionContext`.

```csharp
[Activity]
public async Task MyActivityAsync()
{
// This is a naive loop simulating work, but similar heartbeat/cancellation logic applies to
// other scenarios as well
while (true)
{
// Send heartbeat
ActivityExecutionContext.Current.Heartbeat();

// Do some work, passing the cancellation token
await Task.Delay(1000, ActivityExecutionContext.Current.CancellationToken);
}
}
```

### Request Cancellation {#request-cancellation}

**How to request Cancellation of a Workflow using the Temporal .NET SDK**

Use `CancelAsync` on the `WorkflowHandle` to cancel a Workflow Execution.

```csharp
// Get a workflow handle by its workflow ID. This could be made specific to a run by passing run ID.
// This could also just be a handle that is returned from StartWorkflowAsync instead.
var handle = myClient.GetWorkflowHandle("my-workflow-id");

// Send cancellation. This returns when cancellation is received by the server. Wait on the handle's
// result to wait for cancellation to be applied.
await handle.CancelAsync();
```

**How to request Cancellation of an Activity in .NET using the Temporal .NET SDK**

By default, Activities are automatically cancelled when the Workflow is cancelled since the workflow cancellation token is used by activities by default.
To issue a cancellation explicitly, a new cancellation token can be created.

```csharp
[WorkflowRun]
public async Task RunAsync()
{
// Create a source linked to workflow cancellation. A new source could be created instead if we
// didn't want it associated with workflow cancellation.
using var cancelActivitySource = CancellationTokenSource.CreateLinkedTokenSource(
Workflow.CancellationToken);

// Start the activity. Whether this workflow waits on the activity to handle the cancellation
// or not is dependent upon the CancellationType option. We leave the default here which sends
// the cancellation but does not wait on it to be handled.
var activityTask = Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyNormalActivity(),
new()
{
ScheduleToCloseTimeout = TimeSpan.FromMinutes(5),
CancellationToken = cancelActivitySource.Token;
});
activityTask.Start();

// Wait 5 minutes, then cancel it
await Workflow.DelayAsync(TimeSpan.FromMinutes(5));
cancelActivitySource.Cancel();

// Wait on the activity which will throw cancellation which will fail the workflow
await activityTask;
}
```

## Termination {#termination}

**How to Terminate a Workflow Execution in .NET using the Temporal .NET SDK**

To Terminate a Workflow Execution in .NET, use the [TerminateAsync()](https://dotnet.temporal.io/api/Temporalio.Client.WorkflowHandle.html#Temporalio_Client_WorkflowHandle_TerminateAsync_System_String_Temporalio_Client_WorkflowTerminateOptions_) method on the Workflow handle.

```csharp
// Get a workflow handle by its workflow ID. This could be made specific to a run by passing run ID.
// This could also just be a handle that is returned from StartWorkflowAsync instead.
var handle = myClient.GetWorkflowHandle("my-workflow-id");

// Terminate
await handle.TerminateAsync();
```

Workflow Executions can also be Terminated directly from the WebUI. In this case, a custom note can be logged from the UI when that happens.
76 changes: 76 additions & 0 deletions docs/develop/dotnet/child-workflows.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
id: child-workflows
title: Child Workflows - .NET SDK feature guide
sidebar_label: Child Workflows
description: Start a Child Workflow Execution using the Temporal .NET SDK. This guide covers the usage of ExecuteChildWorkflowAsync() and StartChildWorkflowAsync() methods, logging Child Workflow related events, and setting Parent Close Policies.
keywords:
- start child workflow execution
- temporal .net sdk
- child workflow api
- child workflow events
- startchildworkflowexecutioninitiated
- childworkflowexecutionstarted
- childworkflowexecutioncompleted
- workflow execution event history
- parent close policy
- executechildworkflowasync
- startchildworkflowasync
- workflow run id
- signal child workflow
- temporal .net examples
- manage child workflows
- child workflow options
- parentclosepolicy
- child workflow in .net
tags:
- developer-guide
- sdk
- dotnet
- child-workflow
---


This page shows how to do the following:
- [Start a Child Workflow Execution](#child-workflows)
- [Set a Parent Close Policy](#parent-close-policy)

## Start a Child Workflow Execution {#child-workflows}

**How to start a Child Workflow Execution using the Temporal .NET SDK**

A [Child Workflow Execution](/encyclopedia/child-workflows) is a Workflow Execution that is scheduled from within another Workflow using a Child Workflow API.

When using a Child Workflow API, Child Workflow related Events ([StartChildWorkflowExecutionInitiated](/references/events#startchildworkflowexecutioninitiated), [ChildWorkflowExecutionStarted](/references/events#childworkflowexecutionstarted), [ChildWorkflowExecutionCompleted](/references/events#childworkflowexecutioncompleted), etc...) are logged in the Workflow Execution Event History.

Always block progress until the [ChildWorkflowExecutionStarted](/references/events#childworkflowexecutionstarted) Event is logged to the Event History to ensure the Child Workflow Execution has started.
After that, Child Workflow Executions may be abandoned using the default _Abandon_ [Parent Close Policy](/encyclopedia/child-workflows#parent-close-policy) set in the Child Workflow Options.

To spawn a Child Workflow Execution in .NET, use the `ExecuteChildWorkflowAsync()` method which starts the Child Workflow and waits for completion or
use the `StartChildWorkflowAsync()` method to start a Child Workflow and return its handle.
This is useful if you want to do something after it has only started, or to get the Workflow/Run ID, or to be able to signal it while running.

:::note

`ExecuteChildWorkflowAsync()` is a helper method for `StartChildWorkflowAsync()` plus `await handle.GetResultAsync()`.

:::

```csharp
await Workflow.ExecuteChildWorkflowAsync((MyChildWorkflow wf) => wf.RunAsync());
```

## Set a Parent Close Policy {#parent-close-policy}

**How to set a Parent Close Policy using the Temporal .NET SDK**

A [Parent Close Policy](/encyclopedia/child-workflows#parent-close-policy) determines what happens to a Child Workflow Execution if its Parent changes to a Closed status (Completed, Failed, or Timed Out).

The default Parent Close Policy option is set to terminate the Child Workflow Execution.

Set the `ParentClosePolicy` property inside the [`ChildWorkflowOptions`](https://dotnet.temporal.io/api/Temporalio.Workflows.ChildWorkflowOptions.html) for `ExecuteChildWorkflowAsync` or `StartChildWorkflowAsync` to specify the behavior of the Child Workflow when the Parent Workflow closes.

```csharp
await Workflow.ExecuteChildWorkflowAsync(
(MyChildWorkflow wf) => wf.RunAsync(),
new() { ParentClosePolicy = ParentClosePolicy.Abandon });
```
41 changes: 41 additions & 0 deletions docs/develop/dotnet/continue-as-new.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
id: continue-as-new
title: Continue-As-New - .NET SDK feature guide
sidebar_label: Continue-As-New
description: Continue-As-New
keywords:
- sdk
- dotnet
- continue-as-new
tags:
- sdk
- dotnet
- continue-as-new
---

This page describes how to Continue-As-New using the Temporal .NET SDK.

[Continue-As-New](/workflows#continue-as-new) enables a Workflow Execution to close successfully and create a new Workflow Execution in a single atomic operation if the number of Events in the Event History is becoming too large.
The Workflow Execution spawned from the use of Continue-As-New has the same Workflow Id, a new Run Id, and a fresh Event History and is passed all the appropriate parameters.

:::caution

As a precautionary measure, the Workflow Execution's Event History is limited to [51,200 Events](https://github.com/temporalio/temporal/blob/e3496b1c51bfaaae8142b78e4032cc791de8a76f/service/history/configs/config.go#L382) or [50 MB](https://github.com/temporalio/temporal/blob/e3496b1c51bfaaae8142b78e4032cc791de8a76f/service/history/configs/config.go#L380) and will warn you after 10,240 Events or 10 MB.

:::

To prevent a Workflow Execution Event History from exceeding this limit and failing, use Continue-As-New to start a new Workflow Execution with a fresh Event History.

A very large Event History can adversely affect the performance of a Workflow Execution.
For example, in the case of a Workflow Worker failure, the full Event History must be pulled from the Temporal Service and given to another Worker via a Workflow Task.
If the Event history is very large, it may take some time to load it.

The Continue-As-New feature enables developers to complete the current Workflow Execution and start a new one atomically.

The new Workflow Execution has the same Workflow Id, but a different Run Id, and has its own Event History.

To Continue-As-New in .NET, throw an exception created by `CreateContinueAsNewException()` from inside your Workflow, which will stop the Workflow immediately and Continue-As-New.

```csharp
throw Workflow.CreateContinueAsNewException((MyWorkflow wf) => wf.RunAsync(myNewParam));
```