engine.EngineMetrics.Throughput is exported, documented as "the recent requests-per-second rate", and never assigned by any engine. Verified against 468ce53:
engine/engine.go:173 // Throughput is the recent requests-per-second rate.
engine/engine.go:174 Throughput float64
engine/epoll/loop.go:1150 // RequestCount (plus Throughput and the adaptive controller's <- comment only
adaptive/engine.go:929 Throughput: pm.Throughput + sm.Throughput, <- 0 + 0
std, epoll and io_uring never write it. The only line that touches the field at all is adaptive's aggregation, which sums two values that are always zero. So every Metrics() call from every engine reports Throughput: 0.
The rate is computed — at adaptive/telemetry.go:85, snap.ThroughputRPS = float64(deltaReqs) / elapsed — but into adaptive's own controller snapshot, not into EngineMetrics. The exported field and the working one are different structs that happen to share a name.
Why it matters more than it looks
A field that is exported, documented, and always zero is indistinguishable from a real measurement of zero. A consumer reading Metrics().Throughput gets a plausible number and no signal that nothing measured it — which is strictly worse than the field not existing.
This is not hypothetical. probatorium's /debug/vars publisher was just widened to export all 52 EngineMetrics scalar fields (goceleris/probatorium#389), so celeris.engine_throughput now appears in every refapp's metrics endpoint, reading a flat 0 on every engine on every cell. It was published deliberately, with a comment saying so, on the reasoning that a field which exists and is never published cannot be told apart from one that is published and never moves — but the right end to fix is this one.
Options
- Populate it.
RequestCount and a timestamp are already tracked per engine; the adaptive controller derives the rate from exactly those. The awkward part is that "recent" needs a window, and Metrics() is a snapshot with no natural interval — whatever is chosen should be stated in the doc comment.
- Remove it, and let consumers derive a rate from
RequestCount and their own sampling interval, which is what probatorium already does.
Option 2 is a breaking change to an exported struct, so it belongs in v2.0.0 unless the field is judged too new to matter. Filing against v1.6.0 for the decision, not for a particular fix — if the answer is "remove", it should move to the v2.0.0 milestone rather than ship half-done.
No engine regression test would have caught this: there is nothing to assert against a field nobody writes.
engine.EngineMetrics.Throughputis exported, documented as "the recent requests-per-second rate", and never assigned by any engine. Verified against468ce53:std, epoll and io_uring never write it. The only line that touches the field at all is adaptive's aggregation, which sums two values that are always zero. So every
Metrics()call from every engine reportsThroughput: 0.The rate is computed — at
adaptive/telemetry.go:85,snap.ThroughputRPS = float64(deltaReqs) / elapsed— but intoadaptive's own controller snapshot, not intoEngineMetrics. The exported field and the working one are different structs that happen to share a name.Why it matters more than it looks
A field that is exported, documented, and always zero is indistinguishable from a real measurement of zero. A consumer reading
Metrics().Throughputgets a plausible number and no signal that nothing measured it — which is strictly worse than the field not existing.This is not hypothetical. probatorium's
/debug/varspublisher was just widened to export all 52EngineMetricsscalar fields (goceleris/probatorium#389), soceleris.engine_throughputnow appears in every refapp's metrics endpoint, reading a flat 0 on every engine on every cell. It was published deliberately, with a comment saying so, on the reasoning that a field which exists and is never published cannot be told apart from one that is published and never moves — but the right end to fix is this one.Options
RequestCountand a timestamp are already tracked per engine; the adaptive controller derives the rate from exactly those. The awkward part is that "recent" needs a window, andMetrics()is a snapshot with no natural interval — whatever is chosen should be stated in the doc comment.RequestCountand their own sampling interval, which is what probatorium already does.Option 2 is a breaking change to an exported struct, so it belongs in v2.0.0 unless the field is judged too new to matter. Filing against v1.6.0 for the decision, not for a particular fix — if the answer is "remove", it should move to the v2.0.0 milestone rather than ship half-done.
No engine regression test would have caught this: there is nothing to assert against a field nobody writes.