Pub/sub PGListener holds a pooled Hikari connection for the JVM lifetime, triggering a false leak warning on every boot
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
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_connectionsthat 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-SNAPSHOTimage 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
- The pub/sub
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. - When those coordinates are not available (JNDI or any datasource strategy that does not expose a JDBC URL), the listener still works, falling back to the current behaviour rather than failing to start.
- A normal boot logs no
Apparent connection leak detectedwarning attributable toJDBCPubSubImpl. - The dedicated connection is closed on
stop()/stopListening()and on listener restart, so repeated start/stop cycles do not accumulate connections. - Pub/sub behaviour is unchanged: topics are still received after the change, including after a listener reconnect.
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in JDBCPubSubImpl.java at PGListener and the listener/start/stop entry points shown in the stack trace; trace DbConnectionFactory and the available datasource strategies. Verify how a dedicated JDBC connection can be created while preserving the fallback behavior, then exercise boot and listener restart/stop flows. Done means no attributable leak warning, no pooled connection held, clean shutdown, and unchanged topic delivery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, postgresql
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100