From b3f5e953e47463f9559e3eb3b31af267ffb86d40 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 9 Oct 2025 22:32:52 -0500 Subject: [PATCH 01/14] Testing out api --- server/go.mod | 2 +- server/go.sum | 4 +- src/lib/pages/task-queue.svelte | 80 ++++++++++++++++++++++++++++++ src/lib/services/worker-service.ts | 60 ++++++++++++++++++++++ src/lib/types/api.ts | 11 +++- src/lib/utilities/route-for-api.ts | 5 +- 6 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/lib/services/worker-service.ts diff --git a/server/go.mod b/server/go.mod index be9f002afb..03061d7fa8 100644 --- a/server/go.mod +++ b/server/go.mod @@ -11,7 +11,7 @@ require ( github.com/labstack/echo/v4 v4.13.4 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.3.0 - go.temporal.io/api v1.51.0 + go.temporal.io/api v1.54.0 golang.org/x/net v0.40.0 golang.org/x/oauth2 v0.30.0 google.golang.org/grpc v1.66.0 diff --git a/server/go.sum b/server/go.sum index 875a544786..82da900640 100644 --- a/server/go.sum +++ b/server/go.sum @@ -68,8 +68,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -go.temporal.io/api v1.51.0 h1:9+e14GrIa7nWoWoudqj/PSwm33yYjV+u8TAR9If7s/g= -go.temporal.io/api v1.51.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= +go.temporal.io/api v1.54.0 h1:/sy8rYZEykgmXRjeiv1PkFHLXIus5n6FqGhRtCl7Pc0= +go.temporal.io/api v1.54.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= diff --git a/src/lib/pages/task-queue.svelte b/src/lib/pages/task-queue.svelte index e0f5e5634f..1b60eb348f 100644 --- a/src/lib/pages/task-queue.svelte +++ b/src/lib/pages/task-queue.svelte @@ -2,9 +2,50 @@ import { page } from '$app/state'; import WorkerTable from '$lib/components/worker-table.svelte'; + import CodeBlock from '$lib/holocene/code-block.svelte'; import { getPollers } from '$lib/services/pollers-service'; + import { + type DescribeWorkerResponse, + listWorkers, + type ListWorkersResponse, + // describeWorker, + } from '$lib/services/worker-service'; + import { stringifyWithBigInt } from '$lib/utilities/parse-with-big-int'; const { queue: taskQueue, namespace } = $derived(page.params); + + let listWorkersResponse = $state(null); + let describeWorkerResponses = $state>( + {}, + ); + + async function loadWorkers() { + try { + const response = await listWorkers({ queue: taskQueue, namespace }); + console.log('Workers response:', response); + // listWorkersResponse = response; + + // const describePromises = response.workers.map(async (worker) => { + // const describeResponse = await describeWorker({ + // queue: taskQueue, + // namespace, + // identity: worker.identity, + // }); + // return { identity: worker.identity, response: describeResponse }; + // }); + + // const results = await Promise.all(describePromises); + // describeWorkerResponses = results.reduce( + // (acc, { identity, response }) => { + // acc[identity] = response; + // return acc; + // }, + // {} as Record, + // ); + } catch (error) { + console.error('Error loading workers:', error); + } + } {#await getPollers({ queue: taskQueue, namespace }) then workers} @@ -13,5 +54,44 @@ {taskQueue} + +
+

Worker API Responses

+ + {#await loadWorkers()} +

Loading workers...

+ {:then} + {#if listWorkersResponse} +
+

List Workers Response

+ +
+ + {#if listWorkersResponse.workers.length > 0} +
+

Describe Worker Responses

+ {#each listWorkersResponse.workers as worker (worker.identity)} +
+

Worker: {worker.identity}

+ {#if describeWorkerResponses[worker.identity]} + + {/if} +
+ {/each} +
+ {/if} + {/if} + {:catch error} +

Error loading workers: {error.message}

+ {/await} +
{/await} diff --git a/src/lib/services/worker-service.ts b/src/lib/services/worker-service.ts new file mode 100644 index 0000000000..6819527473 --- /dev/null +++ b/src/lib/services/worker-service.ts @@ -0,0 +1,60 @@ +import type { NamespaceScopedRequest } from '$lib/types/global'; +import { requestFromAPI } from '$lib/utilities/request-from-api'; +import { routeForApi } from '$lib/utilities/route-for-api'; + +export type ListWorkersRequest = NamespaceScopedRequest & { + queue: string; +}; + +export type DescribeWorkerRequest = NamespaceScopedRequest & { + queue: string; + identity: string; +}; + +export type WorkerInfo = { + identity: string; + buildId: string; + lastAccessTime: string; + taskQueueTypes: number[]; + rateLimitPerSecond: number; +}; + +export type ListWorkersResponse = { + workers: WorkerInfo[]; +}; + +export type DescribeWorkerResponse = { + buildId: string; + capabilities: { + useVersioning: boolean; + activityOnly: boolean; + }; + lastAccessTime: string; + rateLimitPerSecond: number; + taskQueues: { + name: string; + kind: number; + }[]; +}; + +export async function listWorkers( + parameters: ListWorkersRequest, + request = fetch, +): Promise { + const route = routeForApi('workers', parameters); + return await requestFromAPI(route, { + request, + params: { taskQueue: parameters.queue }, + }); +} + +export async function describeWorker( + parameters: DescribeWorkerRequest, + request = fetch, +): Promise { + const route = routeForApi('worker', parameters); + return await requestFromAPI(route, { + request, + params: { identity: parameters.identity }, + }); +} diff --git a/src/lib/types/api.ts b/src/lib/types/api.ts index 480da8c8be..73cf5bfc0b 100644 --- a/src/lib/types/api.ts +++ b/src/lib/types/api.ts @@ -43,7 +43,10 @@ export type ParameterlessAPIRoutePath = | 'user' | 'nexus-endpoints' | 'namespaces'; -export type WorkerAPIRoutePath = 'worker-task-reachability'; +export type WorkerAPIRoutePath = + | 'worker-task-reachability' + | 'workers' + | 'worker'; export type SchedulesAPIRoutePath = 'schedules'; export type ScheduleAPIRoutePath = | 'schedule' @@ -90,6 +93,7 @@ export type APIRouteParameters = { endpointId: string; deploymentName: string; version: string; + workerInstanceKey: string; }; export type WorkflowListRouteParameters = Pick; @@ -140,6 +144,11 @@ export type TaskQueueRouteParameters = Pick< 'namespace' | 'queue' >; +export type WorkerRouteParameters = Pick< + APIRouteParameters, + 'namespace' | 'queue' | 'workerInstanceKey' +>; + export type ValidWorkflowEndpoints = WorkflowsAPIRoutePath; export type ValidWorkflowParameters = diff --git a/src/lib/utilities/route-for-api.ts b/src/lib/utilities/route-for-api.ts index 142e83f4b3..33c67d23b7 100644 --- a/src/lib/utilities/route-for-api.ts +++ b/src/lib/utilities/route-for-api.ts @@ -28,6 +28,7 @@ import type { WorkerDeploymentsAPIRoutePath, WorkerDeploymentVersionAPIRoutePath, WorkerDeploymentVersionRouteParameters, + WorkerRouteParameters, WorkflowActivitiesAPIRoutePath, WorkflowActivitiesRouteParameters, WorkflowAPIRoutePath, @@ -145,6 +146,8 @@ export function pathForApi( 'task-queue.compatibility': `/namespaces/${parameters?.namespace}/task-queues/${parameters?.queue}/worker-build-id-compatibility`, 'task-queue.rules': `/namespaces/${parameters?.namespace}/task-queues/${parameters?.queue}/worker-versioning-rules`, user: '/me', + workers: `/namespaces/${parameters?.namespace}/workers`, + worker: `/namespaces/${parameters?.namespace}/workers/describe/${parameters?.workerInstanceKey}`, 'worker-task-reachability': `/namespaces/${parameters?.namespace}/worker-task-reachability`, 'workflow.terminate': `/namespaces/${parameters?.namespace}/workflows/${parameters?.workflowId}/terminate`, 'workflow.cancel': `/namespaces/${parameters.namespace}/workflows/${parameters.workflowId}/cancel`, @@ -190,7 +193,7 @@ export function routeForApi( ): string; export function routeForApi( route: WorkerAPIRoutePath, - parameters: NamespaceRouteParameters, + parameters: NamespaceRouteParameters | WorkerRouteParameters, shouldEncode?: boolean, ): string; export function routeForApi( From 2b15a344af8526fac892d8264b92e0bc004e7ba6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 5 Dec 2025 14:18:46 -0600 Subject: [PATCH 02/14] Add worker-info to Workers tab --- .../components/task-queue/worker-info.svelte | 435 +++++++++--------- src/lib/pages/task-queue.svelte | 46 +- src/lib/pages/workflow-workers.svelte | 43 +- src/lib/services/worker-service.ts | 8 +- .../[namespace]/task-queues/+layout.svelte | 4 - 5 files changed, 261 insertions(+), 275 deletions(-) diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index 5f52d611ac..2192da924c 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -3,12 +3,19 @@ import Timestamp from '$lib/components/timestamp.svelte'; import Badge from '$lib/holocene/badge.svelte'; + import Card from '$lib/holocene/card.svelte'; import Icon from '$lib/holocene/icon/icon.svelte'; import Link from '$lib/holocene/link.svelte'; - import { type WorkerInfo } from '$lib/services/worker-service'; + import { + type WorkerInfo, + type WorkerPollerInfo, + type WorkerSlotsInfo, + } from '$lib/services/worker-service'; import { routeForTaskQueue } from '$lib/utilities/route-for'; import { fromScreamingEnum } from '$lib/utilities/screaming-enums'; + import SdkLogo from '../lines-and-dots/sdk-logo.svelte'; + type Props = { worker: WorkerInfo; }; @@ -63,7 +70,7 @@
-
+
Active Workers
@@ -74,13 +81,13 @@ ? 'bg-green-500' : 'bg-gray-400'}" > - + {status}
-
+ -
+
Total Processed Tasks
@@ -88,19 +95,19 @@
{workflowProcessedTasks} workflow · {activityProcessedTasks} activity
-
+ -
+
Available Slots
{totalAvailableSlots}
-
+
{capacityPercent}% capacity free
-
+
-
+
Sticky Cache Hit
@@ -108,18 +115,17 @@
{currentStickyCacheSize} cached workflows
-
+
-
+
-
-
+ +
-

{heartbeat.workerIdentity}

{#if isRunning} @@ -129,29 +135,14 @@ {/if} {status} -
-
-
- Instance: - {heartbeat.workerInstanceKey} -
-
- Group: - {heartbeat.hostInfo.workerGroupingKey} -
+

{heartbeat.workerIdentity}

-
+
-
+
Task Queue
{heartbeat.taskQueue}
-
+
SDK
-
{heartbeat.sdkName}
-
-
-
- SDK Version -
-
{heartbeat.sdkVersion}
-
-
-
- Host Name -
-
{heartbeat.hostInfo.hostName}
-
-
-
- Process ID -
-
{heartbeat.hostInfo.processId}
-
-
-
- Last Heartbeat -
-
- ~ ago -
+
- -
+

Task Slots

@@ -221,9 +188,9 @@
-
+

Pollers

@@ -243,195 +210,217 @@ {@render pollerCard('Nexus Poller', heartbeat.nexusPollerInfo)}
-
+
-
-
-
- -

Host Information

-
-
-
- Hostname - {heartbeat.hostInfo.hostName} -
-
- Process ID - {heartbeat.hostInfo.processId} -
-
- Worker Grouping Key - - {heartbeat.hostInfo.workerGroupingKey} - -
-
- CPU Usage -
-
-
-
- {heartbeat.hostInfo.currentHostCpuUsage.toFixed(1)}% -
-
-
- Memory Usage -
-
-
-
- {heartbeat.hostInfo.currentHostMemUsage.toFixed(1)}% -
-
-
-
- - {#if heartbeat.deploymentVersion?.deploymentName} -
-
- -

Deployment Version

-
-
-
-
Build ID
- - {heartbeat.deploymentVersion.buildId} - -
-
- SDK - {heartbeat.sdkName} -
-
- Version - {heartbeat.sdkVersion} -
-
-
- {/if} - -
-
- -

Sticky Cache

-
-
-
- {totalStickyCacheHit} - Cache Hits -
-
- {currentStickyCacheSize} - Cache Size -
-
-
- -
-
- -

Timestamps

-
-
-
-
Start Time
- -
-
-
Last Heartbeat
- -
- ~ ago -
-
-
-
+
+ {@render timestamps()} + {@render hostInfo()} + {@render deploymentInfo()} + {@render usage()} + {@render cache()}
-{#snippet slotCard( - title: string, - slots: import('$lib/services/worker-service').WorkerSlotsInfo, -)} -
+{#snippet slotCard(title: string, slots: WorkerSlotsInfo)} +
{title} {slots.slotSupplierKind}
- - {slots.currentUsedSlots} + + {slots.currentUsedSlots ?? 0} Used
- {slots.currentAvailableSlots}{slots.currentAvailableSlots ?? 0} Available
- - {slots.totalProcessedTasks} + + {slots.totalProcessedTasks ?? 0} Processed
-
- -
-
+ {#if slots.currentAvailableSlots} +
+ {#each new Array(slots.currentAvailableSlots) as _, i} +
+ {/each} +
+ {/if} +
{/snippet} -{#snippet pollerCard( - title: string, - poller: import('$lib/services/worker-service').WorkerPollerInfo, -)} -
+{#snippet pollerCard(title: string, poller: WorkerPollerInfo)} +
{title} - - {poller.currentPollers} + + {poller.currentPollers ?? 0}
-
+
{#if poller.lastSuccessfulPollTime} Last poll: {:else} No activity {/if}
-
+
+{/snippet} + +{#snippet hostInfo()} + +
+ +

Host Information

+
+
+
+ Hostname + {heartbeat.hostInfo.hostName} +
+
+ Process ID + {heartbeat.hostInfo.processId} +
+
+ Instance Key + + {heartbeat.workerInstanceKey} + +
+
+ Worker Grouping Key + + {heartbeat.hostInfo.workerGroupingKey} + +
+
+
+{/snippet} + +{#snippet deploymentInfo()} + {#if heartbeat.deploymentVersion?.deploymentName} + +
+ +

Deployment Version

+
+
+
+
Deployment
+ + {heartbeat.deploymentVersion.deploymentName} + +
+
+ Build ID + {heartbeat.deploymentVersion.buildId} +
+
+
+ {/if} +{/snippet} + +{#snippet usage()} + +
+ +

Usage

+
+
+ +
+

+ {heartbeat.hostInfo.currentHostCpuUsage.toFixed(1)}% +

+

+ CPU Usage +

+
+
+ +
+

+ {heartbeat.hostInfo.currentHostMemUsage.toFixed(1)}% +

+

+ Memory Usage +

+
+
+
+
+{/snippet} + +{#snippet cache()} + +
+ +

Sticky Cache

+
+
+ +
+

+ {totalStickyCacheHit} +

+

+ Cache Hits +

+
+
+ +
+

{currentStickyCacheSize}

+

+ Cache Size +

+
+
+
+
+{/snippet} + +{#snippet timestamps()} + +
+ +

Timestamps

+
+
+
+
Start Time
+ +
+
+
Last Heartbeat
+ +
+ ~{heartbeat.elapsedSinceLastHeartbeat} ago +
+
+
+
{/snippet} diff --git a/src/lib/pages/task-queue.svelte b/src/lib/pages/task-queue.svelte index dac731f51d..0890e2c727 100644 --- a/src/lib/pages/task-queue.svelte +++ b/src/lib/pages/task-queue.svelte @@ -3,34 +3,32 @@ import WorkerInfo from '$lib/components/task-queue/worker-info.svelte'; import WorkerTable from '$lib/components/worker-table.svelte'; - import CodeBlock from '$lib/holocene/code-block.svelte'; import Skeleton from '$lib/holocene/skeleton/index.svelte'; import { getPollers } from '$lib/services/pollers-service'; - import { listWorkers } from '$lib/services/worker-service'; - import { stringifyWithBigInt } from '$lib/utilities/parse-with-big-int'; + import { listWorkersForTaskQueue } from '$lib/services/worker-service'; const { queue: taskQueue, namespace } = $derived(page.params); -{#await getPollers({ queue: taskQueue, namespace }) then workers} -
-

- {taskQueue} -

- -
-{/await} +
+

Task Queue

+

+ {taskQueue} +

-{#await listWorkers({ queue: taskQueue, namespace })} - -{:then response} - {#each response?.workersInfo as worker} - - {/each} -
-

List Workers Response

- -
-{:catch error} -

Error loading workers: {error.message}

-{/await} + {#await listWorkersForTaskQueue({ queue: taskQueue, namespace })} + + {:then response} + {#if !response?.workersInfo} + {#await getPollers({ queue: taskQueue, namespace }) then workers} + + {/await} + {:else} + {#each response.workersInfo as worker} + + {/each} + {/if} + {:catch error} +

Error loading workers: {error.message}

+ {/await} +
diff --git a/src/lib/pages/workflow-workers.svelte b/src/lib/pages/workflow-workers.svelte index c0f52460bd..66e4c791ff 100644 --- a/src/lib/pages/workflow-workers.svelte +++ b/src/lib/pages/workflow-workers.svelte @@ -1,28 +1,31 @@
- -

- {translate('common.task-queue')}: - {taskQueue} -

-
+ {#await listWorkersForTaskQueue({ queue: taskQueue, namespace })} + + {:then response} + {#if !response?.workersInfo} + {#await getPollers({ queue: taskQueue, namespace }) then workers} + + {/await} + {:else} + {#each response.workersInfo as worker} + + {/each} + {/if} + {:catch error} +

Error loading workers: {error.message}

+ {/await}
diff --git a/src/lib/services/worker-service.ts b/src/lib/services/worker-service.ts index c5dc04d9b6..adc22f4075 100644 --- a/src/lib/services/worker-service.ts +++ b/src/lib/services/worker-service.ts @@ -1,4 +1,4 @@ -import type { Timestamp } from '@temporalio/common'; +import type { Duration, Timestamp } from '@temporalio/common'; import type { WorkerDeploymentVersion } from '$lib/types/deployments'; import type { NamespaceScopedRequest } from '$lib/types/global'; @@ -54,7 +54,7 @@ export type WorkerHeartbeat = { status: string; startTime: Timestamp; heartbeatTime: Timestamp; - elapsedSinceLastHeartbeat: Timestamp; + elapsedSinceLastHeartbeat: Duration; workflowTaskSlotsInfo: WorkerSlotsInfo; activityTaskSlotsInfo: WorkerSlotsInfo; nexusTaskSlotsInfo: WorkerSlotsInfo; @@ -91,14 +91,14 @@ export type DescribeWorkerResponse = { }[]; }; -export async function listWorkers( +export async function listWorkersForTaskQueue( parameters: ListWorkersRequest, request = fetch, ): Promise { const route = routeForApi('workers', parameters); const response = await requestFromAPI(route, { request, - params: { taskQueue: parameters.queue }, + params: { query: `TaskQueue="${parameters.queue}"` }, }); return response; } diff --git a/src/routes/(app)/namespaces/[namespace]/task-queues/+layout.svelte b/src/routes/(app)/namespaces/[namespace]/task-queues/+layout.svelte index 45155471cd..78635ae367 100644 --- a/src/routes/(app)/namespaces/[namespace]/task-queues/+layout.svelte +++ b/src/routes/(app)/namespaces/[namespace]/task-queues/+layout.svelte @@ -1,14 +1,10 @@ -

- {translate('common.task-queue')} -

{@render children()} From e992c5031f3d8ad1a5f2dffa41228bc61f42e96d Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 5 Dec 2025 15:02:38 -0600 Subject: [PATCH 03/14] Add worker-table if new api fails --- .../components/task-queue/worker-info.svelte | 104 +++++------------- src/lib/pages/task-queue.svelte | 6 +- src/lib/pages/workflow-workers.svelte | 6 +- 3 files changed, 38 insertions(+), 78 deletions(-) diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index 2192da924c..dc4855fec8 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -25,12 +25,6 @@ const { namespace } = $derived(page.params); const heartbeat = $derived(worker.workerHeartbeat); const status = $derived(fromScreamingEnum(heartbeat.status, 'WorkerStatus')); - const totalAvailableSlots = $derived( - (heartbeat.workflowTaskSlotsInfo?.currentAvailableSlots ?? 0) + - (heartbeat.activityTaskSlotsInfo?.currentAvailableSlots ?? 0) + - (heartbeat.nexusTaskSlotsInfo?.currentAvailableSlots ?? 0) + - (heartbeat.localActivitySlotsInfo?.currentAvailableSlots ?? 0), - ); const totalProcessedTasks = $derived( (heartbeat.workflowTaskSlotsInfo?.totalProcessedTasks ?? 0) + @@ -51,27 +45,13 @@ heartbeat?.currentStickyCacheSize ?? 0, ); - const capacityPercent = $derived( - totalAvailableSlots > 0 - ? Math.round( - (totalAvailableSlots / - (totalAvailableSlots + - (heartbeat.workflowTaskSlotsInfo?.currentUsedSlots ?? 0) + - (heartbeat.activityTaskSlotsInfo?.currentUsedSlots ?? 0) + - (heartbeat.nexusTaskSlotsInfo?.currentUsedSlots ?? 0) + - (heartbeat.localActivitySlotsInfo?.currentUsedSlots ?? 0))) * - 100, - ) - : 0, - ); - const isRunning = $derived(status === 'Running');
-
+
-
+
Active Workers
1
@@ -88,7 +68,7 @@ -
+
Total Processed Tasks
{totalProcessedTasks}
@@ -96,26 +76,6 @@ {workflowProcessedTasks} workflow · {activityProcessedTasks} activity
- - -
- Available Slots -
-
{totalAvailableSlots}
-
- {capacityPercent}% capacity free -
-
- - -
- Sticky Cache Hit -
-
{totalStickyCacheHit}
-
- {currentStickyCacheSize} cached workflows -
-
@@ -281,6 +241,32 @@ {/snippet} +{#snippet timestamps()} + +
+
+
Start Time
+ +
+
+
Last Heartbeat
+ +
+ ~{heartbeat.elapsedSinceLastHeartbeat} ago +
+
+
+
+{/snippet} + {#snippet hostInfo()}
@@ -339,7 +325,7 @@
-

Usage

+

Host Usage

@@ -394,33 +380,3 @@
{/snippet} - -{#snippet timestamps()} - -
- -

Timestamps

-
-
-
-
Start Time
- -
-
-
Last Heartbeat
- -
- ~{heartbeat.elapsedSinceLastHeartbeat} ago -
-
-
-
-{/snippet} diff --git a/src/lib/pages/task-queue.svelte b/src/lib/pages/task-queue.svelte index 0890e2c727..f305a79573 100644 --- a/src/lib/pages/task-queue.svelte +++ b/src/lib/pages/task-queue.svelte @@ -28,7 +28,9 @@ {/each} {/if} - {:catch error} -

Error loading workers: {error.message}

+ {:catch _} + {#await getPollers({ queue: taskQueue, namespace }) then workers} + + {/await} {/await} diff --git a/src/lib/pages/workflow-workers.svelte b/src/lib/pages/workflow-workers.svelte index 66e4c791ff..0f236e256b 100644 --- a/src/lib/pages/workflow-workers.svelte +++ b/src/lib/pages/workflow-workers.svelte @@ -25,7 +25,9 @@ {/each} {/if} - {:catch error} -

Error loading workers: {error.message}

+ {:catch _} + {#await getPollers({ queue: taskQueue, namespace }) then workers} + + {/await} {/await} From 1dfd237f8d1edd507d98437534f92c33713d4fd5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 8 Dec 2025 15:39:46 -0600 Subject: [PATCH 04/14] Add workers list page --- .../components/task-queue/worker-info.svelte | 48 +-------- .../task-queue/worker-insights.svelte | 11 ++ .../task-queue/worker-table-cell.svelte | 87 +++++++++++++++ .../task-queue/worker-table-row.svelte | 85 +++++++++++++++ .../components/task-queue/worker-table.svelte | 67 ++++++++++++ .../task-queue/worker-totals.svelte | 84 +++++++++++++++ .../workers/worker-search-table.svelte | 54 ++++++++++ .../workflow/workflow-summary.svelte | 4 +- .../holocene/filter-or-copy-buttons.svelte | 4 +- src/lib/i18n/locales/en/workers.ts | 12 +++ src/lib/layouts/workflow-header.svelte | 6 +- src/lib/pages/task-queue.svelte | 27 +---- src/lib/pages/workflow-workers.svelte | 32 +----- src/lib/services/worker-service.ts | 101 +++++++++++++----- src/lib/types/index.ts | 1 + src/lib/utilities/route-for.test.ts | 4 +- src/lib/utilities/route-for.ts | 22 +++- src/lib/utilities/screaming-enums.ts | 6 ++ src/lib/utilities/update-query-parameters.ts | 1 + .../[namespace]/workers/+page.svelte | 17 +++ .../workers/[instance]/+page.svelte | 34 ++++++ 21 files changed, 571 insertions(+), 136 deletions(-) create mode 100644 src/lib/components/task-queue/worker-insights.svelte create mode 100644 src/lib/components/task-queue/worker-table-cell.svelte create mode 100644 src/lib/components/task-queue/worker-table-row.svelte create mode 100644 src/lib/components/task-queue/worker-table.svelte create mode 100644 src/lib/components/task-queue/worker-totals.svelte create mode 100644 src/lib/components/workers/worker-search-table.svelte create mode 100644 src/routes/(app)/namespaces/[namespace]/workers/+page.svelte create mode 100644 src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index dc4855fec8..80060d218d 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -12,7 +12,7 @@ type WorkerSlotsInfo, } from '$lib/services/worker-service'; import { routeForTaskQueue } from '$lib/utilities/route-for'; - import { fromScreamingEnum } from '$lib/utilities/screaming-enums'; + import { toWorkerStatusReadable } from '$lib/utilities/screaming-enums'; import SdkLogo from '../lines-and-dots/sdk-logo.svelte'; @@ -24,60 +24,16 @@ const { namespace } = $derived(page.params); const heartbeat = $derived(worker.workerHeartbeat); - const status = $derived(fromScreamingEnum(heartbeat.status, 'WorkerStatus')); - - const totalProcessedTasks = $derived( - (heartbeat.workflowTaskSlotsInfo?.totalProcessedTasks ?? 0) + - (heartbeat.activityTaskSlotsInfo?.totalProcessedTasks ?? 0) + - (heartbeat.nexusTaskSlotsInfo?.totalProcessedTasks ?? 0) + - (heartbeat.localActivitySlotsInfo?.totalProcessedTasks ?? 0), - ); - - const workflowProcessedTasks = $derived( - heartbeat.workflowTaskSlotsInfo?.totalProcessedTasks ?? 0, - ); - const activityProcessedTasks = $derived( - heartbeat.activityTaskSlotsInfo?.totalProcessedTasks ?? 0, - ); + const status = $derived(toWorkerStatusReadable(heartbeat.status)); const totalStickyCacheHit = $derived(heartbeat?.totalStickyCacheHit ?? 0); const currentStickyCacheSize = $derived( heartbeat?.currentStickyCacheSize ?? 0, ); - const isRunning = $derived(status === 'Running');
-
- -
- Active Workers -
-
1
-
- - - {status} - -
-
- - -
- Total Processed Tasks -
-
{totalProcessedTasks}
-
- {workflowProcessedTasks} workflow · {activityProcessedTasks} activity -
-
-
-
diff --git a/src/lib/components/task-queue/worker-insights.svelte b/src/lib/components/task-queue/worker-insights.svelte new file mode 100644 index 0000000000..1f31b2f976 --- /dev/null +++ b/src/lib/components/task-queue/worker-insights.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/lib/components/task-queue/worker-table-cell.svelte b/src/lib/components/task-queue/worker-table-cell.svelte new file mode 100644 index 0000000000..7338e8add1 --- /dev/null +++ b/src/lib/components/task-queue/worker-table-cell.svelte @@ -0,0 +1,87 @@ + + + + {#if href} + {value} + {:else if children} + {@render children?.()} + {:else} + {value} + {/if} + + diff --git a/src/lib/components/task-queue/worker-table-row.svelte b/src/lib/components/task-queue/worker-table-row.svelte new file mode 100644 index 0000000000..94d43db339 --- /dev/null +++ b/src/lib/components/task-queue/worker-table-row.svelte @@ -0,0 +1,85 @@ + + + + {#each columns as { label } (label)} + {#if label === translate('workers.identity')} + {worker.workerHeartbeat.workerIdentity} + {:else if label === translate('workers.task-queue')} + + {:else if label === translate('workers.instance')} + + {:else if label === translate('workers.status')} + + + {#if isRunning} + + {/if} + {status} + + + {:else if label === translate('workers.sdk')} + + + + {:else if label === translate('workers.workflow-task-slots')} + {worker.workerHeartbeat?.workflowTaskSlotsInfo?.currentUsedSlots ?? 0} / + {worker.workerHeartbeat?.workflowTaskSlotsInfo?.currentAvailableSlots ?? + 0} + {:else if label === translate('workers.activity-task-slots')} + {worker.workerHeartbeat?.activityTaskSlotsInfo?.currentUsedSlots ?? 0} / + {worker.workerHeartbeat?.activityTaskSlotsInfo?.currentAvailableSlots ?? + 0} + {:else if label === translate('workers.nexus-task-slots')} + {worker.workerHeartbeat?.nexusTaskSlotsInfo?.currentUsedSlots ?? 0} / {worker + .workerHeartbeat?.nexusTaskSlotsInfo?.currentAvailableSlots ?? 0} + {/if} + {/each} + diff --git a/src/lib/components/task-queue/worker-table.svelte b/src/lib/components/task-queue/worker-table.svelte new file mode 100644 index 0000000000..0da6de37df --- /dev/null +++ b/src/lib/components/task-queue/worker-table.svelte @@ -0,0 +1,67 @@ + + +{#if !error} + + {translate('workers.workers')} + + + {#each columns as { label }} + {label} + {/each} + + {#each visibleItems as worker} + + {/each} + + + + + +{:else} + {#await getPollers({ queue: taskQueue, namespace }) then workers} + + {/await} +{/if} diff --git a/src/lib/components/task-queue/worker-totals.svelte b/src/lib/components/task-queue/worker-totals.svelte new file mode 100644 index 0000000000..e5e8459f2c --- /dev/null +++ b/src/lib/components/task-queue/worker-totals.svelte @@ -0,0 +1,84 @@ + + +
+ +
+ Active Workers +
+
{totalActive}
+
+ + Running +
+
+ +
+ Inactive Workers +
+
{totalInactive}
+
+ Inactive +
+
+ + +
+ Total Processed Tasks +
+
{totalProcessedTasks}
+
+ {workflowProcessedTasks} workflow · {activityProcessedTasks} activity +
+
+
diff --git a/src/lib/components/workers/worker-search-table.svelte b/src/lib/components/workers/worker-search-table.svelte new file mode 100644 index 0000000000..6a4bf3bb50 --- /dev/null +++ b/src/lib/components/workers/worker-search-table.svelte @@ -0,0 +1,54 @@ + + + + {translate('workers.workers')} + + + {#each columns as { label }} + {label} + {/each} + + {#each visibleItems as worker} + + {/each} + + + + + diff --git a/src/lib/components/workflow/workflow-summary.svelte b/src/lib/components/workflow/workflow-summary.svelte index c09ba4db64..80c9ffaa3a 100644 --- a/src/lib/components/workflow/workflow-summary.svelte +++ b/src/lib/components/workflow/workflow-summary.svelte @@ -15,7 +15,7 @@ } from '$lib/stores/workflow-run'; import { formatDate } from '$lib/utilities/format-date'; import { formatDistanceAbbreviated } from '$lib/utilities/format-time'; - import { routeForWorkers } from '$lib/utilities/route-for'; + import { routeForWorkflowWorkers } from '$lib/utilities/route-for'; $: ({ workflow } = $workflowRun); $: elapsedTime = formatDistanceAbbreviated({ @@ -73,7 +73,7 @@
.copy-or-filter { - @apply absolute bottom-0 right-0 top-0 inline-flex gap-2 px-2; + @apply absolute bottom-0 right-0 top-0 inline-flex gap-1 px-1; } .copy-or-filter-button { - @apply surface-primary relative top-[50%] h-fit translate-y-[-50%] rounded-full p-1 text-primary hover:surface-inverse; + @apply surface-primary relative top-[40%] h-7 w-7 translate-y-[-40%] rounded-full p-0.5 text-primary hover:surface-inverse; } .filtered { diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 887059633e..f68ca8bc61 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -1,7 +1,9 @@ export const Namespace = 'workers' as const; export const Strings = { + worker: 'Worker', workers: 'Workers', + 'back-to-workers': 'Back to Workers', version: 'Version', versioning: 'Versioning', retirability: 'Retirability', @@ -10,6 +12,16 @@ export const Strings = { 'redirect-rules': 'Redirect Rules', default: 'Default', overall: 'Overall', + identity: 'Identity', + instance: 'Instance', + 'task-queue': 'Task Queue', + status: 'Status', + sdk: 'SDK', + 'workflow-task-slots': 'Workflow Task Slots', + 'activity-task-slots': 'Activity Task Slots', + 'nexus-task-slots': 'Nexus Task Slots', + 'empty-state-title': 'No Workers Found', + 'error-message-fetching': 'An error occurred while fetching workers.', 'compatible-build-ids': 'Compatible Build IDs', 'version-sets': 'Version Sets', 'no-version-sets-found': 'No Version Sets found', diff --git a/src/lib/layouts/workflow-header.svelte b/src/lib/layouts/workflow-header.svelte index d85823287c..acb1208fd3 100644 --- a/src/lib/layouts/workflow-header.svelte +++ b/src/lib/layouts/workflow-header.svelte @@ -36,11 +36,11 @@ routeForPendingActivities, routeForRelationships, routeForUserMetadata, - routeForWorkers, routeForWorkflowMemo, routeForWorkflowQuery, routeForWorkflows, routeForWorkflowSearchAttributes, + routeForWorkflowWorkers, } from '$lib/utilities/route-for'; import { isWorkflowTaskFailure } from '$lib/utilities/workflow-task-failures'; @@ -243,10 +243,10 @@ diff --git a/src/lib/pages/task-queue.svelte b/src/lib/pages/task-queue.svelte index f305a79573..b9297f4008 100644 --- a/src/lib/pages/task-queue.svelte +++ b/src/lib/pages/task-queue.svelte @@ -1,13 +1,9 @@
@@ -15,22 +11,5 @@

{taskQueue}

- - {#await listWorkersForTaskQueue({ queue: taskQueue, namespace })} - - {:then response} - {#if !response?.workersInfo} - {#await getPollers({ queue: taskQueue, namespace }) then workers} - - {/await} - {:else} - {#each response.workersInfo as worker} - - {/each} - {/if} - {:catch _} - {#await getPollers({ queue: taskQueue, namespace }) then workers} - - {/await} - {/await} +
diff --git a/src/lib/pages/workflow-workers.svelte b/src/lib/pages/workflow-workers.svelte index 0f236e256b..0a041979a0 100644 --- a/src/lib/pages/workflow-workers.svelte +++ b/src/lib/pages/workflow-workers.svelte @@ -1,33 +1,5 @@ -
- {#await listWorkersForTaskQueue({ queue: taskQueue, namespace })} - - {:then response} - {#if !response?.workersInfo} - {#await getPollers({ queue: taskQueue, namespace }) then workers} - - {/await} - {:else} - {#each response.workersInfo as worker} - - {/each} - {/if} - {:catch _} - {#await getPollers({ queue: taskQueue, namespace }) then workers} - - {/await} - {/await} -
+ diff --git a/src/lib/services/worker-service.ts b/src/lib/services/worker-service.ts index adc22f4075..90cd9d538a 100644 --- a/src/lib/services/worker-service.ts +++ b/src/lib/services/worker-service.ts @@ -1,17 +1,35 @@ import type { Duration, Timestamp } from '@temporalio/common'; +import type { WorkerStatus } from '$lib/types'; import type { WorkerDeploymentVersion } from '$lib/types/deployments'; import type { NamespaceScopedRequest } from '$lib/types/global'; import { requestFromAPI } from '$lib/utilities/request-from-api'; import { routeForApi } from '$lib/utilities/route-for-api'; +export type WorkerQueryParams = { + workerInstanceKey?: string; + workerIdentity?: string; + hostName?: string; + taskQueue?: string; + deploymentName?: string; + buildId?: string; + sdkName?: string; + sdkVersion?: string; + startTime?: Timestamp; + lastHeartbeatTime?: Timestamp; + status?: WorkerStatus; +}; + export type ListWorkersRequest = NamespaceScopedRequest & { - queue: string; + query?: string; +}; + +export type ListTaskQueueWorkersRequest = NamespaceScopedRequest & { + taskQueue: string; }; export type DescribeWorkerRequest = NamespaceScopedRequest & { - queue: string; - identity: string; + workerInstanceKey: string; }; export type HostInfo = { @@ -51,7 +69,7 @@ export type WorkerHeartbeat = { deploymentVersion: WorkerDeploymentVersion; sdkName: string; sdkVersion: string; - status: string; + status: WorkerStatus; startTime: Timestamp; heartbeatTime: Timestamp; elapsedSinceLastHeartbeat: Duration; @@ -75,33 +93,65 @@ export type WorkerInfo = { export type ListWorkersResponse = { workersInfo: WorkerInfo[]; + nextPageToken?: string; }; -export type DescribeWorkerResponse = { - buildId: string; - capabilities: { - useVersioning: boolean; - activityOnly: boolean; +type PaginatedWorkerListPromise = ( + pageSize: number, + token: string, +) => Promise<{ items: WorkerInfo[]; nextPageToken: string }>; + +export const fetchPaginatedWorkers = async ( + parameters: ListWorkersRequest, + request = fetch, +): Promise => { + return (pageSize = 100, token = '') => { + const route = routeForApi('workers', parameters); + return requestFromAPI(route, { + request, + params: { + maximumPageSize: String(pageSize), + nextPageToken: token, + query: parameters.query, + }, + }).then(({ workersInfo, nextPageToken }) => { + if (!workersInfo) { + throw new Error('No workers info in response'); + } + return { + items: workersInfo, + nextPageToken: nextPageToken ? String(nextPageToken) : '', + }; + }); }; - lastAccessTime: string; - rateLimitPerSecond: number; - taskQueues: { - name: string; - kind: number; - }[]; }; -export async function listWorkersForTaskQueue( - parameters: ListWorkersRequest, +export const fetchPaginatedWorkersForTaskQueue = async ( + parameters: ListTaskQueueWorkersRequest, request = fetch, -): Promise { - const route = routeForApi('workers', parameters); - const response = await requestFromAPI(route, { - request, - params: { query: `TaskQueue="${parameters.queue}"` }, - }); - return response; -} +): Promise => { + return (pageSize = 100, token = '') => { + const route = routeForApi('workers', parameters); + return requestFromAPI(route, { + request, + params: { + query: `TaskQueue="${parameters.taskQueue}"`, + maximumPageSize: String(pageSize), + nextPageToken: token, + }, + }).then(({ workersInfo, nextPageToken }) => { + if (!workersInfo) { + throw new Error('No workers info in response'); + } + return { + items: workersInfo, + nextPageToken: nextPageToken ? String(nextPageToken) : '', + }; + }); + }; +}; + +export type DescribeWorkerResponse = { workerInfo: WorkerInfo }; export async function describeWorker( parameters: DescribeWorkerRequest, @@ -110,6 +160,5 @@ export async function describeWorker( const route = routeForApi('worker', parameters); return await requestFromAPI(route, { request, - params: { identity: parameters.identity }, }); } diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index 5070d8a159..879987dc56 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -181,6 +181,7 @@ export type PendingNexusOperationState = export type CallbackState = temporal.api.enums.v1.CallbackState; export type VersioningBehavior = temporal.api.enums.v1.VersioningBehavior; export type EventType = temporal.api.enums.v1.EventType; +export type WorkerStatus = temporal.api.enums.v1.WorkerStatus; // temporal.api.enums.v1.ResetReapplyExcludeType export enum ResetReapplyExcludeType { diff --git a/src/lib/utilities/route-for.test.ts b/src/lib/utilities/route-for.test.ts index 8204b1b8d3..f77ee3a2a0 100644 --- a/src/lib/utilities/route-for.test.ts +++ b/src/lib/utilities/route-for.test.ts @@ -19,11 +19,11 @@ import { routeForScheduleCreate, routeForSchedules, routeForTaskQueue, - routeForWorkers, routeForWorkflow, routeForWorkflowQuery, routeForWorkflows, routeForWorkflowsWithQuery, + routeForWorkflowWorkers, } from './route-for'; describe('routeFor', () => { @@ -117,7 +117,7 @@ describe('routeFor', () => { }); it('should route to "workers"', () => { - const path = routeForWorkers({ + const path = routeForWorkflowWorkers({ namespace: 'default', workflow: 'abc', run: 'def', diff --git a/src/lib/utilities/route-for.ts b/src/lib/utilities/route-for.ts index bcfd0c98ae..dd1c9f7552 100644 --- a/src/lib/utilities/route-for.ts +++ b/src/lib/utilities/route-for.ts @@ -202,7 +202,13 @@ export const routeForEventHistoryEvent = ({ return `${routeForWorkflow(parameters)}/history/events/${eventId || requestId}`; }; -export const routeForWorkers = (parameters: WorkflowParameters): string => { +export const routeForWorkers = (parameters: NamespaceParameter): string => { + return `${routeForNamespace({ namespace: parameters.namespace })}/workers`; +}; + +export const routeForWorkflowWorkers = ( + parameters: WorkflowParameters, +): string => { return `${routeForWorkflow(parameters)}/workers`; }; @@ -214,6 +220,20 @@ export const routeForWorkerDeployments = ({ return resolve('/namespaces/[namespace]/worker-deployments', { namespace }); }; +export const routeForWorkerInstance = ({ + namespace, + workerInstanceKey, +}: { + namespace: string; + workerInstanceKey: string; +}) => { + const workerInstanceKeyEncoded = encodeURIForSvelte(workerInstanceKey); + return resolve('/namespaces/[namespace]/workers/[workerInstanceKey]', { + namespace, + workerInstanceKey: workerInstanceKeyEncoded, + }); +}; + export const routeForWorkerDeployment = ({ namespace, deployment, diff --git a/src/lib/utilities/screaming-enums.ts b/src/lib/utilities/screaming-enums.ts index 48b442f1fa..db9a4e3ac3 100644 --- a/src/lib/utilities/screaming-enums.ts +++ b/src/lib/utilities/screaming-enums.ts @@ -3,6 +3,7 @@ import type { CallbackState, NamespaceState, PendingNexusOperationState, + WorkerStatus, WorkflowExecutionStatus, } from '$lib/types'; import type { BatchOperationState, BatchOperationType } from '$lib/types/batch'; @@ -98,3 +99,8 @@ export const toCallbackStateReadable = ( if (!state) return state; return fromScreamingEnum(state, 'CallbackState'); }; + +export const toWorkerStatusReadable = (state?: WorkerStatus): string => { + if (!state) return 'Unknown'; + return fromScreamingEnum(state, 'WorkerStatus') as unknown as string; +}; diff --git a/src/lib/utilities/update-query-parameters.ts b/src/lib/utilities/update-query-parameters.ts index 4d3b47512f..56b44e64d7 100644 --- a/src/lib/utilities/update-query-parameters.ts +++ b/src/lib/utilities/update-query-parameters.ts @@ -31,6 +31,7 @@ export const updateQueryParameters = async ({ const params = {}; let replaced = false; + console.log('Updating query parameter:'); url.searchParams.forEach((value, key) => { if (key !== parameter) { params[key] = value; diff --git a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte new file mode 100644 index 0000000000..8a7bb98b8a --- /dev/null +++ b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte @@ -0,0 +1,17 @@ + + + +
+

+ {translate('workers.workers')} +

+
+ diff --git a/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte new file mode 100644 index 0000000000..08b0f7b86d --- /dev/null +++ b/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte @@ -0,0 +1,34 @@ + + + + +
+
+
+ + {translate('workers.back-to-workers')} + +
+
+
+{#await describeWorker({ namespace, workerInstanceKey }) then data} + +{:catch error} +

Error loading worker info: {error.message}

+{/await} From 1eadb997a313994e86f79ff0f609769061f935ab Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 8 Dec 2025 16:10:40 -0600 Subject: [PATCH 05/14] Make filterable a prop --- .../task-queue/worker-table-cell.svelte | 11 +++- .../task-queue/worker-table-row.svelte | 22 ++++++-- .../components/task-queue/worker-table.svelte | 3 +- .../workers/worker-search-table.svelte | 55 ++++++++++--------- src/lib/i18n/locales/en/workers.ts | 7 ++- 5 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/lib/components/task-queue/worker-table-cell.svelte b/src/lib/components/task-queue/worker-table-cell.svelte index 7338e8add1..53ef0c00c6 100644 --- a/src/lib/components/task-queue/worker-table-cell.svelte +++ b/src/lib/components/task-queue/worker-table-cell.svelte @@ -11,11 +11,17 @@ type Props = { attribute?: string; value: string; + filterable?: boolean; href?: string; children?: Snippet; }; - let { attribute, value, href, children }: Props = $props(); - + let { + attribute, + value, + filterable = false, + href, + children, + }: Props = $props(); const query = $derived(page.url.searchParams.get('query') || ''); const onRowFilterClick = () => { @@ -82,6 +88,7 @@ show={filterOrCopyButtonsVisible} content={value} onFilter={onRowFilterClick} + {filterable} filtered={query.includes(`${attribute}=`)} /> diff --git a/src/lib/components/task-queue/worker-table-row.svelte b/src/lib/components/task-queue/worker-table-row.svelte index 94d43db339..bb8307191b 100644 --- a/src/lib/components/task-queue/worker-table-row.svelte +++ b/src/lib/components/task-queue/worker-table-row.svelte @@ -13,9 +13,10 @@ worker: WorkerInfo; namespace: string; columns: { label: string }[]; + filterable?: boolean; }; - let { columns, worker, namespace }: Props = $props(); + let { columns, worker, namespace, filterable = false }: Props = $props(); const status = $derived( toWorkerStatusReadable(worker.workerHeartbeat.status), ); @@ -25,11 +26,22 @@ {#each columns as { label } (label)} {#if label === translate('workers.identity')} - {worker.workerHeartbeat.workerIdentity} + {:else if label === translate('workers.task-queue')} + {:else if label === translate('workers.host-name')} + {:else if label === translate('workers.instance')} {:else if label === translate('workers.status')} - + - + {:else if label === translate('workers.sdk')} fetchPaginatedWorkers({ namespace, query })); - - {translate('workers.workers')} - - - {#each columns as { label }} - {label} + {translate('workers.workers')} + + + {#each columns as { label }} + {label} + {/each} + + {#each visibleItems as worker} + {/each} - - {#each visibleItems as worker} - - {/each} - - - - - + + + + + +{/key} diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index f68ca8bc61..aea191072e 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -12,14 +12,15 @@ export const Strings = { 'redirect-rules': 'Redirect Rules', default: 'Default', overall: 'Overall', + 'host-name': 'Host Name', identity: 'Identity', instance: 'Instance', 'task-queue': 'Task Queue', status: 'Status', sdk: 'SDK', - 'workflow-task-slots': 'Workflow Task Slots', - 'activity-task-slots': 'Activity Task Slots', - 'nexus-task-slots': 'Nexus Task Slots', + 'workflow-task-slots': 'Workflow Slots', + 'activity-task-slots': 'Activity Slots', + 'nexus-task-slots': 'Nexus Slots', 'empty-state-title': 'No Workers Found', 'error-message-fetching': 'An error occurred while fetching workers.', 'compatible-build-ids': 'Compatible Build IDs', From 567aa2d5b748b5120a075b9b56a6133da25ecce9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 12 Jan 2026 12:04:41 -0600 Subject: [PATCH 06/14] Refactor workers page to designs --- .../components/task-queue/worker-info.svelte | 472 +++++++++--------- .../task-queue/worker-table-row.svelte | 14 +- .../components/workers/worker-status.svelte | 51 ++ src/lib/holocene/badge.svelte | 1 + src/lib/i18n/locales/en/workers.ts | 1 + src/lib/utilities/route-for-api.ts | 1 + src/lib/utilities/screaming-enums.ts | 17 +- .../workers/[instance]/+page.svelte | 4 +- .../[workflow]/[run]/workers/+page.svelte | 10 +- 9 files changed, 302 insertions(+), 269 deletions(-) create mode 100644 src/lib/components/workers/worker-status.svelte diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index 80060d218d..8e0a5c77cd 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -11,10 +11,12 @@ type WorkerPollerInfo, type WorkerSlotsInfo, } from '$lib/services/worker-service'; + import { formatDurationAbbreviated } from '$lib/utilities/format-time'; import { routeForTaskQueue } from '$lib/utilities/route-for'; import { toWorkerStatusReadable } from '$lib/utilities/screaming-enums'; import SdkLogo from '../lines-and-dots/sdk-logo.svelte'; + import WorkerStatus from '../workers/worker-status.svelte'; type Props = { worker: WorkerInfo; @@ -27,226 +29,197 @@ const status = $derived(toWorkerStatusReadable(heartbeat.status)); const totalStickyCacheHit = $derived(heartbeat?.totalStickyCacheHit ?? 0); + const totalStickyCacheMiss = $derived(heartbeat?.totalStickyCacheMiss ?? 0); const currentStickyCacheSize = $derived( heartbeat?.currentStickyCacheSize ?? 0, ); - const isRunning = $derived(status === 'Running'); + + const cacheHitRate = $derived(() => { + const total = totalStickyCacheHit + totalStickyCacheMiss; + if (total === 0) return 0; + return ((totalStickyCacheHit / total) * 100).toFixed(1); + });
-
-
- -
-
-
- - {#if isRunning} - - {/if} - {status} - -

{heartbeat.workerIdentity}

-
-
-
+
+
+ +

{heartbeat.workerIdentity}

+
-
-
-
- Task Queue -
- - {heartbeat.taskQueue} - -
-
-
- SDK -
- -
-
+
+
+ Task Queue + + {heartbeat.taskQueue} + +
+
+ Start + +
+
+ Last Heartbeat
-

- Task Slots -

-
- {@render slotCard( - 'Workflow Tasks', - heartbeat.workflowTaskSlotsInfo, - )} - {@render slotCard( - 'Activity Tasks', - heartbeat.activityTaskSlotsInfo, - )} - {@render slotCard('Nexus Tasks', heartbeat.nexusTaskSlotsInfo)} - {@render slotCard( - 'Local Activities', - heartbeat.localActivitySlotsInfo, - )} -
+ + + {formatDurationAbbreviated( + String(heartbeat.elapsedSinceLastHeartbeat), + )} ago +
+
+
+ SDK + +
+
+
-
-

- Pollers -

-
- {@render pollerCard( - 'Workflow Poller', - heartbeat.workflowPollerInfo, - )} - {@render pollerCard( - 'Workflow Sticky Poller', - heartbeat.workflowStickyPollerInfo, - )} - {@render pollerCard( - 'Activity Poller', - heartbeat.activityPollerInfo, - )} - {@render pollerCard('Nexus Poller', heartbeat.nexusPollerInfo)} -
-
-
+
+
+ {@render taskSlotCard( + 'Workflow', + heartbeat.workflowTaskSlotsInfo, + heartbeat.workflowPollerInfo, + )} + {@render taskSlotCard( + 'Activity', + heartbeat.activityTaskSlotsInfo, + heartbeat.activityPollerInfo, + )} + {@render taskSlotCard( + 'Nexus Tasks', + heartbeat.nexusTaskSlotsInfo, + heartbeat.nexusPollerInfo, + )} + {@render taskSlotCard( + 'Local Activities', + heartbeat.localActivitySlotsInfo, + null, + )}
-
- {@render timestamps()} +
{@render hostInfo()} - {@render deploymentInfo()} - {@render usage()} - {@render cache()} + {@render hostUsage()} + {@render workflowCache()} + {@render diagnostics()}
-{#snippet slotCard(title: string, slots: WorkerSlotsInfo)} - -
- {title} - {slots.slotSupplierKind} +{#snippet taskSlotCard( + title: string, + slots: WorkerSlotsInfo, + poller: WorkerPollerInfo | null, +)} + +
+

{title}

+ {slots.slotSupplierKind}
-
-
- - {slots.currentUsedSlots ?? 0} - - Used -
+ +
- {slots.currentAvailableSlots ?? 0} - Available +
Slots
+
+

+ {slots.currentUsedSlots ?? 0} +

+

+ {#if slots.currentAvailableSlots} + {slots.currentAvailableSlots - slots.currentUsedSlots || 0} + {:else} + - + {/if} +

+
+
+

Used

+

+ {#if slots.currentAvailableSlots} + Available out of {slots.currentAvailableSlots} + {:else}None Available + {/if} +

+
+
- - {slots.totalProcessedTasks ?? 0} +
Tasks Processed
+ + {(slots.totalProcessedTasks ?? 0).toLocaleString()} - Processed
-
- {#if slots.currentAvailableSlots} -
- {#each new Array(slots.currentAvailableSlots) as _, i} -
- {/each} -
- {/if} - -{/snippet} - -{#snippet pollerCard(title: string, poller: WorkerPollerInfo)} - -
- {title} - - {poller.currentPollers ?? 0} - -
-
- {#if poller.lastSuccessfulPollTime} - Last poll: - {:else} - No activity - {/if} -
-
-{/snippet} -{#snippet timestamps()} - -
-
-
Start Time
- -
-
-
Last Heartbeat
- -
- ~{heartbeat.elapsedSinceLastHeartbeat} ago + {#if poller} +
+
+ Poller + + {poller.isAutoscaling ? 'Autoscaling' : 'Manual'} + +
+ + {poller.currentPollers ?? 0} + +
+ {#if poller.lastSuccessfulPollTime} + Last Poll + {:else} + No Activity + {/if} +
-
+ {/if}
{/snippet} {#snippet hostInfo()} -
- -

Host Information

-
-
-
- Hostname - {heartbeat.hostInfo.hostName} +

Host Info

+
+
+ Host Name + + {heartbeat.hostInfo.hostName} +
-
+
Process ID - {heartbeat.hostInfo.processId} + {heartbeat.hostInfo.processId}
-
+
Instance Key - + {heartbeat.workerInstanceKey}
-
+
Worker Grouping Key - + {heartbeat.hostInfo.workerGroupingKey}
@@ -254,85 +227,92 @@ {/snippet} -{#snippet deploymentInfo()} - {#if heartbeat.deploymentVersion?.deploymentName} - -
- -

Deployment Version

+{#snippet hostUsage()} + +
+

Host Usage

+
+
+
+
+ + + CPU Usage + + {heartbeat.hostInfo.currentHostCpuUsage.toFixed(0)}% +
+
+
+
-
-
-
Deployment
- - {heartbeat.deploymentVersion.deploymentName} - +
+
+ + + Memory Usage + + {heartbeat.hostInfo.currentHostMemUsage.toFixed(0)}%
-
- Build ID - {heartbeat.deploymentVersion.buildId} +
+
- - {/if} +
+ {/snippet} -{#snippet usage()} +{#snippet workflowCache()} -
- -

Host Usage

-
+

Workflow Cache

- -
-

- {heartbeat.hostInfo.currentHostCpuUsage.toFixed(1)}% -

-

- CPU Usage -

-
-
- -
-

- {heartbeat.hostInfo.currentHostMemUsage.toFixed(1)}% -

-

- Memory Usage -

-
-
+
+ + {currentStickyCacheSize.toLocaleString()} + +
Cache size
+
+
+ + {cacheHitRate()}% + +
Cache hits
+
+
+
+ + {totalStickyCacheHit.toLocaleString()} + +
Active thread count
{/snippet} -{#snippet cache()} +{#snippet diagnostics()} -
- -

Sticky Cache

-
+

Diagnostics

- -
-

- {totalStickyCacheHit} -

-

- Cache Hits -

-
-
- -
-

{currentStickyCacheSize}

-

- Cache Size -

-
-
+
+ + {cacheHitRate()}% + +
Poll success rate
+
+
+ None +
Rate limit
+
{/snippet} diff --git a/src/lib/components/task-queue/worker-table-row.svelte b/src/lib/components/task-queue/worker-table-row.svelte index bb8307191b..05f44cfe9a 100644 --- a/src/lib/components/task-queue/worker-table-row.svelte +++ b/src/lib/components/task-queue/worker-table-row.svelte @@ -1,11 +1,11 @@ @@ -55,16 +54,7 @@ /> {:else if label === translate('workers.status')} - - {#if isRunning} - - {/if} - {status} - + {:else if label === translate('workers.sdk')} + import { cva } from 'class-variance-authority'; + + import HeartBeat from '$lib/components/heart-beat-indicator.svelte'; + import type { ReadableWorkerStatus } from '$lib/utilities/screaming-enums'; + + interface Props { + delay?: number; + status?: ReadableWorkerStatus; + 'test-id'?: string; + } + + let { delay = 0, status = 'Running', 'test-id': testId }: Props = $props(); + + const label: Record = { + Unspecified: 'Unspecified', + Running: 'Running', + 'Shutting Down': 'Shutting Down', + Shutdown: 'Shutdown', + }; + + const workerStatus = cva( + [ + 'flex items-center rounded-sm px-1 py-0.5 h-5 whitespace-nowrap text-black gap-1 font-medium', + ], + { + variants: { + status: { + Unspecified: 'bg-slate-100', + Running: 'bg-blue-300', + 'Shutting Down': 'bg-yellow-200', + Shutdown: 'bg-red-200', + }, + }, + }, + ); + + const isRunning = $derived(status === 'Running'); + + +
+ + {label[status]} + {#if isRunning} + + {/if} + +
diff --git a/src/lib/holocene/badge.svelte b/src/lib/holocene/badge.svelte index 949160f488..11f8744a52 100644 --- a/src/lib/holocene/badge.svelte +++ b/src/lib/holocene/badge.svelte @@ -12,6 +12,7 @@ danger: 'bg-red-200', count: 'h-6 w-6 min-w-max rounded-full bg-blue-300', subtle: 'surface-subtle dark:text-white font-normal select-all', + ghost: 'surface-primary text-primary border border-subtle', }; const types = cva( diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index aea191072e..993ec8e881 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -3,6 +3,7 @@ export const Namespace = 'workers' as const; export const Strings = { worker: 'Worker', workers: 'Workers', + 'view-all-workers': 'View All Workers', 'back-to-workers': 'Back to Workers', version: 'Version', versioning: 'Versioning', diff --git a/src/lib/utilities/route-for-api.ts b/src/lib/utilities/route-for-api.ts index 20e7d8fd03..b3140034f1 100644 --- a/src/lib/utilities/route-for-api.ts +++ b/src/lib/utilities/route-for-api.ts @@ -117,6 +117,7 @@ const encode = ( endpointId: '', deploymentName: '', version: '', + workerInstanceKey: '', }, ); }; diff --git a/src/lib/utilities/screaming-enums.ts b/src/lib/utilities/screaming-enums.ts index db9a4e3ac3..e3e0b6549c 100644 --- a/src/lib/utilities/screaming-enums.ts +++ b/src/lib/utilities/screaming-enums.ts @@ -100,7 +100,18 @@ export const toCallbackStateReadable = ( return fromScreamingEnum(state, 'CallbackState'); }; -export const toWorkerStatusReadable = (state?: WorkerStatus): string => { - if (!state) return 'Unknown'; - return fromScreamingEnum(state, 'WorkerStatus') as unknown as string; +export type ReadableWorkerStatus = + | 'Unspecified' + | 'Running' + | 'Shutting Down' + | 'Shutdown'; + +export const toWorkerStatusReadable = ( + state?: WorkerStatus, +): ReadableWorkerStatus => { + if (!state) return 'Unspecified'; + return fromScreamingEnum( + state, + 'WorkerStatus', + ) as unknown as ReadableWorkerStatus; }; diff --git a/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte index 08b0f7b86d..d3eaa0ac75 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte @@ -19,10 +19,10 @@
- {translate('workers.back-to-workers')} + {translate('workers.view-all-workers')}
diff --git a/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte index 44a0dc5f6a..2464a7bb63 100644 --- a/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte @@ -1,17 +1,15 @@ - + From 48ef22af1796f1f4960a4d1e9373070bcc0f2a47 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 5 Feb 2026 14:20:46 -0600 Subject: [PATCH 07/14] New worker nav items --- .gitignore | 3 +- .serena/project.yml | 61 ++++++++++++++++--- src/lib/i18n/locales/en/workers.ts | 1 + src/lib/pages/deployments.svelte | 20 ++++-- src/routes/(app)/+layout.svelte | 34 +++++++---- .../worker-deployments/+page.server.ts | 11 ++++ .../worker-deployments/+page.svelte | 17 ++++-- .../[namespace]/workers/+page.svelte | 55 +++++++++++++++-- 8 files changed, 165 insertions(+), 37 deletions(-) create mode 100644 src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts diff --git a/.gitignore b/.gitignore index 3bc16487b4..f53556b113 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,5 @@ server/ui/assets go.work go.work.sum .claude/* -!.claude/skills/ \ No newline at end of file +!.claude/skills/ +.omc diff --git a/.serena/project.yml b/.serena/project.yml index e827832722..fd03e14574 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -1,9 +1,3 @@ -# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby) -# * For C, use cpp -# * For JavaScript, use typescript -# Special requirements: -# * csharp: Requires the presence of a .sln file in the project folder. -language: typescript # whether to use the project's gitignore file to ignore files # Added on 2025-04-07 @@ -63,5 +57,58 @@ excluded_tools: [] # initial prompt for the project. It will always be given to the LLM upon activating the project # (contrary to the memories, which are loaded on demand). initial_prompt: '' - +# the name by which the project can be referenced within Serena project_name: 'ui' + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default. +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# This setting can, in turn, be overridden by CLI parameters (--mode). +default_modes: + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default) +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +fixed_tools: [] + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: utf-8 + + +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# java julia kotlin lua markdown +# matlab nix pascal perl php +# powershell python python_jedi r rego +# ruby ruby_solargraph rust scala swift +# terraform toml typescript typescript_vts vue +# yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- typescript diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 993ec8e881..87ef81221b 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -3,6 +3,7 @@ export const Namespace = 'workers' as const; export const Strings = { worker: 'Worker', workers: 'Workers', + 'worker-views': 'Worker Views', 'view-all-workers': 'View All Workers', 'back-to-workers': 'Back to Workers', version: 'Version', diff --git a/src/lib/pages/deployments.svelte b/src/lib/pages/deployments.svelte index bbb6f13b19..4756524e07 100644 --- a/src/lib/pages/deployments.svelte +++ b/src/lib/pages/deployments.svelte @@ -11,6 +11,12 @@ import { fetchPaginatedDeployments } from '$lib/services/deployments-service'; import type { APIErrorResponse } from '$lib/utilities/request-from-api'; + interface Props { + hideHeader?: boolean; + } + + let { hideHeader = false }: Props = $props(); + let error = $state(''); const namespace = $derived(page.params.namespace); @@ -39,12 +45,14 @@ ]; -
-

- {translate('deployments.worker-deployments')} -

- Public Preview -
+{#if !hideHeader} +
+

+ {translate('deployments.worker-deployments')} +

+ Public Preview +
+{/if} {#key [namespace]} path.includes(batchOperationsRoute), }, { - href: workerDeploymentsRoute, + href: workersRoute, icon: 'merge', label: translate('deployments.deployments'), tooltip: translate('deployments.worker-deployments'), - isActive: (path) => path.includes(workerDeploymentsRoute), + isActive: (path) => path.includes(workersRoute), }, { href: nexusRoute, @@ -182,14 +182,14 @@ workflowsRoute, schedulesRoute, batchOperationsRoute, - workerDeploymentsRoute, + workersRoute, archivalRoute, } = $derived(routes); let showNamespacePicker = $derived( [ workflowsRoute, schedulesRoute, - workerDeploymentsRoute, + workersRoute, batchOperationsRoute, archivalRoute, ].some((route) => page.url.href.includes(route)), @@ -205,10 +205,6 @@ subPath: 'batch-operations', fullRoute: routeForBatchOperations({ namespace }), }, - { - subPath: 'worker-deployments', - fullRoute: routeForWorkerDeployments({ namespace }), - }, ]; for (const { subPath, fullRoute } of namespacePages) { @@ -217,6 +213,18 @@ } } + // Handle workers page with view query param preservation + if (page.url.pathname.endsWith('workers')) { + const view = page.url.searchParams.get('view'); + const base = routeForWorkers({ namespace }); + return view ? `${base}?view=${view}` : base; + } + + // Handle legacy worker-deployments path during redirect + if (page.url.pathname.includes('worker-deployments')) { + return `${routeForWorkers({ namespace })}?view=deployments`; + } + return routeForWorkflows({ namespace }); } diff --git a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts new file mode 100644 index 0000000000..c6f373a121 --- /dev/null +++ b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts @@ -0,0 +1,11 @@ +import type { RequestEvent } from '@sveltejs/kit'; +import { redirect } from '@sveltejs/kit'; + +export const load = ({ params, url }: RequestEvent) => { + const { namespace } = params; + + const queryString = url.searchParams.toString(); + const redirectUrl = `/namespaces/${namespace}/workers?view=deployments${queryString ? `&${queryString}` : ''}`; + + redirect(301, redirectUrl); +}; diff --git a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte index da3d7ee384..1d0ed6446d 100644 --- a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte @@ -1,9 +1,16 @@ - - +

Redirecting...

diff --git a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte index 8a7bb98b8a..27c8272cd4 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte @@ -3,15 +3,60 @@ import PageTitle from '$lib/components/page-title.svelte'; import WorkerSearchTable from '$lib/components/workers/worker-search-table.svelte'; + import Badge from '$lib/holocene/badge.svelte'; + import TabList from '$lib/holocene/tab/tab-list.svelte'; + import Tab from '$lib/holocene/tab/tab.svelte'; + import Tabs from '$lib/holocene/tab/tabs.svelte'; import { translate } from '$lib/i18n/translate'; + import WorkerDeployments from '$lib/pages/deployments.svelte'; + import { routeForWorkers } from '$lib/utilities/route-for'; const { namespace } = $derived(page.params); + const view = $derived(page.url.searchParams.get('view')); + const isDeploymentsView = $derived(view === 'deployments'); + + const workersHref = $derived(routeForWorkers({ namespace })); + const deploymentsHref = $derived( + `${routeForWorkers({ namespace })}?view=deployments`, + ); + + const pageTitle = $derived( + isDeploymentsView + ? translate('deployments.worker-deployments') + : translate('workers.workers'), + ); - +
-

- {translate('workers.workers')} -

+
+

+ {pageTitle} +

+ {#if isDeploymentsView} + Public Preview + {/if} +
+ + + + + +
- + +{#if isDeploymentsView} + +{:else} + +{/if} From 89985d6aab6e3c003cc3177280b758eec0c7f47b Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 5 Feb 2026 14:27:17 -0600 Subject: [PATCH 08/14] Dont use url params --- src/routes/(app)/+layout.svelte | 11 ++-- .../worker-deployments/+page.server.ts | 11 ---- .../worker-deployments/+page.svelte | 52 +++++++++++++++---- .../[namespace]/workers/+page.svelte | 32 ++++-------- 4 files changed, 57 insertions(+), 49 deletions(-) delete mode 100644 src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 5c9b2e75dc..0d419c58d8 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -32,6 +32,7 @@ routeForNamespaces, routeForNexus, routeForSchedules, + routeForWorkerDeployments, routeForWorkers, routeForWorkflows, } from '$lib/utilities/route-for'; @@ -213,16 +214,14 @@ } } - // Handle workers page with view query param preservation + // Handle workers page if (page.url.pathname.endsWith('workers')) { - const view = page.url.searchParams.get('view'); - const base = routeForWorkers({ namespace }); - return view ? `${base}?view=${view}` : base; + return routeForWorkers({ namespace }); } - // Handle legacy worker-deployments path during redirect + // Handle worker-deployments page if (page.url.pathname.includes('worker-deployments')) { - return `${routeForWorkers({ namespace })}?view=deployments`; + return routeForWorkerDeployments({ namespace }); } return routeForWorkflows({ namespace }); diff --git a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts deleted file mode 100644 index c6f373a121..0000000000 --- a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { RequestEvent } from '@sveltejs/kit'; -import { redirect } from '@sveltejs/kit'; - -export const load = ({ params, url }: RequestEvent) => { - const { namespace } = params; - - const queryString = url.searchParams.toString(); - const redirectUrl = `/namespaces/${namespace}/workers?view=deployments${queryString ? `&${queryString}` : ''}`; - - redirect(301, redirectUrl); -}; diff --git a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte index 1d0ed6446d..85ecfbbb0e 100644 --- a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte @@ -1,16 +1,50 @@ -

Redirecting...

+ +
+
+

+ {pageTitle} +

+ Public Preview +
+ + + + + + +
+ + diff --git a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte index 27c8272cd4..2f34f7efbc 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte @@ -3,28 +3,21 @@ import PageTitle from '$lib/components/page-title.svelte'; import WorkerSearchTable from '$lib/components/workers/worker-search-table.svelte'; - import Badge from '$lib/holocene/badge.svelte'; import TabList from '$lib/holocene/tab/tab-list.svelte'; import Tab from '$lib/holocene/tab/tab.svelte'; import Tabs from '$lib/holocene/tab/tabs.svelte'; import { translate } from '$lib/i18n/translate'; - import WorkerDeployments from '$lib/pages/deployments.svelte'; - import { routeForWorkers } from '$lib/utilities/route-for'; + import { + routeForWorkerDeployments, + routeForWorkers, + } from '$lib/utilities/route-for'; const { namespace } = $derived(page.params); - const view = $derived(page.url.searchParams.get('view')); - const isDeploymentsView = $derived(view === 'deployments'); const workersHref = $derived(routeForWorkers({ namespace })); - const deploymentsHref = $derived( - `${routeForWorkers({ namespace })}?view=deployments`, - ); + const deploymentsHref = $derived(routeForWorkerDeployments({ namespace })); - const pageTitle = $derived( - isDeploymentsView - ? translate('deployments.worker-deployments') - : translate('workers.workers'), - ); + const pageTitle = $derived(translate('workers.workers')); @@ -33,9 +26,6 @@

{pageTitle}

- {#if isDeploymentsView} - Public Preview - {/if}
@@ -43,20 +33,16 @@ label={translate('workers.workers')} id="workers-tab" href={workersHref} - active={!isDeploymentsView} + active={true} /> -{#if isDeploymentsView} - -{:else} - -{/if} + From 722bd8125bda9ae43367428ff3751094e14e7954 Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Fri, 20 Feb 2026 17:38:33 -0700 Subject: [PATCH 09/14] Clean up types and remove Shutdown status --- .gitignore | 3 - package.json | 14 +- pnpm-lock.yaml | 236 ++++++++++++------ .../components/task-queue/worker-info.svelte | 12 +- .../task-queue/worker-table-cell.svelte | 23 +- .../task-queue/worker-table-row.svelte | 2 +- .../task-queue/worker-totals.svelte | 2 +- .../workers/worker-search-table.svelte | 8 +- .../components/workers/worker-status.svelte | 2 - src/lib/services/worker-service.ts | 107 +------- src/lib/types/api.ts | 5 - src/lib/types/index.ts | 22 ++ src/lib/utilities/route-for-api.ts | 4 +- src/lib/utilities/screaming-enums.ts | 6 +- src/lib/utilities/update-query-parameters.ts | 1 - src/routes/(app)/+layout.svelte | 10 - .../worker-deployments/+page.svelte | 9 +- .../[namespace]/workers/+page.svelte | 6 +- 18 files changed, 222 insertions(+), 250 deletions(-) diff --git a/.gitignore b/.gitignore index 311d881a10..751271e0cf 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,5 @@ go.work.sum .claude/settings.local.json .claude/* !.claude/skills/ -<<<<<<< HEAD .omc -======= .sisyphus/* ->>>>>>> main diff --git a/package.json b/package.json index b9f88a1bd5..69108d8bc3 100644 --- a/package.json +++ b/package.json @@ -145,13 +145,13 @@ "@sveltejs/adapter-vercel": "^4.0.0", "@sveltejs/kit": "2.49.5", "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@temporalio/activity": "1.14.1", - "@temporalio/client": "1.14.1", - "@temporalio/common": "1.14.1", - "@temporalio/proto": "1.14.1", - "@temporalio/testing": "1.14.1", - "@temporalio/worker": "1.14.1", - "@temporalio/workflow": "1.14.1", + "@temporalio/activity": "1.15.0", + "@temporalio/client": "1.15.0", + "@temporalio/common": "1.15.0", + "@temporalio/proto": "1.15.0", + "@temporalio/testing": "1.15.0", + "@temporalio/worker": "1.15.0", + "@temporalio/workflow": "1.15.0", "@types/base-64": "^1.0.0", "@types/cors": "^2.8.13", "@types/express": "^4.17.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba7a94adf1..67cb495cf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -209,26 +209,26 @@ importers: specifier: ^5.0.3 version: 5.1.1(svelte@5.46.4)(vite@6.4.1(@types/node@18.19.130)(jiti@1.21.7)(terser@5.44.1)(yaml@2.8.2)) '@temporalio/activity': - specifier: 1.14.1 - version: 1.14.1 + specifier: 1.15.0 + version: 1.15.0 '@temporalio/client': - specifier: 1.14.1 - version: 1.14.1 + specifier: 1.15.0 + version: 1.15.0 '@temporalio/common': - specifier: 1.14.1 - version: 1.14.1 + specifier: 1.15.0 + version: 1.15.0 '@temporalio/proto': - specifier: 1.14.1 - version: 1.14.1 + specifier: 1.15.0 + version: 1.15.0 '@temporalio/testing': - specifier: 1.14.1 - version: 1.14.1(esbuild@0.25.0) + specifier: 1.15.0 + version: 1.15.0(esbuild@0.25.0) '@temporalio/worker': - specifier: 1.14.1 - version: 1.14.1(esbuild@0.25.0) + specifier: 1.15.0 + version: 1.15.0(esbuild@0.25.0) '@temporalio/workflow': - specifier: 1.14.1 - version: 1.14.1 + specifier: 1.15.0 + version: 1.15.0 '@types/base-64': specifier: ^1.0.0 version: 1.0.2 @@ -1987,41 +1987,41 @@ packages: '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - '@temporalio/activity@1.14.1': - resolution: {integrity: sha512-wG2fTNgomhcKOzPY7mqhKqe8scawm4BvUYdgX1HJouHmVNRgtZurf2xQWJZQOTxWrsXfdoYqzohZLzxlNtcC5A==} - engines: {node: '>= 18.0.0'} + '@temporalio/activity@1.15.0': + resolution: {integrity: sha512-kKEIrHMTsANiEpDVZd+v3OT6kU20UtcvAK7iibJNzQnoZUGC7XnqdKQlBuGYBxgpJzD9ppGgskXRczW+XU5Kng==} + engines: {node: '>= 20.0.0'} - '@temporalio/client@1.14.1': - resolution: {integrity: sha512-AfWSA0LYzBvDLFiFgrPWqTGGq1NGnF3d4xKnxf0PGxSmv5SLb/aqQ9lzHg4DJ5UNkHO4M/NwzdxzzoaR1J5F8Q==} - engines: {node: '>= 18.0.0'} + '@temporalio/client@1.15.0': + resolution: {integrity: sha512-SxTGqRIa2+Vy4P9+06ZpUf4u7ZZmOXfx/kr9XvNqAApLxTMKjTQIg5OH5Wt4JLUtIR7dFkuHIyhewdRyG+hSsQ==} + engines: {node: '>= 20.0.0'} - '@temporalio/common@1.14.1': - resolution: {integrity: sha512-y49wOm3AIEKZufIQ/QU5JhTSaHJIEkiUt5bGB0/uSzCg8P4g8Cz0XoVPSbDwuCix533O9cOKcliYq7Gzjt/sIA==} - engines: {node: '>= 18.0.0'} + '@temporalio/common@1.15.0': + resolution: {integrity: sha512-tBfC3fdOExsNoS5krkMUXnaMtCRKj05Jts4+TH+cgHpbys68nslFvUQLqwPIw2x6155Divb9MF219a/75itbTg==} + engines: {node: '>= 20.0.0'} - '@temporalio/core-bridge@1.14.1': - resolution: {integrity: sha512-mrXXIFK5yNvsSZsTejLnL64JMuMliQjFKktSGITm2Ci7cWZ/ZTOVN6u+hCsUKfadYYv83jSuOC9Xe3z3RK273w==} - engines: {node: '>= 18.0.0'} + '@temporalio/core-bridge@1.15.0': + resolution: {integrity: sha512-Qdrs5zju5MiOwmERCWzQ6uHZwn1JaQk/ppS2UHbxqZndjbAEFPDU9KQqFLkxWAicHMy7+LGPnA4DpVOANGlTZA==} + engines: {node: '>= 20.0.0'} - '@temporalio/nexus@1.14.1': - resolution: {integrity: sha512-51oTeJ8nntAMF8boFSlzVdHlyC7y/LaLQPZMjEEOV2pi8O9yOI7GZvYDIAHhY8Z8AcDVgbXb8x0BbkjkwNiUiQ==} - engines: {node: '>= 18.0.0'} + '@temporalio/nexus@1.15.0': + resolution: {integrity: sha512-E6CdIjskkbK2aObxcb76Z4V3o1D3QDxEtsxmuHX5D7HEABuYGdV+oeOzDyxMlfeY9GyIM9Nvky4XCiSz2h2XRA==} + engines: {node: '>= 20.0.0'} - '@temporalio/proto@1.14.1': - resolution: {integrity: sha512-mCsUommDPXbXbBu60p1g4jpSqVb+GNR67yR0uKTU8ARb4qVZQo7SQnOUaneoxDERDXuR/yIjVCektMm+7Myb+A==} - engines: {node: '>= 18.0.0'} + '@temporalio/proto@1.15.0': + resolution: {integrity: sha512-Awy4Fjzyba7Pg/CVZjjQ3x2CWkDL1qELyTZWcLlyjXq8bX694JVfBsmiMmF6tHn5/ySOIxDTcc0MSScZ0oKX5A==} + engines: {node: '>= 20.0.0'} - '@temporalio/testing@1.14.1': - resolution: {integrity: sha512-iUyNHdoud5kD62LsXZWIryaPURpyaqCti3jNVaeL8nyQTmYyUXq6Trp5SuEEzbOMO7jLCoYI9cTfwzxAXy2p2g==} - engines: {node: '>= 18.0.0'} + '@temporalio/testing@1.15.0': + resolution: {integrity: sha512-OPbl9Fk969aoGAIamviL+3QT3xWY95MMzcKO+7UUBkmOJTIIbptsWY6ub5d21nmHVS4ewtlj04//dSYBD3EJYQ==} + engines: {node: '>= 20.0.0'} - '@temporalio/worker@1.14.1': - resolution: {integrity: sha512-wFfN5gc03eq1bYAuJNsG9a1iWBG6hL9zAfYbxiJdshPhpHa82BtHGvXD447oT2BX3zqI+Jf2b0m/N0wgkW6wyQ==} - engines: {node: '>= 18.0.0'} + '@temporalio/worker@1.15.0': + resolution: {integrity: sha512-9e0AWP2OxYFgeztMdkoWYbDVqmNubreRkG7/frVKFT3xHdIOrFQ2W6Yomv61q3oKMXTIrpvjClHtiTjAUr70uA==} + engines: {node: '>= 20.0.0'} - '@temporalio/workflow@1.14.1': - resolution: {integrity: sha512-MzshcoRo8zjQYa9WHrv3XC8LVvpRNSVaW3kOSTmHuTYDh/7be48WODOgs5yUpbnkpsw6rjVCDCgtB/K02cQwDg==} - engines: {node: '>= 18.0.0'} + '@temporalio/workflow@1.15.0': + resolution: {integrity: sha512-VaMhVtlA0hLLM/pna26vFSdn5W1Arq2+ccgItdbdRdZUa0X8eW2B7sl/PcUYQOWc4aMUGnvPATKUGUoFKyCxSg==} + engines: {node: '>= 20.0.0'} '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} @@ -3578,6 +3578,10 @@ packages: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.19.0: + resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} + engines: {node: '>=10.13.0'} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -7581,6 +7585,10 @@ packages: resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -7612,6 +7620,16 @@ packages: webpack-cli: optional: true + webpack@5.105.2: + resolution: {integrity: sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -9706,56 +9724,56 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@temporalio/activity@1.14.1': + '@temporalio/activity@1.15.0': dependencies: - '@temporalio/client': 1.14.1 - '@temporalio/common': 1.14.1 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 abort-controller: 3.0.0 - '@temporalio/client@1.14.1': + '@temporalio/client@1.15.0': dependencies: '@grpc/grpc-js': 1.14.3 - '@temporalio/common': 1.14.1 - '@temporalio/proto': 1.14.1 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 abort-controller: 3.0.0 long: 5.3.2 uuid: 11.1.0 - '@temporalio/common@1.14.1': + '@temporalio/common@1.15.0': dependencies: - '@temporalio/proto': 1.14.1 + '@temporalio/proto': 1.15.0 long: 5.3.2 ms: 3.0.0-canary.1 nexus-rpc: 0.0.1 proto3-json-serializer: 2.0.2 - '@temporalio/core-bridge@1.14.1': + '@temporalio/core-bridge@1.15.0': dependencies: '@grpc/grpc-js': 1.14.3 - '@temporalio/common': 1.14.1 + '@temporalio/common': 1.15.0 - '@temporalio/nexus@1.14.1': + '@temporalio/nexus@1.15.0': dependencies: - '@temporalio/client': 1.14.1 - '@temporalio/common': 1.14.1 - '@temporalio/proto': 1.14.1 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 long: 5.3.2 nexus-rpc: 0.0.1 - '@temporalio/proto@1.14.1': + '@temporalio/proto@1.15.0': dependencies: long: 5.3.2 protobufjs: 7.5.4 - '@temporalio/testing@1.14.1(esbuild@0.25.0)': + '@temporalio/testing@1.15.0(esbuild@0.25.0)': dependencies: - '@temporalio/activity': 1.14.1 - '@temporalio/client': 1.14.1 - '@temporalio/common': 1.14.1 - '@temporalio/core-bridge': 1.14.1 - '@temporalio/proto': 1.14.1 - '@temporalio/worker': 1.14.1(esbuild@0.25.0) - '@temporalio/workflow': 1.14.1 + '@temporalio/activity': 1.15.0 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/core-bridge': 1.15.0 + '@temporalio/proto': 1.15.0 + '@temporalio/worker': 1.15.0(esbuild@0.25.0) + '@temporalio/workflow': 1.15.0 abort-controller: 3.0.0 transitivePeerDependencies: - '@swc/helpers' @@ -9763,17 +9781,17 @@ snapshots: - uglify-js - webpack-cli - '@temporalio/worker@1.14.1(esbuild@0.25.0)': + '@temporalio/worker@1.15.0(esbuild@0.25.0)': dependencies: '@grpc/grpc-js': 1.14.3 '@swc/core': 1.15.5 - '@temporalio/activity': 1.14.1 - '@temporalio/client': 1.14.1 - '@temporalio/common': 1.14.1 - '@temporalio/core-bridge': 1.14.1 - '@temporalio/nexus': 1.14.1 - '@temporalio/proto': 1.14.1 - '@temporalio/workflow': 1.14.1 + '@temporalio/activity': 1.15.0 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/core-bridge': 1.15.0 + '@temporalio/nexus': 1.15.0 + '@temporalio/proto': 1.15.0 + '@temporalio/workflow': 1.15.0 abort-controller: 3.0.0 heap-js: 2.7.1 memfs: 4.51.1 @@ -9782,21 +9800,21 @@ snapshots: protobufjs: 7.5.4 rxjs: 7.8.2 source-map: 0.7.6 - source-map-loader: 4.0.2(webpack@5.104.0(@swc/core@1.15.5)(esbuild@0.25.0)) + source-map-loader: 4.0.2(webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0)) supports-color: 8.1.1 - swc-loader: 0.2.6(@swc/core@1.15.5)(webpack@5.104.0(@swc/core@1.15.5)(esbuild@0.25.0)) + swc-loader: 0.2.6(@swc/core@1.15.5)(webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0)) unionfs: 4.6.0 - webpack: 5.104.0(@swc/core@1.15.5)(esbuild@0.25.0) + webpack: 5.105.2(@swc/core@1.15.5)(esbuild@0.25.0) transitivePeerDependencies: - '@swc/helpers' - esbuild - uglify-js - webpack-cli - '@temporalio/workflow@1.14.1': + '@temporalio/workflow@1.15.0': dependencies: - '@temporalio/common': 1.14.1 - '@temporalio/proto': 1.14.1 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 nexus-rpc: 0.0.1 '@testing-library/dom@10.4.0': @@ -11503,6 +11521,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + enhanced-resolve@5.19.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -15321,11 +15344,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@4.0.2(webpack@5.104.0(@swc/core@1.15.5)(esbuild@0.25.0)): + source-map-loader@4.0.2(webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.104.0(@swc/core@1.15.5)(esbuild@0.25.0) + webpack: 5.105.2(@swc/core@1.15.5)(esbuild@0.25.0) source-map-support@0.5.13: dependencies: @@ -15750,11 +15773,11 @@ snapshots: picocolors: 1.1.1 stable: 0.1.8 - swc-loader@0.2.6(@swc/core@1.15.5)(webpack@5.104.0(@swc/core@1.15.5)(esbuild@0.25.0)): + swc-loader@0.2.6(@swc/core@1.15.5)(webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0)): dependencies: '@swc/core': 1.15.5 '@swc/counter': 0.1.3 - webpack: 5.104.0(@swc/core@1.15.5)(esbuild@0.25.0) + webpack: 5.105.2(@swc/core@1.15.5)(esbuild@0.25.0) symbol-tree@3.2.4: {} @@ -15840,6 +15863,18 @@ snapshots: '@swc/core': 1.15.5 esbuild: 0.25.0 + terser-webpack-plugin@5.3.16(@swc/core@1.15.5)(esbuild@0.25.0)(webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.1 + webpack: 5.105.2(@swc/core@1.15.5)(esbuild@0.25.0) + optionalDependencies: + '@swc/core': 1.15.5 + esbuild: 0.25.0 + terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 @@ -16379,6 +16414,11 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + web-namespaces@2.0.1: {} web-streams-polyfill@3.3.3: {} @@ -16423,6 +16463,38 @@ snapshots: - esbuild - uglify-js + webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.19.0 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.16(@swc/core@1.15.5)(esbuild@0.25.0)(webpack@5.105.2(@swc/core@1.15.5)(esbuild@0.25.0)) + watchpack: 2.5.1 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index 8e0a5c77cd..af803ec0f9 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -6,11 +6,11 @@ import Card from '$lib/holocene/card.svelte'; import Icon from '$lib/holocene/icon/icon.svelte'; import Link from '$lib/holocene/link.svelte'; - import { - type WorkerInfo, - type WorkerPollerInfo, - type WorkerSlotsInfo, - } from '$lib/services/worker-service'; + import type { + WorkerInfo, + WorkerPollerInfo, + WorkerSlotsInfo, + } from '$lib/types'; import { formatDurationAbbreviated } from '$lib/utilities/format-time'; import { routeForTaskQueue } from '$lib/utilities/route-for'; import { toWorkerStatusReadable } from '$lib/utilities/screaming-enums'; @@ -129,7 +129,7 @@ {#snippet taskSlotCard( title: string, slots: WorkerSlotsInfo, - poller: WorkerPollerInfo | null, + poller: WorkerPollerInfo, )}
diff --git a/src/lib/components/task-queue/worker-table-cell.svelte b/src/lib/components/task-queue/worker-table-cell.svelte index 53ef0c00c6..357aa05079 100644 --- a/src/lib/components/task-queue/worker-table-cell.svelte +++ b/src/lib/components/task-queue/worker-table-cell.svelte @@ -33,21 +33,14 @@ clearParameters: [attribute], }); } else { - if (query) { - const newQuery = `${query} AND ${attribute}="${value}"`; - updateQueryParameters({ - parameter: 'query', - value: newQuery, - url: page.url, - }); - } else { - const newQuery = `${attribute}="${value}"`; - updateQueryParameters({ - parameter: 'query', - value: newQuery, - url: page.url, - }); - } + const newQuery = query + ? `${query} AND ${attribute}="${value}"` + : `${attribute}="${value}"`; + updateQueryParameters({ + parameter: 'query', + value: newQuery, + url: page.url, + }); } }; diff --git a/src/lib/components/task-queue/worker-table-row.svelte b/src/lib/components/task-queue/worker-table-row.svelte index 05f44cfe9a..686515c64d 100644 --- a/src/lib/components/task-queue/worker-table-row.svelte +++ b/src/lib/components/task-queue/worker-table-row.svelte @@ -1,6 +1,6 @@ @@ -42,11 +42,11 @@ > - {#each columns as { label }} + {#each columns as { label } (label)} {label} {/each} - {#each visibleItems as worker} + {#each visibleItems as worker, i (i)} {/each} diff --git a/src/lib/components/workers/worker-status.svelte b/src/lib/components/workers/worker-status.svelte index fb3e7cf9a1..f5cec496cc 100644 --- a/src/lib/components/workers/worker-status.svelte +++ b/src/lib/components/workers/worker-status.svelte @@ -16,7 +16,6 @@ Unspecified: 'Unspecified', Running: 'Running', 'Shutting Down': 'Shutting Down', - Shutdown: 'Shutdown', }; const workerStatus = cva( @@ -29,7 +28,6 @@ Unspecified: 'bg-slate-100', Running: 'bg-blue-300', 'Shutting Down': 'bg-yellow-200', - Shutdown: 'bg-red-200', }, }, }, diff --git a/src/lib/services/worker-service.ts b/src/lib/services/worker-service.ts index 90cd9d538a..283e097479 100644 --- a/src/lib/services/worker-service.ts +++ b/src/lib/services/worker-service.ts @@ -1,101 +1,14 @@ -import type { Duration, Timestamp } from '@temporalio/common'; - -import type { WorkerStatus } from '$lib/types'; -import type { WorkerDeploymentVersion } from '$lib/types/deployments'; -import type { NamespaceScopedRequest } from '$lib/types/global'; +import type { + DescribeWorkerRequest, + DescribeWorkerResponse, + ListTaskQueuePartitionsRequest, + ListWorkersRequest, + ListWorkersResponse, + WorkerInfo, +} from '$lib/types'; import { requestFromAPI } from '$lib/utilities/request-from-api'; import { routeForApi } from '$lib/utilities/route-for-api'; -export type WorkerQueryParams = { - workerInstanceKey?: string; - workerIdentity?: string; - hostName?: string; - taskQueue?: string; - deploymentName?: string; - buildId?: string; - sdkName?: string; - sdkVersion?: string; - startTime?: Timestamp; - lastHeartbeatTime?: Timestamp; - status?: WorkerStatus; -}; - -export type ListWorkersRequest = NamespaceScopedRequest & { - query?: string; -}; - -export type ListTaskQueueWorkersRequest = NamespaceScopedRequest & { - taskQueue: string; -}; - -export type DescribeWorkerRequest = NamespaceScopedRequest & { - workerInstanceKey: string; -}; - -export type HostInfo = { - hostName: string; - workerGroupingKey: string; - processId: string; - currentHostCpuUsage: number; - currentHostMemUsage: number; -}; - -export type WorkerSlotsInfo = { - currentAvailableSlots?: number; - currentUsedSlots?: number; - slotSupplierKind?: string; - totalProcessedTasks?: number; - totalFailedTasks?: number; - lastIntervalProcessedTasks?: number; - lastIntervalFailedTasks?: number; -}; - -export type WorkerPollerInfo = { - currentPollers: number; - lastSuccessfulPollTime: Timestamp; - isAutoscaling: boolean; -}; - -export type PluginInfo = { - name: string; - version: string; -}; - -export type WorkerHeartbeat = { - workerInstanceKey: string; - workerIdentity: string; - hostInfo: HostInfo; - taskQueue: string; - deploymentVersion: WorkerDeploymentVersion; - sdkName: string; - sdkVersion: string; - status: WorkerStatus; - startTime: Timestamp; - heartbeatTime: Timestamp; - elapsedSinceLastHeartbeat: Duration; - workflowTaskSlotsInfo: WorkerSlotsInfo; - activityTaskSlotsInfo: WorkerSlotsInfo; - nexusTaskSlotsInfo: WorkerSlotsInfo; - localActivitySlotsInfo: WorkerSlotsInfo; - workflowPollerInfo: WorkerPollerInfo; - workflowStickyPollerInfo: WorkerPollerInfo; - activityPollerInfo: WorkerPollerInfo; - nexusPollerInfo: WorkerPollerInfo; - totalStickyCacheHit: number; - totalStickyCacheMiss: number; - currentStickyCacheSize: number; - plugins: PluginInfo[]; -}; - -export type WorkerInfo = { - workerHeartbeat: WorkerHeartbeat; -}; - -export type ListWorkersResponse = { - workersInfo: WorkerInfo[]; - nextPageToken?: string; -}; - type PaginatedWorkerListPromise = ( pageSize: number, token: string, @@ -127,7 +40,7 @@ export const fetchPaginatedWorkers = async ( }; export const fetchPaginatedWorkersForTaskQueue = async ( - parameters: ListTaskQueueWorkersRequest, + parameters: ListTaskQueuePartitionsRequest, request = fetch, ): Promise => { return (pageSize = 100, token = '') => { @@ -151,8 +64,6 @@ export const fetchPaginatedWorkersForTaskQueue = async ( }; }; -export type DescribeWorkerResponse = { workerInfo: WorkerInfo }; - export async function describeWorker( parameters: DescribeWorkerRequest, request = fetch, diff --git a/src/lib/types/api.ts b/src/lib/types/api.ts index 8f4cddd972..619600928a 100644 --- a/src/lib/types/api.ts +++ b/src/lib/types/api.ts @@ -161,11 +161,6 @@ export type TaskQueueRouteParameters = Pick< 'namespace' | 'queue' >; -export type WorkerRouteParameters = Pick< - APIRouteParameters, - 'namespace' | 'queue' | 'workerInstanceKey' ->; - export type ValidWorkflowEndpoints = WorkflowsAPIRoutePath; export type ValidWorkflowParameters = diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index 4ec0698e4c..94855d0a42 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -78,6 +78,16 @@ export type PauseWorkflowRequest = export type UnpauseWorkflowRequest = // temporal.api.workflowservice.v1.IUnpauseWorkflowExecutionRequest; PauseOrUnpauseWorkflowRequest; +export type ListTaskQueuePartitionsRequest = + temporal.api.workflowservice.v1.IListTaskQueuePartitionsRequest; +export type ListWorkersRequest = + temporal.api.workflowservice.v1.IListWorkersRequest; +export type ListWorkersResponse = + temporal.api.workflowservice.v1.IListWorkersResponse; +export type DescribeWorkerRequest = + temporal.api.workflowservice.v1.IDescribeWorkerRequest; +export type DescribeWorkerResponse = + temporal.api.workflowservice.v1.IDescribeWorkerResponse; // api.history @@ -301,6 +311,18 @@ export type EventLink = temporal.api.common.v1.ILink; // api.failure export type Failure = temporal.api.failure.v1.IFailure; +// api.worker +export type WorkerHostInfo = temporal.api.worker.v1.IWorkerHostInfo & { + workerGroupingKey?: string; +}; +export type WorkerHeartbeat = temporal.api.worker.v1.IWorkerHeartbeat & { + hostInfo?: WorkerHostInfo; +}; +export type WorkerPollerInfo = temporal.api.worker.v1.IWorkerPollerInfo; +export type WorkerSlotsInfo = temporal.api.worker.v1.IWorkerSlotsInfo; +export type WorkerInfo = temporal.api.worker.v1.IWorkerInfo; +export type PluginInfo = temporal.api.worker.v1.PluginInfo; + // google export type Timestamp = google.protobuf.ITimestamp; diff --git a/src/lib/utilities/route-for-api.ts b/src/lib/utilities/route-for-api.ts index 0e8b6134e4..b8e724a400 100644 --- a/src/lib/utilities/route-for-api.ts +++ b/src/lib/utilities/route-for-api.ts @@ -3,6 +3,7 @@ import { get } from 'svelte/store'; import { resolve } from '$app/paths'; import { page } from '$app/stores'; +import type { DescribeWorkerRequest } from '$lib/types'; import type { APIRouteParameters, APIRoutePath, @@ -32,7 +33,6 @@ import type { WorkerDeploymentsAPIRoutePath, WorkerDeploymentVersionAPIRoutePath, WorkerDeploymentVersionRouteParameters, - WorkerRouteParameters, WorkflowActivitiesAPIRoutePath, WorkflowActivitiesRouteParameters, WorkflowAPIRoutePath, @@ -215,7 +215,7 @@ export function routeForApi( ): string; export function routeForApi( route: WorkerAPIRoutePath, - parameters: NamespaceRouteParameters | WorkerRouteParameters, + parameters: DescribeWorkerRequest, shouldEncode?: boolean, ): string; export function routeForApi( diff --git a/src/lib/utilities/screaming-enums.ts b/src/lib/utilities/screaming-enums.ts index e3e0b6549c..8de7139654 100644 --- a/src/lib/utilities/screaming-enums.ts +++ b/src/lib/utilities/screaming-enums.ts @@ -100,11 +100,7 @@ export const toCallbackStateReadable = ( return fromScreamingEnum(state, 'CallbackState'); }; -export type ReadableWorkerStatus = - | 'Unspecified' - | 'Running' - | 'Shutting Down' - | 'Shutdown'; +export type ReadableWorkerStatus = 'Unspecified' | 'Running' | 'Shutting Down'; export const toWorkerStatusReadable = ( state?: WorkerStatus, diff --git a/src/lib/utilities/update-query-parameters.ts b/src/lib/utilities/update-query-parameters.ts index f774783435..2728579f98 100644 --- a/src/lib/utilities/update-query-parameters.ts +++ b/src/lib/utilities/update-query-parameters.ts @@ -32,7 +32,6 @@ export const updateQueryParameters = async ({ const params = {}; let replaced = false; - console.log('Updating query parameter:'); url.searchParams.forEach((value, key) => { if (key !== parameter) { params[key] = value; diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 5b5b3ad2f4..347647ab5f 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -247,16 +247,6 @@ } } - // Handle workers page - if (page.url.pathname.endsWith('workers')) { - return routeForWorkers({ namespace }); - } - - // Handle workers page - if (page.url.pathname.includes('workers')) { - return routeForWorkers({ namespace }); - } - return routeForWorkflows({ namespace }); } diff --git a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte index 5b1423009d..266a412b77 100644 --- a/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte @@ -16,15 +16,16 @@ const workersHref = $derived(routeForWorkers({ namespace })); const deploymentsHref = $derived(routeForWorkerDeployments({ namespace })); - - const pageTitle = $derived(translate('deployments.worker-deployments')); - +

- {pageTitle} + {translate('deployments.worker-deployments')}

diff --git a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte index 2f34f7efbc..6d43a1e512 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte @@ -16,15 +16,13 @@ const workersHref = $derived(routeForWorkers({ namespace })); const deploymentsHref = $derived(routeForWorkerDeployments({ namespace })); - - const pageTitle = $derived(translate('workers.workers')); - +

- {pageTitle} + {translate('workers.workers')}

From 004bf4d5a076e1e87d9360ffd3889b306c87d398 Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Wed, 4 Mar 2026 13:35:31 -0700 Subject: [PATCH 10/14] Small fixes and improvements --- .../components/task-queue/worker-info.svelte | 148 ++++++++++-------- .../task-queue/worker-table-cell.svelte | 6 +- .../task-queue/worker-table-row.svelte | 108 +++++-------- .../components/task-queue/worker-table.svelte | 27 ++-- .../task-queue/worker-totals.svelte | 85 ++++------ .../workers/worker-search-table.svelte | 19 +-- src/lib/i18n/locales/en/common.ts | 2 + src/lib/i18n/locales/en/workers.ts | 28 +++- src/lib/services/worker-service.ts | 3 +- src/lib/types/index.ts | 2 - src/lib/utilities/route-for.ts | 7 +- .../+page.svelte | 8 +- 12 files changed, 224 insertions(+), 219 deletions(-) rename src/routes/(app)/namespaces/[namespace]/workers/{[instance] => [workerInstanceKey]}/+page.svelte (84%) diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index af803ec0f9..2af894d5d6 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -6,6 +6,7 @@ import Card from '$lib/holocene/card.svelte'; import Icon from '$lib/holocene/icon/icon.svelte'; import Link from '$lib/holocene/link.svelte'; + import { translate } from '$lib/i18n/translate'; import type { WorkerInfo, WorkerPollerInfo, @@ -26,7 +27,7 @@ const { namespace } = $derived(page.params); const heartbeat = $derived(worker.workerHeartbeat); - const status = $derived(toWorkerStatusReadable(heartbeat.status)); + const status = $derived(toWorkerStatusReadable(heartbeat?.status)); const totalStickyCacheHit = $derived(heartbeat?.totalStickyCacheHit ?? 0); const totalStickyCacheMiss = $derived(heartbeat?.totalStickyCacheMiss ?? 0); @@ -34,7 +35,7 @@ heartbeat?.currentStickyCacheSize ?? 0, ); - const cacheHitRate = $derived(() => { + const cacheHitRate = $derived.by(() => { const total = totalStickyCacheHit + totalStickyCacheMiss; if (total === 0) return 0; return ((totalStickyCacheHit / total) * 100).toFixed(1); @@ -45,49 +46,51 @@
-

{heartbeat.workerIdentity}

+

{heartbeat?.workerIdentity}

- Task Queue + {translate('common.task-queue')} - {heartbeat.taskQueue} + {heartbeat?.taskQueue}
- Start + {translate('common.start')}
- Last Heartbeat + {translate('workflows.last-heartbeat')}
{formatDurationAbbreviated( - String(heartbeat.elapsedSinceLastHeartbeat), + String(heartbeat?.elapsedSinceLastHeartbeat), )} ago
- SDK + {translate('workers.sdk')}
@@ -96,23 +99,23 @@
{@render taskSlotCard( - 'Workflow', - heartbeat.workflowTaskSlotsInfo, - heartbeat.workflowPollerInfo, + translate('common.workflows-plural', { count: 1 }), + heartbeat?.workflowTaskSlotsInfo, + heartbeat?.workflowPollerInfo, )} {@render taskSlotCard( - 'Activity', - heartbeat.activityTaskSlotsInfo, - heartbeat.activityPollerInfo, + translate('common.activities-plural', { count: 1 }), + heartbeat?.activityTaskSlotsInfo, + heartbeat?.activityPollerInfo, )} {@render taskSlotCard( - 'Nexus Tasks', - heartbeat.nexusTaskSlotsInfo, - heartbeat.nexusPollerInfo, + translate('workers.nexus-tasks'), + heartbeat?.nexusTaskSlotsInfo, + heartbeat?.nexusPollerInfo, )} {@render taskSlotCard( - 'Local Activities', - heartbeat.localActivitySlotsInfo, + translate('workers.local-activities'), + heartbeat?.localActivitySlotsInfo, null, )}
@@ -139,7 +142,7 @@
-
Slots
+
{translate('workers.slots')}

{slots.currentUsedSlots ?? 0} @@ -153,18 +156,23 @@

-

Used

+

{translate('workers.used')}

{#if slots.currentAvailableSlots} - Available out of {slots.currentAvailableSlots} - {:else}None Available + {translate('workers.available-out-of', { + count: slots.currentAvailableSlots, + })} + {:else} + {translate('workers.none-available')} {/if}

-
Tasks Processed
+
+ {translate('workers.tasks-processed')} +
{(slots.totalProcessedTasks ?? 0).toLocaleString()} @@ -183,12 +191,10 @@
{#if poller.lastSuccessfulPollTime} - Last Poll + {translate('workers.last-poll')} + {:else} - No Activity + {translate('workers.no-activity')} {/if}
@@ -199,28 +205,30 @@ {#snippet hostInfo()} -

Host Info

+

{translate('workers.host-info')}

- Host Name + {translate('workers.host-name')} - {heartbeat.hostInfo.hostName} + {heartbeat?.hostInfo?.hostName}
- Process ID - {heartbeat.hostInfo.processId} + {translate('workers.process-id')} + {heartbeat?.hostInfo?.processId}
-
- Instance Key +
+ {translate('workers.instance')} - {heartbeat.workerInstanceKey} + {heartbeat?.workerInstanceKey}
- Worker Grouping Key + {translate('workers.worker-grouping')} - {heartbeat.hostInfo.workerGroupingKey} + {heartbeat?.hostInfo?.workerGroupingKey}
@@ -230,22 +238,22 @@ {#snippet hostUsage()}
-

Host Usage

+

{translate('workers.host-usage')}

- CPU Usage + {translate('workers.cpu-usage')} - {heartbeat.hostInfo.currentHostCpuUsage.toFixed(0)}% + {heartbeat?.hostInfo?.currentHostCpuUsage.toFixed(0)}%
@@ -255,15 +263,15 @@
- Memory Usage + {translate('workers.memory-usage')} - {heartbeat.hostInfo.currentHostMemUsage.toFixed(0)}% + {heartbeat?.hostInfo?.currentHostMemUsage.toFixed(0)}%
@@ -275,43 +283,59 @@ {#snippet workflowCache()} -

Workflow Cache

+

+ {translate('workers.workflow-cache')} +

{currentStickyCacheSize.toLocaleString()} -
Cache size
+
+ {translate('workers.cache-size')} +
- {cacheHitRate()}% + {cacheHitRate}% -
Cache hits
+
+ {translate('workers.cache-hits')} +
{totalStickyCacheHit.toLocaleString()} -
Active thread count
+
+ {translate('workers.active-thread-count')} +
{/snippet} {#snippet diagnostics()} -

Diagnostics

+

+ {translate('workers.diagnostics')} +

- {cacheHitRate()}% + {cacheHitRate}% -
Poll success rate
+
+ {translate('workers.poll-success-rate')} +
- None -
Rate limit
+ {translate('common.none')} +
+ {translate('workers.rate-limit')} +
diff --git a/src/lib/components/task-queue/worker-table-cell.svelte b/src/lib/components/task-queue/worker-table-cell.svelte index 357aa05079..b16614216a 100644 --- a/src/lib/components/task-queue/worker-table-cell.svelte +++ b/src/lib/components/task-queue/worker-table-cell.svelte @@ -8,13 +8,14 @@ import { translate } from '$lib/i18n/translate'; import { updateQueryParameters } from '$lib/utilities/update-query-parameters'; - type Props = { + interface Props { attribute?: string; value: string; filterable?: boolean; href?: string; children?: Snippet; - }; + } + let { attribute, value, @@ -22,6 +23,7 @@ href, children, }: Props = $props(); + const query = $derived(page.url.searchParams.get('query') || ''); const onRowFilterClick = () => { diff --git a/src/lib/components/task-queue/worker-table-row.svelte b/src/lib/components/task-queue/worker-table-row.svelte index 686515c64d..0b4f2b901e 100644 --- a/src/lib/components/task-queue/worker-table-row.svelte +++ b/src/lib/components/task-queue/worker-table-row.svelte @@ -1,5 +1,4 @@ - {#each columns as { label } (label)} - {#if label === translate('workers.identity')} - - {:else if label === translate('workers.task-queue')} - - {:else if label === translate('workers.host-name')} - - {:else if label === translate('workers.instance')} - - {:else if label === translate('workers.status')} - - - - {:else if label === translate('workers.sdk')} - - - - {:else if label === translate('workers.workflow-task-slots')} - {worker.workerHeartbeat?.workflowTaskSlotsInfo?.currentUsedSlots ?? 0} / - {worker.workerHeartbeat?.workflowTaskSlotsInfo?.currentAvailableSlots ?? - 0} - {:else if label === translate('workers.activity-task-slots')} - {worker.workerHeartbeat?.activityTaskSlotsInfo?.currentUsedSlots ?? 0} / - {worker.workerHeartbeat?.activityTaskSlotsInfo?.currentAvailableSlots ?? - 0} - {:else if label === translate('workers.nexus-task-slots')} - {worker.workerHeartbeat?.nexusTaskSlotsInfo?.currentUsedSlots ?? 0} / {worker - .workerHeartbeat?.nexusTaskSlotsInfo?.currentAvailableSlots ?? 0} - {/if} - {/each} + + + + + + + + + + diff --git a/src/lib/components/task-queue/worker-table.svelte b/src/lib/components/task-queue/worker-table.svelte index 0f318703be..da47c52e8a 100644 --- a/src/lib/components/task-queue/worker-table.svelte +++ b/src/lib/components/task-queue/worker-table.svelte @@ -9,19 +9,22 @@ import WorkerTableRow from './worker-table-row.svelte'; - let { namespace, taskQueue } = $props(); + interface Props { + namespace: string; + taskQueue: string; + } + + let { namespace, taskQueue }: Props = $props(); let error: string | null = $state(null); - const columns = $derived([ + const columns = [ { label: translate('workers.status') }, { label: translate('workers.instance') }, + { label: translate('workers.task-queue') }, { label: translate('workers.identity') }, { label: translate('workers.host-name') }, - { label: translate('workers.workflow-task-slots') }, - { label: translate('workers.activity-task-slots') }, - { label: translate('workers.nexus-task-slots') }, { label: translate('workers.sdk') }, - ]); + ]; const onFetch = $derived(() => fetchPaginatedWorkersForTaskQueue({ namespace, taskQueue }), @@ -44,17 +47,17 @@ emptyStateMessage={translate('workers.empty-state-title')} errorMessage={translate('workers.error-message-fetching')} > - {translate('workers.workers')} + + {translate('workers.workers')} + - {#each columns as { label }} + {#each columns as { label } (label)} {label} {/each} - {#each visibleItems as worker} - + {#each visibleItems as worker (worker.workerHeartbeat.workerInstanceKey)} + {/each} diff --git a/src/lib/components/task-queue/worker-totals.svelte b/src/lib/components/task-queue/worker-totals.svelte index 0fa31d7466..d035fed777 100644 --- a/src/lib/components/task-queue/worker-totals.svelte +++ b/src/lib/components/task-queue/worker-totals.svelte @@ -1,84 +1,65 @@
- Active Workers + {translate('workers.active-workers')}
-
{totalActive}
+
{active}
- Running + + {translate('workflows.running')} +
- Inactive Workers + {translate('workers.inactive-workers')}
-
{totalInactive}
+
{inactive}
- Inactive + + {translate('common.inactive')} +
- Total Processed Tasks + {translate('workers.total-processed-tasks')}
-
{totalProcessedTasks}
+
{totalTasks}
- {workflowProcessedTasks} workflow · {activityProcessedTasks} activity + {workflowTasks} + {translate('common.workflows-plural', { count: 1 })} · {activityTasks} + {translate('common.activities-plural', { count: 1 })}
diff --git a/src/lib/components/workers/worker-search-table.svelte b/src/lib/components/workers/worker-search-table.svelte index c0edded95f..5c5db516ed 100644 --- a/src/lib/components/workers/worker-search-table.svelte +++ b/src/lib/components/workers/worker-search-table.svelte @@ -7,7 +7,11 @@ import { translate } from '$lib/i18n/translate'; import { fetchPaginatedWorkers } from '$lib/services/worker-service'; - let { namespace } = $props(); + interface Props { + namespace: string; + } + + let { namespace }: Props = $props(); const query = $derived(page.url.searchParams.get('query') || ''); @@ -17,9 +21,6 @@ { label: translate('workers.task-queue') }, { label: translate('workers.identity') }, { label: translate('workers.host-name') }, - { label: translate('workers.workflow-task-slots') }, - { label: translate('workers.activity-task-slots') }, - { label: translate('workers.nexus-task-slots') }, { label: translate('workers.sdk') }, ]; @@ -37,17 +38,17 @@ emptyStateMessage={translate('workers.empty-state-title')} errorMessage={translate('workers.error-message-fetching')} > - {translate('workers.workers')} + + {translate('workers.workers')} + {#each columns as { label } (label)} {label} {/each} - {#each visibleItems as worker, i (i)} - + {#each visibleItems as worker (worker.workerHeartbeat.workerInstanceKey)} + {/each} diff --git a/src/lib/i18n/locales/en/common.ts b/src/lib/i18n/locales/en/common.ts index a65ec46161..382d12d964 100644 --- a/src/lib/i18n/locales/en/common.ts +++ b/src/lib/i18n/locales/en/common.ts @@ -108,6 +108,8 @@ export const Strings = { workflows: 'Workflows', 'workflows-plural_one': 'Workflow', 'workflows-plural_other': 'Workflows', + 'activities-plural_one': 'Activity', + 'activities-plural_other': 'Activities', schedules: 'Schedules', 'schedules-plural_one': '{{ count }} Schedule', 'schedules-plural_other': '{{ count }} Schedules', diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 87ef81221b..13d3fc60d2 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -20,9 +20,6 @@ export const Strings = { 'task-queue': 'Task Queue', status: 'Status', sdk: 'SDK', - 'workflow-task-slots': 'Workflow Slots', - 'activity-task-slots': 'Activity Slots', - 'nexus-task-slots': 'Nexus Slots', 'empty-state-title': 'No Workers Found', 'error-message-fetching': 'An error occurred while fetching workers.', 'compatible-build-ids': 'Compatible Build IDs', @@ -41,4 +38,29 @@ export const Strings = { 'Viewing workers for pinned Build ID. Go to the Task Queue page to view all workers.', 'viewing-auto-upgrade-build-ids': 'Viewing workers for current and ramping Build IDs. Go to the Task Queue page to view all workers.', + 'active-workers': 'Active Workers', + 'inactive-workers': 'Inactive Workers', + 'total-processed-tasks': 'Total Processed Tasks', + 'nexus-tasks': 'Nexus Tasks', + 'local-activities': 'Local Activities', + slots: 'Slots', + used: 'Used', + 'available-out-of': 'Available out of {count}', + 'none-available': 'None Available', + 'tasks-processed': 'Tasks Processed', + 'last-poll': 'Last Poll', + 'no-activity': 'No Activity', + 'host-info': 'Host Info', + 'process-id': 'Process ID', + 'worker-grouping': 'Worker Grouping', + 'host-usage': 'Host Usage', + 'cpu-usage': 'CPU Usage', + 'memory-usage': 'Memory Usage', + 'workflow-cache': 'Workflow Cache', + 'cache-size': 'Cache size', + 'cache-hits': 'Cache hits', + 'active-thread-count': 'Active thread count', + diagnostics: 'Diagnostics', + 'poll-success-rate': 'Poll success rate', + 'rate-limit': 'Rate limit', } as const; diff --git a/src/lib/services/worker-service.ts b/src/lib/services/worker-service.ts index 283e097479..8d3c3efbd7 100644 --- a/src/lib/services/worker-service.ts +++ b/src/lib/services/worker-service.ts @@ -1,7 +1,6 @@ import type { DescribeWorkerRequest, DescribeWorkerResponse, - ListTaskQueuePartitionsRequest, ListWorkersRequest, ListWorkersResponse, WorkerInfo, @@ -40,7 +39,7 @@ export const fetchPaginatedWorkers = async ( }; export const fetchPaginatedWorkersForTaskQueue = async ( - parameters: ListTaskQueuePartitionsRequest, + parameters: ListWorkersRequest & { taskQueue: string }, request = fetch, ): Promise => { return (pageSize = 100, token = '') => { diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index dc9b76c2c2..cd35cfe894 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -49,8 +49,6 @@ export type PauseWorkflowRequest = temporal.api.workflowservice.v1.IPauseWorkflowExecutionRequest; export type UnpauseWorkflowRequest = temporal.api.workflowservice.v1.IUnpauseWorkflowExecutionRequest; -export type ListTaskQueuePartitionsRequest = - temporal.api.workflowservice.v1.IListTaskQueuePartitionsRequest; export type ListWorkersRequest = temporal.api.workflowservice.v1.IListWorkersRequest; export type ListWorkersResponse = diff --git a/src/lib/utilities/route-for.ts b/src/lib/utilities/route-for.ts index 7d8d49e94b..40a3969978 100644 --- a/src/lib/utilities/route-for.ts +++ b/src/lib/utilities/route-for.ts @@ -303,16 +303,13 @@ export const routeForTimeline = ({ export const routeForWorkers = ( parameters: NamespaceParameter, ): ResolvedPathname => { - return resolve( - `${routeForNamespace({ namespace: parameters.namespace })}/workers`, - {}, - ); + return `${routeForNamespace({ namespace: parameters.namespace })}/workers`; }; export const routeForWorkflowWorkers = ( parameters: WorkflowParameters, ): ResolvedPathname => { - return resolve(`${routeForWorkflow(parameters)}/workers`, {}); + return `${routeForWorkflow(parameters)}/workers`; }; export const routeForWorkerDeployments = ({ diff --git a/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte similarity index 84% rename from src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte rename to src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte index d3eaa0ac75..c282389472 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/[instance]/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte @@ -3,6 +3,7 @@ import PageTitle from '$lib/components/page-title.svelte'; import WorkerInfo from '$lib/components/task-queue/worker-info.svelte'; + import Error from '$lib/holocene/error.svelte'; import Link from '$lib/holocene/link.svelte'; import { translate } from '$lib/i18n/translate'; import { describeWorker } from '$lib/services/worker-service'; @@ -12,7 +13,10 @@ const workerInstanceKey = $derived(page.params.instance); - +
@@ -30,5 +34,5 @@ {#await describeWorker({ namespace, workerInstanceKey }) then data} {:catch error} -

Error loading worker info: {error.message}

+ {/await} From cddca19ffb95d6f4d96b0438013aa3ae1d5c0d57 Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Wed, 4 Mar 2026 14:15:51 -0700 Subject: [PATCH 11/14] Add filtering to workers table --- .../lines-and-dots/workflow-details.svelte | 2 +- .../filter-bar.svelte | 56 ++++++ .../filter-list.svelte} | 84 +++++---- .../filter.svelte | 45 +++-- .../manual-query.svelte | 37 ++-- .../search-attribute-menu.svelte | 48 ++--- .../status-filter-chip.svelte | 164 ++++++++++++++++++ .../shared-search-attribute-filter/types.ts | 1 + .../filter-bar.svelte | 51 ++---- .../components/task-queue/worker-info.svelte | 3 +- .../task-queue/worker-insights.svelte | 4 +- .../components/task-queue/worker-table.svelte | 71 -------- .../workers/worker-search-table.svelte | 58 ------- .../components/workers/worker-status.svelte | 19 +- .../task-queue-workers-table.svelte | 33 ++++ .../workers-table/workers-table-cell.svelte} | 5 +- .../workers-table/workers-table-row.svelte} | 26 ++- .../workers-table-with-search.svelte | 20 +++ .../workers-table/workers-table.svelte | 58 +++++++ src/lib/i18n/locales/en/workers.ts | 2 +- src/lib/i18n/locales/en/workflows.ts | 1 + src/lib/models/worker-status.ts | 13 ++ src/lib/services/worker-service.ts | 33 +--- src/lib/stores/filters.ts | 15 ++ src/lib/stores/search-attributes.ts | 26 ++- src/lib/utilities/get-sdk-version.ts | 17 +- src/lib/utilities/screaming-enums.ts | 3 +- .../[namespace]/workers/+page.svelte | 27 ++- 28 files changed, 604 insertions(+), 318 deletions(-) create mode 100644 src/lib/components/shared-search-attribute-filter/filter-bar.svelte rename src/lib/components/{standalone-activities/activities-summary-filter-bar/dropdown-filter-list.svelte => shared-search-attribute-filter/filter-list.svelte} (50%) rename src/lib/components/{standalone-activities/activities-summary-filter-bar => shared-search-attribute-filter}/filter.svelte (55%) rename src/lib/components/{standalone-activities/activities-summary-filter-bar => shared-search-attribute-filter}/manual-query.svelte (75%) rename src/lib/components/{standalone-activities/activities-summary-filter-bar => shared-search-attribute-filter}/search-attribute-menu.svelte (74%) create mode 100644 src/lib/components/shared-search-attribute-filter/status-filter-chip.svelte create mode 100644 src/lib/components/shared-search-attribute-filter/types.ts delete mode 100644 src/lib/components/task-queue/worker-table.svelte delete mode 100644 src/lib/components/workers/worker-search-table.svelte create mode 100644 src/lib/components/workers/workers-table/task-queue-workers-table.svelte rename src/lib/components/{task-queue/worker-table-cell.svelte => workers/workers-table/workers-table-cell.svelte} (96%) rename src/lib/components/{task-queue/worker-table-row.svelte => workers/workers-table/workers-table-row.svelte} (70%) create mode 100644 src/lib/components/workers/workers-table/workers-table-with-search.svelte create mode 100644 src/lib/components/workers/workers-table/workers-table.svelte create mode 100644 src/lib/models/worker-status.ts diff --git a/src/lib/components/lines-and-dots/workflow-details.svelte b/src/lib/components/lines-and-dots/workflow-details.svelte index c5c7e03698..a07d1fa818 100644 --- a/src/lib/components/lines-and-dots/workflow-details.svelte +++ b/src/lib/components/lines-and-dots/workflow-details.svelte @@ -305,7 +305,7 @@ {/if} {#if sdk && sdkVersion} - SDK + {translate('workflows.sdk')} diff --git a/src/lib/components/shared-search-attribute-filter/filter-bar.svelte b/src/lib/components/shared-search-attribute-filter/filter-bar.svelte new file mode 100644 index 0000000000..f1d5dd0e85 --- /dev/null +++ b/src/lib/components/shared-search-attribute-filter/filter-bar.svelte @@ -0,0 +1,56 @@ + + +
+
+
+ + +
+
+ +
+
+ {#if viewManualQuery} + + {/if} +
diff --git a/src/lib/components/standalone-activities/activities-summary-filter-bar/dropdown-filter-list.svelte b/src/lib/components/shared-search-attribute-filter/filter-list.svelte similarity index 50% rename from src/lib/components/standalone-activities/activities-summary-filter-bar/dropdown-filter-list.svelte rename to src/lib/components/shared-search-attribute-filter/filter-list.svelte index 6db1c9e76c..6a5050decd 100644 --- a/src/lib/components/standalone-activities/activities-summary-filter-bar/dropdown-filter-list.svelte +++ b/src/lib/components/shared-search-attribute-filter/filter-list.svelte @@ -1,47 +1,59 @@
- - + +
diff --git a/src/lib/components/standalone-activities/activities-summary-filter-bar/manual-query.svelte b/src/lib/components/shared-search-attribute-filter/manual-query.svelte similarity index 75% rename from src/lib/components/standalone-activities/activities-summary-filter-bar/manual-query.svelte rename to src/lib/components/shared-search-attribute-filter/manual-query.svelte index 1221aa6f23..701649a394 100644 --- a/src/lib/components/standalone-activities/activities-summary-filter-bar/manual-query.svelte +++ b/src/lib/components/shared-search-attribute-filter/manual-query.svelte @@ -1,4 +1,5 @@
-
-{/snippet} - -
-
-
- - -
- {@render actionToggleButtons()} -
- {#if viewManualQuery} - - {/if} -
+ diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index 2af894d5d6..7f5bc0fa1c 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -13,6 +13,7 @@ WorkerSlotsInfo, } from '$lib/types'; import { formatDurationAbbreviated } from '$lib/utilities/format-time'; + import { formatSDKName } from '$lib/utilities/get-sdk-version'; import { routeForTaskQueue } from '$lib/utilities/route-for'; import { toWorkerStatusReadable } from '$lib/utilities/screaming-enums'; @@ -89,7 +90,7 @@
{translate('workers.sdk')}
diff --git a/src/lib/components/task-queue/worker-insights.svelte b/src/lib/components/task-queue/worker-insights.svelte index 1f31b2f976..aba0638192 100644 --- a/src/lib/components/task-queue/worker-insights.svelte +++ b/src/lib/components/task-queue/worker-insights.svelte @@ -1,11 +1,11 @@ - + diff --git a/src/lib/components/task-queue/worker-table.svelte b/src/lib/components/task-queue/worker-table.svelte deleted file mode 100644 index da47c52e8a..0000000000 --- a/src/lib/components/task-queue/worker-table.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - -{#if !error} - - - {translate('workers.workers')} - - - - {#each columns as { label } (label)} - {label} - {/each} - - {#each visibleItems as worker (worker.workerHeartbeat.workerInstanceKey)} - - {/each} - - - - - -{:else} - {#await getPollers({ queue: taskQueue, namespace }) then workers} - - {/await} -{/if} diff --git a/src/lib/components/workers/worker-search-table.svelte b/src/lib/components/workers/worker-search-table.svelte deleted file mode 100644 index 5c5db516ed..0000000000 --- a/src/lib/components/workers/worker-search-table.svelte +++ /dev/null @@ -1,58 +0,0 @@ - - -{#key query} - - - {translate('workers.workers')} - - - - {#each columns as { label } (label)} - {label} - {/each} - - {#each visibleItems as worker (worker.workerHeartbeat.workerInstanceKey)} - - {/each} - - - - - -{/key} diff --git a/src/lib/components/workers/worker-status.svelte b/src/lib/components/workers/worker-status.svelte index f5cec496cc..eadc2815a3 100644 --- a/src/lib/components/workers/worker-status.svelte +++ b/src/lib/components/workers/worker-status.svelte @@ -2,20 +2,20 @@ import { cva } from 'class-variance-authority'; import HeartBeat from '$lib/components/heart-beat-indicator.svelte'; - import type { ReadableWorkerStatus } from '$lib/utilities/screaming-enums'; + import { translate } from '$lib/i18n/translate'; + import type { WorkerStatus } from '$lib/models/worker-status'; interface Props { delay?: number; - status?: ReadableWorkerStatus; - 'test-id'?: string; + status?: WorkerStatus; } - let { delay = 0, status = 'Running', 'test-id': testId }: Props = $props(); + let { delay = 0, status = 'Running' }: Props = $props(); - const label: Record = { - Unspecified: 'Unspecified', - Running: 'Running', + const label: Record = { + Running: translate('workflows.running'), 'Shutting Down': 'Shutting Down', + Unspecified: translate('events.event-classification.unspecified'), }; const workerStatus = cva( @@ -36,10 +36,7 @@ const isRunning = $derived(status === 'Running'); -
+
{label[status]} {#if isRunning} diff --git a/src/lib/components/workers/workers-table/task-queue-workers-table.svelte b/src/lib/components/workers/workers-table/task-queue-workers-table.svelte new file mode 100644 index 0000000000..96fe3fc2bd --- /dev/null +++ b/src/lib/components/workers/workers-table/task-queue-workers-table.svelte @@ -0,0 +1,33 @@ + + +{#if useFallback} + {#await getPollers({ queue: taskQueue, namespace }) then workers} + + {/await} +{:else} + +{/if} diff --git a/src/lib/components/task-queue/worker-table-cell.svelte b/src/lib/components/workers/workers-table/workers-table-cell.svelte similarity index 96% rename from src/lib/components/task-queue/worker-table-cell.svelte rename to src/lib/components/workers/workers-table/workers-table-cell.svelte index b16614216a..7ce99217e0 100644 --- a/src/lib/components/task-queue/worker-table-cell.svelte +++ b/src/lib/components/workers/workers-table/workers-table-cell.svelte @@ -10,7 +10,8 @@ interface Props { attribute?: string; - value: string; + copyable?: boolean; + value?: string; filterable?: boolean; href?: string; children?: Snippet; @@ -18,6 +19,7 @@ let { attribute, + copyable = true, value, filterable = false, href, @@ -83,6 +85,7 @@ show={filterOrCopyButtonsVisible} content={value} onFilter={onRowFilterClick} + {copyable} {filterable} filtered={query.includes(`${attribute}=`)} /> diff --git a/src/lib/components/task-queue/worker-table-row.svelte b/src/lib/components/workers/workers-table/workers-table-row.svelte similarity index 70% rename from src/lib/components/task-queue/worker-table-row.svelte rename to src/lib/components/workers/workers-table/workers-table-row.svelte index 0b4f2b901e..2c73ec3326 100644 --- a/src/lib/components/task-queue/worker-table-row.svelte +++ b/src/lib/components/workers/workers-table/workers-table-row.svelte @@ -1,12 +1,12 @@ + +{#key query} + +{/key} diff --git a/src/lib/components/workers/workers-table/workers-table.svelte b/src/lib/components/workers/workers-table/workers-table.svelte new file mode 100644 index 0000000000..64298ddac0 --- /dev/null +++ b/src/lib/components/workers/workers-table/workers-table.svelte @@ -0,0 +1,58 @@ + + + + + {translate('workers.workers')} + + + + {#each columns as { label } (label)} + {label} + {/each} + + {#each visibleItems as worker (worker.workerHeartbeat.workerInstanceKey)} + + {/each} + + + + + diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 13d3fc60d2..4234322f3a 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -19,7 +19,7 @@ export const Strings = { instance: 'Instance', 'task-queue': 'Task Queue', status: 'Status', - sdk: 'SDK', + sdk: 'Worker SDK', 'empty-state-title': 'No Workers Found', 'error-message-fetching': 'An error occurred while fetching workers.', 'compatible-build-ids': 'Compatible Build IDs', diff --git a/src/lib/i18n/locales/en/workflows.ts b/src/lib/i18n/locales/en/workflows.ts index d06ea88181..3b920e0708 100644 --- a/src/lib/i18n/locales/en/workflows.ts +++ b/src/lib/i18n/locales/en/workflows.ts @@ -348,4 +348,5 @@ export const Strings = { 'timeline-minimized': 'Timeline and Event History are collapsed to minimized height', 'timeline-expanded': 'Timeline and Event History are expanded to full height', + sdk: 'Workflow SDK', } as const; diff --git a/src/lib/models/worker-status.ts b/src/lib/models/worker-status.ts new file mode 100644 index 0000000000..28892d4024 --- /dev/null +++ b/src/lib/models/worker-status.ts @@ -0,0 +1,13 @@ +export type WorkerStatus = 'Unspecified' | 'Running' | 'Shutting Down' | null; + +export type WorkerFilters = readonly (WorkerStatus | 'All')[]; + +export const workerStatuses: readonly WorkerStatus[] = [ + 'Running', + 'Shutting Down', +] as const; + +export const workerStatusFilters: WorkerFilters = [ + 'All', + ...workerStatuses, +] as const; diff --git a/src/lib/services/worker-service.ts b/src/lib/services/worker-service.ts index 8d3c3efbd7..db70f87f42 100644 --- a/src/lib/services/worker-service.ts +++ b/src/lib/services/worker-service.ts @@ -27,36 +27,11 @@ export const fetchPaginatedWorkers = async ( query: parameters.query, }, }).then(({ workersInfo, nextPageToken }) => { - if (!workersInfo) { - throw new Error('No workers info in response'); - } + // if (!workersInfo) { + // throw new Error('No workers info in response'); + // } return { - items: workersInfo, - nextPageToken: nextPageToken ? String(nextPageToken) : '', - }; - }); - }; -}; - -export const fetchPaginatedWorkersForTaskQueue = async ( - parameters: ListWorkersRequest & { taskQueue: string }, - request = fetch, -): Promise => { - return (pageSize = 100, token = '') => { - const route = routeForApi('workers', parameters); - return requestFromAPI(route, { - request, - params: { - query: `TaskQueue="${parameters.taskQueue}"`, - maximumPageSize: String(pageSize), - nextPageToken: token, - }, - }).then(({ workersInfo, nextPageToken }) => { - if (!workersInfo) { - throw new Error('No workers info in response'); - } - return { - items: workersInfo, + items: workersInfo ?? [], nextPageToken: nextPageToken ? String(nextPageToken) : '', }; }); diff --git a/src/lib/stores/filters.ts b/src/lib/stores/filters.ts index 7eaccadd83..46c125b439 100644 --- a/src/lib/stores/filters.ts +++ b/src/lib/stores/filters.ts @@ -84,6 +84,21 @@ export const activityFilters = writable( updateActivityFilters, ); +const updateWorkerFilters: StartStopNotifier = ( + set, +) => { + return parameters.subscribe(({ query }) => { + if (!query && get(workerFilters).length) { + set([]); + } + }); +}; + +export const workerFilters = writable( + [], + updateWorkerFilters, +); + const updateEventCategoryFilter: StartStopNotifier< EventTypeCategory[] | null > = (set) => { diff --git a/src/lib/stores/search-attributes.ts b/src/lib/stores/search-attributes.ts index a7939e1f01..df21b05633 100644 --- a/src/lib/stores/search-attributes.ts +++ b/src/lib/stores/search-attributes.ts @@ -1,4 +1,4 @@ -import { derived, get, type Readable, writable } from 'svelte/store'; +import { derived, get, readable, type Readable, writable } from 'svelte/store'; import { z } from 'zod/v3'; @@ -191,6 +191,30 @@ export const sortedSearchAttributeOptions: Readable = }); }); +export const workerSearchAttributes: Readable = readable({ + WorkerStatus: SEARCH_ATTRIBUTE_TYPE.KEYWORD, + WorkerInstanceKey: SEARCH_ATTRIBUTE_TYPE.KEYWORD, + WorkerIdentity: SEARCH_ATTRIBUTE_TYPE.KEYWORD, + HostName: SEARCH_ATTRIBUTE_TYPE.KEYWORD, + TaskQueue: SEARCH_ATTRIBUTE_TYPE.KEYWORD, + StartTime: SEARCH_ATTRIBUTE_TYPE.DATETIME, + LastHeartbeatTime: SEARCH_ATTRIBUTE_TYPE.DATETIME, + DeploymentName: SEARCH_ATTRIBUTE_TYPE.DATETIME, + BuildId: SEARCH_ATTRIBUTE_TYPE.DATETIME, + SdkName: SEARCH_ATTRIBUTE_TYPE.DATETIME, + SdkVersion: SEARCH_ATTRIBUTE_TYPE.DATETIME, +}); + +export const workerSearchAttributeOptions: Readable = + derived(workerSearchAttributes, ($workerSearchAttributes) => { + return Object.entries($workerSearchAttributes).map(([key, value]) => { + return { + label: key, + value: key, + type: value, + }; + }); + }); export const activitySearchAttributeOptions: Readable = derived(customSearchAttributeOptions, ($customSearchAttributeOptions) => { return [ diff --git a/src/lib/utilities/get-sdk-version.ts b/src/lib/utilities/get-sdk-version.ts index 182ca4f948..a5f99dda4d 100644 --- a/src/lib/utilities/get-sdk-version.ts +++ b/src/lib/utilities/get-sdk-version.ts @@ -2,6 +2,18 @@ import type { WorkflowTaskCompletedEvent } from '$lib/types/events'; import { capitalize } from './format-camel-case'; +export const formatSDKName = (sdkName: string | undefined): string => { + let sdk = ''; + if (!sdkName) return sdk; + + sdk = capitalize(sdkName.split('-')[1]); + if (sdk === 'Dotnet') { + sdk = '.NET'; + } + + return sdk; +}; + export const getSDKandVersion = ( tasks: WorkflowTaskCompletedEvent[], ): { sdk: string; version: string } => { @@ -16,10 +28,7 @@ export const getSDKandVersion = ( const sdkName = sdkMetadata?.sdkName; const sdkVersion = sdkMetadata?.sdkVersion; if (sdkName) { - sdk = capitalize(sdkName.split('-')[1]); - if (sdk === 'Dotnet') { - sdk = '.NET'; - } + sdk = formatSDKName(sdkName); } if (sdkVersion) { version = sdkVersion; diff --git a/src/lib/utilities/screaming-enums.ts b/src/lib/utilities/screaming-enums.ts index 8de7139654..e11ef7ff63 100644 --- a/src/lib/utilities/screaming-enums.ts +++ b/src/lib/utilities/screaming-enums.ts @@ -1,3 +1,4 @@ +import type { WorkerStatus as ReadableWorkerStatus } from '$lib/models/worker-status'; import type { ArchivalState, CallbackState, @@ -100,8 +101,6 @@ export const toCallbackStateReadable = ( return fromScreamingEnum(state, 'CallbackState'); }; -export type ReadableWorkerStatus = 'Unspecified' | 'Running' | 'Shutting Down'; - export const toWorkerStatusReadable = ( state?: WorkerStatus, ): ReadableWorkerStatus => { diff --git a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte index 6d43a1e512..ab82f1c928 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/+page.svelte @@ -1,12 +1,21 @@ @@ -43,4 +59,11 @@
- + + + From cb9bda3602290902847ad14c604f602d8fc0a99d Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Thu, 5 Mar 2026 10:57:13 -0700 Subject: [PATCH 12/14] Update no workers polling alert --- src/lib/components/worker-table.svelte | 2 +- .../workers/no-workers-polling-alert.svelte | 34 +++++++++++++++++++ .../components/workers/worker-status.svelte | 4 +-- .../workflow/workflow-call-stack-error.svelte | 27 --------------- src/lib/i18n/locales/en/workers.ts | 1 + src/lib/i18n/locales/en/workflows.ts | 7 ++-- src/lib/layouts/workflow-header.svelte | 2 -- src/lib/models/worker-status.ts | 4 +-- src/lib/pages/workflow-workers.svelte | 5 --- .../[workflow]/[run]/workers/+page.svelte | 6 ++-- 10 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 src/lib/components/workers/no-workers-polling-alert.svelte delete mode 100644 src/lib/components/workflow/workflow-call-stack-error.svelte delete mode 100644 src/lib/pages/workflow-workers.svelte diff --git a/src/lib/components/worker-table.svelte b/src/lib/components/worker-table.svelte index 52737720ca..2af0455b96 100644 --- a/src/lib/components/worker-table.svelte +++ b/src/lib/components/worker-table.svelte @@ -178,7 +178,7 @@ {:else} - + {/each} diff --git a/src/lib/components/workers/no-workers-polling-alert.svelte b/src/lib/components/workers/no-workers-polling-alert.svelte new file mode 100644 index 0000000000..ce450c7c87 --- /dev/null +++ b/src/lib/components/workers/no-workers-polling-alert.svelte @@ -0,0 +1,34 @@ + + + diff --git a/src/lib/components/workers/worker-status.svelte b/src/lib/components/workers/worker-status.svelte index eadc2815a3..dab8c099f1 100644 --- a/src/lib/components/workers/worker-status.svelte +++ b/src/lib/components/workers/worker-status.svelte @@ -14,7 +14,7 @@ const label: Record = { Running: translate('workflows.running'), - 'Shutting Down': 'Shutting Down', + ShuttingDown: 'Shutting Down', Unspecified: translate('events.event-classification.unspecified'), }; @@ -27,7 +27,7 @@ status: { Unspecified: 'bg-slate-100', Running: 'bg-blue-300', - 'Shutting Down': 'bg-yellow-200', + ShuttingDown: 'bg-yellow-200', }, }, }, diff --git a/src/lib/components/workflow/workflow-call-stack-error.svelte b/src/lib/components/workflow/workflow-call-stack-error.svelte deleted file mode 100644 index 9cb6992968..0000000000 --- a/src/lib/components/workflow/workflow-call-stack-error.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - -{#if runningWithNoWorkers} -
- - {translate('workflows.workflow-error-no-workers-description', { - taskQueue: workflow?.taskQueue ?? '', - })} - -
-{/if} diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 4234322f3a..5ec0fe909c 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -63,4 +63,5 @@ export const Strings = { diagnostics: 'Diagnostics', 'poll-success-rate': 'Poll success rate', 'rate-limit': 'Rate limit', + 'troubleshooting-workers-link': 'Troubleshooting workers', } as const; diff --git a/src/lib/i18n/locales/en/workflows.ts b/src/lib/i18n/locales/en/workflows.ts index 3b920e0708..27494b8c70 100644 --- a/src/lib/i18n/locales/en/workflows.ts +++ b/src/lib/i18n/locales/en/workflows.ts @@ -155,9 +155,11 @@ export const Strings = { 'workflow-404-title': 'This is not the Workflow you are looking for', 'workflow-error-title': 'We are having technical difficulties retrieving this Workflow', - 'workflow-error-no-workers-title': 'No Workers Running', + 'workflow-error-no-workers-title': 'No workers polling', 'workflow-error-no-workers-description': - 'There are no Workers polling the {{taskQueue}} Task Queue.', + 'There are no workers polling the {{taskQueue}} task queue.', + 'workers-alert-description': + 'Check your deployment or orchestration system (Kubernetes, ECS, etc.). Review worker logs for crash information. Restart or redeploy workers.', 'workflow-error-no-compatible-workers-title': 'No Compatible Workers Running', 'workflow-error-no-compatible-workers-description': 'There are no compatible Workers polling the {{taskQueue}} Task Queue.', @@ -203,7 +205,6 @@ export const Strings = { 'workflow-task-handler': 'Workflow Task Handler', 'activity-handler': 'Activity Handler', 'nexus-handler': 'Nexus Handler', - 'workers-empty-state': 'No Workers Found', 'call-stack-empty-state': 'No Call Stacks Found', 'no-workers-failure-message': 'This will fail if you have no workers running.', diff --git a/src/lib/layouts/workflow-header.svelte b/src/lib/layouts/workflow-header.svelte index 410c7104e4..ea9ab871cd 100644 --- a/src/lib/layouts/workflow-header.svelte +++ b/src/lib/layouts/workflow-header.svelte @@ -6,7 +6,6 @@ import CodecServerErrorBanner from '$lib/components/codec-server-error-banner.svelte'; import WorkflowDetails from '$lib/components/lines-and-dots/workflow-details.svelte'; import { timestamp } from '$lib/components/timestamp.svelte'; - import WorkflowCallStackError from '$lib/components/workflow/workflow-call-stack-error.svelte'; import WorkflowActions from '$lib/components/workflow-actions.svelte'; import WorkflowStatus from '$lib/components/workflow-status.svelte'; import Alert from '$lib/holocene/alert.svelte'; @@ -175,7 +174,6 @@
- {#if cancelInProgress}
- import TaskQueueWorkerInsights from '$lib/components/task-queue/worker-insights.svelte'; - - - diff --git a/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte index 2464a7bb63..a48be447a8 100644 --- a/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/workers/+page.svelte @@ -2,8 +2,9 @@ import { page } from '$app/state'; import PageTitle from '$lib/components/page-title.svelte'; + import TaskQueueWorkerInsights from '$lib/components/task-queue/worker-insights.svelte'; + import NoWorkersPollingAlert from '$lib/components/workers/no-workers-polling-alert.svelte'; import { translate } from '$lib/i18n/translate'; - import WorkflowWorkers from '$lib/pages/workflow-workers.svelte'; const workflowId = $derived(page.params.workflow); @@ -12,4 +13,5 @@ title={`${translate('workflows.workers-tab')} | ${workflowId}`} url={page.url.href} /> - + + From abd41a2f8ff2411fb6ddec29167aac3d98d4c073 Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Thu, 5 Mar 2026 17:13:09 -0700 Subject: [PATCH 13/14] Worker details page updates --- .../components/task-queue/worker-info.svelte | 248 +++++++++--------- src/lib/holocene/icon/paths.ts | 2 + src/lib/holocene/icon/svg/microchip.svelte | 11 + src/lib/i18n/locales/en/workers.ts | 4 +- src/lib/layouts/workflow-run-layout.svelte | 1 - .../workers/[workerInstanceKey]/+page.svelte | 2 +- 6 files changed, 136 insertions(+), 132 deletions(-) create mode 100644 src/lib/holocene/icon/svg/microchip.svelte diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index 7f5bc0fa1c..b7fe8514a0 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -1,25 +1,30 @@
-
-
- -

{heartbeat?.workerIdentity}

-
+
+ +

+ +

+
-
-
- {translate('common.task-queue')} - - {heartbeat?.taskQueue} - -
-
- {translate('common.start')} - -
-
- {translate('workflows.last-heartbeat')} -
- - - {formatDurationAbbreviated( - String(heartbeat?.elapsedSinceLastHeartbeat), - )} ago - -
-
-
- {translate('workers.sdk')} + + + {translate('workflows.last-heartbeat')} + + + + {translate('common.start')} + + + + + {translate('common.task-queue')} + + + + {translate('workers.sdk')} + -
-
-
+ + +
@@ -123,7 +138,6 @@
{@render hostInfo()} - {@render hostUsage()} {@render workflowCache()} {@render diagnostics()}
@@ -143,12 +157,14 @@
-
{translate('workers.slots')}
-
-

+

+ {translate('workers.slots')} +
+
+

{slots.currentUsedSlots ?? 0}

-

+

{#if slots.currentAvailableSlots} {slots.currentAvailableSlots - slots.currentUsedSlots || 0} {:else} @@ -156,7 +172,7 @@ {/if}

-
+

{translate('workers.used')}

{#if slots.currentAvailableSlots} @@ -171,23 +187,23 @@

-
+
{translate('workers.tasks-processed')}
- + {(slots.totalProcessedTasks ?? 0).toLocaleString()}
{#if poller}
-
- Poller +
+ {translate('workers.poller')} {poller.isAutoscaling ? 'Autoscaling' : 'Manual'}
- + {poller.currentPollers ?? 0}
@@ -205,86 +221,68 @@ {/snippet} {#snippet hostInfo()} - -

{translate('workers.host-info')}

-
-
- {translate('workers.host-name')} - - {heartbeat?.hostInfo?.hostName} - -
-
- {translate('workers.process-id')} - {heartbeat?.hostInfo?.processId} -
-
- {translate('workers.instance')} - - {heartbeat?.workerInstanceKey} - -
-
- {translate('workers.worker-grouping')} - - {heartbeat?.hostInfo?.workerGroupingKey} - -
-
-
-{/snippet} +
+ +

+ {translate('workers.host-info')} +

+
+
{translate('workers.host-name')}
+
{heartbeat?.hostInfo?.hostName}
-{#snippet hostUsage()} - -
-

{translate('workers.host-usage')}

-
-
+
{translate('workers.process-id')}
+
{heartbeat?.hostInfo?.processId}
+ +
+ {translate('workers.worker-grouping')} +
+
{heartbeat?.hostInfo?.workerGroupingKey}
+
+
+
- - + + {translate('workers.cpu-usage')} {heartbeat?.hostInfo?.currentHostCpuUsage.toFixed(0)}%
-
+
- + {translate('workers.memory-usage')} {heartbeat?.hostInfo?.currentHostMemUsage.toFixed(0)}%
-
+
-
- + +
{/snippet} {#snippet workflowCache()} -

+

{translate('workers.workflow-cache')}

@@ -292,7 +290,7 @@ {currentStickyCacheSize.toLocaleString()} -
+
{translate('workers.cache-size')}
@@ -300,16 +298,14 @@ {cacheHitRate}% -
+
{translate('workers.cache-hits')}
- - {totalStickyCacheHit.toLocaleString()} - -
+ - +
{translate('workers.active-thread-count')}
@@ -318,23 +314,19 @@ {#snippet diagnostics()} -

+

{translate('workers.diagnostics')}

- - {cacheHitRate}% - -
+ - % +
{translate('workers.poll-success-rate')}
- {translate('common.none')} -
+ {translate('common.none')} +
{translate('workers.rate-limit')}
diff --git a/src/lib/holocene/icon/paths.ts b/src/lib/holocene/icon/paths.ts index 9a4b81c346..56b32118cf 100644 --- a/src/lib/holocene/icon/paths.ts +++ b/src/lib/holocene/icon/paths.ts @@ -83,6 +83,7 @@ import lock from './svg/lock.svelte'; import logout from './svg/logout.svelte'; import marker from './svg/marker.svelte'; import merge from './svg/merge.svelte'; +import microchip from './svg/microchip.svelte'; import microsoft from './svg/microsoft.svelte'; import minimize from './svg/minimize.svelte'; import moon from './svg/moon.svelte'; @@ -227,6 +228,7 @@ export const icons = { logout, marker, merge, + microchip, microsoft, minimize, moon, diff --git a/src/lib/holocene/icon/svg/microchip.svelte b/src/lib/holocene/icon/svg/microchip.svelte new file mode 100644 index 0000000000..adb80b665a --- /dev/null +++ b/src/lib/holocene/icon/svg/microchip.svelte @@ -0,0 +1,11 @@ + + + + + diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 5ec0fe909c..876e1eb352 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -45,7 +45,7 @@ export const Strings = { 'local-activities': 'Local Activities', slots: 'Slots', used: 'Used', - 'available-out-of': 'Available out of {count}', + 'available-out-of': 'Available out of {{count}}', 'none-available': 'None Available', 'tasks-processed': 'Tasks Processed', 'last-poll': 'Last Poll', @@ -53,7 +53,6 @@ export const Strings = { 'host-info': 'Host Info', 'process-id': 'Process ID', 'worker-grouping': 'Worker Grouping', - 'host-usage': 'Host Usage', 'cpu-usage': 'CPU Usage', 'memory-usage': 'Memory Usage', 'workflow-cache': 'Workflow Cache', @@ -64,4 +63,5 @@ export const Strings = { 'poll-success-rate': 'Poll success rate', 'rate-limit': 'Rate limit', 'troubleshooting-workers-link': 'Troubleshooting workers', + poller: 'Poller', } as const; diff --git a/src/lib/layouts/workflow-run-layout.svelte b/src/lib/layouts/workflow-run-layout.svelte index 452745e34b..152c7591e0 100644 --- a/src/lib/layouts/workflow-run-layout.svelte +++ b/src/lib/layouts/workflow-run-layout.svelte @@ -8,7 +8,6 @@ import SkeletonWorkflow from '$lib/holocene/skeleton/workflow.svelte'; import { translate } from '$lib/i18n/translate'; import WorkflowHeader from '$lib/layouts/workflow-header.svelte'; - import { Action } from '$lib/models/workflow-actions'; import { fetchAllEvents, throttleRefresh, diff --git a/src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte index c282389472..c9a9327069 100644 --- a/src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workers/[workerInstanceKey]/+page.svelte @@ -10,7 +10,7 @@ import { routeForWorkers } from '$lib/utilities/route-for'; const namespace = $derived(page.params.namespace); - const workerInstanceKey = $derived(page.params.instance); + const workerInstanceKey = $derived(page.params.workerInstanceKey); Date: Thu, 5 Mar 2026 17:38:30 -0700 Subject: [PATCH 14/14] Add Go dependency warning for usage stats --- .../components/task-queue/worker-info.svelte | 105 +++++++++++------- src/lib/i18n/locales/en/workers.ts | 4 + 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/lib/components/task-queue/worker-info.svelte b/src/lib/components/task-queue/worker-info.svelte index b7fe8514a0..1eb54133cf 100644 --- a/src/lib/components/task-queue/worker-info.svelte +++ b/src/lib/components/task-queue/worker-info.svelte @@ -11,10 +11,12 @@ import Timestamp from '$lib/components/timestamp.svelte'; import { timestamp } from '$lib/components/timestamp.svelte'; import WorkerStatus from '$lib/components/workers/worker-status.svelte'; + import Alert from '$lib/holocene/alert.svelte'; import Badge from '$lib/holocene/badge.svelte'; import Card from '$lib/holocene/card.svelte'; import Copyable from '$lib/holocene/copyable/index.svelte'; import Icon from '$lib/holocene/icon/icon.svelte'; + import Link from '$lib/holocene/link.svelte'; import { translate } from '$lib/i18n/translate'; import type { WorkerInfo, @@ -40,6 +42,13 @@ const currentStickyCacheSize = $derived( heartbeat?.currentStickyCacheSize ?? 0, ); + const isGoSdk = $derived(heartbeat?.sdkName?.startsWith('temporal-go')); + const goDependencyPotentiallyMissing = $derived( + isGoSdk + ? !heartbeat?.hostInfo?.currentHostCpuUsage && + !heartbeat?.hostInfo?.currentHostMemUsage + : false, + ); const cacheHitRate = $derived.by(() => { const total = totalStickyCacheHit + totalStickyCacheMiss; @@ -220,6 +229,63 @@ {/snippet} +{#snippet goDependencyWarning()} + +

{translate('workers.go-dependency-warning-description')}

+ {translate('workers.go-dependency-warning-link')} +
+{/snippet} + +{#snippet hostUsage()} + +
+
+ + + {translate('workers.cpu-usage')} + + {heartbeat?.hostInfo?.currentHostCpuUsage.toFixed(0)}% +
+
+
+
+
+
+
+ + + {translate('workers.memory-usage')} + + {heartbeat?.hostInfo?.currentHostMemUsage.toFixed(0)}% +
+
+
+
+
+ {#if goDependencyPotentiallyMissing} + {@render goDependencyWarning()} + {/if} +
+{/snippet} + {#snippet hostInfo()}
@@ -239,44 +305,7 @@
{heartbeat?.hostInfo?.workerGroupingKey}
- -
-
- - - {translate('workers.cpu-usage')} - - {heartbeat?.hostInfo?.currentHostCpuUsage.toFixed(0)}% -
-
-
-
-
-
-
- - - {translate('workers.memory-usage')} - - {heartbeat?.hostInfo?.currentHostMemUsage.toFixed(0)}% -
-
-
-
-
-
+ {@render hostUsage()}
{/snippet} diff --git a/src/lib/i18n/locales/en/workers.ts b/src/lib/i18n/locales/en/workers.ts index 876e1eb352..e4135e7491 100644 --- a/src/lib/i18n/locales/en/workers.ts +++ b/src/lib/i18n/locales/en/workers.ts @@ -64,4 +64,8 @@ export const Strings = { 'rate-limit': 'Rate limit', 'troubleshooting-workers-link': 'Troubleshooting workers', poller: 'Poller', + 'go-dependency-warning': 'Potential missing dependency', + 'go-dependency-warning-description': + 'This worker is not showing any usage. You may need to enable a dependency for the Go SDK in order to see this data.', + 'go-dependency-warning-link': 'Learn more.', } as const;