fbb1f34e9a
Adds the per-instance Redis Stream consumer group (live-broadcast-{instance_id})
that reads the telemetry stream and fans out each position to subscribed
WebSocket connections without affecting the durable-write consumer path.
Key changes:
- src/shared/codec.ts: moved decodePosition/CodecError out of src/core/ so
src/live/broadcast.ts can decode positions without crossing the enforced
src/core/ ↔ src/live/ boundary; src/core/codec.ts now re-exports from there
- src/shared/types.ts: added Position and AttributeValue (same move, same reason);
src/core/types.ts re-exports both to preserve existing import paths
- src/live/broadcast.ts: createBroadcastConsumer factory — XREADGROUP loop,
immediate ACK semantics, toPositionMessage mapper, fanOut per event/topic
- src/live/device-event-map.ts: createDeviceEventMap factory — in-memory cache
of entry_devices × entries join, refreshed every LIVE_DEVICE_EVENT_REFRESH_MS
- src/db/migrations/0002_positions_faulty.sql: adds faulty boolean column and
positions_device_ts_idx for snapshot-on-subscribe query (task 1.5.5)
- src/main.ts: wired authClient, authzClient, registry, liveServer,
deviceEventMap, broadcastConsumer; shutdown chain: liveServer → deviceEventMap
+ broadcastConsumer → durable-write consumer → metricsServer → Redis → Postgres
- test/live-broadcast.test.ts: 4 unit tests covering single subscriber, multiple
subscribers, orphan device, and multi-event device fan-out
20 lines
1.0 KiB
SQL
20 lines
1.0 KiB
SQL
-- Migration: 0002_positions_faulty
|
|
-- Adds the faulty column to positions and ensures the (device_id, ts DESC) index
|
|
-- needed by the snapshot-on-subscribe query exists.
|
|
--
|
|
-- The faulty column is set post-hoc by operators in Directus when a position is
|
|
-- flagged as unrealistic. The snapshot-on-subscribe query (task 1.5.5) filters
|
|
-- WHERE faulty = false to exclude flagged positions from the initial map state.
|
|
-- The live broadcast path (Redis stream → fan-out) never touches this column
|
|
-- because faulty flags are applied after the fact.
|
|
|
|
ALTER TABLE positions
|
|
ADD COLUMN IF NOT EXISTS faulty boolean NOT NULL DEFAULT false;
|
|
|
|
-- Index for the snapshot DISTINCT ON query:
|
|
-- SELECT DISTINCT ON (device_id) ... ORDER BY device_id, ts DESC
|
|
-- TimescaleDB scans only the latest chunks for devices with recent activity,
|
|
-- but the (device_id, ts DESC) index makes per-device latest-position lookups
|
|
-- efficient regardless of chunk age.
|
|
CREATE INDEX IF NOT EXISTS positions_device_ts_idx ON positions (device_id, ts DESC);
|