# broadcastVisitors: двойной проход по map под lock **Контекст:** В `WSHub.broadcastVisitors` было два последовательных прохода по `h.clients` под одним lock — сначала сбор `visitors`, затем сбор `clients`. Между проходами состояние не меняется (lock удерживается), но второй проход избыточен. ## Проблема ```go // Было: два прохода под одним lock h.mu.Lock() for c := range h.clients { // 1-й проход: сбор visitors visitors = append(visitors, ...) } for c := range h.clients { // 2-й проход: сбор clients (дубликат) clients = append(clients, c) } h.mu.Unlock() ``` 1. Два прохода по `map[*Client]bool` — лишняя работа 2. После unlock рассылка идёт по снэпшоту `clients` — состояние может устареть (client уже отключился), но это приемлемо ## Решение Один проход: ```go h.mu.Lock() for c := range h.clients { visitors = append(visitors, VisitorDot{...}) clients = append(clients, c) } h.mu.Unlock() ``` Файл: `backend/internal/handlers/websocket.go:91-106` ## Связанные заметки - [[architecture-overview]] — таблица P2 - [[atomic-websocket-origin-check]] — WebSocket origin check ## Источник Code review 2026-07-01, P2 fix. #backend #websocket #concurrency #performance