Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ templates/
fullstack/ web/ (Vite + React + Tailwind + shadcn/ui), api/ (Hono + pg)
scripts/ migrate, doctor, demo, support
tests/ agents, blueprint, contracts, gateway, git, github-auth,
host, images, policy, render, shell, teardown, templates,
tools, workflow
host, images, policy, render, shell, store, teardown,
templates, tools, workflow
```

There is no `tasks.ts`, `scaffold.ts`, `shell.ts`, `github.ts`, or `format.ts`:
Expand Down
7 changes: 7 additions & 0 deletions app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ export function db(): pg.Pool {
allowExitOnIdle: true,
options: "-c statement_timeout=15000 -c lock_timeout=5000",
});
// Postgres can close an idle connection of the pool, for example in a
// restart or a failover. The pool then removes the client and emits
// "error". If no listener gets the event, Node stops the process. The
// next query gets a new connection, so the listener only logs the error.
pool.on("error", (error) => {
console.error("Lost an idle Postgres connection:", error);
});
}
return pool;
}
Expand Down
29 changes: 29 additions & 0 deletions tests/store.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Postgres can close an idle connection of the pool, for example in a
* restart. The pool then emits "error", and an "error" event with no listener
* stops the gateway and the workflows host. No test connects to Postgres: the
* pool opens a connection only for a query.
*/
import { afterEach, describe, expect, it, vi } from "vitest";
import { db } from "../app/store.js";

describe("db", () => {
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllEnvs();
});

it("logs the error of an idle connection and does not throw it", () => {
vi.stubEnv("DATABASE_URL", "postgres://factory@127.0.0.1:5432/factory");
const logged = vi.spyOn(console, "error").mockImplementation(() => {});
const error = new Error(
"terminating connection due to administrator command",
);

expect(() => db().emit("error", error)).not.toThrow();
expect(logged).toHaveBeenCalledWith(
"Lost an idle Postgres connection:",
error,
);
});
});
Loading