-
Notifications
You must be signed in to change notification settings - Fork 2
Feature ETP-4296: Add guide for diagnosing idle in transaction connections #701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
96 changes: 96 additions & 0 deletions
96
...etendo-classic/how-to-guides/how-to-diagnose-idle-in-transaction-connections.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| --- | ||
| title: How to Diagnose Idle in Transaction Connections | ||
| tags: | ||
| - Connection Pool | ||
| - Troubleshooting | ||
| - Database | ||
| - PostgreSQL | ||
| - Hibernate | ||
| - DAL | ||
| status: beta | ||
| --- | ||
|
|
||
| # How to Diagnose Idle in Transaction Connections | ||
|
|
||
| !!! example "IMPORTANT: THIS IS A BETA VERSION" | ||
| This page is under active development and may contain **unstable or incomplete features**. Use it **at your own risk**. | ||
|
|
||
| ## Overview | ||
|
|
||
| Etendo serves most requests through `DalFilter`, which opens a Hibernate/DAL session at the start of the request and closes it once the response is sent. Custom servlets, processes, or webservice endpoints that call `OBDal.getInstance()` or open a `SessionHandler` session outside that lifecycle are responsible for closing the session themselves. | ||
|
|
||
| A session that is never closed keeps its underlying JDBC connection checked out of the pool with an open transaction. PostgreSQL reports that connection as `idle in transaction`. Left unbounded, this blocks autovacuum on the tables the transaction touched, causes table bloat, and degrades database performance over time. Restarting Tomcat clears the pool and hides the symptom temporarily, which is why the underlying leak can go unnoticed for a while. | ||
|
|
||
| ## Detecting the Problem | ||
|
|
||
| Query PostgreSQL for connections stuck in this state: | ||
|
|
||
| ```sql | ||
| SELECT pid, now() - xact_start AS duration, state, LEFT(query, 80) AS query_snippet | ||
| FROM pg_stat_activity | ||
| WHERE state = 'idle in transaction'; | ||
| ``` | ||
|
|
||
| A growing number of rows that never clears until Tomcat restarts indicates a session that is not being closed somewhere in the request path. | ||
|
|
||
| ## Finding the Leak | ||
|
|
||
| Review custom code paths that open a DAL or Hibernate session directly instead of relying on `DalFilter`. This is common in servlets that extend `HttpServlet` or `HttpBaseServlet` directly, bypassing the standard filter chain. Each of these paths must close its session explicitly in a `finally` block: | ||
|
|
||
| ```java | ||
| try { | ||
| // ... request processing that uses OBDal.getInstance() ... | ||
| } finally { | ||
| OBDal.getInstance().commitAndClose(); | ||
| } | ||
| ``` | ||
|
|
||
| Or, when using `SessionHandler` directly: | ||
|
|
||
| ```java | ||
| try { | ||
| // ... request processing ... | ||
| } finally { | ||
| SessionHandler.getInstance().commitAndClose(); | ||
| } | ||
| ``` | ||
|
|
||
| To confirm which code path is responsible before changing anything, enable abandoned-connection logging. This logs the stack trace of where a connection was borrowed once it has been checked out longer than expected, without closing anything: | ||
|
|
||
| ```properties title="gradle.properties" | ||
| db.pool.logAbandoned=true | ||
| db.pool.suspectTimeout=<seconds> | ||
| ``` | ||
|
|
||
| Apply the change with: | ||
|
|
||
| ```bash | ||
| ./gradlew setup | ||
| ``` | ||
|
|
||
| !!! warning | ||
| Logging abandoned connections adds overhead to every connection borrow, because a stack trace has to be generated. Use it to diagnose the leak, then disable it once the responsible code path is identified. | ||
|
|
||
| ## Mitigating at the Pool Level | ||
|
|
||
| While the code fix is developed and rolled out, the connection pool can be configured to forcibly reclaim connections that have been checked out too long. Add the following properties to `gradle.properties`: | ||
|
|
||
| ```properties title="gradle.properties" | ||
| db.pool.removeAbandoned=true | ||
| db.pool.removeAbandonedTimeout=<seconds> | ||
| ``` | ||
|
|
||
| Then apply the change: | ||
|
|
||
| ```bash | ||
| ./gradlew setup | ||
| ``` | ||
|
|
||
| See [How to Use an External Connection Pool](how-to-use-an-external-connection-pool.md#pool-configuration) for the full reference of pool configuration properties. | ||
|
|
||
| !!! warning | ||
| Set `removeAbandonedTimeout` well above the longest legitimate transaction or background process duration in the environment. Any operation still running past that timeout has its connection reclaimed while in use, which corrupts that operation. This setting is a temporary safety net, not a substitute for closing the session in code. | ||
|
|
||
| --- | ||
|
|
||
| This work is licensed under :material-creative-commons: :fontawesome-brands-creative-commons-by: :fontawesome-brands-creative-commons-sa: [CC BY-SA 2.5 ES](https://creativecommons.org/licenses/by-sa/2.5/es/){target="_blank"} by [Futit Services S.L](https://etendo.software){target="_blank"}. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
...etendo-classic/how-to-guides/how-to-diagnose-idle-in-transaction-connections.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| --- | ||
| title: Cómo diagnosticar conexiones en estado idle in transaction | ||
| tags: | ||
| - Pool de conexiones | ||
| - Solución de problemas | ||
| - Base de datos | ||
| - PostgreSQL | ||
| - Hibernate | ||
| - DAL | ||
| status: beta | ||
| --- | ||
|
|
||
| # Cómo diagnosticar conexiones en estado idle in transaction { #how-to-diagnose-idle-in-transaction-connections } | ||
|
|
||
| !!! example "IMPORTANTE: ESTA ES UNA VERSIÓN BETA" | ||
| Esta página está en desarrollo activo y puede contener **funcionalidades inestables o incompletas**. Úsela **bajo su propia responsabilidad**. | ||
|
|
||
| ## Visión general { #overview } | ||
|
|
||
| Etendo atiende la mayoría de las solicitudes a través de `DalFilter`, que abre una sesión de Hibernate/DAL al inicio de la solicitud y la cierra una vez enviada la respuesta. Los servlets, procesos o endpoints de servicios web personalizados que llaman a `OBDal.getInstance()` o abren una sesión de `SessionHandler` fuera de ese ciclo de vida son responsables de cerrar la sesión ellos mismos. | ||
|
|
||
| Una sesión que nunca se cierra mantiene su conexión JDBC subyacente retirada del pool con una transacción abierta. PostgreSQL informa esa conexión como `idle in transaction`. Si no se controla, esto bloquea el autovacuum en las tablas que tocó la transacción, provoca hinchazón de las tablas y degrada el rendimiento de la base de datos con el tiempo. Reiniciar Tomcat vacía el pool y oculta el síntoma temporalmente, por lo que la fuga subyacente puede pasar inadvertida durante un tiempo. | ||
|
|
||
| ## Detección del problema { #detecting-the-problem } | ||
|
|
||
| Consulte PostgreSQL para buscar conexiones atascadas en este estado: | ||
|
|
||
| ```sql | ||
| SELECT pid, now() - xact_start AS duration, state, LEFT(query, 80) AS query_snippet | ||
| FROM pg_stat_activity | ||
| WHERE state = 'idle in transaction'; | ||
| ``` | ||
|
|
||
| Un número creciente de filas que nunca se despeja hasta que se reinicia Tomcat indica que, en algún punto del recorrido de la solicitud, no se está cerrando una sesión. | ||
|
|
||
| ## Cómo encontrar la fuga { #finding-the-leak } | ||
|
|
||
| Revise los caminos de código personalizados que abren una sesión DAL o de Hibernate directamente en lugar de depender de `DalFilter`. Esto es habitual en servlets que extienden directamente `HttpServlet` o `HttpBaseServlet`, evitando la cadena de filtros estándar. Cada uno de estos caminos debe cerrar su sesión explícitamente en un bloque `finally`: | ||
|
|
||
| ```java | ||
| try { | ||
| // ... procesamiento de la solicitud que usa OBDal.getInstance() ... | ||
| } finally { | ||
| OBDal.getInstance().commitAndClose(); | ||
| } | ||
| ``` | ||
|
|
||
| O, cuando se usa `SessionHandler` directamente: | ||
|
|
||
| ```java | ||
| try { | ||
| // ... procesamiento de la solicitud ... | ||
| } finally { | ||
| SessionHandler.getInstance().commitAndClose(); | ||
| } | ||
| ``` | ||
|
|
||
| Para confirmar qué camino de código es responsable antes de cambiar nada, habilite el registro de conexiones abandonadas. Esto registra la traza de pila de dónde se tomó prestada una conexión una vez que ha estado retirada más tiempo del esperado, sin cerrar nada: | ||
|
|
||
| ```properties title="gradle.properties" | ||
| db.pool.logAbandoned=true | ||
| db.pool.suspectTimeout=<segundos> | ||
| ``` | ||
|
|
||
| Aplique el cambio con: | ||
|
|
||
| ```bash | ||
| ./gradlew setup | ||
| ``` | ||
|
|
||
| !!! warning | ||
| Registrar las conexiones abandonadas añade sobrecarga a cada solicitud de conexión, porque se debe generar una traza de pila. Utilícelo para diagnosticar la fuga y luego deshabilítelo una vez identificado el camino de código responsable. | ||
|
|
||
| ## Mitigación a nivel de pool { #mitigating-at-the-pool-level } | ||
|
|
||
| Mientras se desarrolla e implementa la corrección de código, el pool de conexiones puede configurarse para recuperar por la fuerza las conexiones que se han retirado durante demasiado tiempo. Añada las siguientes propiedades a `gradle.properties`: | ||
|
|
||
| ```properties title="gradle.properties" | ||
| db.pool.removeAbandoned=true | ||
| db.pool.removeAbandonedTimeout=<segundos> | ||
| ``` | ||
|
|
||
| Luego aplique el cambio: | ||
|
|
||
| ```bash | ||
| ./gradlew setup | ||
| ``` | ||
|
|
||
| Consulte [Cómo usar un pool de conexiones externo](how-to-use-an-external-connection-pool.md#pool-configuration) para ver la referencia completa de las propiedades de configuración del pool. | ||
|
|
||
| !!! warning | ||
| Establezca `removeAbandonedTimeout` con un margen amplio por encima de la duración de la transacción o el proceso en segundo plano legítimo más largo del entorno. Cualquier operación que siga en ejecución más allá de ese tiempo de espera tendrá su conexión recuperada mientras está en uso, lo que corrompe esa operación. Este ajuste es una red de seguridad temporal, no un sustituto de cerrar la sesión en el código. | ||
|
|
||
| --- | ||
|
|
||
| This work is licensed under :material-creative-commons: :fontawesome-brands-creative-commons-by: :fontawesome-brands-creative-commons-sa: [CC BY-SA 2.5 ES](https://creativecommons.org/licenses/by-sa/2.5/es/){target="_blank"} by [Futit Services S.L](https://etendo.software){target="_blank"}. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.