45 lines
1.2 KiB
Go
45 lines
1.2 KiB
Go
package cdc
|
|
|
|
import "sync"
|
|
|
|
// Broadcaster is the in-process fan-out the consumer feeds. Subscribers (the
|
|
// WS/SSE transport, wired in the frontend task) register a callback; every
|
|
// consumed Event is delivered to all current subscribers. It is the single
|
|
// seam between the CDC pipeline and live delivery, so the transport can be
|
|
// built and swapped without touching the pipeline.
|
|
type Broadcaster struct {
|
|
mu sync.RWMutex
|
|
nextID int
|
|
subs map[int]func(Event)
|
|
}
|
|
|
|
// NewBroadcaster returns an empty Broadcaster ready for subscriptions.
|
|
func NewBroadcaster() *Broadcaster {
|
|
return &Broadcaster{subs: map[int]func(Event){}}
|
|
}
|
|
|
|
// Subscribe registers fn and returns an unsubscribe function. fn is called
|
|
// synchronously from the consumer loop, so it must not block; a transport that
|
|
// needs buffering should push onto its own channel inside fn.
|
|
func (b *Broadcaster) Subscribe(fn func(Event)) (unsubscribe func()) {
|
|
b.mu.Lock()
|
|
id := b.nextID
|
|
b.nextID++
|
|
b.subs[id] = fn
|
|
b.mu.Unlock()
|
|
return func() {
|
|
b.mu.Lock()
|
|
delete(b.subs, id)
|
|
b.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
// Publish delivers e to every current subscriber.
|
|
func (b *Broadcaster) Publish(e Event) {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
for _, fn := range b.subs {
|
|
fn(e)
|
|
}
|
|
}
|