feat: aggregate statistics from stored telemetry - #835
Conversation
Normalize invalid measurements to NULL so malformed telemetry cannot break result rendering or aggregate statistics.
Index timestamp lookups used by telemetry statistics and result listing.
PR Summary by QodoAdd cached monthly aggregate telemetry statistics (with optional public report)
AI Description
Diagram
High-Level Assessment
Files changed (14)
|
Code Review by Qodo
1. Empty cache on JSON failure
|
Add monthly aggregate statistics for speed, latency, client, IP version, and country, with cached summaries and a privacy threshold.
a93d5ee to
b2a6153
Compare
stats.phpshows individual telemetry results, but gives no way to see what thecollected data adds up to. This adds a monthly aggregate report so operators can
see the distribution of the tests stored on their server.
The report covers download/upload distributions, ping/jitter, client type,
IPv4/IPv6 and country. Results are cached, public reporting is opt-in, and
groups with fewer than 100 tests are not reported separately.
Light theme
Why
The CLI is packaged for OpenWrt, and Turris OS runs it on a schedule rather than
on demand, so a router accumulates measurements instead of producing one at a
time. Those routers are not a single platform — Turris Omnia, MOX and Omnia NG
all run it, and the 32-bit PowerPC Turris 1.x cannot run the Go client at all,
which is why a Rust port exists (openwrt/packages#30186).
That raises a question an operator currently cannot answer: what do all those
tests add up to, and is the hardware itself limiting what they show?
It also matters to the people sending the data. Users ask what telemetry is
collected, where it goes, and where they could see it. A published aggregate
answers all three at once, and answers them with the data rather than with a
paragraph of assurance.
None of this is specific to Turris. Anyone collecting telemetry eventually wants
context for the population behind it, and this provides that without inventing
information the telemetry does not contain — there is no notion of DSL or fibre
in it, and none is guessed at.
Notes
Aggregation is done in PHP because the measurement columns are
text, malformedhistorical values exist, and percentile support differs between the supported
database backends. On SQLite,
MAX(dl)over'99.9','312.5','1024.0',''and'abc'returns'abc', andAVG(dl)returns 287.28 where the mean ofthe real values is 478.8. Percentiles use a sparse histogram, and completed
months are cached permanently.
Malformed measurements are retained as rows but excluded from aggregates.
Individual results, IP addresses and raw User-Agent strings are never published.
Which month is current is taken from the database rather than from PHP, because
the backends disagree on what their timestamp column records: SQLite's
CURRENT_TIMESTAMPis UTC, PostgreSQL'snow()and MSSQL'sgetdate()areserver-local.
Tested against seeded SQLite telemetry, including malformed measurements and
privacy-threshold edge cases.
tests/telemetry_stats_test.phpcovers thehelpers directly and is wired into
npm test, which was previously a stub.Follow-up worth considering
The client breakdown can only say what a client calls itself. Extending the CLI
to name its platform in the User-Agent — librespeed/speedtest-cli#131 does this
for the Go client — would let this report show which architectures are measuring
against a server, with no change to what telemetry stores.
@ljelinek-cznic, would you be willing to try this on librespeed.turris.cz? The
figures above come from seeded data, and what I cannot check here is whether
they read sensibly against a real month: whether the client split matches what
you would expect, whether the country parsing copes with your
ispinfo, and howlong
stats_build.phptakes on a table that size.