Problem Statement
Every dotCMS boot logs a WARN with a full stack trace that reads like a bug and is not one:
WARN pool.ProxyLeakTask: Connection leak detection triggered for org.postgresql.jdbc.PgConnection@… on thread main
java.lang.Exception: Apparent connection leak detected
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:100)
at com.dotcms.dotpubsub.JDBCPubSubImpl$PGListener.<init>(JDBCPubSubImpl.java:128)
at com.dotcms.dotpubsub.JDBCPubSubImpl.listener(JDBCPubSubImpl.java:63)
at com.dotcms.dotpubsub.JDBCPubSubImpl.start(JDBCPubSubImpl.java:88)
at com.dotcms.dotpubsub.QueuingPubSubWrapper.start(QueuingPubSubWrapper.java:74)
at org.apache.felix.framework.OSGIUtil.<init>(OSGIUtil.java:155)
…
at com.dotmarketing.startup.runalways.Task00004LoadStarter.executeUpgrade(Task00004LoadStarter.java:16)
PGListener takes a connection from the Hikari pool and holds it for the lifetime of the listener thread — which is correct for what it does, since a Postgres LISTEN needs a persistent connection:
// JDBCPubSubImpl.java:121
private final Lazy<Connection> connection =
Lazy.of(() -> Try.of(() -> DbConnectionFactory.getDataSource().getConnection())
.getOrElseThrow(DotRuntimeException::new));
Hikari's leak detector has no way to know that, so it flags any connection held past DB_LEAK_DETECTION_THRESHOLD (default 300s / 60s depending on the datasource strategy). The warning is a false positive — the connection is released in stop() → stopListening().
Two real consequences behind the noise:
- A pooled connection is permanently withdrawn from the pool for the JVM's lifetime. On installations with a tight
max_connections that is a slot nobody accounts for, and it does not show up as in-use work.
- Boot logs carry a scary stack trace that is not actionable, which trains operators and support to ignore leak warnings — including real ones.
A LISTEN connection should not come from the request pool at all.
Steps to Reproduce
- Start any dotCMS instance backed by PostgreSQL with the default leak-detection threshold (no special configuration required — the current
dotcms-test:1.0.0-SNAPSHOT image reproduces it).
- Let the first boot run through the starter load (
Task00004LoadStarter).
- Search the log for
Apparent connection leak detected.
Observed: the warning above is logged with a stack trace rooted at JDBCPubSubImpl$PGListener.<init>, and one pool connection stays checked out for as long as the JVM runs.
Expected: no leak warning, and the pool keeps all of its connections available for request work.
Acceptance Criteria
dotCMS Version
Reproduced on dotcms/dotcms-test:1.0.0-SNAPSHOT built from main at 2026-08-06. The code path is long-standing and not specific to that build.
Notes
Found incidentally while reproducing #36222 (TC-056) on a local ES→OS migration stack. Unrelated to that issue's subsystem — filed separately rather than bundled into its fix.
Problem Statement
Every dotCMS boot logs a
WARNwith a full stack trace that reads like a bug and is not one:PGListenertakes a connection from the Hikari pool and holds it for the lifetime of the listener thread — which is correct for what it does, since a PostgresLISTENneeds a persistent connection:Hikari's leak detector has no way to know that, so it flags any connection held past
DB_LEAK_DETECTION_THRESHOLD(default 300s / 60s depending on the datasource strategy). The warning is a false positive — the connection is released instop()→stopListening().Two real consequences behind the noise:
max_connectionsthat is a slot nobody accounts for, and it does not show up as in-use work.A
LISTENconnection should not come from the request pool at all.Steps to Reproduce
dotcms-test:1.0.0-SNAPSHOTimage reproduces it).Task00004LoadStarter).Apparent connection leak detected.Observed: the warning above is logged with a stack trace rooted at
JDBCPubSubImpl$PGListener.<init>, and one pool connection stays checked out for as long as the JVM runs.Expected: no leak warning, and the pool keeps all of its connections available for request work.
Acceptance Criteria
LISTENconnection no longer comes from the Hikari request pool — it uses a dedicated connection built from the same JDBC coordinates, so the pool keeps its full capacity.Apparent connection leak detectedwarning attributable toJDBCPubSubImpl.stop()/stopListening()and on listener restart, so repeated start/stop cycles do not accumulate connections.dotCMS Version
Reproduced on
dotcms/dotcms-test:1.0.0-SNAPSHOTbuilt frommainat 2026-08-06. The code path is long-standing and not specific to that build.Notes
Found incidentally while reproducing #36222 (TC-056) on a local ES→OS migration stack. Unrelated to that issue's subsystem — filed separately rather than bundled into its fix.