[backend-scheduler] expose pending job depth and fix job-duration bucketing - #7772
Open
zalegrala wants to merge 3 commits into
Open
[backend-scheduler] expose pending job depth and fix job-duration bucketing#7772zalegrala wants to merge 3 commits into
zalegrala wants to merge 3 commits into
Conversation
…keting
Nothing exposed queue depth. jobs_created_total counts dispatch and jobs_active counts work
already handed to a worker, so a submission that fanned out 145 jobs reported 1 -- the other
144 were invisible. jobs_pending{tenant, job_type} reports what is enqueued and not yet
dispatched.
Depth is also the only signal that can drive scale-up. jobs_active is bounded by the worker
count and rises only as capacity rises, so reading it to decide whether to add capacity is
circular. This matters most for redaction, which suppresses the tenant's compaction while it
runs: an autoscaler driven by compaction backlog watches that backlog vanish exactly when
redaction work starts, and scales down mid-run.
The gauge resets before each publish. It is keyed by tenant, and a tenant whose queue drains
stops appearing in the snapshot rather than reporting zero, so without the reset its last
value would persist for the process lifetime and an autoscaler would hold scale forever.
job_duration_seconds moves off the client defaults, which stop at 10s and put no boundary
between 2.5s and 5s -- where about 60% of redaction jobs land fleet-wide, so p50, p90 and p99
were all interpolations inside one bucket. Now powers of two from 10ms to ~11m.
tempodb_cache_store_size_bytes was the only histogram in the tree without native-histogram
configuration; it now matches the other 33.
…rams Active Redaction Jobs reads the same on the first block as on the last -- it counts what a worker is running now, which is bounded by the worker count. Pending Redaction Jobs plots the queue depth per tenant, so the dashboard answers how much is left rather than only whether something is running. Both job-duration panels gain native-histogram queries next to the classic-bucket ones. The classic buckets are coarse where redaction actually lands, and the native series resolves it properly wherever native histograms are retained; keeping both means the panel renders on stacks that have either. Compiled output regenerated with make tempo-mixin.
The metrics table listed jobs_active but had nothing for queue depth. Notes the distinction that matters for autoscaling: active is bounded by the worker count, pending is not.
zalegrala
marked this pull request as ready for review
August 14, 2026 22:13
zalegrala
requested review from
carles-grafana,
electron0zero,
ie-pham,
javiermolinar,
knylander-grafana,
mapno,
mattdurham,
mdisibio,
ruslan-mikhailov,
stoewer,
yvrhdn and
zhxiaogg
as code owners
August 14, 2026 22:13
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves observability and scaling signals for the backend scheduler by exposing pending queue depth, correcting job-duration histogram bucket boundaries for meaningful percentiles, and extending native-histogram coverage—along with corresponding dashboard, docs, and changelog updates.
Changes:
- Add
tempo_backend_scheduler_jobs_pending{tenant,job_type}and publish it on the scheduler maintenance loop from a per-tenant/type queue-depth snapshot. - Replace
backend_scheduler_job_duration_secondsbuckets with powers-of-two from 10ms to ~11m and complete native-histogram options fortempodb_cache_store_size_bytes. - Update Tempo mixin dashboards (and compiled output) plus docs/changelog entries to reflect the new/updated metrics.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tempodb/backend/cache/cache.go | Adds shared namespace constant and native-histogram opts for the cache store-size histogram. |
| modules/backendscheduler/work/work.go | Adds PendingJobCounts() snapshot API for pending queue depth. |
| modules/backendscheduler/work/interface.go | Exposes PendingJobCounts() on the scheduler work interface. |
| modules/backendscheduler/metrics.go | Adds backend_scheduler_jobs_pending gauge, updates job-duration histogram buckets, and adds publisher helper. |
| modules/backendscheduler/backendscheduler.go | Publishes pending-depth metric on the maintenance ticker. |
| modules/backendscheduler/work/pending_counts_test.go | Unit test for correctness and “no stale series” semantics of the pending-depth snapshot. |
| modules/backendscheduler/metrics_pending_test.go | Unit test ensuring Reset() behavior removes drained-series from jobs_pending. |
| operations/tempo-mixin/dashboards/tempo-backendwork.json | Adds “Pending Redaction Jobs” panel and adds native-histogram queries to job-duration panels. |
| operations/tempo-mixin-compiled/dashboards/tempo-backendwork.json | Regenerated compiled dashboard JSON reflecting mixin changes. |
| docs/sources/tempo/reference-tempo-architecture/components/compaction.md | Documents the new jobs_pending metric in the compaction/backendscheduler metrics list. |
| .chloggen/backendwork-dashboard-redaction-progress.yaml | Changelog entry for dashboard improvements around redaction progress and native hist queries. |
| .chloggen/backend-scheduler-jobs-pending.yaml | Changelog entry for new pending-depth metric, bucket changes, and native-hist coverage completion. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
924
to
+927
| // BusyBlocksForTenant returns a map of blockID -> jobID for every block | ||
| // currently referenced by a pending, registered, or active job for the tenant. | ||
| // Acquires pendingMtx exactly once and returns a snapshot. | ||
| // PendingJobCounts returns the number of enqueued, not-yet-dispatched jobs per tenant and type. |
Comment on lines
+951
to
952
|
|
||
| func (w *Work) BusyBlocksForTenant(tenantID string) map[string]string { |
Comment on lines
+1500
to
1521
| { | ||
| "datasource": { | ||
| "type": "prometheus", | ||
| "uid": "${metrics}" | ||
| }, | ||
| "editorMode": "code", | ||
| "expr": "histogram_quantile(0.99, sum(rate(tempo_backend_scheduler_job_duration_seconds{cluster=~\"$cluster\", namespace=~\"$namespace\"}[$__rate_interval])))", | ||
| "legendFormat": "p99 (native)", | ||
| "range": true, | ||
| "refId": "N99" | ||
| }, | ||
| { | ||
| "datasource": { | ||
| "type": "prometheus", | ||
| "uid": "${metrics}" | ||
| }, | ||
| "editorMode": "code", | ||
| "expr": "histogram_quantile(0.5, sum(rate(tempo_backend_scheduler_job_duration_seconds{cluster=~\"$cluster\", namespace=~\"$namespace\"}[$__rate_interval])))", | ||
| "legendFormat": "p50 (native)", | ||
| "range": true, | ||
| "refId": "N50" | ||
| } |
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.
What this PR does:
Adds the missing scale signal for backend work, and fixes the job-duration histogram so its percentiles mean something.
tempo_backend_scheduler_jobs_pending{tenant, job_type}— jobs enqueued and not yet dispatched. Nothing exposed queue depth before:jobs_created_totalcounts dispatch andjobs_activecounts work already handed to a worker, so a submission that fanned out 145 jobs reported1while the other 144 were invisible to Prometheus.Depth is also the only signal that can drive scale-up.
jobs_activeis bounded by the worker count and rises only as capacity rises, so reading it to decide whether to add capacity is circular.This matters most for redaction, which suppresses the tenant's compaction while it runs. An autoscaler driven by compaction backlog therefore watches that backlog disappear exactly when redaction work begins, and scales down mid-run. A trigger on
sum(tempo_backend_scheduler_jobs_pending{job_type="JOB_TYPE_REDACTION"})holds scale for the duration and releases when the queue drains.The gauge
Reset()s before each publish. It is keyed by tenant, and a tenant whose queue drains stops appearing in the snapshot rather than reporting zero — without the reset its last non-zero value would persist for the process lifetime, so a finished redaction would look permanently backlogged and an autoscaler would never scale back down. That is the behaviour the test pins.backend_scheduler_job_duration_secondsbuckets. The client-library defaults stop at 10s and place no boundary between 2.5s and 5s. Fleet-wide over 7 days, ~60% of redaction jobs land in that single bucket, so p50, p90 and p99 are all linear interpolations inside it — they move together and describe nothing. Now powers of two from 10ms to ~11m, which resolves fast retention jobs, splits the redaction mass, and gives long jobs a real bucket instead of+Inf.Dashboards using
histogram_quantileare unaffected. Queries pinned to specificlevalues need updating.Native histograms.
tempodb_cache_store_size_byteswas the only histogram in the tree without native-histogram configuration; it now matches the other 33, so coverage is complete at 34/34.Worth noting separately: ops currently stores only the
_bucketseries forbackend_scheduler_job_duration_seconds— no_sum, no_count, and no native histogram — even though the code has emitted native-histogram config for this metric all along. That is an ingestion-path question rather than a code one, and it is why the classic buckets still matter today.Dashboard. The Backend Work dashboard gains a "Pending Redaction Jobs" panel leading the Redaction row. "Active Redaction Jobs" reads the same on the first block as on the last — it counts what a worker holds right now, which is bounded by the worker count — so nothing on the dashboard previously showed how much of a redaction was left. Both job-duration panels also gain native-histogram queries alongside the classic-bucket ones, so the panel renders on stacks that retain either representation.
Which issue(s) this PR fixes:
N/A (tracked internally).
Checklist
.chloggen/