«MySQL server has gone away» после пяти минут простоя: приложение держит соединение, которое база уже закрыла

Что было. Веб-приложение на Django в Docker, база данных MariaDB в соседнем контейнере. Пока приложение под нагрузкой, всё хорошо. После нескольких минут простоя первый же запрос падает с OperationalError (2006, 'Server has gone away'). Перезапуск контейнера помогает до следующей паузы.

Почему. Сервер базы данных закрывает соединения, которые молчат дольше wait_timeout. Приложение об этом не знает: оно держит соединение открытым в пуле и при первом запросе пишет в уже мёртвый сокет. Ответ: 2006. Разработчики закрыли обращение как «не ошибка приложения»: это вопрос согласования настроек. Отдельный случай той же ошибки: запрос или результат больше max_allowed_packet, тогда сервер обрывает соединение сразу, без простоя.

Как выяснить, какой из двух вариантов.

  • Ошибка появляется только после паузы: таймаут простоя.
  • Ошибка на конкретном большом запросе или импорте дампа: размер пакета. В журнале сервера будет упоминание packet.
  • Значения на сервере: SHOW VARIABLES LIKE 'wait_timeout'; SHOW VARIABLES LIKE 'max_allowed_packet';. Образы контейнеров часто идут с агрессивными таймаутами.

Что настроить.

  1. Со стороны приложения (предпочтительно): время жизни соединения в пуле меньше серверного таймаута. В Django это CONN_MAX_AGE, в SQLAlchemy pool_recycle плюс pool_pre_ping=True, в Node-драйверах idleTimeout и проверка при выдаче из пула. Соединение старше порога переоткрывается заранее, а не по ошибке.
  2. Со стороны сервера: увеличить wait_timeout для длительных фоновых задач (например, до 3600) и max_allowed_packet под размер данных. Менять в конфиге сервера, а не только в сессии.
  3. Автопереподключение в драйвере как страховка, но не как единственная мера: первый запрос после разрыва всё равно потеряется, если драйвер не повторяет его сам.

Про Docker. Отдельная тема из обсуждений: сетевые таймауты между контейнерами при неактивности (например, если между ними стоит прокси или включён IPv6 без маршрута). Проверить docker network inspect и попробовать отключить IPv6 в сети compose, если ошибка возникает даже при коротких простоях.

Причины ошибки перечислены в документации MySQL.