Rbtz.PostgrexDisconnectTracker
Pins Postgrex disconnection errors to the ExUnit test that caused them.
When a test leaks a Postgrex connection — e.g. a spawned task outlives the test, a sandbox owner exits mid-query, or a driver crash hits the pool — the error logged by Postgrex looks like this:
[error] Postgrex.Protocol (#PID<0.512.0>) disconnected: ** (DBConnection.ConnectionError) client #PID<0.1234.0> exitedThe message names the client PID, but that PID no longer exists and has no connection back to the test suite. You're left grepping for which test touched the affected code. In a CI run of a thousand tests that can be a bad afternoon.
This library attaches a telemetry handler to your repo and an OTP :logger handler, and maintains an ETS map from query-executing PIDs (and their ancestors) to the test that started them. When Postgrex logs a disconnect, the handler parses the client PID, looks up the owning test, and prints an annotated error like:
=== POSTGREX DISCONNECTION ===
Test: MyApp.AccountsTest - test registers a user
Client PID: #PID<0.1234.0>
Stacktrace: ...
Error: ...
==============================Why / when to use this
Use it when:
-
You've seen intermittent
Postgrex.Protocol ... disconnectederrors in CI and don't know which test is responsible. -
You're hunting a leaked connection, a background task that outlives its test, or a misconfigured
Ecto.Adapters.SQL.Sandboxowner. - A CI run is flaky and you suspect one bad test is knocking out the connection pool and poisoning later tests.
Don't use it when:
- You aren't seeing Postgrex disconnect errors. The tracker adds a telemetry handler on every query — the overhead is small but non-zero, and the output is noise unless disconnects are actually happening.
-
You're running production code. This is a test-only utility — only attach it from
test_helper.exs.
It's a diagnostic aid, not a fix. Once it points you at the offending test, you still need to patch that test (close the connection, wait for the task, re-scope the sandbox, etc.).
Installation
Add rbtz_postgrex_disconnect_tracker to your mix.exs test deps:
def deps do
[
{:rbtz_postgrex_disconnect_tracker, "~> 0.1", only: :test}
]
end
Run mix deps.get.
Setup (recommended)
One call in test/test_helper.exs, before ExUnit.start/1:
Rbtz.PostgrexDisconnectTracker.setup(telemetry_event: [:my_app, :repo, :query])
ExUnit.start():telemetry_event is the Ecto query telemetry event for your repo — usually [:<your_otp_app>, :repo, :query]. Ecto fires it on every query; the tracker uses it to learn which process is running queries for which test.
Then in your DataCase (or whichever ExUnit.CaseTemplate sets up the sandbox), register the current test at the start of each setup:
defmodule MyApp.DataCase do
use ExUnit.CaseTemplate
setup tags do
Rbtz.PostgrexDisconnectTracker.register_test("#{tags.module} - #{tags.test}")
pid = Ecto.Adapters.SQL.Sandbox.start_owner!(MyApp.Repo, shared: not tags[:async])
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
:ok
end
endThat's it. When a disconnect is logged, you'll see the test name in the output.
Setup (individual functions)
If you need to wire the pieces yourself — for example, to attach the logger on a different subset of tests, or to hold off on the telemetry handler until a suite-level setup — call them separately instead of setup/1:
Rbtz.PostgrexDisconnectTracker.Tracker.init(telemetry_event: [:my_app, :repo, :query])
Rbtz.PostgrexDisconnectTracker.Logger.attach()setup/1 is exactly equivalent to these two calls in this order.
License
MIT. See LICENSE.