From 5a277eec228ca3627e5b3c8526e4caa7c2a0d1e4 Mon Sep 17 00:00:00 2001 From: Clemens Portele Date: Sat, 29 Aug 2026 14:21:05 +0200 Subject: [PATCH] features/sql: add a leak detection threshold for the connection pool connectionInfo.pool.leakDetectionThreshold enables HikariCP's leak detection: a connection out of the pool for longer than the threshold is logged with the stack trace of the code that leased it, and a second message follows if it is returned after all. Off by default; the value has to be at least 2s and is checked for equality when a pool is shared. --- .../sql/domain/ConnectionInfoSql.java | 26 +++++++++++++++++++ .../features/sql/infra/db/SqlConnectorRx.java | 10 +++++++ 2 files changed, 36 insertions(+) diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/ConnectionInfoSql.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/ConnectionInfoSql.java index 8f7e3eac0..288812390 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/ConnectionInfoSql.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/ConnectionInfoSql.java @@ -211,6 +211,32 @@ interface PoolSettings { @Nullable String getIdleTimeout(); + /** + * @langEn Diagnostic option. If set to a duration of at least `2s`, a warning with the stack + * trace of the borrowing code is logged whenever a connection has been out of the pool for + * longer than that, and a second message is logged when such a connection is returned after + * all. A connection that is never returned only produces the first message, which is how a + * leak is told apart from a slow request. Long-running requests such as large exports or + * multi-action transactions legitimately hold a connection for a while, so choose a value + * above the longest expected request. `0` disables the check. The messages are written by + * the logger `com.zaxxer.hikari`, the warning at level `WARN` and the return message at + * level `INFO`; a logging configuration that restricts third-party loggers has to let them + * through. + * @langDe Diagnoseoption. Bei einer Dauer von mindestens `2s` wird eine Warnung mit dem + * Stacktrace des anfordernden Codes protokolliert, sobald eine Verbindung länger als diese + * Dauer aus dem Pool entnommen ist, und eine zweite Meldung, wenn eine solche Verbindung + * doch noch zurückgegeben wird. Eine Verbindung, die nie zurückgegeben wird, erzeugt nur + * die erste Meldung; so lässt sich ein Leck von einer langsamen Anfrage unterscheiden. + * Langlaufende Anfragen wie große Exporte oder Transaktionen mit vielen Aktionen halten + * eine Verbindung berechtigterweise länger, der Wert sollte daher über der längsten + * erwarteten Anfrage liegen. `0` deaktiviert die Prüfung. Die Meldungen schreibt der Logger + * `com.zaxxer.hikari`, die Warnung mit Level `WARN` und die Rückgabemeldung mit Level + * `INFO`; eine Logging-Konfiguration, die Fremd-Logger einschränkt, muss sie durchlassen. + * @default 0 + */ + @Nullable + String getLeakDetectionThreshold(); + /** * @langEn If enabled for multiple providers with matching `host`, `database` and `user`, a * single connection pool will be shared between these providers. If any of the other diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/infra/db/SqlConnectorRx.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/infra/db/SqlConnectorRx.java index 3a81c4864..37d054154 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/infra/db/SqlConnectorRx.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/infra/db/SqlConnectorRx.java @@ -293,6 +293,11 @@ public Tuple canBeSharedWith( Objects.equals( this.connectionInfo.getPool().getIdleTimeout(), connectionInfoSql.getPool().getIdleTimeout())) + .put( + "leakDetectionThreshold", + Objects.equals( + this.connectionInfo.getPool().getLeakDetectionThreshold(), + connectionInfoSql.getPool().getLeakDetectionThreshold())) .put( "driverOptions", Objects.equals( @@ -335,6 +340,11 @@ private HikariConfig createHikariConfig() { config.setMinimumIdle(asyncStartup ? 0 : minConnections); config.setInitializationFailTimeout(asyncStartup ? -1 : getInitFailTimeout(connectionInfo)); config.setIdleTimeout(parseMs(connectionInfo.getPool().getIdleTimeout())); + if (Objects.nonNull(connectionInfo.getPool().getLeakDetectionThreshold())) { + // Hikari itself disables values below 2s or above maxLifetime with a warning + config.setLeakDetectionThreshold( + parseMs(connectionInfo.getPool().getLeakDetectionThreshold())); + } config.setPoolName(poolName); config.setKeepaliveTime(300_000);