feat(iios): socket.io Redis adapter for cross-replica realtime (P9 scaling)

Adds an opt-in RedisIoAdapter (wired in main.ts when REDIS_URL is set) so
socket.io room emits fan out across instances via Redis pub/sub — a client on
replica B now receives messages emitted by replica A. Without REDIS_URL the
in-memory adapter is kept (single-instance dev unchanged). Adds redis to
docker-compose, REDIS_URL to the env contract, and smoke-realtime-cluster.mjs
which proves cross-instance delivery fails without Redis and passes with it.
Delivery is at-least-once at N>1; clients dedupe by message id (documented).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 19:28:11 +05:30
parent a520b26398
commit 427396653f
8 changed files with 237 additions and 8 deletions
+9 -4
View File
@@ -65,9 +65,15 @@ for the full, commented list. Highlights:
```
- **Postgres** — managed (RDS / Cloud SQL / Neon). One logical DB, tenant-isolated by scope.
- **Redis** — **not required for a single instance.** Add it when you run **multiple replicas
with live chat** (socket.io needs its Redis adapter to fan-out across instances) or when
BullMQ queues are introduced.
- **Redis** — **not required for a single instance.** Set **`REDIS_URL`** when you run
**multiple replicas with live chat**: the built-in [socket.io Redis adapter](../packages/iios-service/src/realtime/redis-io.adapter.ts)
fans room emits across all instances, so a client on replica B sees messages emitted by
replica A. Without `REDIS_URL` the in-memory adapter is used (single instance). Verified by
`scripts/smoke-realtime-cluster.mjs` (two instances + one Redis; cross-instance delivery
fails without Redis, passes with it).
> Delivery is **at-least-once** across instances — with N>1 the inline send-emit and the
> relay's outbox-bridge emit can both fire for the same message, so clients should **dedupe
> by message `id`** (the payload always carries one).
- **Platform ports** (OPA policy, CMP consent, MDM, CRRE, SAS) — today in-process permissive
stubs (`LocalDevPorts`). For production, point these at real external services; the service
already calls them **fail-closed**.
@@ -94,7 +100,6 @@ for the full, commented list. Highlights:
## Not yet built (prod-readiness gaps)
- **CI/CD pipeline** to build/push this image and run migrations.
- **Redis + socket.io Redis adapter** wiring (needed at N>1 with realtime).
- **Real platform-port services** (OPA/CMP/MDM) — today permissive stubs.
- **Secrets manager** integration (currently plain env).
- A **schema-compatibility gate** in CI to enforce expand-contract migrations.