Service Bus Message Flow dashboard card¶
Motivation¶
The optional Service Bus integration had only a one-line inbound counter on the Recent searches page. There was no at-a-glance view of who is driving BLAST work, what is in flight, and where it runs. This adds a calm, honest flow diagram to the dashboard.
User-facing change¶
- New Message Flow strip on the dashboard (between the Cluster plane and Resource plane sections). It renders only when the Service Bus integration is effective-enabled — the default dashboard is unchanged.
- The strip shows producer color dots (one stable color per submitter alias), active-job count, and target-cluster count. When the integration is on but nothing is running it shows a single "no active messages" line instead of an empty diagram.
- An expand control opens a modal with three lanes:
- Producers — active-job submitters by alias (shown as-is), colored per submitter, busiest first.
- Broker — one box per in-flight
JobStaterow (statusqueued/running), box width proportional tolog10(query sequence letters), left band tinted with the submitter's color. Clicking a box shows the real job JSON fetched from/api/monitor/jobs/{job_id}. - Consumers — grouped by AKS cluster name with running/queued counts.
- Footer badge with live Service Bus queue / scheduled / DLQ counts (degrades to "counts unavailable (reason)" when the managed identity lacks the Manage claim).
Honesty / degrade behavior¶
The broker lane reflects active job state, not raw queue messages — the request queue drains in under a second so its depth is almost always zero (see Service Bus integration). When the integration is off the card hides itself; when counts are unavailable the footer says so; nothing is animated to fake activity.
API / IaC diff summary¶
- New
GET /api/monitor/message-flow(read-only,require_caller, never 500 — degrades to{"enabled": false}via_graceful). The enabled snapshot is served through the shared monitor cache (cached_snapshot, TTL ~30s) so the per-poll Table scan + Service Bus management call run at most once per window regardless of open tab count. The cache key is isolated per caller (or a singlesharedbucket whenBLAST_JOBS_SHARED_VISIBILITY=true) so one caller's private active-job list is never served to another from cache. - New service
api/services/message_flow.pybuilds the snapshot from the jobstate repo + Service Bus config (no new Azure SDK surface, no SAS tokens). - Snapshot fields:
scope(own/shared),active_total,active_shown,broker_truncated,read_truncatedso the SPA labels a truncated view honestly instead of implying it sees every active job. - The JSON-detail view reuses the existing
/api/monitor/jobs/{job_id}endpoint; the modal strips rawowner_oid/tenant_idGUIDs before rendering (charter §12 — sanitise UI output). No infra/Bicep changes.
Robustness review (self-critique, severity-ordered)¶
Fixed before completion:
- Critical — no caching meant a full Table scan + Service Bus management
call on every 20 s poll per tab; added
cached_snapshotwith a per-caller cache key (load collapse + privacy isolation). - High — payload-heavy reads and un-TTL'd Service Bus counts are now bounded by the same 30 s cache window.
- High — modal JSON no longer echoes raw
owner_oid/tenant_id. - Medium — added
scope/broker_truncated/read_truncatedso the producer-lane semantics and any >cap truncation are shown, not hidden; added an enabled-path route test; box<button>now carries anaria-label.
Verified non-defects: read-only GET (no idempotency/concurrency write races),
require_caller enforced, _graceful never 500s, SB-off hides the card (no
fake animation), box-click ownership check consistent across shared-visibility
and cluster-shared rows.
Validation evidence¶
uv run pytest -q api/tests/test_message_flow.py— 8 passed.uv run pytest -q api/tests(full suite) — 3334 passed, 3 skipped.uv run ruff check— clean on all new backend files.cd web && npx vitest run src/components/cards/MessageFlow— 9 passed.cd web && npm run build— succeeds;npx eslintclean on new files.