The relay found a media track's receipts track by a configured suffix alone. It now reads every
catalog it forwards on a subscription or a PUBLISH (the track named `catalog`, MSF §5: each
Group's first object independent, the objects after it delta updates, an earlier Group's update
ignored) and pairs each track with the receipts track its `wai_receipts_track` names, in its
namespace (the packaging draft §5.3.3: "a receiver finds a track's receipts chain and receipts
track by them"; WAI moq-streaming-format §4.1). A catalog's naming overrides the suffix for the
tracks it names; the door, the cache's and the DVR store's pairing and a receipts track's
exemption from delivery timeouts follow it, and a subscription made before its catalog arrived
is changed when it does. A naming the texts make unusable (not a non-empty string, a namespace
that is not MOQT §8.8's rendering, the track's own name, a name the catalog lists no track of
packaging `wai-receipts` under, one receipts track two tracks name) is refused, logged and
counted (`wai_relay_catalog_receipts_refused_total{why}`), and the track is paired as one no
catalog names, by the suffix; an alias two tracks state is neither's. The catalog's
`wai_receipt_alias` is kept for each track.
WAI decision D75 records this reading and the relay's delivery-gap claims, which follow.
Tests: `receipts_names`'s unit tests (independent and delta catalogs, each refusal);
`tests/receipts_catalog.rs` (a door passes a track on the receipts track its catalog names, not
its suffix-named one; a receipts track two tracks name is refused and the suffix pairs them).
Planted faults, each caught: the suffix used though a catalog names another track, a receipts
track two tracks name taken, an unlisted one taken.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
76 lines
6.4 KiB
Markdown
76 lines
6.4 KiB
Markdown
# wai-metrics
|
|
|
|
The text exposition format (version 0.0.4) that a WAI service answers `GET /metrics` with, in a
|
|
crate of its own with no dependencies. The services beside `wai-rs` (`wai-audit`, `wai-ledger`,
|
|
`wai-fleet`, `wai-key-proxy`, `wai-quantum-service`) and the room service, ingest gateway and relay
|
|
of `joule-code` depend on it by path.
|
|
|
|
- **`Exposition`** writes counters and gauges family by family: each family's `# HELP` and
|
|
`# TYPE` lines, then its samples. Names are checked against the format's grammars, a family is
|
|
written once, label values are escaped (`\`, `"` and a line feed), help text likewise (`\` and a
|
|
line feed), and values are written as the format reads them (`+Inf`, `-Inf`, `NaN`).
|
|
- **Every label is fixed in the code.** A label's name and value are `&'static str`: a value that
|
|
exists only at run time (a room's id, a principal, an account, a client's address) cannot become
|
|
a label. The one value written at run time is the `code` of `Requests`, a request's status, which
|
|
is a number. A service's series are the ones its code names, and their number is bounded by it.
|
|
- **The scrape is the operator's.** `through_a_front` tells a request that came through a front by
|
|
the fields a front sets (`Forwarded`, `Via`, `X-Forwarded-For`, `X-Forwarded-Host`,
|
|
`X-Forwarded-Proto`, `X-Real-IP`, `CF-Connecting-IP`). A service answers such a request by any
|
|
method as it does a path it does not have, so `/metrics` is read at the service's own address. A
|
|
front that sets none of these fields is configured not to pass the path on.
|
|
- **`Counts`** counts by labels fixed in the code (an outcome, a role, a protocol), each series
|
|
written from the start at 0 when the service names it so.
|
|
- **`Requests`** counts a service's requests by route, method and status. The route is the pattern
|
|
a path matched (`/v1/channels/{name}/report`), never the path; a method outside HTTP's own is
|
|
`other`.
|
|
- **`read`** reads the format for a service's tests. It reads every text the format's grammar
|
|
allows: blanks and tabs between tokens and around a line, comments, and every spelling of a
|
|
value (`Inf`, `Infinity` and `NaN` in any case). It refuses a text that breaks one of the rules
|
|
it holds, with the line and the reason: names, quoting and escapes, one `# HELP` and one `# TYPE`
|
|
a family, each before the first sample, a family's lines together, no series twice, the text
|
|
ended by a line feed. It also refuses a counter that is negative or NaN, and a family named as a
|
|
counter is without its `_total`, which are a service's rules rather than the grammar's.
|
|
`read::identifier_like` tells a label value that looks like a key, a digest, an address or an
|
|
account, which a service's tests refuse in its exposition. `read::declared_routes` reads the
|
|
routes a router's source declares and `read::path_of` makes a path of each, so that a service's
|
|
tests ask every route it has and a route left out of its labels is seen.
|
|
|
|
```rust
|
|
let requests = wai_metrics::Requests::default();
|
|
requests.count("/v1/health", wai_metrics::method("GET"), 200);
|
|
|
|
let mut e = wai_metrics::Exposition::new();
|
|
e.gauge("wai_example_sessions", "Sessions open now.", 2u64);
|
|
e.family("wai_example_refused_total", wai_metrics::Kind::Counter, "Requests refused, by why.")
|
|
.sample(&[("why", "full")], 1u64);
|
|
requests.write(&mut e, "wai_example_requests_total", "Requests answered.");
|
|
let body = e.finish(); // answered as wai_metrics::CONTENT_TYPE
|
|
```
|
|
|
|
## What each service exposes
|
|
|
|
Every service's counters start from 0 when it starts. Every family below is in every answer, with
|
|
no sample where nothing was counted yet. Each service answers `HEAD /metrics` as it does `GET`,
|
|
without the text.
|
|
|
|
| service | families |
|
|
|---|---|
|
|
| `wai-room` | `wai_room_requests_total`, `wai_room_grants_total{role}`, `wai_room_present`, `wai_room_publishers`, `wai_room_audience`, `wai_room_viewer_grants`, `wai_room_rooms_total`, `wai_room_relay_connected` |
|
|
| `wai-ingest` | `wai_ingest_requests_total`, `wai_ingest_sessions{protocol}`, `wai_ingest_sessions_opened_total{protocol}`, `wai_ingest_refused_total`, `wai_ingest_records_total`, `wai_ingest_energy_unmetered` |
|
|
| `wai-audit` | `wai_audit_requests_total`, `wai_audit_sessions_total{outcome}`, `wai_audit_cmcd_batches_total{outcome}`, `wai_audit_reports_total{outcome}`, `wai_audit_channels` |
|
|
| `wai-ledger` | `wai_ledger_requests_total`, `wai_ledger_tariffs`, `wai_ledger_sources`, `wai_ledger_invoices`, `wai_ledger_verifications` |
|
|
| `wai-fleet` | `wai_fleet_requests_total`, `wai_fleet_enrolments`, `wai_fleet_records_held`, `wai_fleet_reports`, `wai_fleet_records_total{outcome}`, `wai_fleet_verifications` |
|
|
| `wai-key-proxy` | `wai_key_proxy_requests_total`, `wai_key_proxy_receipts_total{decision,delivery}`, `wai_key_proxy_receipts_held`, `wai_key_proxy_forwarding`, `wai_key_proxy_proofs_in_flight`, `wai_key_proxy_upstream_total{class}` |
|
|
| `wai-relay` (on its control listener) | `wai_relay_sessions`, `wai_relay_sessions_opened_total`, `wai_relay_tracks`, `wai_relay_subscriptions`, `wai_relay_queued`, `wai_relay_objects_in_total`, `wai_relay_fetches_served_total{from}`, `wai_relay_door_refused_total`, `wai_relay_door_groups_dropped_total`, `wai_relay_door_running_total{outcome}`, `wai_relay_door_admitted_on_run_total`, `wai_relay_door_runs_contradicted_total`, `wai_relay_catalog_receipts_refused_total{why}`, `wai_relay_delivery_gap_claims_total{outcome}`, `wai_relay_requests_refused_total{why}`, `wai_relay_age_withheld_total`, `wai_relay_subscriptions_too_far_behind_total`, `wai_relay_objects_dropped_too_far_behind_total`, `wai_relay_probes_total{direction}`, `wai_relay_probes_refused_total`, `wai_relay_probes_ended_for_shape_total`, `wai_relay_probe_bytes_total{direction}`, `wai_relay_upstream_total{outcome}`, `wai_relay_ratings_held`, `wai_relay_going_away`, `wai_relay_energy_unmetered`, `wai_relay_control_requests_total{op}`, `wai_relay_control_refused_total{why}` |
|
|
| `wai-quantum-service` | `wai_quantum_requests_total`, `wai_quantum_jobs{status}`, `wai_quantum_memory_held_bytes`, `wai_quantum_restored_jobs{outcome}`, `wai_quantum_lent_turns_total{pool,event}`, `wai_quantum_energy_unmetered` |
|
|
|
|
Each `*_requests_total` is labelled `route`, `method` and `code`. The relay answers on its control
|
|
listener (the operator's surface, loopback or a Unix socket of its own user), not on its media
|
|
ports, and so has no `*_requests_total`. Each service's README says what
|
|
its families count.
|
|
|
|
## Checks
|
|
|
|
```sh
|
|
cargo test --locked
|
|
```
|