Skip to content

Commit ade0863

Browse files
flippedcoderSushisourcebrianmacdonald-temporal
authored
Update docs to refine explanations for non-determinism (#4448)
* perf(non-determinism): updated versioning boilerplate to include activities to handle non-deterministic operations * perf(non-determinism): updated encyclopedia pages to include activities to handle non-deterministic operations * perf(non-determinism): added callout to use cases page * perf(non-determinism): added explanation about replay without saying determinism * Update docs/develop/java/workflows/basics.mdx Co-authored-by: Spencer Judge <spencer@temporal.io> * chore(determinism): updated technical details and softened language * Update docs/develop/dotnet/workflows/basics.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * Update docs/develop/dotnet/workflows/basics.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * Update docs/develop/java/workflows/basics.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * Update docs/develop/java/workflows/basics.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * Update docs/develop/java/workflows/basics.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * Update docs/develop/java/workflows/basics.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * Update docs/develop/typescript/best-practices/debugging.mdx Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io> * chore(determinism): updated explanation for Workflow replay --------- Co-authored-by: Spencer Judge <spencer@temporal.io> Co-authored-by: Brian MacDonald <brian.macdonald@temporal.io>
1 parent 269ad52 commit ade0863

18 files changed

Lines changed: 112 additions & 114 deletions

File tree

docs/develop/dotnet/workflows/basics.mdx

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,9 @@ All Workflow Definition parameters must be serializable.
4444

4545
## Workflow logic requirements {#workflow-logic-requirements}
4646

47-
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints).
48-
Therefore, each language is limited to the use of certain idiomatic techniques.
49-
However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
47+
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). Each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with application code outside the Workflow.
5048

51-
This means there are several things Workflows cannot do such as:
49+
This means there are several things Workflows shouldn't do such as:
5250

5351
- Perform IO (network, disk, stdio, etc)
5452
- Access/alter external mutable state
@@ -65,34 +63,28 @@ This is especially true with `Task`s.
6563
Temporal requires that the deterministic `TaskScheduler.Current` is used, but many .NET async calls will use `TaskScheduler.Default` implicitly (and some analyzers even encourage this).
6664
Here are some known gotchas to avoid with .NET tasks inside of Workflows:
6765

68-
- Do not use `Task.Run` - this uses the default scheduler and puts work on the thread pool.
69-
- Use `Workflow.RunTaskAsync` instead.
70-
- Can also use `Task.Factory.StartNew` with current scheduler or instantiate the `Task` and run `Task.Start` on it.
71-
- Do not use `Task.ConfigureAwait(false)` - this will not use the current context.
72-
- If you must use `Task.ConfigureAwait`, use `Task.ConfigureAwait(true)`.
73-
- There is no significant performance benefit to `Task.ConfigureAwait` in workflows anyways due to how the scheduler works.
74-
- Do not use anything that defaults to the default task scheduler.
75-
- Do not use `Task.Delay`, `Task.Wait`, timeout-based `CancellationTokenSource`, or anything that uses .NET built-in timers.
76-
- `Workflow.DelayAsync`, `Workflow.WaitConditionAsync`, or non-timeout-based cancellation token source is suggested.
77-
- Do not use `Task.WhenAny`.
78-
- Use `Workflow.WhenAnyAsync` instead.
66+
- Use `Workflow.RunTaskAsync` instead of `Task.Run`. `Task.Run` uses the default scheduler and puts work on the thread pool.
67+
- You can also use `Task.Factory.StartNew` with current scheduler or instantiate the `Task` and run `Task.Start` on it.
68+
- If you need to use `Task.ConfigureAwait`, use `Task.ConfigureAwait(true)`. `Task.ConfigureAwait(false)` won't use the current context.
69+
- There is no significant performance benefit to `Task.ConfigureAwait` in workflows because of how the scheduler works.
70+
- Avoid anything that defaults to the default task scheduler.
71+
- Use `Workflow.DelayAsync`, `Workflow.WaitConditionAsync`, or non-timeout-based cancellation token sources instead of `Task.Delay`, `Task.Wait`, timeout-based `CancellationTokenSource`, or anything that uses .NET built-in timers.
72+
- Use `Workflow.WhenAnyAsync` instead of `Task.WhenAny`.
7973
- Technically this only applies to an enumerable set of tasks with results or more than 2 tasks with results. Other
8074
uses are safe. See [this issue](https://github.com/dotnet/runtime/issues/87481).
81-
- Do not use `Task.WhenAll`
82-
- Use `Workflow.WhenAllAsync` instead.
75+
- Use `Workflow.WhenAllAsync` instead of `Task.WhenAll`.
8376
- Technically `Task.WhenAll` is currently deterministic in .NET and safe, but it is better to use the wrapper to be
8477
sure.
85-
- Do not use `CancellationTokenSource.CancelAsync`.
86-
- Use `CancellationTokenSource.Cancel` instead.
87-
- Do not use `System.Threading.Semaphore` or `System.Threading.SemaphoreSlim` or `System.Threading.Mutex`.
88-
- Use `Temporalio.Workflows.Semaphore` or `Temporalio.Workflows.Mutex` instead.
78+
- Use `CancellationTokenSource.Cancel` instead of `CancellationTokenSource.CancelAsync`.
79+
- Use `Temporalio.Workflows.Semaphore` or `Temporalio.Workflows.Mutex` instead of `System.Threading.Semaphore`, `System.Threading.SemaphoreSlim`, or `System.Threading.Mutex`.
8980
- _Technically_ `SemaphoreSlim` does work if only the async form of `WaitAsync` is used without no timeouts and
9081
`Release` is used. But anything else can deadlock the workflow and its use is cumbersome since it must be disposed.
9182
- Be wary of additional libraries' implicit use of the default scheduler.
9283
- For example, while there are articles for `Dataflow` about [using a specific scheduler](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-specify-a-task-scheduler-in-a-dataflow-block), there are hidden implicit uses of `TaskScheduler.Default`. For example, see [this bug](https://github.com/dotnet/runtime/issues/83159).
9384

9485
In order to help catch wrong scheduler use, by default the Temporal .NET SDK adds an event source listener for info-level task events.
9586
While this technically receives events from all uses of tasks in the process, we make sure to ignore anything that is not running in a Workflow in a high performant way (basically one thread local check).
87+
9688
For code that does run in a Workflow and accidentally starts a task in another scheduler, an `InvalidWorkflowOperationException` will be thrown which "pauses" the Workflow (fails the Workflow Task which continually retries until the code is fixed).
9789
This is unfortunately a runtime-only check, but can help catch mistakes early. If this needs to be turned off for any reason, set `DisableWorkflowTracingEventListener` to `true` in Worker options.
9890

docs/develop/dotnet/workflows/versioning.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import { CaptionedImage } from '@site/src/components';
3131

3232
Since Workflow Executions in Temporal can run for long periods — sometimes months or even years — it's common to need to make changes to a Workflow Definition, even while a particular Workflow Execution is in progress.
3333

34-
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints).
35-
If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows.
34+
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints). If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows. This only applies to Workflow orchestration logic. Non-deterministic work such as API calls, and database queries should be placed in Activities, which Temporal retries reliably.
35+
3636
With Versioning, you can modify your Workflow Definition so that new executions use the updated code, while existing ones continue running the original version.
3737
There are two primary Versioning methods that you can use:
3838

docs/develop/go/workflows/basics.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,7 @@ func main() {
183183

184184
### How to develop Workflow logic {#workflow-logic-requirements}
185185

186-
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints).
187-
Therefore, each language is limited to the use of certain idiomatic techniques.
188-
However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
186+
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). Each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with application code outside the Workflow.
189187

190188
In Go, Workflow Definition code cannot directly do the following:
191189

docs/develop/go/workflows/versioning.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ tags:
2020

2121
Since Workflow Executions in Temporal can run for long periods — sometimes months or even years — it's common to need to make changes to a Workflow Definition, even while a particular Workflow Execution is in progress.
2222

23-
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints).
24-
If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows.
23+
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints). If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows. This only applies to Workflow orchestration logic. Non-deterministic work such as API calls, and database queries should be placed in Activities, which Temporal retries reliably.
24+
2525
With Versioning, you can modify your Workflow Definition so that new executions use the updated code, while existing ones continue running the original version.
2626
There are two primary Versioning methods that you can use:
2727

docs/develop/java/workflows/basics.mdx

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -211,34 +211,23 @@ When you set the Workflow Type this way, the value of the `name` parameter does
211211

212212
## Workflow logic requirements {#workflow-logic-requirements}
213213

214-
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints).
215-
Therefore, each language is limited to the use of certain idiomatic techniques.
216-
However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
214+
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). Each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
217215

218216
When defining Workflows using the Temporal Java SDK, the Workflow code must be written to execute effectively once and to completion.
219217

220218
The following constraints apply when writing Workflow Definitions:
221219

222-
- Do not use mutable global variables in your Workflow implementations.
223-
This will ensure that multiple Workflow instances are fully isolated.
224-
- Your Workflow code must be deterministic.
225-
Do not call non-deterministic functions (such as non-seeded random or `UUID.randomUUID()`) directly from the Workflow code.
226-
The Temporal SDK provides specific API for calling non-deterministic code in your Workflows.
227-
- Do not use programming language constructs that rely on system time.
228-
For example, only use `Workflow.currentTimeMillis()` to get the current time inside a Workflow.
229-
- Do not use native Java `Thread` or any other multi-threaded classes like `ThreadPoolExecutor`.
230-
Use `Async.function` or `Async.procedure`, provided by the Temporal SDK, to execute code asynchronously.
231-
- Do not use synchronization, locks, or other standard Java blocking concurrency-related classes besides those provided by the Workflow class.
232-
There is no need for explicit synchronization because multi-threaded code inside a Workflow is executed one thread at a time and under a global lock.
220+
- Do not use mutable global variables in your Workflow implementations. This will ensure that multiple Workflow instances are fully isolated.
221+
- Workflow code must be deterministic. If you need to call non-deterministic functions (such as non-seeded random or `UUID.randomUUID()`) directly from the Workflow code, the Temporal SDK provides specific API for calling non-deterministic code in your Workflows.
222+
- For operations like calling external APIs, invoking LLMs, querying databases, or performing I/O, use Activities. Activities run outside Workflow replay and are retried reliably.
223+
- Use Temporal-provided functions instead of that rely on system time. For example, use only `Workflow.currentTimeMillis()` to get the current time inside a Workflow.
224+
- Use `Async.function` or `Async.procedure`, provided by the Temporal SDK, to execute code asynchronously instead of native Java `Thread` or any other multi-threaded classes like `ThreadPoolExecutor`.
225+
- Use only the concurrency features provided by the Workflow class. Multi-threaded code inside a Workflow is executed one thread at a time and under a global lock, so there is no need for explicit synchronization.
233226
- Call `Workflow.sleep` instead of `Thread.sleep`.
234227
- Use `Promise` and `CompletablePromise` instead of `Future` and `CompletableFuture`.
235228
- Use `WorkflowQueue` instead of `BlockingQueue`.
236-
- Use `Workflow.getVersion` when making any changes to the Workflow code.
237-
Without this, any deployment of updated Workflow code might break already running Workflows.
238-
- Do not access configuration APIs directly from a Workflow because changes in the configuration might affect a Workflow Execution path.
239-
Pass it as an argument to a Workflow function or use an Activity to load it.
240-
- Use `DynamicWorkflow` when you need a default Workflow that can handle all Workflow Types that are not registered with a Worker.
241-
A single implementation can implement a Workflow Type which by definition is dynamically loaded from some external source.
242-
All standard `WorkflowOptions` and determinism rules apply to Dynamic Workflow implementations.
229+
- Use `Workflow.getVersion` when making any changes to the Workflow code to avoid deployment of updated Workflow code interfering with already-running Workflows.
230+
- Do not access configuration APIs directly from a Workflow because changes in the configuration might affect a Workflow Execution path. Instead, pass it as an argument to a Workflow function or use an Activity to load it.
231+
- Use `DynamicWorkflow` when you need a default Workflow that can handle all Workflow Types that are not registered with a Worker. A single implementation can implement a Workflow Type which by definition is dynamically loaded from some external source. All standard `WorkflowOptions` and determinism rules apply to Dynamic Workflow implementations.
243232

244233
Java Workflow reference: [https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/workflow/package-summary.html](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/workflow/package-summary.html)

docs/develop/java/workflows/versioning.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ tags:
2121

2222
Since Workflow Executions in Temporal can run for long periods — sometimes months or even years — it's common to need to make changes to a Workflow Definition, even while a particular Workflow Execution is in progress.
2323

24-
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints).
25-
If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows.
24+
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints). If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows. This only applies to Workflow orchestration logic. Non-deterministic work such as API calls, and database queries should be placed in Activities, which Temporal retries reliably.
25+
2626
With Versioning, you can modify your Workflow Definition so that new executions use the updated code, while existing ones continue running the original version.
2727
There are two primary Versioning methods that you can use:
2828

docs/develop/php/workflows/basics.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,7 @@ interface YourWorkflowDefinitionInterface
9696

9797
### How to develop Workflow logic {#workflow-logic-requirements}
9898

99-
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints).
100-
Therefore, each language is limited to the use of certain idiomatic techniques.
101-
However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
99+
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). Each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with application code outside the Workflow. used inside your Workflow to interact with external (to the Workflow) application code.
102100

103101
\*\*Temporal uses the [Microsoft Azure Event Sourcing pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing) to recover the state of a Workflow object including its local variable values.
104102

docs/develop/php/workflows/versioning.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ tags:
2929

3030
Since Workflow Executions in Temporal can run for long periods — sometimes months or even years — it's common to need to make changes to a Workflow Definition, even while a particular Workflow Execution is in progress.
3131

32-
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints).
33-
If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows.
32+
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints). If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows. This only applies to Workflow orchestration logic. Non-deterministic work such as API calls, and database queries should be placed in Activities, which Temporal retries reliably.
33+
3434
With Versioning, you can modify your Workflow Definition so that new executions use the updated code, while existing ones continue running the original version.
3535
There are two primary Versioning methods that you can use:
3636

docs/develop/python/workflows/basics.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ class YourWorkflow:
163163
**How to develop Workflow logic using the Temporal Python SDK.**
164164

165165
Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints).
166-
Therefore, each language is limited to the use of certain idiomatic techniques. However, each Temporal SDK provides a
166+
Each Temporal SDK provides a
167167
set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
168168

169169
Workflow code must be deterministic because the Temporal Server may [replay](/develop/python/best-practices/testing-suite#replay) your Workflow to reconstruct its state. This means:

docs/develop/python/workflows/versioning.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import { CaptionedImage } from '@site/src/components';
3232

3333
Since Workflow Executions in Temporal can run for long periods — sometimes months or even years — it's common to need to make changes to a Workflow Definition, even while a particular Workflow Execution is in progress.
3434

35-
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints).
36-
If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows.
35+
The Temporal Platform requires that Workflow code is [deterministic](/workflow-definition#deterministic-constraints). If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows. This only applies to Workflow orchestration logic. Non-deterministic work such as API calls, and database queries should be placed in Activities, which Temporal retries reliably.
36+
3737
With Versioning, you can modify your Workflow Definition so that new executions use the updated code, while existing ones continue running the original version.
3838
There are two primary Versioning methods that you can use:
3939

0 commit comments

Comments
 (0)