OXYGENHUB

Metrics: the bytes pushes and fetches carry 85ec74fd82d8

The protocol's request and response bodies, counted by endpoint in its
handler, before compression: the outer request layer sees compressed
answers of no known size, and oxy's client accepts gzip.

Refs #212
Author
Andy McCall @andymccall <[email protected]>
via
agent agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key c897aff9d2ac
When
· 2026-10-18 06:27 UTC
Signature
◈ sealed by agent:claude@andymccall
Hash
85ec74fd82d8075b2e48d1383547cc3a18886327bb29b28f08c58d8a29929fad
Depends on
2c47eecfeab6 42fa95540468 8a48dd830178 93be9ce4653e a6d824c5ebf5 b9b74d997cec d23464c0ac60
Depended on by
166f6e9bf4f1
In bookmarks
api-unknown-fields cancel-landed-runs diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
In tags
v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
8 atoms, 0 tree operations, 4 files

Browse the tree at this change

modified crates/hub/src/metrics.rs +26 −0
…
36 unchanged lines
37 37
38 38
static HTTP: Mutex<Option<HashMap<(String, String), Hist>>> = Mutex::new(None);
39 39
40 +
/// Bytes the protocol received and sent, by endpoint, counted before the
41 +
/// answer is compressed: what pushes and fetches carry.
42 +
static PROTOCOL: Mutex<std::collections::BTreeMap<&'static str, (u64, u64)>> = Mutex::new(std::collections::BTreeMap::new());
43 +
44 +
/// Count a protocol call's request and response bodies.
45 +
pub fn protocol_bytes(endpoint: &'static str, received: u64, sent: u64) {
46 +
    let mut g = PROTOCOL.lock().unwrap_or_else(|p| p.into_inner());
47 +
    let e = g.entry(endpoint).or_default();
48 +
    e.0 += received;
49 +
    e.1 += sent;
50 +
}
51 +
40 52
/// A request's route class: a few dozen at most, so a label never holds a
41 53
/// user's or repository's name.
42 54
pub fn route_class(path: &str) -> String {
…
214 unchanged lines
257 269
            .collect::<Vec<_>>(),
258 270
    );
259 271
    family(&mut out, "oxyhub_tlog_entries", "Entries in the transparency log.", "gauge", &[(String::new(), app.tlog.size() as f64)]);
272 +
    let protocol: Vec<(&'static str, (u64, u64))> =
273 +
        PROTOCOL.lock().unwrap_or_else(|p| p.into_inner()).iter().map(|(k, v)| (*k, *v)).collect();
274 +
    family(
275 +
        &mut out,
276 +
        "oxyhub_protocol_bytes_total",
277 +
        "Bytes the Oxygen protocol received and sent since the hub started, by endpoint, before compression: push carries pushes, fetch and objects carry fetches.",
278 +
        "counter",
279 +
        &protocol
280 +
            .iter()
281 +
            .flat_map(|(e, (rx, tx))| {
282 +
                [(format!("endpoint=\"{e}\",direction=\"received\""), *rx as f64), (format!("endpoint=\"{e}\",direction=\"sent\""), *tx as f64)]
283 +
            })
284 +
            .collect::<Vec<_>>(),
285 +
    );
260 286
    family(
261 287
        &mut out,
262 288
        "oxyhub_mail_total",
…
91 unchanged lines
modified crates/hub/src/protocol.rs +5 −1
…
457 unchanged lines
458 458
        Endpoint::Push | Endpoint::Checkpoint => Guard::Write(app.repos.write_guard(&repo).await),
459 459
        _ => Guard::Read(app.repos.read_guard(&repo).await),
460 460
    };
461 -
    app.server.handle(repo.to_string(), endpoint, &method, &headers, body).await
461 +
    let (name, received) = (endpoint.name(), body.len() as u64);
462 +
    let res = app.server.handle(repo.to_string(), endpoint, &method, &headers, body).await;
463 +
    let sent = axum::body::HttpBody::size_hint(res.body()).exact().unwrap_or(0);
464 +
    crate::metrics::protocol_bytes(name, received, sent);
465 +
    res
462 466
}
463 467
464 468
enum Guard {
…
3 unchanged lines
modified crates/hub/tests/flow.rs +8 −0
…
5324 unchanged lines
5325 5325
    let text = oxygenhub::metrics::render(&h.app).await;
5326 5326
    assert!(!text.contains("oxyhub_flow_jobs{state=\"queued\"} 0"), "the run's jobs are queued:\n{text}");
5327 5327
    assert!(text.contains("oxyhub_pushes_last_day{result=\"accepted\"}"), "{text}");
5328 +
    // The push's bytes are counted (other tests share the counters, so
5329 +
    // only that they are there and not nothing).
5330 +
    let pushed = text
5331 +
        .lines()
5332 +
        .find_map(|l| l.strip_prefix("oxyhub_protocol_bytes_total{endpoint=\"push\",direction=\"received\"} "))
5333 +
        .and_then(|v| v.parse::<f64>().ok())
5334 +
        .unwrap_or(0.0);
5335 +
    assert!(pushed > 0.0, "{text}");
5328 5336
    // Requests through the router are counted by route class.
5329 5337
    call(&h.app, "GET", "/andy/demo", None, None).await;
5330 5338
    let text = oxygenhub::metrics::render(&h.app).await;
…
188 unchanged lines
modified docs/spec.md +2 −2
…
644 unchanged lines
645 645
- OxygenFlow jobs queued, waiting for approval and running; runs finished in the last 24 hours by result; runners online and not, and seconds since each last called;
646 646
- the transparency log's entries;
647 647
- the database pool's connections (idle and in use);
648 -
- reference updates pushed in the last 24 hours, accepted and refused;
648 +
- reference updates pushed in the last 24 hours, accepted and refused, and the bytes the protocol received and sent by endpoint (`push` carries pushes, `fetch` and `objects` fetches), counted before compression;
649 649
- each owner's latest backup: seconds since it started, and whether it succeeded;
650 650
- mail sent and failed since the hub started;
651 651
- HTTP requests by route class (`page:<tab>`, `api:<area>`, `protocol:<endpoint>`, `static` and so on, never a user's or repository's name; at most 300 classes), method and status class, with latency histograms (5 ms to 30 s).
652 652
653 -
Push and fetch sizes, Diffusion, notifications and query latency are the next slices; the runner's own metrics too.
653 +
Diffusion, notifications and query latency are the next slices; the runner's own metrics too.
654 654
655 655
### 13.6 Coverage reports (oxygenhub#113, done 2026-10-06)
656 656
…
168 unchanged lines