You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Background
The activity feed's SSE-plus-polling-fallback design (per .env.example's NEXT_PUBLIC_ACTIVITY_REFRESH_MS documentation) implicitly assumes a single dashboard server instance holding open SSE connections and pushing events as they're received (e.g. from an incoming webhook). This is a common and reasonable starting design, but doesn't scale horizontally: if the dashboard runs as multiple instances behind a load balancer, a webhook processed by instance A has no way to notify SSE clients connected to instance B.
Problem
As currently designed, running more than one dashboard instance for availability or load would silently break real-time activity delivery for a subset of connected clients (those attached to an instance that isn't the one handling the relevant webhook), with no visible error — clients would just quietly fall back to the polling interval, defeating the purpose of the SSE fast-path.
Expected outcome
Activity events are published to a shared pub/sub layer (e.g. Postgres LISTEN/NOTIFY if Issue #6's Postgres-backed durable mode is in place — a pragmatic choice that avoids introducing a new infra dependency like Redis solely for this — or an explicitly justified alternative) that any dashboard instance can subscribe to, so an event ingested by any instance is broadcast to all SSE clients connected to any instance.
Suggested implementation
Evaluate and explicitly document the trade-off between Postgres LISTEN/NOTIFY (no new infra, payload size limits, at-most-once semantics) vs. introducing Redis pub/sub (a new required dependency, better throughput/features) — pick one with justification rather than defaulting silently.
Implement a small publish/subscribe abstraction so the SSE route subscribes on connection and republishes matching events to its connected clients.
Ensure the reconnect/backfill behavior from Issue Improve IntegrationClient error handling #7 still works correctly in a multi-instance world (a client's backfill request may land on a different instance than the one it was previously connected to, so backfill must read from shared durable storage, not instance-local memory).
Add a way to run and test this locally with at least two dashboard instances (e.g. two pnpm dev processes on different ports, or documented Docker Compose setup) to validate cross-instance delivery.
Clearly document that this feature depends on durable storage mode (Issue Add a server-wide role sync command for admins #6); mock mode can retain today's single-instance-only SSE behavior with a note in the docs.
Acceptance criteria
With two dashboard instances running against the same durable backend, a webhook processed by instance A results in the event reaching an SSE client connected to instance B.
The chosen pub/sub mechanism and its trade-offs are documented in the PR/design doc.
Difficulty: Expert
Type: Performance
Background
The activity feed's SSE-plus-polling-fallback design (per
.env.example'sNEXT_PUBLIC_ACTIVITY_REFRESH_MSdocumentation) implicitly assumes a single dashboard server instance holding open SSE connections and pushing events as they're received (e.g. from an incoming webhook). This is a common and reasonable starting design, but doesn't scale horizontally: if the dashboard runs as multiple instances behind a load balancer, a webhook processed by instance A has no way to notify SSE clients connected to instance B.Problem
As currently designed, running more than one dashboard instance for availability or load would silently break real-time activity delivery for a subset of connected clients (those attached to an instance that isn't the one handling the relevant webhook), with no visible error — clients would just quietly fall back to the polling interval, defeating the purpose of the SSE fast-path.
Expected outcome
Activity events are published to a shared pub/sub layer (e.g. Postgres
LISTEN/NOTIFYif Issue #6's Postgres-backeddurablemode is in place — a pragmatic choice that avoids introducing a new infra dependency like Redis solely for this — or an explicitly justified alternative) that any dashboard instance can subscribe to, so an event ingested by any instance is broadcast to all SSE clients connected to any instance.Suggested implementation
LISTEN/NOTIFY(no new infra, payload size limits, at-most-once semantics) vs. introducing Redis pub/sub (a new required dependency, better throughput/features) — pick one with justification rather than defaulting silently.IntegrationClienterror handling #7 still works correctly in a multi-instance world (a client's backfill request may land on a different instance than the one it was previously connected to, so backfill must read from shared durable storage, not instance-local memory).pnpm devprocesses on different ports, or documented Docker Compose setup) to validate cross-instance delivery.durablestorage mode (Issue Add a server-wide role sync command for admins #6); mock mode can retain today's single-instance-only SSE behavior with a note in the docs.Acceptance criteria
IntegrationClienterror handling #7) continues to function correctly across instances.pnpm typecheckpass.Likely affected files/directories
Activity SSE route (
apps/dashboard/app/api/activity/**or equivalent), new pub/sub abstraction module, webhook handler (publish side),apps/dashboard/lib/storage/(if reusing Postgres LISTEN/NOTIFY), documentation/dev tooling for local multi-instance testing.