Changeset 0.23.1 (#154)

This commit is contained in:
2026-03-06 13:26:25 +00:00
parent 2fc620e1ac
commit 4c6555cb06
38 changed files with 1536 additions and 4031 deletions

View File

@@ -10,9 +10,10 @@ import (
// Handlers subscribe to patterns (exact or trailing wildcard).
// Publishing fans out to all matching subscribers.
type Bus struct {
mu sync.RWMutex
subs map[string][]*subscription
seq uint64 // subscription ID counter
mu sync.RWMutex
subs map[string][]*subscription
seq uint64 // subscription ID counter
broadcastHook func(Event) // called after Publish for cross-pod fan-out; nil-safe
}
type subscription struct {
@@ -28,6 +29,15 @@ func NewBus() *Bus {
}
}
// SetBroadcastHook registers a function called after every Publish.
// Used by the Postgres LISTEN/NOTIFY adapter to fan out across pods.
// The hook is NOT called by publishLocal (avoids re-broadcast loops).
func (b *Bus) SetBroadcastHook(fn func(Event)) {
b.mu.Lock()
b.broadcastHook = fn
b.mu.Unlock()
}
// Subscribe registers a handler for a label pattern.
// Returns an unsubscribe function.
//
@@ -60,7 +70,22 @@ func (b *Bus) Subscribe(pattern string, handler Handler) func() {
// Publish dispatches an event to all matching subscribers.
// Handlers are called synchronously in subscription order.
// Use PublishAsync for non-blocking dispatch.
// After local dispatch, calls the broadcastHook if set (Postgres fan-out).
func (b *Bus) Publish(event Event) {
b.publishLocal(event)
b.mu.RLock()
hook := b.broadcastHook
b.mu.RUnlock()
if hook != nil {
hook(event)
}
}
// publishLocal dispatches to local subscribers only, without triggering
// the broadcastHook. Used by the Postgres listener to re-publish remote
// events without causing an infinite re-broadcast loop.
func (b *Bus) publishLocal(event Event) {
b.mu.RLock()
var matched []Handler
for pattern, subs := range b.subs {
@@ -79,6 +104,7 @@ func (b *Bus) Publish(event Event) {
// PublishAsync dispatches an event to all matching subscribers
// in separate goroutines. Useful for I/O-heavy handlers.
// Also calls the broadcastHook asynchronously if set.
func (b *Bus) PublishAsync(event Event) {
b.mu.RLock()
var matched []Handler
@@ -89,11 +115,15 @@ func (b *Bus) PublishAsync(event Event) {
}
}
}
hook := b.broadcastHook
b.mu.RUnlock()
for _, h := range matched {
go h(event)
}
if hook != nil {
go hook(event)
}
}
// match checks if a concrete label matches a subscription pattern.