Skip to content

Add SDK Worker alerting guidance and triage pages - #5029

Merged
Duncanma merged 6 commits into
mainfrom
sdk-worker-alerting
Aug 5, 2026
Merged

Add SDK Worker alerting guidance and triage pages#5029
Duncanma merged 6 commits into
mainfrom
sdk-worker-alerting

Conversation

@austonli

@austonli austonli commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Temporal SDKs emit metrics that expose failure modes the Temporal Service can't surface on your behalf — Workflow code failing on replay, Workers that stop polling, Task slots that never free up, Local Activities outliving the Workflow Task heartbeat window. Docs currently document the metrics (SDK metrics reference) and some symptoms (Performance bottlenecks), but there's no recommended alert set, no thresholds tied to alerts, and no per-failure-mode triage.

This adds that.

What's here

Page Contents
best-practices/sdk-worker-alerting Recommended alert set — 22 rows with tag filters, thresholds, for durations, and default severities — plus a five-alert starting subset and a table of conditions that fire during normal operation. Entry point for the other three.
troubleshooting/sdk-request-failures NOT_FOUND on respond operations and Activity heartbeat, RESOURCE_EXHAUSTED on user-facing / respond / poll operations, UNIMPLEMENTED and INTERNAL, request latency
troubleshooting/sdk-worker-capacity Task slots exhausted, all pollers disconnected, Task completions at zero, schedule-to-start latency, Sticky Execution cache empty under load
troubleshooting/sdk-execution-failures Non-determinism error, gRPC message too large, Workflow Task failures and latency, Activity failures, unregistered Activity invocation, Local Activity latency

Each failure mode is an anchored heading so alert notifications can deep-link straight to the relevant triage.

Three existing files are updated:

  • references/sdk-metrics — documents status_code on the request-failure metrics, GrpcMessageTooLarge as a failure_reason, and two previously undocumented Go metrics (below). Also corrects the poller_type value sticky_workflow_taskworkflow_sticky_task, which disagreed with both SDKs.
  • troubleshooting/blob-size-limit-error — the Workflow Task case said the Workflow gets stuck retrying. Current server behavior is termination: respondworkflowtaskfailed/api.go calls TerminateWorkflow on WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE. The Activity case, which does retry, is unchanged.
  • bin/check-metrics-reference.js and bin/metrics-baseline.json — allowlist entries for the new tags, and removal of two baseline entries now that the metrics are documented.

Source and attribution

Built on Tiho's SDK alerting work — the alert set, thresholds, severities, and triage sequences are his. Duncan is working with him on contributing the underlying rulesets directly.

Coverage against his essential set: 23 of 24 alerts. Two pairs are merged into single rows where they share a failure mode and triage path (NOT_FOUND on Workflow Task and Activity respond; Workflow Task and Activity completions at zero). One is dropped — see below. His alert #2c, RESOURCE_EXHAUSTED on poll operations, is promoted out of the full 36-alert index because three triage paths depend on it.

Two follow-ups once the rulesets land:

  1. A reference page with all 36 alerts and per-SDK PromQL, generated from the YAMLs via snipsync rather than hand-maintained.
  2. Alert-import sections on Prometheus Grafana setup and Monitor Temporal Platform metrics, which currently dead-end at Grafana's own alerting docs.

PromQL is deliberately omitted here for that reason — it belongs in the generated reference, not duplicated in prose that will drift. The alert tables give the metric plus tag filters, which is what a reader needs to write the query for their own reporter.

Corrections against SDK and server source

Several claims in the source material don't survive verification. Corrected here, and worth fixing in that repo before the YAMLs are contributed:

Claim Verified behavior Source
status_code="NotFound" (PascalCase, Go) UPPER_SNAKE_CASE in every SDK Go canonicalString returns "NOT_FOUND"; Java uses code.name()
worker_type=WorkflowWorker on num_pollers, in 11 runbooks tag is poller_type, values workflow_task / workflow_sticky_task / activity_task / nexus_task sdk-go constants.go, sdk-java PollerTypeMetricsTag
Go cache option maxWorkflowCacheSize worker.SetStickyWorkflowCacheSize(int), default 10,000 sdk-go worker/worker.go
Alert on local_activity_total_execution_latency Not defined in Go, Java, Python, or TypeScript absent from all four
request_failure rate "per task queue" No task_queue tag on that metric sdk-go grpc.go
Sticky cache alert on size == 0 Gauge is current occupancy, not configured capacity — zero is normal on an idle Worker sdk-go internal_task_handlers.go
Workflow Task failure threshold README table says 10/s, runbook body says 20/s internal contradiction

temporal_local_activity_total_execution_latency has no alert here. Its intent — a retry chain accumulating past the heartbeat timeout — is covered as a triage step using local_activity_execution_failed instead.

A better throttling signal, now documented

The Go SDK emits request_resource_exhausted and long_request_resource_exhausted with a cause tag carrying the ResourceExhaustedCause enum. Neither was documented. Both sat in metrics-baseline.json with an empty note, which that file defines as "not yet reviewed; fill it in or document the metric and remove the entry."

This matters because all three RESOURCE_EXHAUSTED sections open triage with "identify the throttle cause," and without this metric a Cloud reader had no way to do it — the only concrete instruction was in a self-hosted admonition pointing at a server dashboard. The cause distinguishes a rate limit you can ask to have raised from an overloaded Service where a higher limit makes things worse.

On usability as an on-call artifact

  • Alert tables carry tag filters, for durations, and grouping guidance, so a reader can build each alert without opening another page.
  • A five-alert starting subset, since 22 is too many to adopt at once.
  • A table of conditions that fire during normal operation — deploys, rolling restarts, scale-down, idle Task Queues — because most of these are binary conditions where the for duration does all the work.
  • Two alerts reframed so they don't fire on healthy Workers: the sticky cache gauge (occupancy, not capacity) and the Task-completion rate (a zero rate is consistent with an idle Task Queue). Both now require corroborating evidence of load.
  • Severity is a default routing suggestion rather than a property of the metric, with the two cases where it doesn't fit spelled out.

Notes for review

  • Thresholds want a second opinion from the SDK teams. They're starting points and the pages say so, but published values read as recommendations. The consequential ones: p99 above 5s for Workflow Task schedule-to-start, 10s for Workflow Task execution latency, rate above 10/s for execution failures, 30m for Local Activity latency.
  • Overlapping thresholds are cross-referenced, not reconciled. Performance bottlenecks gives P95 above 1s for the schedule-to-start metrics; this PR gives p99 above 5s. Both sections now explain the difference — investigation threshold versus paging threshold — and link Worker health, which already supplies Prometheus queries for the same two metrics. Aligning on single values is a follow-up that needs the same SDK-team input as the thresholds.
  • Deployment-neutral. SDK metrics are identical on Cloud and self-hosted, so triage applies to both; steps that route to server dashboards or dynamic config are in self-hosted-only admonitions.
  • Headings are symptom noun phrases rather than the infinitive form STYLE.md prescribes. Deliberate — the IA guide says troubleshooting pages should lead with the symptom, and performance-bottlenecks uses the same pattern — but flagging it as a known deviation.
  • performance-bottlenecks still overlaps these pages structurally. Left alone to keep the diff reviewable; worth consolidating rather than maintaining two symptom lists.

Verified: yarn build exits 0 with onBrokenLinks and onBrokenAnchors both set to throw, so link and anchor integrity is enforced by the build. check:metrics, check:metrics:sdks, and check:orphans all pass.

🤖 Generated with Claude Code

Adds a recommended alert set for Temporal SDK Worker metrics and three
troubleshooting pages covering the failure modes it alerts on.

- best-practices/sdk-worker-alerting: recommended alert set with starting
  thresholds, grouped by request failures, Worker capacity, and execution
  failures
- troubleshooting/sdk-request-failures: gRPC status codes and request
  latency on calls to the Temporal Service
- troubleshooting/sdk-worker-capacity: Task slots, pollers, Task
  completions, schedule-to-start latency, Sticky Execution cache
- troubleshooting/sdk-execution-failures: non-determinism errors,
  oversized Workflow Task responses, Workflow and Activity code failures,
  Local Activity latency

Pages are deployment-neutral, with self-hosted-only triage steps in
admonitions. PromQL is intentionally omitted pending a published
ruleset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@austonli
austonli requested a review from a team as a code owner August 4, 2026 21:21
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
temporal-documentation Ready Ready Preview Aug 5, 2026 6:40pm

Request Review

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📖 Docs PR preview links

Comment thread docs/troubleshooting/request-failures.mdx
Comment thread docs/troubleshooting/request-failures.mdx
@Duncanma

Duncanma commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Hey @austonli, this is awesome, appreciate you creating this from @tsurdilo's content. One thoughts, do we need to call the pages "SDK Worker..." ... so could "How to alert on Temporal SDK Worker metrics" just be "How to alert on Worker metrics". And "How to troubleshoot SDK request failures" could be "Request failures"

Review pass on the SDK Worker alerting pages.

Metric corrections, verified against SDK and server source:

- num_pollers is tagged poller_type (workflow_task, workflow_sticky_task,
  activity_task, nexus_task), not worker_type
- request_failure carries no task_queue tag, so the Task-completions
  alert cannot be scoped per Task Queue
- Removed local_activity_total_execution_latency, which does not exist
  in the Go, Java, Python, or TypeScript SDKs; the retry-chain case now
  uses local_activity_execution_failed
- Gauges take no _total suffix, unlike counters and histograms

Reframed two alerts that would fire on healthy Workers:

- sticky_cache_size reports current occupancy, not configured capacity,
  so zero is normal on an idle Worker; the alert now requires a non-zero
  Workflow Task rate alongside it
- A zero Task-completion rate is equally consistent with an idle Task
  Queue, so the section now requires evidence of demand

Made the alert set implementable: added the tag filters that distinguish
each alert, the for durations, grouping guidance, a five-alert starting
subset, and a table of conditions that fire during normal operation.
Severity is now stated as a default routing suggestion, with the cases
where it does not fit.

Also: noted that worker_task_slots_available works only with fixed-size
slot suppliers, added a RESOURCE_EXHAUSTED on poll operations section so
triage steps no longer reference uncovered conditions, explained Workflow
Task heartbeating where Local Activity alerts depend on it, and replaced
repeated "Why it matters"/"Triage" subheadings with run-in labels to keep
the page table of contents usable.

Reference and cross-page consistency:

- sdk-metrics: documented the status_code tag on request_failure and
  long_request_failure, and GrpcMessageTooLarge as a failure_reason
- blob-size-limit-error: corrected the Workflow Task case, which the
  Temporal Service terminates rather than retrying indefinitely
  (service/history/api/respondworkflowtaskfailed/api.go)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ore alert 27

Audit pass. Verified every metric name, tag, tag value, SDK option, and
described behavior against SDK and server source rather than against the
source material, which was wrong on several of them.

Blocking fixes:

- status_code values are UPPER_SNAKE_CASE in every SDK. The claim that Go
  reports PascalCase (NotFound) was wrong and had reached the metrics
  reference: Go's canonicalString returns "NOT_FOUND"
  (sdk-go internal/common/metrics/tags.go) and Java uses code.name()
  (sdk-java GrpcMetricsInterceptor). A Go reader filtering
  status_code="NotFound" would have matched nothing.
- yarn check:metrics failed on the previous commit. status_code was added
  to two metrics without being added to the checker's tag allowlist or to
  the shared tag list on the page. Both now updated, along with cause.

Reference additions and corrections:

- Documented request_resource_exhausted and long_request_resource_exhausted
  (Go), which carry a cause tag naming the reason the Temporal Service
  rejected the request. Both were in bin/metrics-baseline.json with an
  empty note, which that file defines as "not yet reviewed"; removed the
  entries per its instructions. The three throttling sections now point at
  this metric instead of asking the reader to identify the cause with no
  means to do so.
- poller_type value is workflow_sticky_task, not sticky_workflow_task
  (sdk-go constants.go, sdk-java PollerTypeMetricsTag).

Scope, checked against the upstream alert set:

- Restored alert 27, Unregistered Activity invocation, which had been
  dropped entirely rather than deferred as the previous PR description
  claimed. Go SDK only, and labelled as such.
- Restored Critical severity on Workflow Task schedule-to-start and a 1m
  for duration on non-determinism error, both of which had been changed
  without a factual basis.
- Sticky cache keeps its 15m for duration, which is a deliberate deviation:
  the gauge is occupancy, so a shorter window fires on idle Workers.

Cross-page consistency:

- Both schedule-to-start sections now explain why performance-bottlenecks
  gives a lower threshold for the same metric (investigation, not paging)
  and link the ready-made queries in cloud/worker-health.
- Softened the claim that every SDK supports the BENIGN failure category,
  which was verified in Go only, and noted that internal failures
  increment activity_execution_failed regardless of category.

Verified: yarn build exits 0 with onBrokenLinks and onBrokenAnchors both
set to throw; check:metrics, check:metrics:sdks, and check:orphans pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The prose read as machine-generated and did not match the house style.

Removed all 76 em dashes. The five closest existing pages
(performance-bottlenecks, worker-health, best-practices/worker,
worker-performance, blob-size-limit-error) use none across 2,226 lines,
so the appositive dash was a style break, not just a tic. Replaced with
sentence breaks, colons, and parentheses.

Loosened the register throughout. Contractions where they read naturally,
plain verbs over formal ones, and second person where the reader is the
one taking action: "burning its time re-running Event History" over
"spending its time re-executing", "your Workers are finishing too late,
over and over" over "Workers are consistently finishing too late".

Cut the constructions that made the hedged sections read like legal
copy. "A zero rate on its own does not prove a problem" became "Check for
demand before you act on this one", and the sticky cache section now says
"Alert on this gauge by itself and you will get paged for Workers that
are doing nothing wrong" instead of "Alerting on this gauge alone will
page you on idle Workers".

Also dropped rhetorical filler: "which is why this is worth treating",
"the for duration is doing all the work", "Treat it as actionable only
alongside", and the three-item parallel lists.

No technical content, thresholds, metric names, tags, anchors, or links
changed. Verified: build exits 0, check:metrics, check:metrics:sdks, and
check:orphans all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Naming. The pages sat in Troubleshooting and Best practices with "SDK"
in every title and sidebar label, which repeated what the section already
says. The repo's own pattern is a descriptive title plus a short bare
sidebar label: "Troubleshoot payload and gRPC message size limit errors"
with "Message size limit errors", "Performance bottlenecks troubleshooting
guide" with "Performance bottlenecks".

Renamed to match, including file names, ids, and slugs, since none of these
pages are published yet and the URLs are free to change now:

  Alerting on Worker metrics                            Worker alerting
  Troubleshoot request failures                         Request failures
  Troubleshoot Worker capacity                          Worker capacity
  Troubleshoot Workflow and Activity execution failures  Execution failures

Moved the setup section to the top, retitled "Before you start". It is a
prerequisite: without metrics flowing there is nothing to alert on, and
the metric-naming rules it carries (counter and histogram suffixes, and
gauges taking none) are needed to write the queries in the tables further
down. There was no reason for it to be second to last.

Replaced "Alerts that fire during normal operation", which documented
noise instead of fixing it. Split into the two cases:

- Conditions that appear during a deploy and clear on their own once the
  rollout finishes. The fix is a `for` duration longer than a deploy takes,
  so the section now says to time your rollout and set it from that.
- Conditions no `for` duration makes correct, because the alert is asking
  the wrong question. An idle Task Queue really does have zero Task
  completions and an empty sticky cache; an autoscaled-to-zero Worker
  really does have no pollers. Those need a demand signal or an exclusion,
  and the section points at the two troubleshooting sections that explain
  how.

Restored list structure to "Route alerts by severity". The voice pass had
flattened it into prose, which lost the scannability of what is really a
routing table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@austonli
austonli marked this pull request as ready for review August 5, 2026 02:07
@Duncanma
Duncanma enabled auto-merge (squash) August 5, 2026 18:38
@Duncanma
Duncanma merged commit ad3052e into main Aug 5, 2026
17 checks passed
@Duncanma
Duncanma deleted the sdk-worker-alerting branch August 5, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants