feat(telemetry): move GA emission to Celery beat with refreshed schema - #2798
Merged
Conversation
The single GA "version" event used to be emitted inline from is_up_to_date() — which is on the synchronous request path for the home-page banner and the v2 info endpoint — and was gated behind the cache-miss branch of fetch_remote_hash(). Combined with the branches-pagination bug fixed in #2797 it had been dead in practice. Even when it worked, it was prone to deadlocking request workers (no timeout) and crashing them on non-ConnectionError exceptions (narrow except). Move the call to a Celery beat task that fires hourly. The task delegates to lib.telemetry.send_telemetry(), which guards itself with a 24h cooldown stored in Redis (the persisted volume, not the tmpfs celerybeat schedule) so devices that reboot frequently still emit at most one event per day. The HTTP POST gets timeout=5 and a broad RequestException catch so a sluggish GA can never drag down worker latency. Refresh the payload to match Anthias's current device matrix and GA4 conventions: branch, commit_short — version identity device_type — pi4-64 / pi5 / x86 (was missing) hardware_model — /proc/cpuinfo model (was Pi_Version) is_balena, is_docker — runtime flavour os_release — /etc/os-release PRETTY_NAME resolution, audio_output — viewer config that informs hardware/perf decisions tls_enabled — HTTPS adoption signal asset_count, asset_<type>_count — playlist usage breakdown Drops the legacy NOOBS bool — NOOBS was retired upstream years ago and the field has been useless. Drops Pi_Version in favour of the neutral hardware_model + device_type pair so x86 readouts make sense. Lib import note: lib/telemetry pulls in anthias_app.models.Asset for the count query, matching the existing celery_tasks.py pattern (this is a server-side helper, never imported by the viewer). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
device_id is a non-cryptographic identifier — it just needs to be unique enough for GA to count distinct devices — but Sonar flags random.choice with a generic "weak crypto" warning that fails the quality gate. secrets.choice is a one-line drop-in with the same output shape, costing nothing at this volume. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
/etc/os-release inside the anthias-server container is the image's base layer (Debian trixie pinned in the Dockerfile), not the host's OS. The field would be near-constant across every deployment and tells us nothing about user environments, so it's just schema noise. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Anthias only ships as Docker containers; the field would be a constant True across every deployment. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
8 tasks
vpetersson
added a commit
that referenced
this pull request
May 1, 2026
Resolve conflict in tests/test_updates.py — the migration's pytest function-style version supersedes master's stale unittest_parametrize remnant. Convert tests/test_telemetry.py (added on master via #2798) from unittest.TestCase to pytest function-style + fixtures to match the rest of the migrated suite. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
vpetersson-bot
added a commit
to vpetersson-bot/Anthias
that referenced
this pull request
Aug 7, 2026
The GA4 "Version distribution" report reads a `version_name` dimension that nothing has ever sent — not this payload, and not the pre-Screenly#2798 one either, whose `Pi_Version` was the *hardware* model. So the report showed ~3,700 devices as "(not reported)" and could never have worked. - add `version_name` from get_anthias_release() (pyproject.toml, which ships in the image) rather than an env var, since missing env vars are the known failure mode for telemetry params. Verified inside anthias-anthias-celery-1 on the pi5 testbed: resolves to '2026.7.3' with no extra plumbing - fall back to 'unknown' rather than '', so a device whose version lookup fails gets its own bucket instead of silently rejoining the "(not reported)" pile that hid this - correct the docstring: the event was renamed `version` -> `device_active` in Screenly#2798 and the docstring still said `version`
This was referenced Aug 7, 2026
vpetersson
pushed a commit
that referenced
this pull request
Aug 7, 2026
The GA4 "Version distribution" report reads a `version_name` dimension that nothing has ever sent — not this payload, and not the pre-#2798 one either, whose `Pi_Version` was the *hardware* model. So the report showed ~3,700 devices as "(not reported)" and could never have worked. - add `version_name` from get_anthias_release() (pyproject.toml, which ships in the image) rather than an env var, since missing env vars are the known failure mode for telemetry params. Verified inside anthias-anthias-celery-1 on the pi5 testbed: resolves to '2026.7.3' with no extra plumbing - fall back to 'unknown' rather than '', so a device whose version lookup fails gets its own bucket instead of silently rejoining the "(not reported)" pile that hid this - correct the docstring: the event was renamed `version` -> `device_active` in #2798 and the docstring still said `version` Co-authored-by: vpetersson-bot <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
device_activeevent off the synchronous request path and into a Celery beat task that ticks hourly./tmpand resets on every container restart).timeout=5, broadRequestExceptioncatch.Why
The existing GA call lived inline in
lib/github.py:is_up_to_date(), which is invoked on the home-page render and the/api/v2/infoendpoint. Two separate failure modes:remote_branch_available()paginates on the GitHub API and silently droppedmaster, sofetch_remote_hash()always returned(None, False)andis_up_to_date()early-returned before reaching the GA block. (Fixed in fix(server): unblock migrations + fix upstream version check #2797.)timeout=on the POST, onlyConnectionErrorwas caught, so any HTTPError/Timeout would 500 the request handler. The call was on the sync request path.Periodic Celery beat is the natural home: off the request path, runs whether or not anyone hits the UI, easy to extend to other periodic metrics later.
Cadence + cooldown
add_periodic_task(3600, send_telemetry_task.s(), name='telemetry'))send_telemetry()checkstelemetry-cooldownin Redis; if set within the last 24h, returns without sendingTELEMETRY_COOLDOWN_TTL = 86400This combination gives at-most-one event per device per 24h. Anthias's celerybeat schedule lives at
/tmp/celerybeat-schedule(perdocker-compose.yml.tmpl) and resets on every container restart, so a pure interval would never accumulate the 24h gap on devices that reboot daily. The Redis-backed cooldown survives via the persistedredis-datavolume.Schema refresh
Old payload was Pi-centric and missed x86 deployments entirely. New schema (snake_case per GA4 convention):
branch,commit_shortdevice_typeDEVICE_TYPEenv (pi4-64/pi5/x86)hardware_model/proc/cpuinfomodelPi_Version, renamed for x86is_balena,is_dockeros_release/etc/os-releasePRETTY_NAMEresolution,audio_outputsettingstls_enabledsettings['use_ssl']asset_count,asset_image_count,asset_video_count,asset_webpage_countDropped:
NOOBS— retired upstream, dead signalPi_Version— replaced by neutralhardware_model+device_typepairEvent name changed from
version→device_activeso the WAU/MAU intent is explicit in GA Explorer.What you can answer with the new data
client_idper day, native GA report)device_type×hardware_modelbranch×commit_shortresolutiontls_enabledasset_countasset_*_countTest plan
ruff check+ruff format --checkpasstests/test_telemetry.pycovers: cooldown, opt-out, CI gate, timeout/ConnectionError swallowing, device_id reuse + generation, payload shape including new fieldsdevice_activeevents post-deploy🤖 Generated with Claude Code