Skip to content

Commit 0a15f21

Browse files
skill-temporal-developer-updater[bot]skill-sync[bot]donald-pinckney
authored
Implement planned topic: 0009-versioned-continue-as-new (#211)
* Finalize draft for 0009-versioned-continue-as-new * Fix non-Go languages * Simplify examples, don't give an antipattern! --------- Co-authored-by: skill-sync[bot] <skill-sync[bot]@users.noreply.github.com> Co-authored-by: Donald Pinckney <donald.pinckney@temporal.io>
1 parent 1c1b6d8 commit 0a15f21

6 files changed

Lines changed: 250 additions & 1 deletion

File tree

references/core/versioning.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,58 @@ Worker v2.0 (Build ID: def456)
136136
- Workflows need bug fixes during execution
137137
- Still requires patching for version transitions
138138

139+
## Upgrading on Continue-as-New
140+
141+
> [!NOTE]
142+
> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview.
143+
144+
Long-running Pinned Workflows that use Continue-as-New can upgrade to newer Worker Deployment Versions at the Continue-as-New boundary without patching.
145+
146+
This pattern is for:
147+
148+
- Entity Workflows that run for months or years
149+
- Batch processing Workflows that checkpoint with Continue-as-New
150+
- AI agent Workflows with long sleeps waiting for user input
151+
152+
### How it works
153+
154+
By default, Pinned Workflows stay on their original Worker Deployment Version even when they Continue-as-New. With the upgrade option enabled:
155+
156+
1. Each Workflow run remains pinned to its version (no patching needed during a run).
157+
2. The Temporal Server tells the Workflow when a new **Target Version** becomes available — that is, when the Workflow's Worker Deployment gets a new Current or Ramping Version that the Workflow would move to next.
158+
3. When the Workflow performs Continue-as-New with the upgrade option, the new run starts on the Target Version.
159+
160+
### Detection flag
161+
162+
Active Workflows detect a Target Version change by checking a per-Workflow flag exposed on `WorkflowInfo` (called `target_worker_deployment_version_changed` in the docs). The flag is refreshed after each Workflow Task completes; check it from code that runs as part of a Workflow Task (for example, before accepting an Update, starting an Activity, or starting a child Workflow). See the per-language `references/{your_language}/versioning.md` for the SDK-specific call.
163+
164+
### Triggering the new run
165+
166+
When the flag is set, return a Continue-as-New error with the new run's initial Versioning Behavior set to `AutoUpgrade`. This makes the new run start on the Target Version of its Worker Deployment. The Workflow Type itself retains its Pinned annotation; only the *initial* behavior of the *new* run is overridden so it picks up the Target Version. Once the new run is on the new version, the per-Workflow-type annotation continues to apply on subsequent CaN.
167+
168+
### Limitations
169+
170+
- **Lazy moving only — sleeping Workflows do not auto-upgrade.** Send a Signal to wake an idle Workflow so it can check the flag.
171+
- **Interface compatibility is your responsibility.** When continuing as new to a different version, the previous version's Workflow input must be compatible with the new version's Workflow definition. If incompatible, the new run may fail on its first Workflow Task.
172+
- **Pinned Workflows only.** Auto-Upgrade Workflows already move to the Target Version at Workflow Task boundaries; this pattern adds nothing for them.
173+
174+
### When to use this pattern
175+
176+
- Workflow Type is Pinned **and**
177+
- Workflow runs longer than your Worker Deployment Version lifetime **and**
178+
- Workflow already uses Continue-as-New to bound Event History size.
179+
180+
For long-running Workflows that cannot use Continue-as-New (e.g., compliance audits that need full history), use `AUTO_UPGRADE` with patching instead.
181+
139182
## Choosing an Approach
140183

141184
| Scenario | Recommended Approach |
142185
|----------|---------------------|
143186
| Small change, few running workflows | Patching API |
144187
| Major rewrite | Workflow Type Versioning |
145188
| Many short workflows, frequent deploys | Worker Versioning (PINNED) |
146-
| Long-running workflows needing updates | Worker Versioning (AUTO_UPGRADE) + Patching |
189+
| Long-running workflows, uses Continue-as-New | Worker Versioning (PINNED) + upgrade on Continue-as-New |
190+
| Long-running workflows, no Continue-as-New | Worker Versioning (AUTO_UPGRADE) + Patching |
147191
| Quick fix, can wait for completion | Wait for workflows to complete |
148192

149193
## Best Practices

references/dotnet/versioning.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,49 @@ temporal workflow list --query \
296296
'TemporalWorkerDeploymentVersion = "my-service:v1.0.0" AND ExecutionStatus = "Running"'
297297
```
298298

299+
## Upgrading on Continue-as-New
300+
301+
> [!NOTE]
302+
> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview.
303+
304+
For long-running Pinned Workflows that use Continue-as-New, detect a new Target Worker Deployment Version on `Workflow.TargetWorkerDeploymentVersionChanged` and continue-as-new with `InitialVersioningBehavior.AutoUpgrade` so the new run starts on the Target Version. See `references/core/versioning.md` for the conceptual model.
305+
306+
### Detecting the Target Version change
307+
308+
`Workflow.TargetWorkerDeploymentVersionChanged` is `true` when a new Current or Ramping Version is available for this Workflow's Worker Deployment. The flag is refreshed after each Workflow Task completes.
309+
310+
Check the flag from code that runs as part of a Workflow Task — for example, before accepting an Update, starting an Activity, or starting a child Workflow.
311+
312+
### Continue-as-new with upgrade
313+
314+
When the flag is set, throw the exception from `Workflow.CreateContinueAsNewException`, passing a `ContinueAsNewOptions` whose `InitialVersioningBehavior` is `AutoUpgrade`, so the new run starts on the Target Version of its Worker Deployment.
315+
316+
```csharp
317+
using Temporalio.Common;
318+
using Temporalio.Workflows;
319+
320+
// At a natural Workflow Task boundary, e.g. before accepting Updates,
321+
// starting Activities, starting child Workflows, etc.:
322+
if (Workflow.TargetWorkerDeploymentVersionChanged)
323+
{
324+
throw Workflow.CreateContinueAsNewException(
325+
(MyWorkflow wf) => wf.RunAsync(nextInput),
326+
new ContinueAsNewOptions
327+
{
328+
InitialVersioningBehavior = InitialVersioningBehavior.AutoUpgrade,
329+
});
330+
}
331+
```
332+
333+
> [!IMPORTANT]
334+
> Don't busy-poll the flag on a timer. Check it at a natural Workflow Task boundary — before accepting Updates, starting Activities, starting child Workflows, etc. For idle Workflows, send a Signal to wake them so they can check it (see Limitations).
335+
336+
### Limitations
337+
338+
- **Lazy moving only — idle Workflows do not upgrade.** Send a Signal to wake an idle Workflow so it can check `TargetWorkerDeploymentVersionChanged`.
339+
- **Workflow input must remain compatible across versions.** The new version's Workflow definition must accept the previous version's input; otherwise the new run may fail on its first Workflow Task.
340+
- **Pinned Workflow Types only.** Auto-Upgrade Workflows move at Workflow Task boundaries already; the upgrade-on-CaN pattern adds nothing for them.
341+
299342
## Best Practices
300343

301344
1. **Check for open executions** before removing old code paths

references/go/versioning.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,47 @@ temporal workflow list --query \
226226
'TemporalWorkerDeploymentVersion = "my-service:v1.0.0" AND ExecutionStatus = "Running"'
227227
```
228228

229+
## Upgrading on Continue-as-New
230+
231+
> [!NOTE]
232+
> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview.
233+
234+
For long-running Pinned Workflows that use Continue-as-New, detect a new Target Worker Deployment Version on `WorkflowInfo` and continue-as-new with `ContinueAsNewVersioningBehaviorAutoUpgrade` so the new run starts on the Target Version. See `references/core/versioning.md` for the conceptual model.
235+
236+
### Detecting the Target Version change
237+
238+
`workflow.GetInfo(ctx).GetTargetWorkerDeploymentVersionChanged()` returns `true` when a new Current or Ramping Version is available for this Workflow's Worker Deployment. The flag is refreshed after each Workflow Task completes.
239+
240+
Check the flag from code that runs as part of a Workflow Task — for example, before accepting an Update, starting an Activity, or starting a child Workflow.
241+
242+
### Continue-as-new with upgrade
243+
244+
When the flag is set, return `workflow.NewContinueAsNewErrorWithOptions` with `InitialVersioningBehavior: workflow.ContinueAsNewVersioningBehaviorAutoUpgrade` so the new run starts on the Target Version of its Worker Deployment.
245+
246+
```go
247+
// At a natural Workflow Task boundary, e.g. before accepting Updates,
248+
// starting Activities, starting child Workflows, etc.:
249+
if workflow.GetInfo(ctx).GetTargetWorkerDeploymentVersionChanged() {
250+
return "", workflow.NewContinueAsNewErrorWithOptions(
251+
ctx,
252+
workflow.ContinueAsNewErrorOptions{
253+
InitialVersioningBehavior: workflow.ContinueAsNewVersioningBehaviorAutoUpgrade,
254+
},
255+
"ContinueAsNewWithVersionUpgrade",
256+
nextInput,
257+
)
258+
}
259+
```
260+
261+
> [!IMPORTANT]
262+
> Don't busy-poll the flag on a timer. Check it at a natural Workflow Task boundary — before accepting Updates, starting Activities, starting child Workflows, etc. For idle Workflows, send a Signal to wake them so they can check it (see Limitations).
263+
264+
### Limitations
265+
266+
- **Lazy moving only — idle Workflows do not upgrade.** Send a Signal to wake an idle Workflow so it can check `GetTargetWorkerDeploymentVersionChanged`.
267+
- **Workflow input must remain compatible across versions.** The new version's Workflow definition must accept the previous version's input; otherwise the new run may fail on its first Workflow Task.
268+
- **Pinned Workflow Types only.** Auto-Upgrade Workflows move at Workflow Task boundaries already; the upgrade-on-CaN pattern adds nothing for them.
269+
229270
## Best Practices
230271

231272
1. **Keep GetVersion calls** even when only a single branch remains -- it guards against stale replays and simplifies future changes

references/java/versioning.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,48 @@ temporal workflow count --query \
271271
'TemporalWorkerDeploymentVersion = "order-service:v1.0.0" AND ExecutionStatus = "Running"'
272272
```
273273

274+
## Upgrading on Continue-as-New
275+
276+
> [!NOTE]
277+
> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview.
278+
279+
For long-running Pinned Workflows that use Continue-as-New, detect a new Target Worker Deployment Version on `Workflow.getInfo()` and continue-as-new with `InitialVersioningBehavior.AUTO_UPGRADE` so the new run starts on the Target Version. See `references/core/versioning.md` for the conceptual model.
280+
281+
### Detecting the Target Version change
282+
283+
`Workflow.getInfo().isTargetWorkerDeploymentVersionChanged()` returns `true` when a new Current or Ramping Version is available for this Workflow's Worker Deployment. The flag is refreshed after each Workflow Task completes.
284+
285+
Check the flag from code that runs as part of a Workflow Task — for example, before accepting an Update, starting an Activity, or starting a child Workflow.
286+
287+
### Continue-as-new with upgrade
288+
289+
When the flag is set, call `Workflow.continueAsNew` with a `ContinueAsNewOptions` whose `InitialVersioningBehavior` is `AUTO_UPGRADE` so the new run starts on the Target Version of its Worker Deployment.
290+
291+
```java
292+
import io.temporal.common.InitialVersioningBehavior;
293+
import io.temporal.workflow.ContinueAsNewOptions;
294+
import io.temporal.workflow.Workflow;
295+
296+
// At a natural Workflow Task boundary, e.g. before accepting Updates,
297+
// starting Activities, starting child Workflows, etc.:
298+
if (Workflow.getInfo().isTargetWorkerDeploymentVersionChanged()) {
299+
Workflow.continueAsNew(
300+
ContinueAsNewOptions.newBuilder()
301+
.setInitialVersioningBehavior(InitialVersioningBehavior.AUTO_UPGRADE)
302+
.build(),
303+
nextInput);
304+
}
305+
```
306+
307+
> [!IMPORTANT]
308+
> Don't busy-poll the flag on a timer. Check it at a natural Workflow Task boundary — before accepting Updates, starting Activities, starting child Workflows, etc. For idle Workflows, send a Signal to wake them so they can check it (see Limitations).
309+
310+
### Limitations
311+
312+
- **Lazy moving only — idle Workflows do not upgrade.** Send a Signal to wake an idle Workflow so it can check `isTargetWorkerDeploymentVersionChanged`.
313+
- **Workflow input must remain compatible across versions.** The new version's Workflow definition must accept the previous version's input; otherwise the new run may fail on its first Workflow Task.
314+
- **Pinned Workflow Types only.** Auto-Upgrade Workflows move at Workflow Task boundaries already; the upgrade-on-CaN pattern adds nothing for them.
315+
274316
## Best Practices
275317

276318
1. **Check for open executions** before removing old code paths

references/python/versioning.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,45 @@ temporal workflow list --query \
322322
'TemporalWorkerDeploymentVersion = "my-service:v1.0.0" AND ExecutionStatus = "Running"'
323323
```
324324

325+
## Upgrading on Continue-as-New
326+
327+
> [!NOTE]
328+
> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview.
329+
330+
For long-running Pinned Workflows that use Continue-as-New, detect a new Target Worker Deployment Version on `workflow.info()` and continue-as-new with `ContinueAsNewVersioningBehavior.AUTO_UPGRADE` so the new run starts on the Target Version. See `references/core/versioning.md` for the conceptual model.
331+
332+
### Detecting the Target Version change
333+
334+
`workflow.info().is_target_worker_deployment_version_changed()` returns `True` when a new Current or Ramping Version is available for this Workflow's Worker Deployment. The flag is refreshed after each Workflow Task completes.
335+
336+
Check the flag from code that runs as part of a Workflow Task — for example, before accepting an Update, starting an Activity, or starting a child Workflow.
337+
338+
### Continue-as-new with upgrade
339+
340+
When the flag is set, call `workflow.continue_as_new` with `initial_versioning_behavior=ContinueAsNewVersioningBehavior.AUTO_UPGRADE` so the new run starts on the Target Version of its Worker Deployment.
341+
342+
```python
343+
from temporalio import workflow
344+
from temporalio.workflow import ContinueAsNewVersioningBehavior
345+
346+
# At a natural Workflow Task boundary, e.g. before accepting Updates,
347+
# starting Activities, starting child Workflows, etc.:
348+
if workflow.info().is_target_worker_deployment_version_changed():
349+
workflow.continue_as_new(
350+
next_input,
351+
initial_versioning_behavior=ContinueAsNewVersioningBehavior.AUTO_UPGRADE,
352+
)
353+
```
354+
355+
> [!IMPORTANT]
356+
> Don't busy-poll the flag on a timer. Check it at a natural Workflow Task boundary — before accepting Updates, starting Activities, starting child Workflows, etc. For idle Workflows, send a Signal to wake them so they can check it (see Limitations).
357+
358+
### Limitations
359+
360+
- **Lazy moving only — idle Workflows do not upgrade.** Send a Signal to wake an idle Workflow so it can check `is_target_worker_deployment_version_changed`.
361+
- **Workflow input must remain compatible across versions.** The new version's Workflow definition must accept the previous version's input; otherwise the new run may fail on its first Workflow Task.
362+
- **Pinned Workflow Types only.** Auto-Upgrade Workflows move at Workflow Task boundaries already; the upgrade-on-CaN pattern adds nothing for them.
363+
325364
## Best Practices
326365

327366
1. **Check for open executions** before removing old code paths

references/typescript/versioning.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,46 @@ Worker Versioning is best suited for:
204204

205205
For long-running Workflows, consider combining Worker Versioning with the Patching API, or use Continue-as-New to move Workflows to newer versions.
206206

207+
## Upgrading on Continue-as-New
208+
209+
> [!NOTE]
210+
> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview.
211+
212+
For long-running Pinned Workflows that use Continue-as-New, detect a new Target Worker Deployment Version on `workflowInfo()` and continue-as-new with `InitialVersioningBehavior.AUTO_UPGRADE` so the new run starts on the Target Version. See `references/core/versioning.md` for the conceptual model.
213+
214+
### Detecting the Target Version change
215+
216+
`workflowInfo().targetWorkerDeploymentVersionChanged` is `true` when a new Current or Ramping Version is available for this Workflow's Worker Deployment. The flag is refreshed after each Workflow Task completes.
217+
218+
Check the flag from code that runs as part of a Workflow Task — for example, before accepting an Update, starting an Activity, or starting a child Workflow.
219+
220+
### Continue-as-new with upgrade
221+
222+
When the flag is set, build the Continue-as-New function with `makeContinueAsNewFunc`, passing `initialVersioningBehavior: InitialVersioningBehavior.AUTO_UPGRADE`, so the new run starts on the Target Version of its Worker Deployment.
223+
224+
```ts
225+
import * as wf from '@temporalio/workflow';
226+
import { InitialVersioningBehavior } from '@temporalio/common';
227+
228+
// At a natural Workflow Task boundary, e.g. before accepting Updates,
229+
// starting Activities, starting child Workflows, etc.:
230+
if (wf.workflowInfo().targetWorkerDeploymentVersionChanged) {
231+
const continueAsNew = wf.makeContinueAsNewFunc<typeof myWorkflow>({
232+
initialVersioningBehavior: InitialVersioningBehavior.AUTO_UPGRADE,
233+
});
234+
await continueAsNew(nextInput);
235+
}
236+
```
237+
238+
> [!IMPORTANT]
239+
> Don't busy-poll the flag on a timer. Check it at a natural Workflow Task boundary — before accepting Updates, starting Activities, starting child Workflows, etc. For idle Workflows, send a Signal to wake them so they can check it (see Limitations).
240+
241+
### Limitations
242+
243+
- **Lazy moving only — idle Workflows do not upgrade.** Send a Signal to wake an idle Workflow so it can check `targetWorkerDeploymentVersionChanged`.
244+
- **Workflow input must remain compatible across versions.** The new version's Workflow definition must accept the previous version's input; otherwise the new run may fail on its first Workflow Task.
245+
- **Pinned Workflow Types only.** Auto-Upgrade Workflows move at Workflow Task boundaries already; the upgrade-on-CaN pattern adds nothing for them.
246+
207247
## Best Practices
208248

209249
1. Use descriptive `patchId` names that explain the change

0 commit comments

Comments
 (0)