# Mutex без defer — deadlock при панике **Контекст**: В `backend/internal/middleware/ratelimit.go:61` мьютекс блокируется без `defer Unlock()`. Если код между Lock и Unlock запаникует, мьютекс останется заблокированным навсегда. ## Проблема ```go // ratelimit.go:57-76 — ❌ нет defer func (rl *RateLimiter) Middleware() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := GetClientIP(r) rl.mu.Lock() limiter, ok := rl.visitors[ip] if !ok { limiter = rate.NewLimiter(rl.rate, rl.burst) rl.visitors[ip] = limiter } rl.lastSeen[ip] = time.Now() rl.mu.Unlock() // ❌ если паника в GetClientIP, NewLimiter или другой строке — deadlock if !limiter.Allow() { http.Error(w, `{"error":"rate limit exceeded"}`, 429) return } next.ServeHTTP(w, r) }) } } ``` ## Решение ```go // ✅ defer гарантирует разблокировку даже при панике func (rl *RateLimiter) Middleware() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := GetClientIP(r) rl.mu.Lock() defer rl.mu.Unlock() // ✅ всегда разблокируется limiter, ok := rl.visitors[ip] if !ok { limiter = rate.NewLimiter(rl.rate, rl.burst) rl.visitors[ip] = limiter } rl.lastSeen[ip] = time.Now() if !limiter.Allow() { http.Error(w, `{"error":"rate limit exceeded"}`, 429) return } next.ServeHTTP(w, r) }) } } ``` **Почему Go-сообщество требует `defer`**: мьютекс должен быть разблокирован в любом сценарии — нормальное завершение, return, паника. `defer` гарантирует это. ## Исключение Единственный случай, когда `defer` не обязателен — если между Lock и Unlock нет ни одного вызова функции, который мог бы запаниковать (чистые присваивания). Но это хрупкое предположение. ## Связанные заметки - [[backend-rate-limiting]] — где находится проблемный код - [[MOC-backend-patterns]] — таблица антипаттернов ## Источник Code review PhotoPlaces 2026-06. `ratelimit.go:61`. ## Теги #golang #concurrency #mutex #panic #best-practice #bug