StartClient builds a fresh sqlstore.Container at pkg/whatsmeow/service/whatsmeow.go:322 on every call. The container is a local variable and is never closed — there is no container.Close() anywhere in the file. Each call therefore leaks a whole database/sql pool.
This is not limited to the first connect: RestartInstance ends in Starting fresh instance -> StartInstance -> StartClient (whatsmeow.go:237), and WhatsApp drops the websocket on its own several times a day (Got 503 stream error, failed to read frame header: EOF), each drop triggering Disconnected detected, restarting instance.
Measured
v0.7.2, Postgres 15.6, a single connected instance, nobody using the panel: 17 backends on evogo_auth accumulated over 18h, with zero GET /instance/qr in the window.
- Connections appear in pairs — the
MaxIdleConns default of database/sql — and their backend_start timestamps match the restart lines in the log to the second.
- 6 reconnects in 18h, so ~16 connections/day:
max_connections=100 exhausted in ~6 days. We hit that twice.
- Once exhausted, QR generation stops and
sendText returns 500. The server then reconnects in a loop (11 Starting fresh instance in 80 seconds), burning whatever is left, so the saturation looks sudden.
- It scales with instance count — ten numbers would saturate in half a day.
Requesting QR is just another path into the same StartClient, so /instance/qr makes it faster but is not the cause.
Suggested fix
Create the container once per process (the DSN never changes) and reuse it, or keep a reference per instance and Close() the old one before starting fresh.
Workaround
For anyone hitting this before a fix lands:
ALTER DATABASE evogo_auth SET idle_session_timeout = '1h';
Postgres then reaps the orphaned pools. Safe for live sessions: credentials live in the whatsmeow_* rows, not in the TCP connection, and pgx discards a dead connection silently before use. Took us from 17 backends to 6 with no disconnect and no re-pairing.
StartClientbuilds a freshsqlstore.Containeratpkg/whatsmeow/service/whatsmeow.go:322on every call. The container is a local variable and is never closed — there is nocontainer.Close()anywhere in the file. Each call therefore leaks a wholedatabase/sqlpool.This is not limited to the first connect:
RestartInstanceends inStarting fresh instance->StartInstance->StartClient(whatsmeow.go:237), and WhatsApp drops the websocket on its own several times a day (Got 503 stream error,failed to read frame header: EOF), each drop triggeringDisconnected detected, restarting instance.Measured
v0.7.2, Postgres 15.6, a single connected instance, nobody using the panel: 17 backends on
evogo_authaccumulated over 18h, with zeroGET /instance/qrin the window.MaxIdleConnsdefault ofdatabase/sql— and theirbackend_starttimestamps match the restart lines in the log to the second.max_connections=100exhausted in ~6 days. We hit that twice.sendTextreturns500. The server then reconnects in a loop (11Starting fresh instancein 80 seconds), burning whatever is left, so the saturation looks sudden.Requesting QR is just another path into the same
StartClient, so/instance/qrmakes it faster but is not the cause.Suggested fix
Create the container once per process (the DSN never changes) and reuse it, or keep a reference per instance and
Close()the old one before starting fresh.Workaround
For anyone hitting this before a fix lands:
Postgres then reaps the orphaned pools. Safe for live sessions: credentials live in the
whatsmeow_*rows, not in the TCP connection, and pgx discards a dead connection silently before use. Took us from 17 backends to 6 with no disconnect and no re-pairing.