mirror of
https://github.com/temporalio/skill-temporal-developer.git
synced 2026-09-14 13:52:58 +08:00
b5719bc143
* 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>
233 lines
7.3 KiB
Markdown
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
|