Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
da967f1
docs: P4.2 live monitor + SSE design spec
Jul 18, 2026
046ccaa
docs: P4.2 live monitor implementation plan
Jul 18, 2026
86260d4
feat(backend): event broker — interface, in-memory fanout, pg LISTEN/…
Jul 18, 2026
90f58f3
feat(backend): monitor snapshot aggregations
Jul 18, 2026
822ac4d
feat(backend): monitor snapshot endpoint
Jul 18, 2026
4b013d7
test(backend): pin computeRates window boundaries and peak tie-break
Jul 18, 2026
ee3b254
feat(backend): SSE monitor stream + check-in event publishes
Jul 18, 2026
99a73f7
feat(panel): monitor data layer + SSE frame parser
Jul 18, 2026
d906147
feat(panel): monitor SSE stream hook with coalescing and backoff
Jul 18, 2026
160ceeb
feat(panel): monitor route, page shell, totals and zones cards
Jul 18, 2026
1fefc23
feat(panel): monitor stations liveness and read-only recent feed
Jul 18, 2026
7eb4d33
feat(panel): live-strip on monitor snapshot + open-monitor CTA + zone…
Jul 18, 2026
5a231b8
chore(panel): P4.2 final verification sweep
Jul 18, 2026
368b69f
fix(backend): PR #81 bot-review round — atomic monitor overview, undo…
Jul 19, 2026
09cb230
fix(backend): PR #81 bot round — reconnect broadcast, detached bounde…
Jul 19, 2026
8d25db2
fix(backend): count sync-created attendees toward monitor publish events
Jul 19, 2026
48e580e
fix(panel): PR #81 bot round — StatusPill primitives, stream URL/auth…
Jul 19, 2026
c759439
fix(backend): gate batch publishes to registration check-ins, event-s…
Jul 19, 2026
d489766
fix(panel): shared SSE transport seam, accessible feed action labels,…
Jul 19, 2026
f0e8905
fix(backend): sanitized LISTEN conn config, current-period zone attri…
Jul 19, 2026
1881059
fix(ui): bare StatusPill real sr-only text + visible online label for…
Jul 19, 2026
76a16c1
fix(backend): PR #81 round 4 — broker pool minima clamp, attribution …
Jul 19, 2026
cc6cb14
fix(backend): publish monitor events from external import and attende…
Jul 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions backend/internal/broker/broker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Package broker provides the event-fanout infrastructure backing the
// P4.2 live monitor's SSE stream — the codebase's first pub/sub seam. Its
// entire job is: let a Publish(eventID) call wake up every goroutine
// currently Subscribed to that event, without ever blocking the publisher.
//
// This package deliberately does not import the store package (and must
// never be imported BY it either) — it knows nothing about attendees,
// check-ins, tenants, or Postgres schema beyond a bare event UUID. That
// keeps the seam reusable and testable in isolation: MemBroker needs no
// database at all, and PGBroker's Postgres-specific logic is confined to
// pg_broker.go.
package broker

import (
"context"

"github.com/google/uuid"
)

// Broker is the seam the P4.2 monitor SSE handler (and check-in/undo/
// reprint/heartbeat publish sites) depend on.
type Broker interface {
// Publish signals that eventID's monitor-visible state has changed.
// Implementations must never block on a slow or absent subscriber.
Publish(ctx context.Context, eventID uuid.UUID) error

// Subscribe registers interest in eventID's changes. The returned
// channel is 1-buffered: a pending signal coalesces with any later
// Publish while it remains unread (drop-if-full) — a slow consumer
// therefore never blocks the fanout and never accumulates an unbounded
// backlog; it just eventually reads one signal and re-syncs from
// scratch (this is what the SSE handler pairs with a full snapshot
// re-fetch on every "update" frame, so a coalesced signal never means
// stale data). The returned unsubscribe func is idempotent and safe to
// call concurrently with Publish and with itself.
//
// The channel is NEVER closed — not by Publish, not by unsubscribe, not
// by the broker shutting down. A consumer MUST therefore select on it
// alongside at least one other case (a request context's Done() and/or
// a keep-alive ticker, as the P4.2 SSE handler does) and never `range`
// over it, which would block forever instead of observing shutdown.
Subscribe(eventID uuid.UUID) (<-chan struct{}, func())
}
Loading
Loading