chore(push-gateway): ai02 module updates - config, hub, ws, kafka, health, docs

This commit is contained in:
SpecialX
2026-07-10 17:36:53 +08:00
parent dc0a6feec4
commit 3fac472a57
16 changed files with 2017 additions and 289 deletions

View File

@@ -172,6 +172,15 @@ func (h *Hub) HasUser(userID string) bool {
return ok && len(conns) > 0
}
// IsClosing reports whether CloseAll has been invoked. The /readyz probe uses
// this to return 503 during graceful shutdown so the load balancer stops
// sending new WebSocket upgrades while existing connections drain.
func (h *Hub) IsClosing() bool {
h.mu.RLock()
defer h.mu.RUnlock()
return h.closing
}
// Register adds a new connection for userID. Returns ErrTooManyConnections
// when the per-user cap is exceeded and ErrHubClosing when the Hub is shutting
// down. The returned Connection must be Unregistered by the caller on close.
@@ -262,7 +271,13 @@ func (h *Hub) SendToUser(userID string, message []byte) int {
// Cross-instance broadcast is handled separately via Redis Pub/Sub.
func (h *Hub) Broadcast(message []byte) int {
h.mu.RLock()
snapshot := make([]*Connection, 0, h.ActiveConnections())
// Count inline: calling ActiveConnections() here would re-enter RLock
// while we already hold it, which can deadlock if a writer is waiting.
total := 0
for _, conns := range h.clients {
total += len(conns)
}
snapshot := make([]*Connection, 0, total)
for _, conns := range h.clients {
for _, c := range conns {
snapshot = append(snapshot, c)
@@ -285,7 +300,13 @@ func (h *Hub) Broadcast(message []byte) int {
func (h *Hub) CloseAll() {
h.mu.Lock()
h.closing = true
all := make([]*Connection, 0, h.ActiveConnections())
// Count inline instead of calling ActiveConnections() to avoid reentrant
// RLock while holding the write lock (Go's sync.RWMutex is non-reentrant).
total := 0
for _, conns := range h.clients {
total += len(conns)
}
all := make([]*Connection, 0, total)
for _, conns := range h.clients {
for _, c := range conns {
all = append(all, c)