Add SDK Worker alerting guidance and triage pages - #5029
Merged
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📖 Docs PR preview links
|
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>
austonli
marked this pull request as draft
August 4, 2026 21:56
…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
marked this pull request as ready for review
August 5, 2026 02:07
Duncanma
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
best-practices/sdk-worker-alertingfordurations, 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-failurestroubleshooting/sdk-worker-capacitytroubleshooting/sdk-execution-failuresEach 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— documentsstatus_codeon the request-failure metrics,GrpcMessageTooLargeas afailure_reason, and two previously undocumented Go metrics (below). Also corrects thepoller_typevaluesticky_workflow_task→workflow_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.gocallsTerminateWorkflowonWORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE. The Activity case, which does retry, is unchanged.bin/check-metrics-reference.jsandbin/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:
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:
status_code="NotFound"(PascalCase, Go)UPPER_SNAKE_CASEin every SDKcanonicalStringreturns"NOT_FOUND"; Java usescode.name()worker_type=WorkflowWorkeronnum_pollers, in 11 runbookspoller_type, valuesworkflow_task/workflow_sticky_task/activity_task/nexus_tasksdk-go constants.go,sdk-java PollerTypeMetricsTagmaxWorkflowCacheSizeworker.SetStickyWorkflowCacheSize(int), default 10,000sdk-go worker/worker.golocal_activity_total_execution_latencyrequest_failurerate "per task queue"task_queuetag on that metricsdk-go grpc.gosdk-go internal_task_handlers.gotemporal_local_activity_total_execution_latencyhas no alert here. Its intent — a retry chain accumulating past the heartbeat timeout — is covered as a triage step usinglocal_activity_execution_failedinstead.A better throttling signal, now documented
The Go SDK emits
request_resource_exhaustedandlong_request_resource_exhaustedwith acausetag carrying theResourceExhaustedCauseenum. Neither was documented. Both sat inmetrics-baseline.jsonwith 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
fordurations, and grouping guidance, so a reader can build each alert without opening another page.forduration does all the work.Notes for review
performance-bottlenecksuses the same pattern — but flagging it as a known deviation.performance-bottlenecksstill overlaps these pages structurally. Left alone to keep the diff reviewable; worth consolidating rather than maintaining two symptom lists.Verified:
yarn buildexits 0 withonBrokenLinksandonBrokenAnchorsboth set tothrow, so link and anchor integrity is enforced by the build.check:metrics,check:metrics:sdks, andcheck:orphansall pass.🤖 Generated with Claude Code