fix(#255): disconnect socket.io redis-adapter pub/sub clients on shutdown #256
Reference in New Issue
Block a user
Delete Branch "fix/255-ws-redis-adapter-leak"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Чинит #255: pub/sub ioredis-клиенты socket.io Redis-адаптера (
ws-redis.adapter.ts,connectToRedis()) нигде не дисконнектились → латентная утечка 2 сокетов на shutdown. Closes #255.Адаптер создаётся вручную в main.ts (не DI-провайдер), redis-adapter не владеет lifecycle клиентов, ссылок на них не было. Трасса shutdown:
app.enableShutdownHooks()включён + есть реальный@WebSocketGateway→ SocketModule загружен → наapp.close()SocketModule.close()зовётadapter.close()по серверам, затемadapter.dispose()РОВНО один раз (no-op в AbstractWsAdapter). Это правильное единственное место для teardown адаптер-owned ресурсов.Фикс: держим ссылки на pubClient/subClient, переопределяем
dispose()→super.dispose()+disconnect(false)на оба клиента + null refs (guard от double-close).disconnect(false)— как в redis-sync.extension (idle pub/sub, без QUIT-раунда, без авто-reconnect).How verified
connectToRedis()против живого redis +process._getActiveHandles(): 0 redis-сокетов до, 2 после connect (pub+sub), 0 после dispose() → PASS.@nestjs/websocketssocket-module.js (close()→adapter.dispose()).Review checklist
🤖 Generated with Claude Code
The pub/sub error handlers were `(err) => () => {}` — a noop returning an inner arrow that never runs, so socket.io redis client errors were silently swallowed. Log them via Nest Logger. Adjacent pre-existing bug surfaced in review of #255. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Внутренний architect-lead review (мой review-субагент) по
a0f4c86a: APPROVE. Подтверждено: SocketModule.close() зовёт adapter.dispose() ровно один раз на app.close() (shutdown-hooks включены, есть @WebSocketGateway → SocketModule загружен); super.dispose() базового IoAdapter — no-op; оба клиента сохранены и дисконнектятсяdisconnect(false)(как redis-sync.extension) + null-guard от double-close; рантайм ws не затронут; use-after-close нет (io-серверы закрываются до disconnect).Попутно (тот же файл, flagged ревью как pre-existing) пофикшено в
82b04220: error-handlers были(err) => () => {}(noop, внутренняя стрелка не вызывается → ошибки redis-клиентов молча глотались) → теперь логируются через Nest Logger.Голова →
82b04220, review/needs.Ревью
82b042209— раунд 1 (ПОЛНЫЕ 8 аспектов, отдельный субагент на каждый). Вердикт: approved.Все 8 — LGTM. dispose() закрывает оба pub/sub-клиента socket.io redis-adapter через disconnect(false) (зеркалит sibling redis-sync.extension.onDestroy), вызывается NestJS SocketModule один раз на shutdown ПОСЛЕ закрытия socket.io-серверов — рантайм-WS не рвётся; владелец клиентов = адаптер, слой выбран верно (dispose — канонический хук IoAdapter, DI-хуки ему недоступны). Бонус: прежний баг-глушитель ошибок (err)=>()=>{} заменён реальным logger.error (improvement observability). Покрытие: идентичный sibling тоже не юнит-тестируется, эффект (утечка TCP-хендлов на shutdown) проверяется интеграционно — доп. тест выше планки проекта.
⛔ DROP (кодеру НЕ делать · калибровка):