The server unexpectedly closing a connection bricks a connection pool
lucacasonato opened this issue · comments
Here is a test case that reproduces the issue:
Deno.test("Attempts reconnection when DB goes away unexpectedly", async function () {
const cfg = getMainConfiguration();
const aborter = new AbortController();
const listener = Deno.listen({ port: 0 });
const proxy = (async () => {
for await (const conn of listener) {
const outbound = await Deno.connect({
hostname: cfg.hostname,
port: Number(cfg.port),
});
aborter.signal.addEventListener("abort", () => {
conn.close();
outbound.close();
});
await Promise.all([
copy(conn, outbound),
copy(outbound, conn),
]).catch(() => {});
}
})();
const pool = new Pool({
...cfg,
port: (listener.addr as Deno.NetAddr).port,
}, 1);
const client = await pool.connect();
await client.queryObject("SELECT 1");
client.release();
// This closes ongoing connections. The original connection is now dead, so
// a new connection should be established.
aborter.abort();
const client2 = await pool.connect();
await client2.queryObject("SELECT 1");
client2.release();
listener.close();
await proxy;
});
While this is an artificial reproduction, I have hit this in the wild for connections to supabase.io servers. They seem to just disconnect after a while, causing the Pool
in a state where whatever I do, I get one of these two errors (note the stack traces are not correct, because of a Deno Deploy bug):
ConnectionReset: Connection reset by peer (os error 104)
at async read (deno:ext/net/01_net.js:21:19)
at async BufReader.read (https://deno.land/std@0.114.0/io/buffer.ts:314:18)
at async BufReader.readFull (https://deno.land/std@0.114.0/io/buffer.ts:343:28)
at async Connection.#readMessage (https://deno.land/x/postgres@v0.14.2/connection/connection.ts:95:9)
at async Connection.#simpleQuery (https://deno.land/x/postgres@v0.14.2/connection/connection.ts:451:31)
at async Connection.query (https://deno.land/x/postgres@v0.14.2/connection/connection.ts:650:24)
at async Server.<anonymous> (file:///src/main.tsx:13:28)
at async Server.#respond (https://deno.land/std@0.114.0/http/server.ts:246:30)
BrokenPipe: Broken pipe (os error 32)
at async write (deno:ext/net/01_net.js:26:12)
at async BufWriter.flush (https://deno.land/std@0.114.0/io/buffer.ts:633:29)
at async Connection.#simpleQuery (https://deno.land/x/postgres@v0.14.2/connection/connection.ts:443:9)
at async Connection.query (https://deno.land/x/postgres@v0.14.2/connection/connection.ts:650:24)
at async Server.<anonymous> (file:///src/main.tsx:13:28)
at async Server.#respond (https://deno.land/std@0.114.0/http/server.ts:246:30)
@Soremwar same error with postgres@v0.15.0:
ConnectionReset: Connection reset by peer (os error 54)
at async read (deno:ext/net/01_net.js:21:19)
at async BufReader.read (https://deno.land/std@0.114.0/io/buffer.ts:383:12)
at async BufReader.readFull (https://deno.land/std@0.114.0/io/buffer.ts:415:20)
at async Connection.#readMessage (https://deno.land/x/postgres@v0.15.0/connection/connection.ts:152:5)
at async Connection.#preparedQuery (https://deno.land/x/postgres@v0.15.0/connection/connection.ts:853:27)
at async Connection.query (https://deno.land/x/postgres@v0.15.0/connection/connection.ts:930:16)
at async routerHandler (file:///Users/zheren/git/z1p/src/pages/product/spu-cate-list/get.ts:5:15)
at async Server.handler (file:///Users/zheren/git/z1p/src/main.ts:14:37)
at async Server.#respond (https://deno.land/std@0.122.0/http/server.ts:298:18)
BrokenPipe: Broken pipe (os error 32)
at async write (deno:ext/net/01_net.js:26:12)
at async BufWriter.flush (https://deno.land/std@0.114.0/io/buffer.ts:745:21)
at async Connection.#preparedQuery (https://deno.land/x/postgres@v0.15.0/connection/connection.ts:843:5)
at async Connection.query (https://deno.land/x/postgres@v0.15.0/connection/connection.ts:930:16)
@iugo Are you experiencing this problem with the reproduction that lucacasonato provided?
@iugo Are you experiencing this problem with the reproduction that lucacasonato provided?
I encountered this problem in a new project I was using.
Whenever the backend was idle for a period of time, the backend would get an error when connecting again.
My current solution is, await pool.end()
after the error occurs.