Build1 distinct publisher3 min readPublished
A dev.to cheat sheet routes the incident through pg_stat_activity's state and xact_start columns before anything gets terminated, and its ready-made five-minute sweeper is stricter than its own advice about what counts as suspicious.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
A lock does not care how many backends are queued behind it. That is the mechanical reason a larger `max_connections` is the wrong first move: the session at the front holds its locks until it commits or rolls back [3], and it is not running a query at all, it is waiting on its client with the transaction still open [5]. New slots are new candidates for the same wait. The slower damage runs in parallel, because old snapshots stay open, VACUUM cannot clean some dead rows, and tables grow [4].
The diagnosis is short and the order is what makes it useful. The first query strips normal idle connections with `state <> 'idle'` [6], leaving sessions that are running queries, waiting, or idle inside a transaction [7]. The worked example shows PID 1596183 active for 00:08:32 [8], and the cheat sheet immediately asks the question that decides the repair: is that query slow, or is it waiting for another session [9]. Same duration, two different fixes. That is where the lock graph belongs, and the article names `pg_blocking_pids()` among the functions it covers [10]; the text supplied to us ends mid-sentence before it demonstrates that function [20], so treat the step as signposted rather than shown.
Then the thresholds, which do not agree with each other. The prose says a transaction open for a few seconds may be normal while 30 minutes or several hours is much more suspicious [12], and it warns against killing every `idle in transaction` connection before you know which application owns it and whether it blocks anyone [13]. The copy-paste cleanup query terminates at five minutes [15]. That is a sixth of the window the same page calls suspicious [19]. The two also read different clocks: the investigation query orders by `xact_start`, defined there as when the transaction started [11], while the sweeper filters `state_change` [15]. If your intent is "this transaction has been open too long", `xact_start` is the column that says so. Keep the `pid <> pg_backend_pid()` condition regardless [15]; it excludes the session running the sweep. Termination itself is well behaved: Postgres closes the session, rolls back the open transaction, and releases the locks [14], which is both the point and the bill, since the transaction's work goes with it.
The cheapest item on the page is `application_name`. Set it per service in the connection string and `pg_stat_activity` shows payment-service sitting in `idle in transaction` next to order-service running active [16]; the author recommends doing this for every service in Go, Node.js and similar setups [17]. Postgres will tell you which service abandoned the transaction, provided you once told it the service's name.
In my context, short transactions behind a pooler, the sweeper stays out of cron and goes in the runbook, keyed on transaction age rather than state age. If your workload legitimately holds a transaction across a user's think time, five minutes is not a threshold you can inherit from someone else's page. Either way the stated goal is the right one: find out why the transaction was left open, not just which PID to kill [18].
Ranked by verification strength, evidence, and original report placement.
The cheat sheet describes a common production sequence: queries start waiting, connections increase, the database becomes slower, and connection usage gets close to max_connections; it says one common cause is a session blocking other sessions.
A common example given is a connection in the 'idle in transaction' state, where the application started a transaction but did not COMMIT or ROLLBACK it.
The article states that a transaction may hold locks until it finishes with COMMIT or ROLLBACK.
Listed consequences of an unfinished transaction: locks may still be held, other queries may have to wait, old transaction snapshots may stay open, VACUUM may not be able to clean some dead rows, and tables may become larger over time.
The article defines an 'active' session as one currently running a query, and an 'idle in transaction' session as one waiting for the client while its transaction is still open.
The opening investigation query selects pid, usename, application_name, state, query_start, now() - query_start AS duration and query from pg_stat_activity WHERE state <> 'idle' ORDER BY query_start, which removes normal idle connections from the result.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 27, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Your "Index Only Scan" Did 2,847 Heap Fetches: Covering Indexes Are a Vacuum Problem1 distinct publisher
build
A NetworkPolicy in another repo broke invoicing while every dashboard reported success1 distinct publisher
build
The optional EntityManager is the bug: moving the transaction boundary into AsyncLocalStorage1 distinct publisher
build
Three attackers hide behind one connect button, and encryption only stops one of them1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Single-source, self-contained walkthrough
Every claim traces to one dev.to cheat sheet. The technical content is concrete and internally checkable - full SQL text, named pg_stat_activity columns, explicit function behaviour - which supports the mechanics claims, but there is no second publisher, no benchmark, no incident post-mortem and no vendor documentation in the cluster to corroborate them, and the supplied body is truncated before pg_blocking_pids is demonstrated.
No adoption signal in cluster
The cluster contains no release, deployment, benchmark, pricing, licensing or usage-disclosure data. The article's sample outputs (PID 1596183, order-service/payment-service rows) are illustrative examples, not disclosed production usage, so no adoption level can be measured without guessing.
Framing outruns the article's own hedges
The story framing that one uncommitted transaction explains most Postgres connection exhaustion is stronger than the source, which says only that a blocking session is 'one common cause' and offers no frequency data. The overstatement is compounded by a ready-made sweeper six times stricter than the article's own suspicion threshold and by a promised pg_blocking_pids section that never appears. The gap is moderate rather than severe because the underlying mechanics and SQL are accurate and hedged with production warnings.
Low commercial stake, practitioner credibility
The source is an individual practitioner post on a developer publishing platform. It promotes no product, service, tier or license, and its recommendations are generic PostgreSQL built-ins and the application_name convention, so the visible incentive is audience and reputation on the platform rather than commercial conversion. Assessed only from what the supplied text shows; no sponsorship or affiliation disclosure is present either way.
Coherent but unverified and truncated
Confidence is mid-range: claims about what the article says are highly reliable because the full text is supplied and internally consistent, and the SQL and function semantics are specific enough to act on. It is capped by having a single publisher, no corroboration or adoption measurement, a body that ends mid-sentence, and an unquantified causal premise about how often idle transactions drive exhaustion.