Skip to content

Serve operator metrics and health natively - #562

Merged
rmcdaniel merged 34 commits into
mainfrom
rust-325-operator-metrics
Oct 21, 2026
Merged

rmcdaniel merged 34 commits into
mainfrom
rust-325-operator-metrics

Conversation

@rmcdaniel

Copy link
Copy Markdown
Member

Part of #325 (operations gate: health and metrics; CLI system:* commands and Waterline service mode read these documents).

Native now serves the operator metrics and health documents as published PHP 2.5.14 builds them, from the rules of Workflow 2.5.5's OperatorMetrics and HealthCheck.

Operator metrics (GET /api/system/operator-metrics).

  • Counts. The run, task, activity, backlog, start, history, schedule, worker and command-contract counts use PHP's predicates over the shared tables, with each oldest_* timestamp and its age. Command-contract backfill uses the same WorkflowStarted rule native's run observation already ports.
  • "Now" comparisons. Like Laravel, task comparisons with "now" bind whole seconds, so a task made available in the current second counts as delayed. Activity deadlines and schedule times compare with microseconds, as PHP formats them.
  • Policy sections describe native.
    • structural limits;
    • the 30-second workflow task lease;
    • no repair loop, sticky routing or worker sessions;
    • in-process long-poll wake;
    • capacity_evidence reported as unavailable.
  • Decision: selected-run projections. Native writes run waits in the same transaction as history, so they cannot drift. It reads timelines, timers and lineage from history, so those three projection tables are reported as maintained: false instead of aligned.

Health (GET /api/system/health). PHP's twelve checks with their names, categories, statuses and messages, the category summary, namespace retention, and 503 while a check reports an error. Both routes are admin-only, as PHP's system route group is, and work in named namespaces.

Task rows and run summaries.

  • Task timestamps. New tasks now carry created_at, updated_at, last_dispatched_at and last_dispatch_attempt_at, as PHP sets them in poll mode.
  • Transport classification. Run summaries classify a task as PHP's RunSummaryProjector::taskLiveness does: lease expired, dispatch failed, claim failed (<type>_task_claim_failed), dispatch overdue, then leased or ready. They use PHP's wait reasons and task-problem flag.
  • Before. Native declined to summarize a run whose tasks carried these columns. Without the timestamps, dispatch_overdue and tasks_added_last_minute would always read zero.

Fixture operator-metrics. A fresh namespace goes through seven stages:

  1. empty;
  2. a run waiting for a worker;
  3. the task past PHP's three-second redispatch window;
  4. an activity scheduled;
  5. the activity running;
  6. the activity retrying after a failure;
  7. the completed run.

The contract requires PHP's counts at every stage. It requires each timestamp exactly when its count is not zero, and PHP's health checks and warnings. It deliberately normalizes or excludes:

  • Ready-due and delayed tasks are compared as one total (Laravel's whole-second comparison).
  • Not compared: elapsed ages, summary history byte sizes, tasks_dispatched_last_minute (PHP stamps some new tasks and not others), and the selected-run projection check, which PHP fills asynchronously.

The contract has 27 unit tests.

Local checks.

  • SQLite parity replica. PHP 2.5.14, embedded PHP and native passed 32 fixtures, including operator-metrics, and the comparison passed. The schedule and visibility fixtures need CI's scheduler and CLI artifact.
  • Native suites. The lib suite and the SQLite execution suite pass. The operator metrics and active summary tests also pass on PostgreSQL and MySQL. The rewritten active summary test covers dispatch-failed, claim-failed and leased classification.

Known gaps (port log).

  • Summary history_size_bytes differs from PHP's (8 events: PHP 10,351 bytes, native 7,961).
  • Worker deregistration is refused in named namespaces.
  • The operator dashboards, capacity evidence, and the repair, retention, activity-timeout and cleanup pass endpoints remain.

🤖 Generated with Claude Code

GET /api/system/operator-metrics and GET /api/system/health follow
Workflow 2.5.5's OperatorMetrics and HealthCheck over the shared tables:
PHP's counts and predicates, whole-second "now" comparisons, the twelve
health checks and 503 on error, admin-only in any namespace. Policy
sections describe native, and the timeline, timer and lineage projections
native reads from history are reported as not maintained.

New tasks now carry PHP's creation and dispatch timestamps, and run
summaries classify a task's transport (lease expired, dispatch failed,
claim failed, dispatch overdue) as PHP's projector does instead of
declining to summarize. An operator-metrics parity fixture checks seven
stages of one run against published PHP 2.5.14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
durable-workflow-ops and others added 2 commits October 21, 2026 01:24
…rics

# Conflicts:
#	.github/workflows/server-parity.yml
#	docs/rust-server-port.md
DELETE /api/workers/{id} now works in a named namespace and removes only
that namespace's registration, as published PHP allows. The operator
metrics probe deregisters its worker again. PHP's maintenance loop may
redispatch the overdue task before the probe reads it, so the contract
accepts PHP's repaired state for that stage; native runs no repair loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
durable-workflow-ops and others added 2 commits October 21, 2026 05:00
… fixture

Published PHP's health check runs a task-queue build-id query that MariaDB
rejects under ONLY_FULL_GROUP_BY once an activity exists (#569), so it
answers 500 from the activity_scheduled stage on. The probe now records the
runtime each stage reports and the MySQL-family job's database image; the
contract accepts that 500 only for published PHP on MariaDB at those stages.
Native must still answer 200 everywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rics

# Conflicts:
#	.github/workflows/server-parity.yml
#	docs/rust-server-port.md
@rmcdaniel

Copy link
Copy Markdown
Member Author

Updated with main (84541fe) and fixed the MariaDB fixtures failure from run 39530541048.

Cause. Published PHP 2.5.14's GET /api/system/health answers 500 on MariaDB from the activity_scheduled stage onward. Its task-queue build-id query is rejected under ONLY_FULL_GROUP_BY. I reproduced this locally with debug output and filed it as #569 for the PHP Server owner. Native answers 200 at every stage on MariaDB; I verified that locally too.

Change (31f7d77f8).

  • The probe now records the runtime each stage reports, and the database image of the MySQL-family job (MYSQL_IMAGE, now passed to that job's HTTP record steps).
  • The contract accepts the 500 only for published PHP on MariaDB, and only at those four stages. It still requires native to answer 200 everywhere.
  • Five new contract unit cases bring the total to 34, all passing.

The earlier PHPUnit failure on dedd08a was a cancelled action-policy preflight. It will rerun on this head.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Cancelled this head's CI runs (39547329348, 39547329452) after #566 merged and left this branch in conflict. The branch will be updated, and CI rerun, when it reaches the head of the merge train.

durable-workflow-ops and others added 9 commits October 21, 2026 08:34
…rics

# Conflicts:
#	.github/workflows/server-parity.yml
#	docs/rust-server-port.md
…rics

# Conflicts:
#	docs/rust-server-port.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rics

# Conflicts:
#	docs/rust-server-port.md
#	rust/src/runtime/http.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…racts outside the store

Sonar read the per-operation rejection lists as duplicates and the query
attempt as too complex. The lists are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native answers /api/system/operator-dashboard and /bounded from the run
summaries by OperatorDashboardSummary's rules. The workflow-type variant
validates its list, then answers PHP's 501 for an observer that cannot
filter by type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel
rmcdaniel enabled auto-merge October 21, 2026 11:12
…oard

Native operator dashboard, so Waterline's landing page loads
@rmcdaniel

Copy link
Copy Markdown
Member Author

The intermittent published-sdk-adapters failure here (exit 101 in the optimized build) is rustc being OOM-killed at the 2 GiB build limit. Rebuilding this head locally with the same script fails with SIGKILL at 2 GiB and succeeds at 4 GiB, peaking at 2.31 GiB. #586 raises the limit. It isn't a defect in this change.

@rmcdaniel

Copy link
Copy Markdown
Member Author

#584 (operator dashboard, so Waterline's landing page loads) was stacked on this branch and has merged into it, so this PR now also carries the dashboard port. Its integration tests passed on SQLite, PostgreSQL and MySQL; the port log has both checkpoints.

durable-workflow-ops and others added 13 commits October 21, 2026 13:16
…rics

# Conflicts:
#	docs/rust-server-port.md
# Conflicts:
#	docs/rust-server-port.md
#	rust/tests/execution.rs
…rics

# Conflicts:
#	docs/rust-server-port.md
#	rust/src/runtime/store.rs
# Conflicts:
#	docs/rust-server-port.md
#	rust/tests/execution/query_arguments.rs
…rics

# Conflicts:
#	docs/rust-server-port.md
# Conflicts:
#	docs/rust-server-port.md
#	rust/tests/execution.rs
…rics

# Conflicts:
#	docs/rust-server-port.md
@rmcdaniel

Copy link
Copy Markdown
Member Author

This branch now also contains #580 (query responses), merged in so the two PRs stop conflicting with each other in the port log. If #562 lands first, #580's changes land with it.

@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel
rmcdaniel merged commit 9441e16 into main Oct 21, 2026
10 checks passed
@rmcdaniel
rmcdaniel deleted the rust-325-operator-metrics branch October 21, 2026 15:46
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