Skip to content

Push metrics over OTLP/HTTP instead of Prometheus /metrics (MARSOHS-1593) - #5

Open
ElanHasson wants to merge 1 commit into
mainfrom
otlp-metrics
Open

ElanHasson wants to merge 1 commit into
mainfrom
otlp-metrics

Conversation

@ElanHasson

Copy link
Copy Markdown
Collaborator

Targets main. Supersedes #4, which GitHub auto-closed when its base branch was deleted after #3 merged. Jira: MARSOHS-1593. Design: docs/superpowers/specs/2026-09-20-fork-bootstrap-scheduler-otlp-design.md.

On App Platform nothing can scrape a specific instance, so the Prometheus /metrics endpoint was one of the two reasons worker is pinned at one replica (the other is #3). Both tester serve and tester run now push over OTLP/HTTP, the same path the OHP cluster's otel-gateway uses to reach the DigitalOcean observability edge.

Configuration

Entirely via standard OTEL_* env vars; export is off (and logged once) when no endpoint is set. The App Platform spec will carry:

OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-nyc3.digitalocean.com:443
OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer <token>
OTEL_EXPORTER_OTLP_COMPRESSION=gzip
OTEL_SERVICE_NAME=tester            # tester-runner for worker

New package telemetry

  • Setup(ctx, defaultServiceName) installs an SDK MeterProvider (otlpmetrichttp + periodic reader). Resource defaults (service.name, service.instance.id=hostname) are overridden by OTEL_SERVICE_NAME / OTEL_RESOURCE_ATTRIBUTES. Returns a shutdown that flushes on exit; both commands call it.
  • Instruments (scope github.com/digitalocean/tester):
metric kind attrs from
tester.test.duration (s) histogram, legacy Prometheus buckets kept name,state server
tester.test.last_run (unix s) gauge name,state server
tester.run.queue_wait (s) histogram package server
tester.run.duration (s) histogram package,result server
tester.run.claims counter result=claimed/empty/error server
tester.run.scheduled counter package server
tester.run.reset counter package,result=reset/failed server
tester.runs.pending observable gauge from DB, zero-filled per configured package package,state=queued/running server
tester.runner.polls counter (runner heartbeat) result runner
tester.runner.run.duration (s) histogram package,result runner
  • http/metrics.go, the /metrics route and prometheus/client_golang are removed. Dashboards on tester_tb_run_duration_s move to the edge's rendering of tester.test.duration.

Verified

  • go test -race ./... green. telemetry tests: disabled path, enabled path against a fake OTLP endpoint (asserts the Authorization header arrives), resource precedence, and a manual-reader test asserting every series, unit, bucket bounds and the zero-filled pending gauge.
  • End to end with a local protobuf-decoding OTLP sink: serve exported 7 metrics as service.name=tester, two runners exported tester.runner.* as tester-runner, all gzip with the bearer header.

Follow-ups (not in this PR)

  • App Platform spec: add the OTEL_* env vars to web and worker, then raise worker instance_count (MARSOHS-1594).
  • Alert rules on tester.runs.pending{state=queued} and tester.run.queue_wait (MARSOHS-1479).

Made with Cursor

@ElanHasson
ElanHasson marked this pull request as ready for review September 21, 2026 14:09
@ElanHasson

Copy link
Copy Markdown
Collaborator Author

We need to ensure ALL telemetry is covered.

MARSOHS-1593. On App Platform nothing can scrape a specific instance, so
the Prometheus pull endpoint was one of the reasons the worker was pinned
to one replica. Both `tester serve` and `tester run` now push metrics over
OTLP/HTTP, configured entirely by the standard OTEL_* environment variables
(endpoint, bearer header, gzip, service name, export interval), the same
path the OHP cluster's otel-gateway uses to reach DigitalOcean's
observability edge. With no endpoint set, export is disabled and logged.

New package telemetry:
- Setup installs the SDK MeterProvider with an otlpmetrichttp exporter
  behind a periodic reader; resource = defaults (service.name,
  service.instance.id=hostname) overridden by OTEL_SERVICE_NAME /
  OTEL_RESOURCE_ATTRIBUTES; returns a shutdown that flushes on exit.
- Instruments, all under scope github.com/digitalocean/tester:
  server: tester.test.duration (legacy Prometheus buckets kept),
  tester.test.last_run, tester.run.queue_wait, tester.run.duration,
  tester.run.claims{result}, tester.run.scheduled{package},
  tester.run.reset{package,result}, tester.runs.pending{package,state}
  (observed from the DB, zero-filled for every configured package).
  runner: tester.runner.polls{result} (heartbeat), tester.runner.run.duration.
- bind() re-creates instruments on the installed provider so they are
  direct rather than delegating, and lets tests use a manual reader.

Removed: http/metrics.go, the /metrics route, and the
prometheus/client_golang dependency.

Tests: Setup disabled/enabled (fake OTLP endpoint receives a POST with the
configured Authorization header), resource precedence, and a manual-reader
test asserting every series, unit, bucket boundaries and the zero-filled
pending gauge.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant