Skip to content

UN-4046 [DOCS] Document the PG queue transport in the dashboard-metrics README - #2280

Merged
kirtimanmishrazipstack merged 3 commits into
mainfrom
UN-4046-dashboard-metrics-readme-pg-transport
Sep 10, 2026
Merged

UN-4046 [DOCS] Document the PG queue transport in the dashboard-metrics README#2280
kirtimanmishrazipstack merged 3 commits into
mainfrom
UN-4046-dashboard-metrics-readme-pg-transport

Conversation

@jaseemjaskp

Copy link
Copy Markdown
Contributor

What

Documentation only. backend/dashboard_metrics/README.md described a Celery-only architecture and told readers to start the metrics cron with celery -A backend beat and celery -A backend worker -Q dashboard_metric_events. Neither process exists in the default compose stack any more.

  • Adds a Transport: how the cron actually fires chapter.
  • Renames ## Celery Tasks to ## Scheduled Tasks — the task bodies are shared, only the transport differs.
  • Corrects Quick Commands, Setup, Verify, Troubleshooting and File Structure to match the current fleet.
  • Keeps the Celery path documented throughout, labelled as the rollback target.

Why

UN-3796 added the PG path; UN-4046 made it the default, deleted celery-beat and worker-metrics from docker/docker-compose.yaml, and added worker-pg-metrics. The README was never updated for either.

The concrete cost: someone onboarding from this file starts a worker the fleet no longer runs, sees no error and no queue depth, and has nothing in the docs pointing at pg_owned — which is the flag that actually decides whether anything fires. The README's setup step 5 checked django_celery_beat.PeriodicTask.enabled only, which on a current deploy reads False on healthy rows.

How

The new chapter documents:

  • Dual-written schedule rows. Every dashboard_metrics.* schedule is a pair — django_celery_beat.PeriodicTask and pg_queue.PgPeriodicTask — written from one spec in migrations/0006_split_aggregation_schedule.py. pg_owned selects the firer.
  • The full PG path: leader-elected orchestrator tick (workers/queue_backend/pg_queue/pg_scheduler.py::dispatch_due_periodic_tasks, enqueue and next_run_at advance in one transaction) → pg_queue_message on dashboard_metric_eventsworker-pg-metrics → the HTTP proxy in workers/scheduler/dashboard_metrics_tasks.pyinternal_views.pytasks.py verbatim.
  • Why the proxy hop exists — the workers image has no Django, so the ORM-heavy aggregation cannot run there.
  • Why worker-pg-metrics is its own service — the consumer's health heartbeat freezes while a task runs, so HEALTH_STALE bounds one task's wall clock; a minutes-long aggregation would trip the scheduler's 240s bound.
  • The two env gates and their defaultsPG_SCHEDULER_ENABLED (defaults true) and PG_SCHEDULER_ADOPT_PERIODICS (must be true for the periodics), converged by converge_pg_scheduler on every backend start.
  • Failure modes specific to this transport — fire-and-forget with MAX_ATTEMPTS=1, autoretry_for being inert on the internal-HTTP path, and the silent no-firer state.

Setup step 5 now prints pg_owned and cron_string from PgPeriodicTask alongside the Beat rows.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

No. One markdown file changed, no code, no schema, no config, no schedule rows. Nothing imports or renders this README.

Database Migrations

None.

Env Config

None. The PR documents two existing variables (PG_SCHEDULER_ENABLED, PG_SCHEDULER_ADOPT_PERIODICS); it does not introduce or change either.

Deploy Steps

None.

Relevant Docs

This PR is the doc change. Source of truth verified against origin/main at 335b0a3a:

  • backend/scheduler/ownership.pyPG_SCHEDULER_ENABLED default
  • backend/entrypoint.sh — convergence on start, --periodics derivation
  • backend/pg_queue/models.pyPgPeriodicTask fields
  • backend/pg_queue/management/commands/converge_pg_scheduler.py — ownership, both directions
  • workers/queue_backend/pg_queue/pg_scheduler.py — the dispatch tick
  • workers/scheduler/dashboard_metrics_tasks.py — the proxy
  • workers/run-worker.sh — the pg-metrics role
  • docker/docker-compose.yamlworker-pg-metrics, PG_SCHEDULER_ADOPT_PERIODICS=true

Related Issues or PRs

Dependencies Versions

No dependency changes.

Notes on Testing

Docs only, so no tests. What was checked:

  • Every claim read from the pulled origin/main source listed above, not from memory.
  • markdownlint-cli@0.42.0 --disable MD013 (the repo's pre-commit config) reports the same 63 violations as the base commit — all pre-existing, zero added. Four were introduced mid-edit (3× MD033 from a <details> block, 1× MD040) and removed.
  • Internal anchor links resolve; the one MD051 is the pre-existing [Frontend Components](#frontend-components) TOC entry, deliberately left alone as unrelated to this change.

Screenshots

Not applicable.

Checklist

I have read and understood the Contribution Guidelines.

🤖 Generated with Claude Code

The README described a Celery-only architecture and told readers to start the
cron with `celery -A backend beat` and
`celery -A backend worker -Q dashboard_metric_events`. Neither process is
defined in the default compose stack any more: UN-3796 added the PG path and
UN-4046 made it the default, deleting celery-beat and worker-metrics and
adding worker-pg-metrics. Anyone onboarding from this file started a worker
the fleet no longer runs, and had no way to find out why nothing fired.

Add a Transport chapter covering the dual-written schedule rows, the full PG
path from the leader-elected tick through worker-pg-metrics and the internal
API to tasks.py, why the proxy hop exists (no Django in the workers image),
the two env gates and their defaults, and the failure modes specific to this
transport — fire-and-forget with MAX_ATTEMPTS=1, autoretry_for being inert on
the PG path, and the silent no-firer state.

Rename "Celery Tasks" to "Scheduled Tasks", since the task bodies are shared
and only the transport differs. Keep the Celery path documented throughout as
the rollback target rather than deleting it.

Update the setup and troubleshooting steps to match: step 5 now prints
pg_owned and cron_string from PgPeriodicTask alongside the Beat rows, which is
the check that actually identifies which scheduler owns the cron.

Docs only, no behaviour change.
Self-review found the new Setup section reproduced the exact failure the
change was written to prevent. It listed only the metrics consumer as the
process to start, and stated there was "no separate Beat-equivalent process
to start". That is true of compose, where worker-pg-reaper is already
declared, but false for the host-runner flow the same paragraph tells the
reader to use: the periodic tick runs inside the reaper
(reaper.py:1310 calls dispatch_due_periodic_tasks), so starting only
`run-worker.sh pg-metrics` leaves the schedule with no firer — silently.

Start both processes in Quick Commands and Setup step 4, and say why both
are needed.

Name worker-pg-reaper explicitly in the diagram and in the no-firer failure
mode. "The PG scheduler" was ambiguous against the worker-pg-scheduler
service named forty lines earlier, which is the pipeline-task consumer and
fires no periodics — a reader debugging the silent state would have checked
the wrong service and found it healthy.

Also:
- Qualify the "Celery tasks have max_retries=3" line by transport. It
  contradicted the new failure-modes section, which is correct for the live
  path (MAX_ATTEMPTS=1, autoretry_for inert on internal HTTP).
- Rename the duplicated "Task Name" columns to Python Function / Registered
  Task Name.
- Drop the suite count that disagreed with the tree printed below it.
- Fix the TOC's broken #frontend-components anchor, whose heading is "UI Data
  Flow — What Shows Where". Pre-existing, but in a block this change already
  rewrites, and it was the file's only MD051.
…unbook

Second self-review pass, all verified against source.

The dual-write claim was wrong for most of the rows. It said every schedule
is written "from a single spec by the same migration so the two cannot
drift", citing 0006. That holds for the two aggregation rows and the
reconciliation row only. The three original rows come from two separate
migrations — 0002 for Beat and its PG twin 0004 — and 0006's own docstring
says so ("cannot drift the way 0002 and 0004 can"), as does the drift-guard
test that exists for it. Replace the sentence with a table splitting the two
cases and point at tests/test_pg_periodic_task_declarations.py.

Convergence does not run on "every backend start". The whole block in
entrypoint.sh is gated on --migrate, which compose passes but a deployment
running migrations as a separate job does not. The payoff clause was
"ownership is a values change rather than a remembered procedure", which is
exactly the belief that breaks there.

The rollback runbook could produce the outage it warns about. Releasing the
metrics rows back to Beat needs --periodics too, so
PG_SCHEDULER_ADOPT_PERIODICS must stay true while PG_SCHEDULER_ENABLED goes
false. Unsetting both — the intuitive "roll it all back" — leaves the rows
pg_owned with Beat disabled, i.e. no firer.

Also corrected:
- "logs at ERROR rather than raising" conflated two cases. A non-200 does
  raise; only a 200 reporting per-org errors is logged instead.
- MAX_ATTEMPTS=1 is one service's env in compose, not a property of the
  transport; the consumer default is 5. Attribute it, and mention the httpx
  connection-establishment retries.
- The autoretry_for bullet had the right conclusion via the wrong mechanism.
  The decorator IS in the call path; retry() re-raises when a task is called
  directly instead of dispatched by a worker.
- Note the endpoint path is relative to INTERNAL_API_BASE_URL, which ends
  /internal, so the diagram can't be pasted into curl as-is.
- --periodics covers every mirrored non-pipeline periodic, not just these.
- Stop quoting exact liveness numbers as fact: they are ${VAR:-default}
  overrides and k8s takes its values from a chart in another repo.
@jaseemjaskp

Copy link
Copy Markdown
Contributor Author

Self-review (/pr-review) — findings and resolutions

Two review agents ran against this draft. Since it is a docs PR, the only thing that matters is whether the text is true and will stay true, so both passes fact-checked every technical claim against the live source rather than reading for style. Every finding below was independently re-verified before being acted on.

Three findings were factually wrong claims I had introduced. All fixed in 0fa100ff and a32e7a14.

Critical

# Finding Resolution
1 The process that fires the cron was never listed as something to start. Setup listed only the metrics consumer and stated "there is no separate Beat-equivalent process to start". True of compose, false for the host-runner flow the same paragraph recommends: the tick runs in the reaper (reaper.py:1310dispatch_due_periodic_tasks). Following it literally reproduces the silent no-firer state this PR exists to document. 0fa100ff — both processes listed in Quick Commands and Setup step 4, with the reason.
2 The dual-write claim was wrong for 3 of the 5 rows. I wrote that every schedule is written "from a single spec by the same migration so the two cannot drift", citing 0006. That holds only for the aggregation rows and the reconciliation row. The three original rows come from two migrations — 0002 (Beat) and its PG twin 0004. 0006's own docstring says so: "cannot drift the way 0002 and 0004 can". a32e7a14 — replaced with a table splitting the two cases, pointing at the drift-guard test tests/test_pg_periodic_task_declarations.py.
3 The rollback runbook could cause the outage it warns about. Releasing the metrics rows needs --periodics in both directions, so PG_SCHEDULER_ADOPT_PERIODICS must stay true while PG_SCHEDULER_ENABLED goes false. Unsetting both — the intuitive "roll it all back" — leaves the rows pg_owned with Beat disabled: no firer. a32e7a14 — callout rewritten to lead with the flag interaction.

Important

  • entrypoint.sh does not converge on "every backend start" — the block is gated on --migrate (entrypoint.sh:27). Compose passes it; a deployment running migrations as a separate job does not, which is exactly where the "ownership is a values change" promise breaks. Now stated precisely, with the caveat called out.
  • "the PG scheduler" was ambiguous against the worker-pg-scheduler service named earlier in the same chapter — which is the pipeline-task consumer and fires no periodics. Someone debugging the silent state would have checked the wrong service and found it healthy. Now named worker-pg-reaper explicitly, with the distinction spelled out.
  • A pre-existing max_retries=3 line contradicted the new failure-modes section. Qualified by transport.

Corrections to my own new text

  • MAX_ATTEMPTS=1 is one service's env in compose, not a property of the transport — the consumer default is 5. Now attributed, so nobody assumes it holds in k8s.
  • "logs at ERROR rather than raising" conflated two cases: a non-200 does raise; only a 200 reporting per-org errors is logged instead.
  • The autoretry_for bullet had the right conclusion via the wrong mechanism — the decorator is in the call path; retry() re-raises when a task is called directly rather than dispatched by a worker.
  • The endpoint in the diagram is relative to INTERNAL_API_BASE_URL (which ends /internal), so it can't be pasted into curl as-is. Noted.
  • --periodics covers every mirrored non-pipeline periodic, not only dashboard_metrics.*. Hedged so it stays true when another is mirrored.
  • Stopped quoting exact liveness numbers as fact — they are ${VAR:-default} overrides and k8s takes its values from a chart in another repo.

Deliberately not changed

  • The remaining Celery invocations are all inside explicitly labelled rollback/legacy blocks — intentional, per the stated scope of keeping Celery documented as the rollback target.
  • Reviewer flagged the File Structure tree gaining migration entries as mild scope creep. Left as-is: 0004 and 0006 are the transport's own migrations and the tree previously stopped at 0002, which was itself misleading.
  • run-worker.sh's usage text omits the valid pg-metrics role (run-worker.sh:227-235). Real, but a script bug and out of scope for a docs PR — worth a follow-up.

Verification

  • Every claim traced to a source file:line on origin/main @ 335b0a3a.
  • All internal anchors resolve.
  • markdownlint-cli@0.42.0 --disable MD013 (the repo's own pre-commit config): 62 violations vs the base commit's 63 — none added, and the one removed is the MD051 from the broken #frontend-components TOC anchor this PR repairs.

Not marking ready — leaving that to the author's call.

@sonarqubecloud

Copy link
Copy Markdown

@jaseemjaskp
jaseemjaskp marked this pull request as ready for review September 10, 2026 05:54
@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

via Greptile

RetriggerConfidence Score: 5/5

The PR appears safe to merge because the documentation matches the current transport, scheduling, ownership, and worker implementations.

Summary

  • Documents the reaper-to-PG-queue-to-internal-API execution flow.
  • Explains schedule ownership, environment gates, convergence, rollback, and no-firer failure states.
  • Updates setup, verification, troubleshooting, task naming, and file-layout guidance.
  • No runtime code, schema, configuration, or dependency behavior is changed.

Diagram

sequenceDiagram
    participant R as worker-pg-reaper
    participant P as PgPeriodicTask
    participant Q as pg_queue_message
    participant M as worker-pg-metrics
    participant I as Internal API
    participant T as Dashboard metric tasks
    R->>P: Evaluate due pg_owned schedules
    R->>Q: Enqueue dashboard_metric_events
    R->>P: Advance next_run_at atomically
    M->>Q: Consume message
    M->>I: POST /internal/v1/dashboard-metrics/...
    I->>T: Invoke shared task body
    T-->>I: Return aggregation result
    I-->>M: HTTP response
Loading

Reviews (1) · Last reviewed commit: "docs: correct the drift claim, the --mig..."

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-api-deployment e2e 3 0 0 0 16.2
e2e-coowners e2e 1 0 0 0 1.0
e2e-etl e2e 1 0 0 0 12.0
e2e-login e2e 2 0 0 0 0.9
e2e-prompt-studio e2e 1 0 0 0 4.4
e2e-smoke e2e 2 0 0 0 0.8
e2e-workflow e2e 1 0 0 0 15.7
frontend unit 0 1 0 0 0.0
integration-backend integration 502 0 0 26 58.6
integration-connectors integration 1 0 0 7 8.5
integration-workers integration 157 0 0 1 53.7
ui e2e 0 1 0 0 0.0
unit-backend unit 1259 0 0 1 28.9
unit-connectors unit 63 0 0 0 7.6
unit-core unit 115 0 0 0 1.2
unit-platform-service unit 15 0 0 0 1.6
unit-rig unit 120 0 0 0 4.3
unit-runner unit 5 0 0 0 2.3
unit-sdk1 unit 563 0 0 0 20.8
unit-workers unit 1425 0 0 1 108.3
TOTAL 4236 2 0 36 346.9

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • mcp-server-auth — covered by integration-backend
  • mcp-platform-auth — covered by integration-backend
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

@kirtimanmishrazipstack
kirtimanmishrazipstack merged commit b30166a into main Sep 10, 2026
14 checks passed
@kirtimanmishrazipstack
kirtimanmishrazipstack deleted the UN-4046-dashboard-metrics-readme-pg-transport branch September 10, 2026 06:15
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