Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5243c4a
Add .NET reference files for temporal-developer skill
donald-pinckney Mar 16, 2026
6f5e7dc
Fix .NET alignment issues from self-review
donald-pinckney Mar 16, 2026
d338213
Fix .NET correctness issues from verification pass
donald-pinckney Mar 16, 2026
d80bd35
Update supported language references to include .NET
donald-pinckney Mar 16, 2026
0114238
Edits to advanced features
donald-pinckney Apr 1, 2026
2ee9c02
edits to determinism protection, and move the .editorconfig section
donald-pinckney Apr 1, 2026
3709a85
missed one
donald-pinckney Apr 1, 2026
b41ecc6
edit determinism.md
donald-pinckney Apr 1, 2026
7ae7afb
edit error-handling.md
donald-pinckney Apr 1, 2026
4f19f25
edit gotchas.md
donald-pinckney Apr 1, 2026
c51d32b
edit patterns.md
donald-pinckney Apr 1, 2026
3aa4ce7
edit versioning.md
donald-pinckney Apr 1, 2026
2a38c83
edit observability.md
donald-pinckney Apr 1, 2026
93388e5
fix metrics
donald-pinckney Apr 1, 2026
54ce199
self-review round 1
donald-pinckney Apr 1, 2026
b4c958e
minor correctness fixed
donald-pinckney Apr 2, 2026
9be5c85
Merge branch 'main' into add-dotnet
donald-pinckney Apr 17, 2026
476f53b
Update references/dotnet/patterns.md
donald-pinckney Apr 17, 2026
58bbf52
address comments, clarify reference to earlier code snippet
donald-pinckney Apr 17, 2026
467fd27
clarify that operations are forbidden IN WORKFLOWS
donald-pinckney Apr 17, 2026
014eb91
cleanup workflow cancellation handling example
donald-pinckney Apr 17, 2026
76aa90a
add task token retrieval comment
donald-pinckney Apr 17, 2026
744cf8d
update .net requirements
donald-pinckney Apr 17, 2026
58f8972
Fix propagation of workflow cancellation
donald-pinckney Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
edit gotchas.md
  • Loading branch information
donald-pinckney committed Apr 1, 2026
commit 4f19f2567e4eeb51c89562334d8e8e7de65a7f24
116 changes: 63 additions & 53 deletions references/dotnet/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,55 @@ See `references/dotnet/determinism-protection.md` for the complete list.
**Example:** Transient network errors should be retried. Authentication errors should not be.
See `references/dotnet/error-handling.md` to understand how to classify errors.

## Heartbeating

### Forgetting to Heartbeat Long Activities

```csharp
// BAD: No heartbeat, can't detect stuck activities
[Activity]
public async Task ProcessLargeFileAsync(string path)
{
foreach (var chunk in ReadChunks(path))
await ProcessAsync(chunk); // Takes hours, no heartbeat

// GOOD: Regular heartbeats with progress
[Activity]
public async Task ProcessLargeFileAsync(string path)
{
var chunks = ReadChunks(path);
for (var i = 0; i < chunks.Count; i++)
{
ActivityExecutionContext.Current.Heartbeat($"Processing chunk {i}");
await ProcessAsync(chunks[i]);
}
}
```

### Heartbeat Timeout Too Short

```csharp
// BAD: Heartbeat timeout shorter than processing time
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.ProcessChunkAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(30),
HeartbeatTimeout = TimeSpan.FromSeconds(10), // Too short!
});

// GOOD: Heartbeat timeout allows for processing variance
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.ProcessChunkAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(30),
HeartbeatTimeout = TimeSpan.FromMinutes(2),
});
```

Set heartbeat timeout as high as acceptable for your use case — each heartbeat counts as an action.

## Cancellation

### Not Handling Workflow Cancellation
Expand Down Expand Up @@ -124,7 +173,9 @@ public class GoodWorkflow

### Not Handling Activity Cancellation

Activities must **opt in** to receive cancellation via heartbeating.
Activities must **opt in** to receive cancellation. This requires:
1. **Heartbeating** — Cancellation is delivered via heartbeat
2. **Checking the cancellation token** — Token is triggered when heartbeat detects cancellation

```csharp
// BAD: Activity ignores cancellation
Expand All @@ -134,68 +185,27 @@ public async Task LongActivityAsync()
await DoExpensiveWorkAsync(); // Runs to completion even if cancelled
}

// GOOD: Heartbeat and check cancellation token
// GOOD: Heartbeat, check cancellation, and handle cleanup
[Activity]
public async Task LongActivityAsync()
{
foreach (var item in items)
try
{
ActivityExecutionContext.Current.Heartbeat();
ActivityExecutionContext.Current.CancellationToken.ThrowIfCancellationRequested();
await ProcessAsync(item);
foreach (var item in items)
{
ActivityExecutionContext.Current.Heartbeat();
ActivityExecutionContext.Current.CancellationToken.ThrowIfCancellationRequested();
await ProcessAsync(item);
}
}
}
```

## Heartbeating

### Forgetting to Heartbeat Long Activities

```csharp
// BAD: No heartbeat, can't detect stuck activities
[Activity]
public async Task ProcessLargeFileAsync(string path)
{
foreach (var chunk in ReadChunks(path))
await ProcessAsync(chunk); // Takes hours, no heartbeat

// GOOD: Regular heartbeats with progress
[Activity]
public async Task ProcessLargeFileAsync(string path)
{
var chunks = ReadChunks(path);
for (var i = 0; i < chunks.Count; i++)
catch (OperationCanceledException)
{
ActivityExecutionContext.Current.Heartbeat($"Processing chunk {i}");
await ProcessAsync(chunks[i]);
await CleanupAsync();
throw;
}
}
```

### Heartbeat Timeout Too Short

```csharp
// BAD: Heartbeat timeout shorter than processing time
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.ProcessChunkAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(30),
HeartbeatTimeout = TimeSpan.FromSeconds(10), // Too short!
});

// GOOD: Heartbeat timeout allows for processing variance
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.ProcessChunkAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(30),
HeartbeatTimeout = TimeSpan.FromMinutes(2),
});
```

Set heartbeat timeout as high as acceptable for your use case — each heartbeat counts as an action.

## Testing

### Not Testing Failures
Expand Down