Skip to content

SimpleConnection: a disconnect during handle_connect's query drops the reconnect, leaving an auto_reconnect connection disconnected for good #800

Closed

Description

Versions: Postgrex 0.22.4, and master at 882c105 (after #798). Elixir 1.20.2, OTP 27.0.

What happens

With auto_reconnect: true, Postgrex.Notifications re-LISTENs all its channels right after it reconnects: handle_connect/1 returns {:query, "DO $postgrex_notifications_0$BEGIN LISTEN ...; END...", state}. If the connection drops while that query runs, reconnect_or_stop/4 returns {:keep_state, state, {:next_event, :internal, {:connect, :reconnect}}}. But the connect handler in handle_event(:internal, {:connect, _}, ...) (lib/postgrex/simple_connection.ex, lines 362-364 on master) does:

with {:keep_state, state, _} <- maybe_handle(mod, :handle_connect, [mod_state], state) do
  {:keep_state, state}
end

This discards the action. The process stays alive with connected: false, makes no further connect attempt and arms no idle timeout. It stays deaf until some unrelated event reaches it, for example a listen/2 call or a listener process exiting.

Reproduction

  1. Put a TCP proxy in front of a Postgrex.Notifications started with auto_reconnect: true, and listen/2 on a channel.
  2. Terminate its backend with pg_terminate_backend.
  3. When the proxy sees the reconnect's DO $postgrex_notifications batch, have it close both sockets.

Expected: another reconnect, then delivery. Actual: one connect attempt, then none, and 0 of 20 notifications over 10 s. A proxy-based ExUnit test reproduces this in 3 of 3 runs, and passes 21 of 21 with the change below.

Proposed fix

-        with {:keep_state, state, _} <- maybe_handle(mod, :handle_connect, [mod_state], state) do
-          {:keep_state, state}
-        end
+        maybe_handle(mod, :handle_connect, [mod_state], state)

This keeps handle_connect's actions. On success it also arms the idle timeout after connect, as every other event path already does. Today a connection that stays quiet after connecting never pings, so it cannot notice a dead connection until its first event. Nothing is armed while disconnected, so this does not interact with #797/#798.

A PR with the regression test can follow if this looks right.

Related: #717 (call disconnect on the protocol when reconnecting), #724 (Notifications crashes when the database is disconnected), #637 (auto-reconnect race condition), #797/#798 (the idle timeout while disconnected).

Activity

  1. josevalim commented on Sep 30, 2026

    @josevalim
    Member

    Please send a PR so I can take a look at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions