npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pg-aequor

v0.1.4

Published

Crash-safe, coordination-aware PostgreSQL client for Serverless environments

Readme

Have you ever…

  • …had a Lambda “freeze”, then watched Postgres slowly fill up with idle zombie connections until you hit max_connections?
  • …seen bursts of sorry, too many clients already during traffic spikes?
  • …debugged random runtime crashes where the root cause was a dead PG socket (and the error bubbled out of an event handler)?
  • …felt like you need PgBouncer/RDS Proxy, but you just want a client-side fix?

Standard pg + Lambda scale-outs often end in zombie connections: a Lambda freezes, its TCP socket stays alive on the DB, and a new wave of invocations keeps opening connections until you hit max_connections.

pg-aequor prevents this using Signed Leases + a lightweight Distributed Reaper.

Use cases

  • Zombie connection storms: old frozen containers keep sockets around; new invocations create more connections; the DB falls over.
  • “Unexplained” runtime exits: some runtimes treat unhandled socket errors as fatal. pg-aequor swallows errors in pg event handlers and forces a safe reconnect path.
  • Spiky cold starts: retries with decorrelated jitter + SQLSTATE filtering smooth transient network/DB restarts without turning retries into a synchronized stampede.

Table of contents

Features

  • Signed leases in application_name: each connection self-identifies with expiration + HMAC.
  • Distributed reaper: one request occasionally becomes the “leader” and reaps expired connections.
  • Advisory locks: coordination via Postgres locks (no external coordinator).
  • Crash safety: socket errors are swallowed from event handlers to prevent runtime crashes.
  • Safe retries: decorrelated jitter + SQLSTATE filtering for transient failures.
  • Hooks: lightweight observability callbacks (metrics/tracing).

Install

npm install pg-aequor pg

Note: pg is a peer dependency. Tested with pg@^8.11.0.

Quick start

const { AequorClient } = require('pg-aequor')

const client = new AequorClient({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,

  // Coordination Secret (distinct from DB password)
  coordinationSecret: process.env.COORD_SECRET,
})

await client.connect()
const res = await client.query('SELECT NOW()')
await client.clean() // or: await client.end()

How it works (in one minute)

In standard environments, connections live long. In serverless, containers “freeze”.

We solve this via:

  1. Signed Leases: each connection stores expiration + signature in application_name.
  2. Distributed Reaper: on connect (probabilistically), one instance scans pg_stat_activity and terminates expired connections.
  3. Advisory Locks: pg_try_advisory_lock ensures only one leader reaps at a time.

Operational rules (important)

  • Disposable idle: if a connection is idle longer than its lease TTL, it becomes eligible to be reaped by another instance.
  • Single-connection architecture: the reaper runs on the active connection (under lock) to avoid “reaper storms”.
  • Hooks must be fast: don’t do heavy work inside hooks; use them for metrics/tracing only.

Configuration

Lease / reaper (recommended)

| Option | Type | Default | Notes | | --- | --- | --- | --- | | coordinationSecret | string | (required) | Shared secret for HMAC signing. Do not use DB password. Must be at least 16 bytes. | | leaseMode | 'required' \| 'optional' | 'required' | If optional and coordinationSecret is missing: lease/reaper/heartbeat are disabled. | | leaseTtlMs | number | 90000 | Lease TTL. | | reaper | boolean | true | Enable/disable reaper. | | reaperRunProbability | number | 0.1 | Probability of trying a reaper pass on connect (0..1). | | reaperCooldownMs | number | 30000 | Minimum time between reaper runs per container. | | minConnectionIdleTimeSec | number | 180 | Minimum idle seconds to consider a connection a candidate. | | maxIdleConnectionsToKill | number | 10 | Max zombies to kill in one pass. |

Retries

| Option | Type | Default | | --- | --- | --- | | retries | number | 3 | | minBackoff | number | 100 | | maxBackoff | number | 2000 | | maxConnectRetryTimeMs | number | 15000 | | maxQueryRetryTimeMs | number | 15000 |

We use decorrelated jitter and SQLSTATE-based retry classification to avoid duplicating non-idempotent writes.

Observability (hooks)

const { AequorClient } = require('pg-aequor')

const client = new AequorClient({
  // ...pg config...
  coordinationSecret: process.env.COORD_SECRET,

  hooks: {
    onQueryRetry: ({ retries, err }) => {
      console.warn(`Retry #${retries} due to ${err.code}`)
    },
    onClientDead: ({ source, meta }) => {
      // Great place for EMF/X-Ray/etc
      console.log('Client dead:', source, meta?.sqlstate)
    },
    onQueryStart: ({ startedAt }) => {
      // tracing start
    },
    onQueryEnd: ({ duration }) => {
      // tracing end
    },
  },
})

Production checklist

Required Postgres privileges

The reaper reads pg_stat_activity and calls pg_terminate_backend().

Heads up: on managed Postgres, this may require elevated privileges (or be restricted by policy). If the reaper can’t terminate backends, you’ll typically see permission errors and zombies will remain.

Coordination secret hygiene

  • Use a separate secret (not the DB password).
  • Keep it at least 16 bytes.
  • Rotate carefully: a safe pattern is “deploy new secret everywhere” during a maintenance window, because old/new secrets won’t verify each other’s leases.

Recommended defaults

  • Start with a conservative leaseTtlMs (e.g. 90s) and minConnectionIdleTimeSec (e.g. 180s) to avoid self-inflicted churn.
  • Keep hooks lightweight (metrics only).

FAQ

Will it kill my active connections?

No. The reaper only terminates connections that:

  • match this service prefix, and
  • have a valid signature, and
  • are expired, and
  • are idle for longer than your configured threshold.

Do I still need PgBouncer/RDS Proxy?

If you already have a proxy and it works well for you, keep it. pg-aequor is a pure-client approach intended for cases where you can’t or don’t want to add extra infrastructure.