Skip to content

Worker insights#3087

Draft
Alex-Tideman wants to merge 34 commits intomainfrom
worker-insights
Draft

Worker insights#3087
Alex-Tideman wants to merge 34 commits intomainfrom
worker-insights

Conversation

@Alex-Tideman
Copy link
Collaborator

@Alex-Tideman Alex-Tideman commented Jan 12, 2026

Description & motivation 💭

New top-level Workers page (/namespaces/{namespace}/workers) with two tabs:

  • Instances — A paginated, filterable table of all worker instances showing status, instance key, deployment, build ID, task queue, identity, host name, start time, and SDK version.
  • Deployments — Lists worker deployments with version history (moved from its previous location under /deployments to /workers/deployments).

Worker details page (/workers/{workerInstanceKey}) with:

  • Status and other details
  • Task slot metrics per type (Workflows, Activities, Nexus, Local Activities) with used/available visual meters
  • Poller count, mode (e.g. Autoscaling/Manual), and last successful poll time
  • Host info. (hostname, process ID, worker grouping, CPU and memory usage with meter bars)
  • Workflow cache stats (size, hit rate %)
  • Diagnostics (poll success rate)
  • Go SDK dependency warning when CPU/memory data is missing

Workers tab on Workflow (/namespaces/{namespace/workflows/{...}/workers) and Standalone Activity (/namespaces/{namespace/activities/{...}/workers) details page:

  • shows a workers table filtered by the task queue
  • shows a fallback table based on the legacy pollers API when heartbeats aren't enabled

Screenshots (if applicable) 📸

Worker Instances
Table Empty State SDK Alert Dynamic Config Alert
Screenshot 2026-03-18 at 1 58 32 PM Screenshot 2026-03-18 at 2 01 06 PM Screenshot 2026-03-18 at 2 02 08 PM Screenshot 2026-03-18 at 2 21 52 PM
Worker Details
Running No Heartbeat Info. Go SDK alert
Screenshot 2026-03-18 at 1 56 23 PM Screenshot 2026-03-18 at 1 57 12 PM Screenshot 2026-03-18 at 8 34 11 PM
Task Queue Workers
Workflow Workers Workflow Workers Fallback SDK Alert
Screenshot 2026-03-18 at 2 06 15 PM Screenshot 2026-03-18 at 2 07 19 PM Screenshot 2026-03-18 at 2 03 46 PM

Design Considerations 🎨

Testing 🧪

How was this tested 👻

  • Manual testing
  • E2E tests added
  • Unit tests added

Steps for others to test: 🚶🏽‍♂️🚶🏽‍♀️

Run temporal server from a local build

  1. Ensure you have cloned the temporal repo
  2. Set WorkerHeartbeatsEnabled to false
  3. Run make bins and make start
  4. Running temporal server from a local build does not create the namespace, run temporal operator namespace create --namespace canary
  5. Create a search attribute for the canary namespace, run temporal operator search-attribute create --namespace canary --name CustomKeywordField --type Keyword

Run the UI against a local build of temporal server

  1. Checkout the workflow-insights branch in the UI repo
  2. pnpm dev:local-temporal

Run some sample workflows in the canary-go repo

  1. Run make bins and ./temporal-canary start

On the

  • Workflows details page under the "Workers" tab
  • Standalone activity details page under the "Workers" tab
  • On the task queue details page

the workers table should

  • Revert to a list of pollers (previous UI) if Namespace > capabilities > workerHeartbeats is not enabled
  • Show a list of workers (if using an SDK version that supports it) for the task queue
  • Show an alert r.e. SDK's if Namespace > capabilities > workerHeartbeats is enabled but no workers are being returned
  • On /workers page

    • It should list all instances for a given Namespace
    • Instances should update if the Namespace is changed via the Namespace switcher
    • Filtering should work as expected
    • It should show an alert r.e. SDK's if Namespace > capabilities > workerHeartbeats is enabled but no workers are being returned
    • It should show an alert r.e. enabling Namespace > capabilities > workerHeartbeats if it is currently disabled
    • /workers/deployments should work as expected
  • On the /workers/{workerInstanceKey} page

    • It should list worker and heartbeat info. as expected
    • The "Task Queue" link should go to the /workers page with the TaskQueue filter applied
    • It should 404 if the worker instance doesn't exist
  • On the /activities page

    • Verify filtering still works as expected

Checklists

Draft Checklist

  • Doc link for Go SDK dependency warning when CPU/memory data is missing
  • Update UI for empty states
  • Add links in workers list view for deployment and build ID
  • Worker total count
  • Staleness indicator (?)

Merge Checklist

  • Corresponding Cloud PR

Issue(s) closed

DT-3612

Docs

Any docs updates needed?

@vercel
Copy link

vercel bot commented Jan 12, 2026

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

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment Mar 19, 2026 4:36am

Request Review

@temporal-cicd
Copy link
Contributor

temporal-cicd bot commented Jan 12, 2026

Warnings
⚠️

📊 Strict Mode: 37 errors in 9 files (3.5% of 1072 total)

src/lib/utilities/route-for-api.ts (8)
  • L68:48: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
  • L74:50: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
  • L76:4: Type 'string | null' is not assignable to type 'string'.
  • L103:8: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ namespace: string; workflowId: string; scheduleId: string; queue: string; queryType: string; signalName: string; updateName: string; batchJobId: string; runId: string; activityId: string; endpointId: string; deploymentName: string; version: string; workerInstanceKey: string; }'.
  • L103:38: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Partial'.
  • L105:8: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ namespace: string; workflowId: string; scheduleId: string; queue: string; queryType: string; signalName: string; updateName: string; batchJobId: string; runId: string; activityId: string; endpointId: string; deploymentName: string; version: string; workerInstanceKey: string; }'.
  • L105:57: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Partial'.
  • L191:16: This overload signature is not compatible with its implementation signature.
src/lib/stores/filters.ts (2)
  • L114:2: Argument of type 'StartStopNotifier<EventTypeCategory[] | null>' is not assignable to parameter of type 'StartStopNotifier<EventTypeCategory[] | undefined>'.
  • L130:13: Argument of type 'StartStopNotifier<("Scheduled" | "Unspecified" | "Open" | "New" | "Started" | "Initiated" | "Running" | "Completed" | "Fired" | "Signaled" | "Canceled" | "Failed" | "Terminated" | "TimedOut" | "CancelRequested")[] | null>' is not assignable to parameter of type 'StartStopNotifier<("Scheduled" | "Unspecified" | "Open" | "New" | "Started" | "Initiated" | "Running" | "Completed" | "Fired" | "Signaled" | "Canceled" | "Failed" | "Terminated" | "TimedOut" | "CancelRequested")[] | undefined>'.
src/lib/utilities/screaming-enums.ts (2)
  • L92:14: Type 'PendingNexusOperationState.PENDING_NEXUS_OPERATION_STATE_UNSPECIFIED | undefined' is not assignable to type 'PendingNexusOperationState'.
  • L99:14: Type 'CallbackState.CALLBACK_STATE_UNSPECIFIED | undefined' is not assignable to type 'CallbackState'.
src/lib/components/worker-table.svelte (6)
  • L69:12: Type 'number | null | undefined' is not assignable to type 'string | number'.
  • L69:24: 'workers.versioningInfo' is possibly 'null' or 'undefined'.
  • L75:5: Parameter 'poller' implicitly has an 'any' type.
  • L158:35: 'poller.taskQueueTypes' is possibly 'undefined'.
  • L165:35: 'poller.taskQueueTypes' is possibly 'undefined'.
  • L172:35: 'poller.taskQueueTypes' is possibly 'undefined'.
src/lib/components/workflow/filter-bar/dropdown-filter-chip.svelte (2)
  • L4:33: Could not find a declaration file for module 'date-fns-tz'. '/home/runner/work/ui/ui/node_modules/.pnpm/date-fns-tz@1.3.8_date-fns@2.30.0/node_modules/date-fns-tz/index.js' implicitly has an 'any' type.
  • L316:12: Type 'null' is not assignable to type 'string'.
src/lib/components/shared-search-attribute-filter/status-filter-chip.svelte (2)
  • L79:10: Type 'string | null' is not assignable to type 'string | undefined'.
  • L90:10: Type 'string | null' is not assignable to type 'string | undefined'.
src/lib/components/workers/workers-table/workers-table-cell.svelte (1)
  • L44:8: Type 'string | null | undefined' is not assignable to type 'string | undefined'.
src/lib/layouts/workflow-header.svelte (6)
  • L135:37: Argument of type 'WorkflowExecution | null' is not assignable to parameter of type 'WorkflowExecution'.
  • L136:45: Argument of type 'WorkflowExecution | null' is not assignable to parameter of type 'WorkflowExecution'.
  • L141:13: Type 'WorkflowExecution | null' is not assignable to type 'WorkflowExecution'.
  • L167:9: Type 'WorkflowExecution | null' is not assignable to type 'WorkflowExecution'.
  • L175:20: Type 'WorkflowExecution | null' is not assignable to type 'WorkflowExecution'.
  • L231:12: Type 'string | undefined' is not assignable to type 'string'.
src/lib/layouts/workflow-run-layout.svelte (8)
  • L102:39: Type 'string | undefined' is not assignable to type 'string'.
  • L118:8: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
  • L153:6: Type 'WorkflowExecution | undefined' is not assignable to type 'WorkflowExecution | null'.
  • L167:4: Type 'undefined' is not assignable to type 'NetworkError | null'.
  • L170:18: No overload matches this call.
  • L179:28: Parameter 'fullHistory' implicitly has an 'any' type.
  • L179:41: Parameter 'pause' implicitly has an 'any' type.
  • L211:35: Argument of type 'null' is not assignable to parameter of type '((key: string, value: { eventHistory: WorkflowEvents; workflow: WorkflowExecution | null; workers: IDescribeTaskQueueResponse; workersLoaded: boolean; metadata: WorkflowMetadata | null; userMetadata: { ...; }; }) => { ...; }) | undefined'.

Generated by 🚫 dangerJS against 38e0f61

@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
@temporalio temporalio deleted a comment from temporal-cicd bot Mar 13, 2026
copyable
copyableText={heartbeat.taskQueue}
text={heartbeat.taskQueue}
href={routeForWorkersWithQuery({
Copy link
Collaborator

Choose a reason for hiding this comment

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

We've decided to link to the /workers tab with a filter for TaskQueue applied for now instead of /task-queues/{task-queue}. Do we want to update the Task Queue link on the standalone activity details page to be the same?

Image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah they should be consistent

localFilters = [
createFilter({
attribute,
type: 'Keyword',
Copy link
Contributor

Choose a reason for hiding this comment

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

  • ⚠️ Type 'string | null' is not assignable to type 'string | undefined'.

createFilter({
attribute,
parenthesis: localFilters.length ? ')' : '',
type: 'Keyword',
Copy link
Contributor

Choose a reason for hiding this comment

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

  • ⚠️ Type 'string | null' is not assignable to type 'string | undefined'.


if (!filter || filter.value !== value) {
const newFilter: SearchAttributeFilter = createFilter({
attribute,
Copy link
Contributor

Choose a reason for hiding this comment

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

  • ⚠️ Type 'string | null | undefined' is not assignable to type 'string | undefined'.

@@ -175,7 +174,6 @@
</div>
<CodecServerErrorBanner />
Copy link
Contributor

Choose a reason for hiding this comment

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

  • ⚠️ Type 'WorkflowExecution | null' is not assignable to type 'WorkflowExecution'.

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