mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-25 04:17:15 +00:00
fix(eventbus): deliver events on a bounded per-subscriber worker
The previous fix dispatched each event to every subscriber with a bare `go safeCall`. That unblocked the dispatch loop, but removed the bus's backpressure: under a login-attempt flood (which both notifier subscribers process without rate-limiting) with email/Telegram enabled, every attempt spawned handler goroutines that each block on network I/O for up to ~30s, with no bound — a goroutine and outbound-connection storm. It also let a subscriber's handler run concurrently with itself, racing the Telegram notifier's lazily-cached hostname. Give each subscriber its own bounded queue drained by a single worker goroutine. Dispatch does a non-blocking send per subscriber (dropping only that subscriber's event when its queue is full), so a slow subscriber still can't stall the others, concurrency is bounded to one in-flight handler per subscriber, per-subscriber event order is preserved, and Stop again waits for in-flight handlers to finish.
This commit is contained in:
@@ -174,6 +174,43 @@ func TestBusBlockingSubscriberDoesNotStallOthers(t *testing.T) {
|
||||
close(release)
|
||||
}
|
||||
|
||||
func TestBusSubscriberRunsSerially(t *testing.T) {
|
||||
b := New(16)
|
||||
defer b.Stop()
|
||||
|
||||
var inFlight atomic.Int32
|
||||
var maxSeen atomic.Int32
|
||||
var wg sync.WaitGroup
|
||||
const n = 8
|
||||
wg.Add(n)
|
||||
|
||||
b.Subscribe("serial", func(Event) {
|
||||
cur := inFlight.Add(1)
|
||||
for {
|
||||
m := maxSeen.Load()
|
||||
if cur <= m || maxSeen.CompareAndSwap(m, cur) {
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
inFlight.Add(-1)
|
||||
wg.Done()
|
||||
})
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
b.Publish(Event{Type: EventXrayCrash})
|
||||
}
|
||||
|
||||
select {
|
||||
case <-waitDone(&wg):
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("subscriber did not process all events")
|
||||
}
|
||||
if got := maxSeen.Load(); got != 1 {
|
||||
t.Fatalf("subscriber ran concurrently with itself: max in-flight = %d, want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBusBufferFull(t *testing.T) {
|
||||
b := New(2)
|
||||
defer b.Stop()
|
||||
|
||||
Reference in New Issue
Block a user