refactor: event, idempotency and SSE metrics come from spans, and the last metric classes are gone
Summary
Closes #389, the last area: the eleven instruments in EventMetrics, IdempotencyMetrics and ConnectionMetrics now come from spans, so no hand-written metric class is left. app/core/metrics keeps only setup_metrics, the OTLP MeterProvider the instrumentations use.
Changes
events.published and event.processing.duration are defined on the producer's existing publish_event span, without an error status, and the dashboards keep their event_type label. The producer now gets its tracer through the constructor instead of a module-level one. IdempotencyManager runs each check in an idempotency.check span with hit or miss and leaves spans for blocked duplicates and finished keys. The worker counts the keys in Redis every 30 s; before, both processes scanned Redis at every export. SSERedisBus leaves a span per message and per closed stream, and the API reports open streams every 10 s. BaseMetrics and MetricsProvider go, the contract test checks the span definitions only, and the queue and idempotency pages lose the stale class descriptions.
Type of Change
- Bug fix (non-breaking change that fixes an issue)
- New feature (non-breaking change that adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
- Refactoring (no functional changes)
- CI/CD or tooling changes
Test Plan
- Backend unit tests pass (
cd backend && uv run pytest tests/unit) - Frontend unit tests pass (
cd frontend && npm run test) - Backend E2E tests pass (
docker compose exec -T backend sh -c 'uv sync --group test --no-dev --frozen --no-install-project --no-build && pytest tests/e2e') - Frontend E2E tests pass (
cd frontend && npm run test:e2e) - API spec and client regenerated if routes or schemas changed (
./deploy.sh openapi, then./deploy.sh types), never edited by hand - Manual testing performed
Validation: unit 571 passed, contract 3 passed, e2e 489 passed and 1 skipped. After the suite, the worker's series held 263 publishes with 263 durations, 222 reserved and 222 finished keys, and 229 live keys. An SSE probe gave 2 messages, 1 closed stream and 0 open after closing.
Checklist
- My code follows the project's style guidelines
- I have run
pre-commit run --all-filesand all checks pass - I have added tests that prove my fix/feature works
- I have updated documentation if needed
- My changes don't introduce new warnings
Summary by cubic
Closes #389: the last eleven hand-written metric classes are gone. Event, idempotency, and SSE metrics now come from spans translated by the collector's signaltometrics connector, and app/core/metrics keeps only setup_metrics for the OTLP MeterProvider.
Behavior changes
events.publishedandevent.processing.durationcome from the producer'spublish_eventspan; failed publishes record error spans and are not counted.- Idempotency checks, blocked duplicates, and finished keys each leave spans, and the worker now counts Redis keys every 30 s instead of every process scanning at export.
- SSE messages and closed streams leave spans, and the API reports its open streams per endpoint every 10 s with zeros, replacing the in-process up-down counter.
- The Grafana dashboard, contract test, and docs follow the new span definitions and renamed SSE labels (
sse_endpoint,sse_event_type).