Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

sql-pg: pool is not closed when the "SELECT 1" readiness probe fails in PgClient.make

Closed Beginner friendly
#8,424 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
postgresql, typescript
Domain
database

Research direction

Start in packages/sql/pg/src/PgClient.ts at PgClient.make and trace the pool creation, SELECT 1 acquisition, and release finalizer. Verify the readiness-probe failure path and ensure the constructed pool is ended there while preserving the existing successful-acquisition cleanup behavior.

Written by the indexing model from the issue text.

Description

3.0

Package

@effect/sql-pg 0.53.0 (the same structure is present in the v4 sources at packages/sql/pg/src/PgClient.ts).

Description

In PgClient.make, the pg.Pool is constructed before the acquisition effect:

const pool = new Pg.Pool({ ... })
pool.on("error", () => {})

yield* Effect.acquireRelease(
  Effect.tryPromise({
    try: () => pool.query("SELECT 1"),
    catch: (cause) => new SqlError({ ... })
  }),
  () => Effect.promise(() => pool.end()).pipe(...)
).pipe(...)

Per Effect.acquireRelease semantics, the release finalizer is registered only after a successful acquire — so when the SELECT 1 readiness probe fails, pool.end() is never called and the already-constructed pool object is orphaned.

Impact

Small in practice (pg pools are lazy — a pool that never acquired a client holds no sockets or timers, so the orphaned object is garbage-collectable), but:

  • callers that retry a failed layer build (e.g. a self-healing wrapper around ManagedRuntime that replaces the runtime after a failed build) accumulate pool instances across attempts,
  • the pool.end() finalizer signals the intent to always release the pool, so a failed probe silently violating that contract is surprising.

Suggested fix

End the pool when the probe fails, e.g.:

Effect.acquireRelease(
  Effect.tryPromise({
    try: () => pool.query("SELECT 1"),
    catch: (cause) => new SqlError({ ... })
  }).pipe(Effect.tapErrorCause(() => Effect.promise(() => pool.end()))),
  () => ...
)

or construct the pool inside the acquire effect.

Dominant language
TypeScript
Stars
16.7k
Forks
808
Avg merge
10h 36m
Merged PRs (30d)
449

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Effect-TS/effect

All issues in Effect-TS/effect

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.