Skip to content

Commit 4931605

Browse files
authored
Merge branch 'main' into php/workflow-logger
2 parents 9f38f5d + 3c02631 commit 4931605

File tree

18 files changed

+275
-109
lines changed

18 files changed

+275
-109
lines changed

docs/develop/dotnet/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Build Temporal Applications with the .NET SDK.
2626
- [.NET API Documentation](https://dotnet.temporal.io/api/)
2727
- [.NET SDK Code Samples](https://github.com/temporalio/samples-dotnet)
2828
- [.NET SDK GitHub](https://github.com/temporalio/sdk-dotnet)
29+
- [Temporal 101 in .NET Free Course](https://learn.temporal.io/courses/temporal_101/dotnet/)
2930

3031
**Get Connected with the Temporal .NET Community:**
3132

docs/develop/go/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Build Temporal Applications with the Go SDK.
2323
- [Go API Documentation](https://pkg.go.dev/go.temporal.io/sdk)
2424
- [Go SDK Code Samples](https://github.com/temporalio/samples-go)
2525
- [Go SDK GitHub](https://github.com/temporalio/sdk-go)
26+
- [Temporal 101 in Go Free Course](https://learn.temporal.io/courses/temporal_101/go/)
2627

2728
**Get Connected with the Temporal Go Community:**
2829

docs/develop/java/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Build Temporal Applications with the Java SDK.
2323
- [Java API Documentation](https://javadoc.io/doc/io.temporal/temporal-sdk)
2424
- [Java SDK Code Samples](https://github.com/temporalio/samples-java)
2525
- [Java SDK GitHub](https://github.com/temporalio/sdk-java)
26+
- [Temporal 101 in Java Free Course](https://learn.temporal.io/courses/temporal_101/java/)
2627

2728
**Get Connected with the Temporal Java Community:**
2829

docs/develop/python/continue-as-new.mdx

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,99 @@ tags:
1717
- Temporal SDKs
1818
---
1919

20-
**How to Continue-As-New using the Temporal Python SDK.**
20+
This page answers the following questions for Python developers:
2121

22-
[Continue-As-New](/workflow-execution/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.
23-
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.
22+
- [What is Continue-As-New?](#what)
23+
- [How to Continue-As-New?](#how)
24+
- [When is it right to Continue-as-New?](#when)
25+
- [How to test Continue-as-New?](#how-to-test)
2426

25-
To Continue-As-New in Python, call the [`continue_as_new()`](https://python.temporal.io/temporalio.workflow.html#continue_as_new) function from inside your Workflow, which will stop the Workflow immediately and Continue-As-New.
27+
## What is Continue-As-New? {#what}
28+
29+
[Continue-As-New](/workflow-execution/continue-as-new) lets a Workflow Execution close successfully and creates a new Workflow Execution.
30+
You can think of it as a checkpoint when your Workflow gets too long or approaches certain scaling limits.
31+
32+
The new Workflow Execution is in the same [chain](/workflow-execution#workflow-execution-chain); it keeps the same Workflow Id but gets a new Run Id and a fresh Event History.
33+
It also receives your Workflow's usual parameters.
34+
35+
## How to Continue-As-New using the Python SDK {#how}
36+
37+
First, design your Workflow parameters so that you can pass in the "current state" when you Continue-As-New into the next Workflow run.
38+
This state is typically set to `None` for the original caller of the Workflow.
2639

2740
<div class="copycode-notice-container">
28-
<a href="https://github.com/temporalio/documentation/blob/main/sample-apps/python/continue_as_new/your_workflows_dacx.py">
41+
<a href="https://github.com/temporalio/samples-python/blob/main/message_passing/safe_message_handlers/workflow.py">
2942
View the source code
3043
</a>{' '}
3144
in the context of the rest of the application code.
3245
</div>
46+
```python
47+
@dataclass
48+
class ClusterManagerInput:
49+
state: Optional[ClusterManagerState] = None
50+
test_continue_as_new: bool = False
51+
52+
@workflow.run
53+
async def run(self, input: ClusterManagerInput) -> ClusterManagerResult:
54+
55+
````
56+
The test hook in the above snippet is covered [below](#how-to-test).
3357

58+
Inside your Workflow, call the [`continue_as_new()`](https://python.temporal.io/temporalio.workflow.html#continue_as_new) function with the same type.
59+
This stops the Workflow right away and starts a new one.
60+
61+
<div class="copycode-notice-container">
62+
<a href="https://github.com/temporalio/samples-python/blob/main/message_passing/safe_message_handlers/workflow.py">
63+
View the source code
64+
</a>{' '}
65+
in the context of the rest of the application code.
66+
</div>
3467
```python
35-
# ...
36-
@workflow.defn
37-
class LoopingWorkflow:
38-
@workflow.run
39-
async def run(self, iteration: int) -> None:
40-
if iteration == 5:
41-
return
42-
await asyncio.sleep(10)
43-
workflow.continue_as_new(iteration + 1)
44-
```
68+
workflow.continue_as_new(
69+
ClusterManagerInput(
70+
state=self.state,
71+
test_continue_as_new=input.test_continue_as_new,
72+
)
73+
)
74+
````
4575

46-
:::warning Using Continue-as-New and Updates
76+
### Considerations for Workflows with Message Handlers {#with-message-handlers}
4777

48-
- Temporal _does not_ support Continue-as-New functionality within Update handlers.
49-
- Complete all handlers _before_ using Continue-as-New.
50-
- Use Continue-as-New from your main Workflow Definition method, just as you would complete or fail a Workflow Execution.
78+
If you use Updates or Signals, don't call Continue-as-New from the handlers.
79+
Instead, wait for your handlers to finish in your main Workflow before you run `continue_as_new`.
80+
See the [`all_handlers_finished`](message-passing#wait-for-message-handlers) example for guidance.
5181

52-
:::
82+
## When is it right to Continue-as-New using the Python SDK? {#when}
83+
84+
Use Continue-as-New when your Workflow might hit [Event History Limits](/workflow-execution/event#event-history).
85+
86+
Temporal tracks your Workflow's progress against these limits to let you know when you should Continue-as-New.
87+
Call `workflow.info().is_continue_as_new_suggested()` to check if it's time.
88+
89+
## How to test Continue-as-New using the Python SDK {#how-to-test}
90+
91+
Testing Workflows that naturally Continue-as-New may be time-consuming and resource-intensive.
92+
Instead, add a test hook to check your Workflow's Continue-as-New behavior faster in automated tests.
93+
94+
For example, when `test_continue_as_new == True`, this sample creates a test-only variable called `self.max_history_length` and sets it to a small value.
95+
A helper method in the Workflow checks it each time it considers using Continue-as-New:
96+
97+
<div class="copycode-notice-container">
98+
<a href="https://github.com/temporalio/samples-python/blob/main/message_passing/safe_message_handlers/workflow.py">
99+
View the source code
100+
</a>{' '}
101+
in the context of the rest of the application code.
102+
</div>
103+
104+
```python
105+
def should_continue_as_new(self) -> bool:
106+
if workflow.info().is_continue_as_new_suggested():
107+
return True
108+
# For testing
109+
if (
110+
self.max_history_length
111+
and workflow.info().get_current_history_length() > self.max_history_length
112+
):
113+
return True
114+
return False
115+
```

docs/develop/python/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Build Temporal Applications with the Python SDK.
2323
- [Python API Documentation](https://python.temporal.io)
2424
- [Python SDK Code Samples](https://github.com/temporalio/samples-python)
2525
- [Python SDK Github](https://github.com/temporalio/sdk-python)
26+
- [Temporal 101 in Python Free Course](https://learn.temporal.io/courses/temporal_101/python/)
2627

2728
**Get Connected with the Temporal Python Community:**
2829

docs/develop/typescript/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Build Temporal Applications with the TypeScript SDK.
2323
- [TypeScript API Documentation](https://typescript.temporal.io)
2424
- [TypeScript SDK Code Samples](https://github.com/temporalio/samples-typescript)
2525
- [TypeScript SDK GitHub](https://github.com/temporalio/sdk-typescript)
26+
- [Temporal 101 in TypeScript Free Course](https://learn.temporal.io/courses/temporal_101/typescript/)
2627

2728
**Get Connected with the Temporal TypeScript Community:**
2829

docs/develop/worker-performance.mdx

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ They're used for both Workflow and Activity Tasks.
3535
When a Worker starts processing a Task, it occupies one slot.
3636
The number of available slots directly affects how many tasks a Worker can handle simultaneously.
3737

38-
### Slot suppliers
38+
### Slot suppliers {#slot-suppliers}
3939

4040
A **Slot Supplier** defines a strategy to provide slots for a Worker, increasing or decreasing the Worker's slot count.
4141
The supplier determines when it's acceptable to begin a new Task.
@@ -81,6 +81,8 @@ Available slot suppliers include:
8181
**Worker tuning** lets you manage and customize a Worker's runtime performance characteristics.
8282
They use special types called **Worker tuners** that assign slot suppliers to various Task Types, including Worker, Activity, Nexus, and Local Activity Tasks.
8383

84+
For more on how to configure and use Worker tuners, see [Worker runtime performance tuning](#worker-performance-tuning) below.
85+
8486
:::caution
8587

8688
- Worker tuners supersede the existing `maxConcurrentXXXTask` style Worker options.
@@ -90,12 +92,13 @@ They use special types called **Worker tuners** that assign slot suppliers to va
9092

9193
### Task pollers {#task-poller}
9294

93-
A Worker's **Task pollers** play a crucial role in the Temporal architecture by efficiently distributing work to Workers to support scalable, resilient Workflow Execution.
94-
It actively polls a Task Queue for Tasks to process.
95+
A Worker's **Task pollers** play a crucial role in the Temporal architecture by efficiently
96+
distributing work to Workers to support scalable, resilient Workflow Execution.
97+
They actively poll a Task Queue for Tasks to process.
9598
Pollers create long-polling connections to the Temporal Service, allowing the service to dispatch Tasks to Workers.
96-
When a Task Poller receives a Task, it delivers it to the appropriate Executor Slot for processing.
99+
When a Task Poller receives a Task, it delivers to the appropriate Executor Slot for processing.
97100

98-
Task Pollers enable efficient load balancing across multiple Worker processes and support server-side throttling.
101+
Task Pollers enable efficient load balancing across multiple Worker processes.
99102
The number of Task Pollers can be configured using `WorkerOptions` when creating a new Worker instance.
100103

101104
## Performance metrics for tuning {#metrics}
@@ -125,10 +128,6 @@ For more information about `schedule_to_start` timeout and latency, see [Schedul
125128

126129
The [`sticky_cache_size`](/references/sdk-metrics#sticky_cache_size) and [`workflow_active_thread_count`](/references/sdk-metrics#workflow_active_thread_count) metrics report the size of the Workflow cache and the number of cached Workflow threads.
127130

128-
:::note
129-
Server version ≥ 1.8.0 is required for access to these performance metrics for the JavaSDK.
130-
:::
131-
132131
## Worker performance options {#configuration}
133132

134133
Each Worker can be configured by providing custom Worker options (`WorkerOptions`) at instantiation.
@@ -150,13 +149,18 @@ The `maxConcurrentWorkflowTaskExecutionSize` and `maxConcurrentActivityExecution
150149
`maxConcurrentWorkflowTaskPollers` (JavaSDK: `workflowPollThreadCount`) and `maxConcurrentActivityTaskPollers` (JavaSDK: `activityPollThreadCount`) define the maximum count of pollers performing poll requests on Workflow and Activity Task Queues.
151150
The Worker's requests deliver Tasks to the related executor slots.
152151

152+
Some SDKs (currently Python) have enabled experimental support for automated poller tuning.
153+
This feature can be enabled by setting the `*_task_poller_behavior` options to `PollerBehaviorAutoscaling`.
154+
Names may vary slightly depending on the SDK.
155+
There are options within to configure minimum, maximum, and initial poller counts, but it is unlikely that you will need to adjust them.
156+
153157
### Cache options
154158

155159
A Workflow Cache is created and shared between all Workers on a single host.
156160
It's designed to limit the resources used by the cache for each host/process.
157161
These options are defined on `WorkerFactoryOptions` in JavaSDK and in `worker` package in GoSDK:
158162

159-
- `worker.setStickyWorkflowCacheSize` (JavaSDK: WorkerFactoryOptions#workflowCacheSize`) defines the maximum number of cached Workflows Executions.
163+
- `worker.setStickyWorkflowCacheSize` (JavaSDK: `WorkerFactoryOptions#workflowCacheSize`) defines the maximum number of cached Workflows Executions.
160164
Each cached Workflow contains at least one Workflow thread and its resources.
161165
Resources include memory, etc.
162166
- `maxWorkflowThreadCount` defines the maximum number of Workflow threads that may exist concurrently at any time.
@@ -265,9 +269,10 @@ If a just-started worker were to have no throttle, and there was a backlog of Ta
265269
If each Task allocated 1GB of RAM, the Worker would likely run out of memory and crash.
266270
The throttle enforces a wait before handing out new slots (after a minimum number of slots have been occupied) so you can measure newly consumed resources.
267271

268-
## Worker tuner examples {#examples}
272+
## Performance tuning examples {#examples}
269273

270-
The following examples show how to create and provision composite Worker tuners.
274+
The following examples show how to create and provision composite Worker tuners and set other
275+
performance related options.
271276
Each tuner provides slot suppliers for various Task types.
272277
These examples focus on Activities and Local Activities, since Workflow Tasks normally do not need resource-based tuning.
273278

@@ -386,13 +391,19 @@ const workerOptions = {
386391
### Python SDK
387392

388393
```python
389-
# Just resource based
394+
# Just a resource based tuner, with poller autoscaling
390395
tuner = WorkerTuner.create_resource_based(
391396
target_memory_usage=0.5,
392397
target_cpu_usage=0.5,
393398
)
394-
worker = Worker(client, task_queue="foo", tuner=tuner)
395-
# Combining different types
399+
worker = Worker(
400+
client,
401+
task_queue="foo",
402+
tuner=tuner,
403+
workflow_task_poller_behavior=PollerBehaviorAutoscaling(),
404+
activity_task_poller_behavior=PollerBehaviorAutoscaling()
405+
)
406+
# Combining different types, with poller autoscaling
396407
resource_based_options = ResourceBasedTunerConfig(0.8, 0.9)
397408
tuner = WorkerTuner.create_composite(
398409
workflow_supplier=FixedSizeSlotSupplier(10),
@@ -405,34 +416,40 @@ tuner = WorkerTuner.create_composite(
405416
resource_based_options,
406417
),
407418
)
408-
worker = Worker(client, task_queue="foo", tuner=tuner)
419+
worker = Worker(
420+
client,
421+
task_queue="foo",
422+
tuner=tuner,
423+
workflow_task_poller_behavior=PollerBehaviorAutoscaling(),
424+
activity_task_poller_behavior=PollerBehaviorAutoscaling()
425+
)
409426
```
410427

411428
### .NET C# SDK
412429

413430
```csharp
414431
// Just resource based
415432
var worker = new TemporalWorker(
416-
Client,
417-
new TemporalWorkerOptions("my-task-queue")
418-
{
419-
Tuner = WorkerTuner.CreateResourceBased(0.8, 0.9),
420-
};
433+
Client,
434+
new TemporalWorkerOptions("my-task-queue")
435+
{
436+
Tuner = WorkerTuner.CreateResourceBased(0.8, 0.9),
437+
});
421438
// Combining different types
422439
var resourceTunerOptions = new ResourceBasedTunerOptions(0.8, 0.9);
423440
var worker = new TemporalWorker(
424-
Client,
425-
new TemporalWorkerOptions("my-task-queue")
426-
{
427-
Tuner = new WorkerTuner(
428-
new FixedSizeSlotSupplier(10),
429-
new ResourceBasedSlotSupplier(
430-
new ResourceBasedSlotSupplierOptions(),
431-
resourceTunerOptions),
432-
new ResourceBasedSlotSupplier(
433-
new ResourceBasedSlotSupplierOptions(),
434-
resourceTunerOptions),
435-
};
441+
Client,
442+
new TemporalWorkerOptions("my-task-queue")
443+
{
444+
Tuner = new WorkerTuner(
445+
new FixedSizeSlotSupplier(10),
446+
new ResourceBasedSlotSupplier(
447+
new ResourceBasedSlotSupplierOptions(),
448+
resourceTunerOptions),
449+
new ResourceBasedSlotSupplier(
450+
new ResourceBasedSlotSupplierOptions(),
451+
resourceTunerOptions)),
452+
});
436453
```
437454

438455
## Workflow Cache Tuning
@@ -594,7 +611,7 @@ The values provided by `temporal task-queue describe` can help you manage your W
594611
If this time grows too long, more Workers can boost Workflow efficiency.
595612

596613
- Calculate the demand per Worker by dividing the number of backlogged Tasks (`ApproximateBacklogCount`) by the number of Workers.
597-
Determine if your task processing rate is within an acceptable range for you needs using the per-Worker demand (how many Tasks each Worker has yet to process), the backlog consumption rate (`TasksDispatchRate`, the rate at which Workers are processing Tasks), and the dispatch latency (`ApproximateBacklogAge`, the time the oldest Task has been waiting to be assigned to a Worker).
614+
Determine if your task processing rate is within an acceptable range for your needs using the per-Worker demand (how many Tasks each Worker has yet to process), the backlog consumption rate (`TasksDispatchRate`, the rate at which Workers are processing Tasks), and the dispatch latency (`ApproximateBacklogAge`, the time the oldest Task has been waiting to be assigned to a Worker).
598615

599616
- The backlog increase rate (`BacklogIncreaseRate`) shows the changing demand on your Workers over time.
600617
As this rate increases, you may need to add more Workers until demand and capacity are balanced.
@@ -635,13 +652,14 @@ Increase the maximum number of working slots by adjusting `maxConcurrentWorkflow
635652
1. The Worker hosts are underutilized (no bottlenecks on CPU, load average, etc.).
636653
2. The `worker_task_slots_available` metric from the corresponding Worker type frequently shows a depleted number of available Worker slots.
637654

655+
Alternatively, consider using a resource-based slot supplier as described [here](#slot-suppliers).
656+
638657
### Poller count
639658

640-
:::note
641-
Adjustments to pollers are rarely needed and rarely make a difference. Please consider this step only after adjusting Worker slots in the previous step. The only scenario in which the pollers' adjustment makes sense is when there is a significant network latency between the Workers and Temporal Server.
642-
:::
659+
Sometimes, it can be appropriate to increase the number of task pollers.
660+
This is usually more common in situations where your Workers have somewhat high latency when communicating with the server.
643661

644-
If:
662+
You can use automated poller tuning, or consider adjustment if:
645663

646664
1. the `schedule_to_start` metric is abnormally long, and
647665
2. the Worker hosts are underutilized (there are no bottlenecks on CPU, load average, etc), and

docs/encyclopedia/workflow/workflow-execution/event.mdx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,17 @@ An append-only log of [Events](#event) for your application.
6565
- Event History is durably persisted by the Temporal service, enabling seamless recovery of your application state from crashes or failures.
6666
- It also serves as an audit log for debugging.
6767

68-
**Event History limits**
68+
### Event History limits {#event-history-limits}
6969

7070
The Temporal Service stores the complete Event History for the entire lifecycle of a Workflow Execution.
7171

72-
The Temporal Service logs a [warning after 10Ki (10,240) Events](/workflow-execution/limits) and periodically logs additional warnings as new Events are added.
73-
If the Event History exceeds 50Ki (51,200) Events, the Workflow Execution is terminated.
72+
The Temporal Service logs a [warning after 10,240 Events](/workflow-execution/limits) and periodically logs additional warnings as new Events are added.
73+
74+
The Workflow Execution is terminated when the Event History:
75+
76+
- exceeds 51,200 Events.
77+
- contains more than 2000 Updates.
78+
- contains more than 10000 Signals.
7479

7580
### Event loop {#event-loop}
7681

0 commit comments

Comments
 (0)