Skip to content

db-postgres adapter holds on a pg.Client and never releases it #11727

@loikg

Description

@loikg

Describe the Bug

db-postgres holds onto a connection from the Pg.Pool in connect.ts:

const connectWithReconnect = async function ({
  adapter,
  payload,
  reconnect = false,
}: {
  adapter: PostgresAdapter
  payload: Payload
  reconnect?: boolean
}) {
  let result

  if (!reconnect) {
    result = await adapter.pool.connect()
  } else {
    try {
      result = await adapter.pool.connect()
    } catch (ignore) {
      setTimeout(() => {
        payload.logger.info('Reconnecting to postgres')
        void connectWithReconnect({ adapter, payload, reconnect: true })
      }, 1000)
    }
  }
  if (!result) {
    return
  }
  result.prependListener('error', (err) => {
    try {
      if (err.code === 'ECONNRESET') {
        void connectWithReconnect({ adapter, payload, reconnect: true })
      }
    } catch (ignore) {
      // swallow error
    }
  })
}

The connection is not referenced nor returned out of connectWithReconnect and will live forever unused (based on my understanding). It is never released back to the pool.
Moreover this can cause issues when writing tests that leverage the payload instance as connections can't be cleanly close between tests.

I am not sure what is the intent of connectWithReconnect, since the client is never return and used anywhere, maybe the function can simply be removed or at least the client should be track and release in the adapter destroy method.

Link to the code that reproduces this issue

https://github.com/loikg/payload-db-postgres-reproduction

Reproduction Steps

  1. git clone https://github.com/loikg/payload-db-postgres-reproduction
  2. pnpm install
  3. pnpm test

The test will timeout as Pg.Pool cannot be closed cleanly. As a demonstration a block of code can be un-commented in db.test.ts to force the release of all clients back to the pool, and make the test pass.

Which area(s) are affected? (Select all that apply)

db-postgres

Environment Info

Binaries:
  Node: 22.6.0
  npm: 10.8.2
  Yarn: N/A
  pnpm: 10.6.3
Relevant Packages:
  payload: 3.28.1
  next: 15.2.2
  @payloadcms/db-postgres: 3.28.1
  @payloadcms/email-nodemailer: 3.28.1
  @payloadcms/graphql: 3.28.1
  @payloadcms/next/utilities: 3.28.1
  @payloadcms/payload-cloud: 3.28.1
  @payloadcms/richtext-lexical: 3.28.1
  @payloadcms/translations: 3.28.1
  @payloadcms/ui/shared: 3.28.1
  react: 19.0.0
  react-dom: 19.0.0
Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Fri Mar  7 21:33:48 UTC 2025
  Available memory (MB): 15900
  Available CPU cores: 12

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs-triagePossible bug which hasn't been reproduced yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions