Standalone Activities GA
Standalone Activities and start delay are now enabled by default via activity.enableStandalone and activity.startDelayEnabled. This release adds delayed starts, operator APIs, and batch operations.
Operator APIs can pause or resume an Activity, reset its attempt state with optional heartbeat cleanup or option restoration, and update selected options or restore the original options. Enable them with history.enableStandaloneActivityOperatorCommands: true.
Batch operations can cancel, terminate, or delete multiple Standalone Activities selected by a visibility query or explicit Activity executions. Enable them with frontend.enableBatchOperationsForStandaloneActivities: true. Both settings are namespace-scoped and default to false.
Compatibility note: Describe/List batch responses now use explicit *_WORKFLOW enum values for existing Workflow batch operations. Clients comparing these values with the deprecated enum values must be updated.
Visibility
Query Converter
⚠️ Behavioral and Configuration Changes
This release enables the unified query converter introduced in Temporal v1.30.1. It is now the default and includes breaking changes. You can still disable it by setting the dynamic config system.visibilityEnableUnifiedQueryConverter: false. We plan to remove the legacy query converter in the next release. See below for details.
Summary
The existing (now legacy) query converter implementation was fragmented, with separate versions for Elasticsearch and SQL Visibility stores. This created an inconsistent experience depending on which store you used (for example, a query might be accepted by one store but rejected by another).
Temporal v1.30.1 introduced a unified query converter to consolidate these implementations and provide consistent behavior across all Visibility stores. However, achieving that consistency required a couple of breaking changes:
- Type validation: Comparing a search attribute of type
Twith a value of typeSwhereSdiffers fromTreturns an error (eg:CustomKeyword = 123andCustomInt = '123'will return an error). In the legacy query converter, some of these expressions were accepted and relied on the underlying store to accept or reject them. One exception is numeric types (IntandDouble): for those,CustomInt = 1.5andCustomDouble = 15are allowed. - Filtering a
Texttype search attribute against an empty string (or equivalent) returns an error (eg:CustomText = ''andCustomText = ' 'will return an error). TheTexttype search attribute is meant for full-text search, so you must provide at least one token. In the legacy query converter, these expressions were accepted or rejected depending on the store you chose. However, even when accepted, they match no workflows, which indicates they weren’t intended.
Other Changes
- The type of
VisibilityRow.ExecutionDurationhas changed from*time.Durationto*int64. The typeVisibilityRowis meant to represent a row from the SQL tableexecutions_visibility, which defines theexecution_durationcolumn asBIGINT. Given that the underlying database driver has the freedom to translate Go types at will, usingtime.Durationinstead ofint64can introduce a bug (eg: it might be translated into a string representation oftime.Duration). If you have your own implementation of theVisibilityStoreinterface that depends onVisibilityRow, you will have to review your code and make the necessary changes. - When using Visibility with Elasticsearch, we changed the search query to disallow partial search results, i.e., we are now setting the query parameter
allow_partial_search_results=false. This ensures thatListWorkflowExecutions(and related APIs) won’t miss results and that paginating through the results will go over all matching executions. - Search-attribute-related APIs in the admin handler (eg:
AddSearchAttributes) have been deprecated and now forward to the operator handler. If you were using the deprecatedtctlCLI tool to manage search attributes, it was calling the admin handler. Please replace it with thetemporalCLI (docs).
Upcoming changes in v1.33.0
- Removing support for the legacy query converter.
- The default value of
system.visibilityAllowListwill change tofalse. This might cause issues if you are using Elasticsearch as the Visibility store and assigning a list of values to a custom search attribute that does not support lists of values (all types exceptKeywordListtype). Eg: if you are storing["foo", "bar"]in aKeywordtype custom search attribute, or if you are storing[1, 2, 3]in anInttype custom search attribute. Although it currently works, this is unexpected behavior that Elasticsearch happens to accept (see these release notes for additional details). We highly recommend that you fix your usages of lists of values, as they are not officially supported.
Activity Eager Execution
⚠️ Behavioral and Configuration Changes
system.enableActivityEagerExecution is now enabled by default.
Worker Versioning
⚠️💥 Final release before removal of deprecated Worker Versioning
Correction to the v1.31 release notes: v1.32 is the final release containing the deprecated Worker Versioning implementations. Complete removal is now planned for server v1.33, rather than v1.32.
The following deprecated APIs are planned for removal in v1.33:
UpdateWorkerBuildIdCompatibilityGetWorkerBuildIdCompatibilityUpdateWorkerVersioningRulesGetWorkerVersioningRulesGetWorkerTaskReachabilitySetCurrentDeploymentGetCurrentDeploymentDescribeDeploymentListDeploymentsGetDeploymentReachability
The Build ID Compatibility and Versioning Rules management APIs remain disabled by default in v1.32. Existing legacy routing information and workflow-progress behavior remain available so operators can complete their migration. The deprecated pre-release Deployment APIs already return Unimplemented.
Before upgrading to v1.33, migrate to the current Worker Deployment APIs and drain or complete every running, sleeping, or backlogged Workflow that still depends on a legacy Build ID, compatible Version Set, Assignment Rule, Redirect Rule, or deprecated Deployment routing state.
Rolling back to v1.32 should be treated as emergency recovery for a missed legacy Workflow, not as a migration strategy.
New Capabilities
- Added one-time Versioning Overrides, allowing a Workflow to route to a specified Worker Deployment Version without creating a permanent Pinned override. The override is automatically cleared after a Workflow Task successfully completes on the target Version.
- Added explicit Versioning Overrides when starting Child Workflows, allowing a Child Workflow to use Pinned, Auto-Upgrade, or one-time routing independently of its parent.
⚠️ Behavioral and Configuration Changes
- Version reactivation signals are now enabled by default. A Workflow pinned or moved to a Drained or Inactive Worker Deployment Version can reactivate that Version’s drainage state. This can be disabled with
history.enableVersionReactivationSignals. - The signal-based Worker Deployment Version demotion path is controlled by the new
matching.enableWorkerDeploymentVersionDemotionSignaldynamic config. It defaults tofalseinv1.32, retaining the existing update-based behavior during upgrades. Enabling it before older Version workflows have Continued-As-New can leave Versions stuck inDraining. The default is expected to change totrueinv1.33.
Other Changes
- Added the
worker_deployment_versioning_one_time_override_countmetric to track fulfilled one-time Versioning Overrides. - Reduced unnecessary version-reactivation signals by skipping active Versions and deduplicating signals using routing revisions.
- Fixed current and ramping Version selection when a Task Queue moves between Worker Deployments.
ListWorkerDeploymentsnow removes duplicate Deployments appearing within a single Visibility result page.- Deleting an already-missing Worker Deployment Version now succeeds and removes its stale reference from the Worker Deployment.
- Worker Deployment system workflows are now assigned higher priority, reducing delays when their per-Namespace Task Queue is busy.
Poller Autoscaling
New Capabilities
- Export new metric for debugging poller autoscaling behavior (PR)
- We add an opt-in
poller_scale_decisioncounter metric. If enabled, we emit a metric for each poller scaling decision made by the server (+1s, -1s, or no change), tagged with the reason. - This metric is off by default, and can be enabled for a namespace or a task queue.
- We add an opt-in
- Support enabling poller autoscaling per namespace from the server side (PR)
- We add a new namespace-level dynamic config (
frontend.pollerAutoscalingAutoEnroll). - Each SDK is then responsible for enabling poller autoscaling if this DC is enabled, and if the user hasn’t configured a specific fixed poller count. This has been implemented in all SDKs.
- We add a new namespace-level dynamic config (
- The poller scaling add-to-dispatch ratio threshold is now configurable via the new
matching.pollerScalingTaskAddToDispatchRatiodynamic config (PR). This is the ratio of task add rate to task dispatch rate above which a scale-up decision
is issued, previously hard-coded to1.2. The default is unchanged, so no action is
needed.
Bug Fixes
- Fixed a bug where sticky queues could only issue poller scale-up decisions once a backlog
had formed, never on add/dispatch rate alone (PR).
Matching Observability Improvements
Matching now exposes a per-task-queue view of task arrival and silent drops, and removes the coarser tasks_expired counter that the new metrics replace.
New Capabilities
tasks_added- counts tasks arriving at a task queue. Tagged with:task_add_result: (sync_match,sync_match_unavailable,backlog,throttled,failure)forwarded: whether the add task was forwarded from a child partition.
tasks_dropped- counts backlog task-matching drops. Sync-match tasks are not counted. Tagged with:reason: (internal_error,data_loss,not_found,invalid,expired_read,expired_memory)
⚠️ Behavioral Changes
- The
tasks_expiredmetric has been removed; it is superseded by thetasks_droppedmetric with finerreasontags. Alerts on thetasks_expiredmetric will need to be updated.
Task Queue Dynamic Partitioning
Task Queue Dynamic Partitioning can adjust the number of read and write partitions for selected Workflow and Activity Task Queues based on task-add rate and backlog. This is an experimental operator capability that is disabled by default: matching.partitionScaler has no default policy.
Cluster operators can scope matching.partitionScaler and matching.partitionScaleManager by Namespace, Task Queue, and Task Queue type. The scale manager has defaults, but both manager and scaler settings should be validated together for each workload; no general-purpose production configuration is currently recommended. Refer to the Go documentation for SimplePartitionScalerSettings and PartitionScaleManagerSettings. ShadowModeLogInterval can be used to observe scaler decisions without applying them.
⚠️ Behavioral and Observability Changes
- When an active scaler policy sets a positive
BacklogCap, matching clients use backlog-aware load balancing: add-task routing favors partitions farther below the cap, while poll routing favors partitions with more backlog. WhenBacklogCapis not set, the existing load-balancing behavior is retained. - Added the
partition_scale_eventscounter (tagged withscaler_shadow_mode) and the per-Task Queue gaugespartition_scale_target,partition_scale_read, andpartition_scale_write. The gauges also report changed shadow-mode recommendations whenmetrics.breakdownByTaskQueueis enabled.
Priority and Fairness
New Capabilities
- New Dynamic Config option
matching.fairnessPassDither: This spreads low-weight keys ahead in pass-space so they don't clump at the front after a counter reset (e.g. partition
movement), at the cost of cross-key FIFO ordering for bursts of equal-weight new keys.
⚠️ Behavioral and Configuration Changes
matching.enableMigrationdynamic config has been removed and is always enabled now.
Bug Fixes
- Fixed several issues in the fair task reader that could cause a task queue partition to get stuck with a non-empty backlog, including read/ack levels moving backwards under load, backlog counts not resetting after a gap, and evicted-ack cache handling. Fairness backlogs now drain reliably under these conditions.
- Per-Key rate limit no longer blocks lower-priority tasks from being dispatched.
Worker Management
New Capabilities
-
Proactive Activity Cancellation — The server can now cancel activities on workers without relying on heartbeats, using a Nexus-based worker commands channel. On workflow close (terminate, timeout, cancel, continue-as-new), cancel commands are dispatched for all in-flight activities. This does not yet support standalone activities.
Dynamic config:
frontend.WorkerCommandsEnabled(namespace-scoped, default: off) — enables the worker commands task queue and pollssystem.enableCancelActivityWorkerCommand(namespace-scoped, default: off) — enables the server to send a cancel command to the worker
-
CountWorkers API — New RPC to count workers matching a query filter without retrieving full details.
⚠️ Behavioral and Configuration Changes
- System workers excluded from ListWorkers by default — opt in via
include_system_workerson the request.
Nexus
⚠️ Behavioral and Configuration Changes
- 💥 Nexus callbacks now route by URL scheme by default: worker targets always use
temporal://system, while legacyNexus-Callback-Sourceheader-based routing is available only whencallback.inspectSourceHeaderis enabled. The obsoletenexusoperation.useSystemCallbackURLandcomponent.nexusoperations.useSystemCallbackURLsettings have been removed.
This change has been introduced for security reasons. Previously, ifuseSystemCallbackURLwasfalse(default wastrue), a callback to an external target URL, allowed incallback.allowedAddresses, and having thesourceheader set, would have been executed as an internal request. - The default Nexus callback implementation is now backed by the CHASM framework instead of the previous HSM-backed implementation. Configuration knobs for this implementation differ from the previous one. For the most part, configs have been redefined in
chasm/lib/callback/config.go. To force using the previous implementation, sethistory.enableCHASMCallbackstofalse. - 💥 The dynamic config
component.callbacks.allowedAddresseshas been replaced withcallback.allowedAddresses.
Workflow task completion pagination
Workers can now split a single RespondWorkflowTaskCompleted across multiple requests ("pages"), so a workflow task that exceeds the request size limit can still complete. The server buffers the intermediate pages and, when the final page arrives, reassembles them into the original RespondWorkflowTaskCompleted request and processes it as one. DescribeNamespace advertises support through the workflow_task_completion_pagination capability.
This is pre-release and disabled by default. Dynamic configs:
history.enableWorkflowTaskCompletionPagination(defaultfalse): enables the feature.history.maximumEventBatchSizeInBytes(global, default0, disabled): size at which the event store rolls the current history event batch and starts a new one. Experimental. This determines how much is written to persistence per batch, and each batch is persisted in a single transaction, so it must be set belowsystem.transactionSizeLimitfor pagination to work.history.workflowTaskCompletionBufferSizeLimit(default 40 MiB): buffered size allowed per workflow task. Exceeding it fails the workflow task with causeREQUEST_TOO_LARGE.history.workflowTaskCompletionBufferTotalSizeLimit(default 1 GiB) andhistory.workflowTaskCompletionBufferNamespaceRatio(default0.5): process-wide buffer budget and the share one namespace may hold.