Skip to content

Postgres connection leak: each (re)connect on a logged-out instance leaks an unclosed sqlstore pool (evogo_auth/evogo_users) #106

Description

@gitlovable

Summary

Every (re)connect attempt on an instance whose WhatsApp session was logged out (pairing removed on the phone → whatsmeow LoggedOut) leaks a Postgres connection pool: a new whatsmeow client + sqlstore is created per attempt, the login fails with LoggedOut (expected), and the abandoned sql.DB pool is never Close()d. The idle connections stay parked in Postgres forever.

With any periodic reconnect source, this grows unbounded until the whole Postgres cluster hits max_connections and refuses every client. Aggravated by evolution-go connecting as the postgres superuser by default, which also exhausts superuser_reserved_connections — locking the DBA out during the incident.

We hit this twice in production in two days, through two different trigger paths:

Variant A — external POST /instance/connect (day 1)

Our supervisor retried /instance/connect every 2 min on a logged-out instance.

  • Postgres log: 742 × FATAL: sorry, too many clients already, origin user=postgres, db=evogo_users
  • Connection count of the evolution-go process (nsenter … ss -tn | grep :5432): 167 (healthy baseline for the same deployment: ~15–20)
  • Exact correlation: 88 authenticated POST /instance/connect (HTTP 200) since container start ≈ the accumulated leaked connections (+1 per attempt). 401 attempts (invalid token) do not leak — the leak is in the authenticated path that instantiates the client/sqlstore.
  • docker restart evolution-go instantly dropped the count 167 → 3 and the cluster recovered.

Variant B — internal whatsmeow auto-reconnect (day 2, no API calls at all)

After we stopped retrying via API, a logged-out instance kept cycling through whatsmeow's internal auto-reconnect:

  • 25 × LoggedOut connection events in 6h for one instance, only 2 authenticated /instance/connect in the same window (so not API-triggered)
  • 121 idle connections accumulated in db=evogo_auth (session store), ~7/hour, all state=idle, oldest from the moment the cycling started
  • Same fix: restart frees them instantly; the leak resumes while the logged-out instance keeps retrying.

Suspected root cause

On the connect path, a new whatsmeow client + sqlstore container (its own database/sql pool) is created per attempt. When login fails with LoggedOut, the client is discarded but the store/pool is not closed. An abandoned Go sql.DB keeps its idle connections open indefinitely.

Suggested fix

  1. Close the sqlstore/sql.DB on the connect error path (or reuse the instance's existing client/pool instead of creating a new one per attempt);
  2. Treat LoggedOut as terminal in the reconnect loop: stop auto-retrying a session that requires re-pairing (surface "re-pair required" instead);
  3. Independently: document/support running with a non-superuser DB role — with the default superuser connection, any leak escalates to a full-cluster outage including the reserved slots.

Workarounds we use meanwhile

  • Supervisor treats LoggedOut as terminal and stops calling /instance/connect;
  • docker restart evolution-go when the count grows;
  • Planning a dedicated DB role with CONNECTION LIMIT as a blast-radius guard.

Happy to provide more logs/details if useful. 🇧🇷 Podemos detalhar em PT-BR também, se preferirem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions