Skip to content

fix(workers): resolve worker deployment from task queue - #3883

Open
rossnelson wants to merge 2 commits into
mainfrom
rossnelson/fe-489-resolve-worker-deployment-from-task-queue
Open

rossnelson wants to merge 2 commits into
mainfrom
rossnelson/fe-489-resolve-worker-deployment-from-task-queue

Conversation

@rossnelson

@rossnelson rossnelson commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

When a workflow runs on a task queue served by a Worker Deployment with a compute provider, the worker has not cold-started yet. A user opening the workflow page in that window sees:

No workers polling
There are no workers polling the orders-serverless task queue. Check your deployment or orchestration system (Kubernetes, ECS, etc.). Review worker logs for crash information. Restart or redeploy workers.

Every actionable sentence is wrong. There is no Kubernetes or ECS, no worker logs to read, and nothing to restart — Temporal is invoking the customer's Lambda on their behalf.

Cause

workflow-header.svelte read the deployment from the TemporalWorkerDeployment search attribute. The server only writes that attribute once a versioned worker completes the first workflow task, so during a cold start it is empty and no-workers-polling-alert.svelte falls through to the self-managed branch. The calm serverless message on the other branch was only reachable on a second scale-down — never on a first run.

workflow-details.svelte read the same attribute, so the Deployment / Build ID / Versioning Behavior column was blank at the same moment.

Fix

Fall back to the task queue when the attribute is empty. The response the page already fetches carries versioningInfo.currentDeploymentVersion.deploymentName — a property of the queue, not the workflow, so it is present with zero pollers and needs no extra request.

The search attribute still comes first when present. It records the deployment the workflow actually runs on, and a pinned workflow keeps it after the queue's Current Version moves to another deployment. Current Version only routes new executions and unversioned or AutoUpgrade workflows, so reading the queue first would show the wrong deployment for a pinned workflow.

Extracted to getWorkerDeploymentName() rather than repeating the optional-chain across three call sites.

Guard on currentDeploymentVersion, not versioningInfo

A task queue with no registered version still returns a versioningInfo object:

{ "currentVersion": "__unversioned__", "updateTime": "0001-01-01T00:00:00Z" }

A truthiness check on versioningInfo therefore matches every unversioned queue and would pick the wrong branch for all of them. There is a test covering this.

Also fixed: an alert that never rendered

standalone-activity-layout.svelte had:

runningWithNoWorkers={!response.pollers && ...}

reducePollerTypes always returns an array from .map(), so !response.pollers is ![]always false. That alert has never rendered on the standalone activity page under any conditions. Now checks .length, matching what isRunningWithNoWorkers already does.

That layout also passed no deployment prop at all, so even once visible it could only ever show the self-managed message.

Also removed

workflow-error-no-compatible-workers-title / -description — no component references either.

Verification

Against a local server (1.31.2) with workercontroller.enabled and a real AWS Lambda serverless worker invoked through the worker controller:

  • One DescribeTaskQueue response returned pollers: [] alongside currentDeploymentVersion: {ross-test, v1}, via the same HTTP path the UI calls
  • TemporalWorkerDeployment was confirmed empty on the running workflow at that moment
  • 7 real Lambda cold starts measured at 2.17–2.43s (median 2.36s), confirming the window this fix covers is real but brief

Checks: ESLint clean, svelte-check clean, 5 new unit tests pass. Full suite is 2767 passing — the 26 failing test files are pre-existing @buf/@bufbuild module-resolution failures present on main (verified by stashing).

Deliberately not in this PR

The messaging rework is blocked on a product decision and would be rebuilt if it landed early. A correct cold start resolves in ~2.4s, so a "Starting a worker" message would appear and vanish faster than anyone can read — arguably worse than showing nothing. That decision changes the shape of the state model, so it is tracked separately on FE-489.

Also excluded: a provisioning-failed state. providerValidation is absent from the API response even for a deployment that passed real AWS validation, so connection-status.ts derives pending for a healthy deployment. There is no data source to build it on yet.

This PR is a strict improvement regardless of how those land: the right message fires, and the Deployment column populates.

The workflow page reads the worker deployment from the
TemporalWorkerDeployment search attribute. The server only writes that
attribute once a versioned worker completes the first workflow task, so
during a compute-provider cold start it is empty. Two things follow:
the no-workers alert falls through to the self-managed branch and tells
the user to check Kubernetes or ECS, and the Deployment column in the
detail row renders blank.

Resolve the deployment from the task queue instead. The task queue
response the page already fetches carries
versioningInfo.currentDeploymentVersion.deploymentName, which is a
property of the queue rather than the workflow, so it is present with
zero pollers and needs no extra request. The search attribute stays as
a fallback for workflows pinned to a version that is no longer current.

Guard on currentDeploymentVersion, not on versioningInfo. A task queue
with no registered version still returns a versioningInfo object
containing currentVersion "__unversioned__", so a truthiness check on
versioningInfo matches every unversioned queue.

Also fixes the alert on the standalone activity page, which never
rendered at all: reducePollerTypes always returns an array, so
`!response.pollers` was always false. It now checks length, matching
isRunningWithNoWorkers. That layout also passed no deployment, so it
could only ever show the self-managed message.

Removes workflow-error-no-compatible-workers-title/-description, which
no component references.

Verified against a local server with workercontroller.enabled and a real
AWS Lambda serverless worker: one DescribeTaskQueue response returned
`pollers: []` alongside currentDeploymentVersion while the search
attribute was still empty.

Refs FE-489
@rossnelson
rossnelson requested a review from a team as a code owner September 3, 2026 20:31
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
holocene Ready Ready Preview Sep 17, 2026 1:59pm UTC

Request Review

workflow: WorkflowExecution | null | undefined,
): string | undefined =>
workers?.versioningInfo?.currentDeploymentVersion?.deploymentName ||
workflow?.searchAttributes?.indexedFields?.['TemporalWorkerDeployment'] ||

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The search attribute stays as a fallback for workflows pinned to a version that is no longer current.

Just want to verify that the task queue's current deployment version should always take precedence over the workflow's deployment search attribute.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, and no, it should not. Current Version routes new executions and existing unversioned or AutoUpgrade workflows only (see the doc on TaskQueueVersioningInfo.current_deployment_version). A pinned workflow keeps its deployment after the queue moves on, and the server records that deployment in TemporalWorkerDeployment. Reading the queue first would have shown the wrong deployment for a pinned workflow, beside Version and Behavior columns that still read the attribute.

Swapped the order in 5ea898b: the attribute comes first, and the task queue fills the gap before the first workflow task completes, which is the cold-start window this PR exists for. Added a test for a workflow pinned to a deployment that is no longer current, and fixed the sentence in the description.

The task queue's Current Version routes new executions and unversioned
or AutoUpgrade workflows. A pinned workflow keeps its own deployment
after the queue moves on, and the server records that deployment in the
TemporalWorkerDeployment search attribute. Reading the queue first would
show the wrong deployment for such a workflow, beside Version and
Behavior columns that still read the attribute.

The attribute now comes first. The task queue still fills the gap
before the first workflow task completes, which is the cold-start
window this fix exists for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants