Skip to main content

Upgrade on Continue-as-New

View Markdown

This page covers how to upgrade pinned Workflows to a new Worker Deployment Version with Continue-as-New.

Upgrading on Continue-as-New

Long-running Workflows that use Continue-as-New can upgrade to newer Worker Deployment Versions at Continue-as-New boundaries without requiring patching.

This pattern is ideal for:

  • Entity Workflows that run for months or years
  • Batch processing Workflows that checkpoint with Continue-as-New
  • AI agent Workflows with long sleeps waiting for user input
Public Preview

This feature is in Public Preview as an experimental SDK-level option.

How it works

By default, Pinned Workflows stay on their original Worker Deployment Version even when they Continue-as-New. With the upgrade option enabled:

  1. Each Workflow run remains pinned to its version (no patching needed during a run)
  2. The Temporal Server tells the workflow when a new Target Version becomes available
  3. When the Workflow performs Continue-as-New with the upgrade option, the new run starts on the Target Version

Checking for new versions

When a new Worker Deployment Version becomes Current or Ramping, active Workflows can detect this through target_worker_deployment_version_changed:

func (w *Workflows) ContinueAsNewWithVersionUpgradeV1(
ctx workflow.Context,
attempt int,
) (string, error) {
if attempt > 0 {
return "v1.0", nil
}

// Check GetTargetWorkerDeploymentVersionChanged periodically.
// GetTargetWorkerDeploymentVersionChanged is refreshed after each WFT completes.
for {
// Trigger a WFT when timer expires, thereby refreshing the GetTargetWorkerDeploymentVersionChanged flag.
// Since this is just a test workflow, we aren't doing any real work. In a real workflow regularly
// doing non-sleep workflow tasks, you would not need to artificially trigger a WFT to refresh the
// GetTargetWorkerDeploymentVersionChanged flag. You could choose to check the field periodically, or you
// might want to check before accepting updates, starting activities, or starting child workflows.
err := workflow.Sleep(ctx, 10*time.Millisecond)
if err != nil {
return "", err
}
info := workflow.GetInfo(ctx)
if info.GetTargetWorkerDeploymentVersionChanged() {
return "", workflow.NewContinueAsNewErrorWithOptions(
ctx,
workflow.ContinueAsNewErrorOptions{
// Pass InitialVersioningBehavior=workflow.ContinueAsNewVersioningBehaviorAutoUpgrade
// to make the new run start with AutoUpgrade behavior and use the Target Version of
// its Worker Deployment.
InitialVersioningBehavior: workflow.ContinueAsNewVersioningBehaviorAutoUpgrade,
},
"ContinueAsNewWithVersionUpgrade",
attempt+1,
)
}
}
}

func (w *Workflows) ContinueAsNewWithVersionUpgradeV2(
ctx workflow.Context,
attempt int,
) (string, error) {
return "v2.0", nil
}

Limitations

Current Limitations
  • Lazy moving only: Workflows must execute a step to receive the target-version-changed information. Sleeping Workflows won't proactively get it. If you have idle Workflows that you want to wake up so they can check the target-version-changed flag, you can send them a Signal.
  • Interface compatibility: When continuing as new to a different version, ensure your Workflow input provided by the previous version's workflow definition is compatible with the new version's workflow definition. If incompatible, the new run may fail on its first Workflow Task.