Skip to content

feat(live): add live signal-streaming service#190

Merged
BK1031 merged 3 commits into
mainfrom
bk1031/feat-live-service
Jun 5, 2026
Merged

feat(live): add live signal-streaming service#190
BK1031 merged 3 commits into
mainfrom
bk1031/feat-live-service

Conversation

@BK1031
Copy link
Copy Markdown
Contributor

@BK1031 BK1031 commented Jun 5, 2026

  • New off-car service live that fans out decoded signals from query/live/# to downstream WS/SSE consumers
  • MQTT subscription uses no $share/ prefix so every replica caches the full firehose; clients can land on any replica via round-robin
  • SignalHub supports exact subscriptions, glob patterns (ecu_*, *_status, *) and per-publish dedup across both routes
  • 60s per-(vehicle, signal_name) ring buffer evicted by producer timestamp; snapshot drives initial backfill on connect/reconnect
  • WS endpoint at /live/ws, SSE at /live/sse; SSE Last-Event-ID is the last signal microsecond timestamp and resumes from there
  • gr26's in-pod hub stays in place for legacy clients / TCM dash / debugging — this is additive
  • Wires up: docker-compose.yaml (port 7015), kerbecs.yaml (ws/sse passthrough + default), live.yml GH workflow modeled on gr26.yml, deploy.yml SERVICES list + awk regex, scripts/release.sh GO_DEPENDENTS + GO_CONFIG_SERVICES
  • Follow-up: Gaucho-Racing/infrastructure kustomization needs a mapache/live image entry before the first release will deploy it

BK1031 added 3 commits June 5, 2026 04:39
New off-car service that fans out decoded signals to downstream WS/SSE
consumers and serves a 60s in-memory backfill window to bridge Clickhouse
ingestion delay.

- Subscribes to query/live/# on MQTT without $share/ so every replica
  caches the full firehose; clients can land on any replica.
- SignalHub supports exact subscriptions, glob patterns (path.Match:
  ecu_*, *_status, *) and per-publish dedup across both routes.
- Per-(vehicle, signal_name) ring buffer evicts by producer timestamp;
  snapshot drives initial backfill on connect/reconnect.
- WS (/live/ws) and SSE (/live/sse) endpoints share the hub. SSE
  Last-Event-ID is the last signal microsecond timestamp and is used as
  the resume cursor.
- gr26's in-pod hub remains in place for legacy/TCM-dash/debugging.

Adds the service to docker-compose, kerbecs routing (ws + sse as
passthrough), the release script, deploy workflow, and a per-service
image-build workflow modeled on gr26.yml.
gr26 now stamps signals[i].CreatedAt = time.Now().UTC() in CreateSignals
so the field is meaningful on the MQTT wire (previously zero, since
Clickhouse's DEFAULT now64(6) only applies to the persisted row, not
the published struct). live's cache eviction, Snapshot filter, suppress
maps, and SSE event ids all switch to CreatedAt — a wall-clock anchor
that doesn't drift with producer/CAN-frame clock skew.

Backfill is now sent as a single batched message per transport instead
of one frame/event per signal:
  - WS: first frame is always a JSON array (possibly empty); subsequent
    frames are single Signal objects. Clients distinguish by Array.isArray.
  - SSE: `event: backfill` carries the array in `data:`; `event: signal`
    carries one Signal per event. `id:` on both is CreatedAt micros.

CH insert keeps using the now64(6) default — created_at is not added to
the INSERT column list, so persisted timestamps remain server-authoritative.
@BK1031 BK1031 merged commit 1bd59d4 into main Jun 5, 2026
20 checks passed
@BK1031 BK1031 deleted the bk1031/feat-live-service branch June 5, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant