Files
Donald Pinckney b5719bc143 PR Tracking Initial Release (#4)
* Add initial skill for testing, which is simply Steve's skill (#1)

* Add initial skill for testing, which is simply Steve's skill

* Rename skill to 'temporal-dev' and update version

Updated skill name and version for Temporal Python.

* Use claude to merge Steve's, Max's, and Mason's skills.  (#2)

* Use claude to merge Steve's, Max's, and Mason's skills. Did a review pass using claude's skill devlopment skills

* Add missing things from Steve

* trigger tweaks

* Add in common gotchas from Johann

* add simple feedback mechanism (#3)

* Change skill name to kebab-case, for compatibility with Amp and Cline (#7)

* Clean up references/core/ai-integration.md

* Clean up references/core/common-gotchas.md

* Clean up references/core/common-gotchas.md

* Clean up references/core/determinism.md

* Clean up references/core/determinism.md

* Update error-reference.md

* Update interactive-workflows.md

* Clean up patterns.md

* Cut shell scripts

* Edit troubleshooting.md

* remove interceptors for now

* remove dynamic workflows

* clarify on heartbeating of async activity completions, and prompt it a bit in relation to signals

* Improve references/python/advanced-features.md

* Use explicit namespace in connect

* remove duplicated content from determinism.md, clean up

* Improve references/python/data-handling.md

* Prefer start_to_close_timeout

* don't explicitely provide defaults for retry policies

* error-handling.md cleanup

* move idempotency patterns to patterns.md

* remove multi-param activities

* small edits

* Unify sandbox stuff into one file

* local activities aren't experimental

* Clean up references/python/sync-vs-async.md

* Cleanup observability.md, remove duplicated search attributes

* Cut otel for now

* cut a lot of duplicate stuff from python gotchas, address comments

* de-duplicate content

* Lots of improvements to testing

* cleanup to top level of skill (like CLI install instructions), and to top-level of python

* Improve patterns.md

* clean up ai-patterns.md

* Update readme with installation instructions

* remove ts directory

* De-couple core from python and TypeScript as much as possible

* Remove TypeScript hints

* add prompting for feedback at startup - wait for ethan on slack channel

* shorten url

* Update slack channel

* Automated pass over on python cleanup & deduplication

* Remove multi-patching from Python, since its obvious, dont waste tokens on it. (#34)

* Add TypeScript (#31)

Adds initial support for TypeScript to the skill

---------

Co-authored-by: James Watkins-Harvey <mjameswh@users.noreply.github.com>
Co-authored-by: Chris Olszewski <chrisdolszewski@gmail.com>

* Fix typos and reference links (#36)

* Fix typos and reference links

* 2 more typo fixes

* quick edit to readme (#37)

* Fix saga compensations to run under cancellation protection (#43)

When a workflow is cancelled mid-saga, compensations must run in a
cancellation-protected scope, otherwise they are immediately cancelled
before they can execute.

- Python: wrap compensation loop in asyncio.shield() so it runs even
  when the workflow receives a CancelledError
- TypeScript: wrap compensation loop in CancellationScope.nonCancellable()
  so it runs even when the root scope is cancelled (per official docs:
  "Cleanup logic must be in a nonCancellable scope")
- TypeScript: also fix compensation registration order — register BEFORE
  calling the activity (was already correct in Python)

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Update readme for public preview (#45)

* a few more readme tweaks (#46)

* Add MIT License to the project (#47)

* Add Go (supersedes other PR) (#38)

* progress on go

* Go translation workflow completed.

* missed a few spots

* Manual edits

* Address feedback

* Add gotcha about anonymous local activities

* Sample code for payload converter

* clarify sdk protection mechanisms

* Setup CODEOWNERS to AI SDK team (#48)

* Align version number in SKILL.md and plugin.json. (#49)

---------

Co-authored-by: James Watkins-Harvey <mjameswh@users.noreply.github.com>
Co-authored-by: Chris Olszewski <chrisdolszewski@gmail.com>
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:36:15 -04:00

233 lines
7.3 KiB
Markdown

# Go SDK Versioning
For conceptual overview and guidance on choosing an approach, see `references/core/versioning.md`.
## GetVersion API
`workflow.GetVersion` safely performs backwards-incompatible changes to Workflow Definitions. It returns the version to branch on, recording the result as a marker in the Event History.
```go
v := workflow.GetVersion(ctx, "changeID", workflow.DefaultVersion, maxSupported)
```
- `changeID`: unique string identifying the change
- `minSupported`: oldest version still supported (`workflow.DefaultVersion` is `-1`)
- `maxSupported`: current/newest version
- Returns `maxSupported` for new executions; returns the recorded version on replay
### Three-Step Lifecycle
**Step 1: Add GetVersion with both code paths**
Original code calls `ActivityA`. You want to replace it with `ActivityC`:
```go
v := workflow.GetVersion(ctx, "Step1", workflow.DefaultVersion, 1)
if v == workflow.DefaultVersion {
// Old code path (for replay of existing workflows)
err = workflow.ExecuteActivity(ctx, ActivityA, data).Get(ctx, &result1)
} else {
// New code path
err = workflow.ExecuteActivity(ctx, ActivityC, data).Get(ctx, &result1)
}
```
For new executions, `GetVersion` returns `1` and records a marker. For replay of pre-change workflows (no marker), it returns `DefaultVersion` (`-1`).
**Step 2: Remove old branch (increase minSupported)**
After all `DefaultVersion` Workflow Executions have completed:
```go
v := workflow.GetVersion(ctx, "Step1", 1, 1)
// Only the new code path remains
err = workflow.ExecuteActivity(ctx, ActivityC, data).Get(ctx, &result1)
```
Keep the `GetVersion` call even with a single branch. This ensures:
1. If an older execution replays on this code, it fails fast instead of proceeding incorrectly
2. If you need further changes, you just bump `maxSupported`
**Step 3: Further changes (bump maxSupported)**
Later, replace `ActivityC` with `ActivityD`:
```go
v := workflow.GetVersion(ctx, "Step1", 1, 2)
if v == 1 {
err = workflow.ExecuteActivity(ctx, ActivityC, data).Get(ctx, &result1)
} else {
err = workflow.ExecuteActivity(ctx, ActivityD, data).Get(ctx, &result1)
}
```
After all version-1 executions complete, collapse again:
```go
_ = workflow.GetVersion(ctx, "Step1", 2, 2)
err = workflow.ExecuteActivity(ctx, ActivityD, data).Get(ctx, &result1)
```
### Using GetVersion in Loops
The return value for a given `changeID` is immutable once recorded. In loops, append the iteration number to the `changeID`:
```go
for i := 0; i < 10; i++ {
v := workflow.GetVersion(ctx, fmt.Sprintf("myChange-%d", i), workflow.DefaultVersion, 1)
if v == workflow.DefaultVersion {
// old path
} else {
// new path
}
}
```
## Workflow Type Versioning
Create a new Workflow Type for incompatible changes:
```go
// Original
func MyWorkflow(ctx workflow.Context, input Input) (string, error) {
// v1 implementation
}
// New version
func MyWorkflowV2(ctx workflow.Context, input Input) (string, error) {
// v2 implementation
}
```
Register both with the Worker:
```go
w := worker.New(c, "my-task-queue", worker.Options{})
w.RegisterWorkflow(MyWorkflow)
w.RegisterWorkflow(MyWorkflowV2)
```
Route new executions to the new type. Old workflows continue on the old type. Check for open executions before removing the old type:
```bash
temporal workflow list --query 'WorkflowType = "MyWorkflow" AND ExecutionStatus = "Running"'
```
## Worker Versioning
Worker Versioning manages versions at the deployment level, allowing multiple Worker versions to run simultaneously.
### Key Concepts
**Worker Deployment**: A logical service grouping similar Workers together (e.g., "loan-processor"). All versions of your code live under this umbrella.
**Worker Deployment Version**: A specific snapshot of your code identified by a deployment name and Build ID (e.g., "loan-processor:v1.0" or "loan-processor:abc123").
### Configuring Workers for Versioning
```go
w := worker.New(c, "my-task-queue", worker.Options{
DeploymentOptions: worker.DeploymentOptions{
UseVersioning: true,
Version: worker.WorkerDeploymentVersion{
DeploymentName: "my-service",
BuildId: "v1.0.0", // or git commit hash
},
DefaultVersioningBehavior: workflow.VersioningBehaviorPinned,
},
})
```
**Configuration fields:**
- `UseVersioning`: enables Worker Versioning
- `Version`: identifies the Worker Deployment Version (deployment name + build ID)
- `DefaultVersioningBehavior`: `VersioningBehaviorPinned` or `VersioningBehaviorAutoUpgrade`
- Build ID: typically a git commit hash, version number, or timestamp
### PINNED vs AUTO_UPGRADE Behaviors
**PINNED Behavior**
Workflows stay locked to their original Worker version.
**When to use PINNED:**
- Short-running workflows (minutes to hours)
- Consistency is critical (e.g., financial transactions)
- You want to eliminate version compatibility complexity
- Building new applications and want simplest development experience
**AUTO_UPGRADE Behavior**
Workflows can move to newer versions.
**When to use AUTO_UPGRADE:**
- Long-running workflows (weeks or months)
- Workflows need to benefit from bug fixes during execution
- Migrating from traditional rolling deployments
- You are already using GetVersion for version transitions
**Important:** AUTO_UPGRADE workflows still need GetVersion to handle version transitions safely since they can move between Worker versions.
### Worker Configuration with Default Behavior
```go
// For short-running workflows, prefer PINNED
w := worker.New(c, "orders-task-queue", worker.Options{
DeploymentOptions: worker.DeploymentOptions{
UseVersioning: true,
Version: worker.WorkerDeploymentVersion{
DeploymentName: "order-service",
BuildId: os.Getenv("BUILD_ID"),
},
DefaultVersioningBehavior: workflow.VersioningBehaviorPinned,
},
})
```
### Deployment Strategies
**Blue-Green Deployments**
Maintain two environments and switch traffic between them:
1. Deploy new code to idle environment
2. Run tests and validation
3. Switch traffic to new environment
4. Keep old environment for instant rollback
**Rainbow Deployments**
Multiple versions run simultaneously:
- New workflows use latest version
- Existing workflows complete on their original version
- Add new versions alongside existing ones
- Gradually sunset old versions as workflows complete
This works well with Kubernetes where you manage multiple ReplicaSets running different Worker versions.
Deploy a new version, then set it as current:
```bash
temporal worker deployment set-current-version \
--deployment-name my-service \
--build-id v2.0.0
```
### Querying Workflows by Worker Version
```bash
# Find workflows on a specific Worker version
temporal workflow list --query \
'TemporalWorkerDeploymentVersion = "my-service:v1.0.0" AND ExecutionStatus = "Running"'
```
## Best Practices
1. **Keep GetVersion calls** even when only a single branch remains -- it guards against stale replays and simplifies future changes
2. **Use `TemporalChangeVersion` search attribute** to find Workflows running on old versions:
```bash
temporal workflow list --query \
'WorkflowType = "MyWorkflow" AND ExecutionStatus = "Running" AND TemporalChangeVersion = "Step1"'
```
3. **Test with replay** before removing old branches to verify determinism is preserved
4. **Prefer Worker Versioning** for large-scale deployments to avoid accumulating patching branches