feat(scale): swappable pub/sub layer + fix chat.js missed awaits (Phase 6)
Two things: 1. FIX a live regression the async conversion missed: chat.js calls repos via the lazy repos() helper (not the R. prefix), so my sweep skipped it — effectiveStatus / broadcastPresence read `repos().users.byId(userId)` synchronously, but that's a Promise now, so presence broadcasts always reported status 'active' and dropped last_seen. Now awaited (effectiveStatus/broadcastPresence async); touchSeen is a fire-and-forget UPDATE with .catch. Audited all non-R. repo calls — only chat.js was affected (media.js backfill was already awaited). 2. Swappable pub/sub for multi-instance real-time fan-out (the actual blocker to running >1 instance — not the DB). server/pubsub.js picks a backend by PUBSUB_BACKEND (default 'memory'). Local socket delivery is UNCHANGED; publish is additive — memory = no-op (zero hot-path cost, identical single-instance behaviour), redis = fan-out to other instances with a self-echo guard. chat.js pushToUser/broadcastPresence now also publish; each instance subscribes to deliver remote events to its local sockets. Interface is tiny so Redis is one swappable file (Postgres LISTEN/NOTIFY or NATS could drop in the same way — never hardwired, as requested). Dormant redis service added to compose behind the 'scale' profile; redis dep added; PUBSUB_BACKEND/REDIS_URL documented. Validated: smoke 22/22 (memory), e2e chat delivery green. NOTE: full multi-instance also needs distributed presence (isOnline is per-process) + meeting-signaling sharing — chat/presence fan out via this layer; those are follow-ups. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,20 @@ services:
|
||||
timeout: 3s
|
||||
retries: 12
|
||||
|
||||
# Redis — cross-instance real-time fan-out (chat/presence), used only when PUBSUB_BACKEND=redis. Dormant
|
||||
# by default (behind the 'scale' profile, like livekit), so a normal deploy never starts it and the app
|
||||
# stays single-instance on the in-memory pubsub. To run multiple app instances: start this
|
||||
# (`docker compose --profile scale up -d`), set PUBSUB_BACKEND=redis + REDIS_URL in .env, and put the app
|
||||
# behind a load balancer with sticky sessions for the /ws WebSocket. (Meeting SIGNALING state is still
|
||||
# per-process — cross-instance meetings need sticky routing or further work; chat/presence fan out here.)
|
||||
bizgazeredis:
|
||||
image: redis:7-alpine
|
||||
container_name: bizgaze-redis
|
||||
restart: unless-stopped
|
||||
profiles: ["scale"]
|
||||
networks:
|
||||
- npm
|
||||
|
||||
# LiveKit SFU — meeting media server. Optional: only started/used when the app's .env has
|
||||
# LIVEKIT_URL/API_KEY/API_SECRET set (otherwise meetings use the built-in P2P mesh). NPM proxies
|
||||
# wss://livekit.bizgaze.com -> livekit:7880 (signaling); media flows over the published UDP/TCP
|
||||
|
||||
Reference in New Issue
Block a user