Si no esperas lo inesperado no lo reconocerás cuando llegue.
- Heráclito de Efeso (540 AC-470 AC) Filósofo griego
Uno de estos sucesos típicos suele ser el inexplicable y aleatorio bloqueo de una sesión o de toda una aplicación entera. Tras el correspondiente susto y examen exhaustivo, detectamos la causa en una consulta de base de datos que, eventualmente, tarda demasiado. En algunas ocasiones las causas son muy poco evidentes y muy difíciles de detectar, ya que sólo se producen por la coincidencia de determinados eventos (periódicos o no), e incluso en un determinado orden.
¿Y por qué tarda tanto una consulta que en nuestro entorno de preproducción se lleva apenas unas pocas decenas de milisegundos? Pues típicamente porque ocurre un bloqueo en la base de datos debido a una transacción de larga duración. Si las cosas se complican, puede existir incluso un deadlock (bloqueo mutuo) que deje nuestro sistema completamente bloqueado y produzca un efecto dominó en otras aplicaciones dependientes de la misma base de datos.
Obviamente, la solución pasa por detectar la casuística concreta y evitarla, pero mientras buscamos la causa o la solución, necesitamos mantener nuestro sistema funcionando con normalidad. Para protegernos a este tipo de eventualidades, se debe establecer un valor de timeout en nuestras consultas y transacciones a bases de datos que impida que una consulta se quede indefinidamente esperando el resultado y, por tanto, todas las sesiones de la aplicación que llegan a ese punto. En los entornos JEE, donde se configuran pools de conexiones a bases de datos que permiten un uso eficiente de recursos y conexiones de bases de datos, la ausencia de estos timeouts en esas condiciones pueden suponer el agotamiento de las conexiones del pool, debido a que no se liberan las conexiones y, por tanto, la imposibilidad de que nuevas sesiones de la aplicación puedan funcionar.
Configuración de Statement Timeout en Glassfish |
Usando PostgreSQL, este atributo sin embargo nos ha dado una desagradable sorpresa:
Caused by: org.postgresql.util.PSQLException: Method org.postgresql.jdbc4.Jdbc4PreparedStatement.setQueryTimeout(int) is not yet implemented. at org.postgresql.Driver.notImplemented(Driver.java:753) at org.postgresql.jdbc2.AbstractJdbc2Statement.setQueryTimeout(AbstractJdbc2Statement.java:635) at com.sun.gjc.spi.base.ConnectionHolder.prepareStatement(ConnectionHolder.java:477) at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.prepareStatement(DatabaseAccessor.java:1162) at oracle.toplink.essentials.internal.databaseaccess.DatabaseCall.prepareStatement(DatabaseCall.java:612) at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:485)
Pues si, aunque parezca increíble, el driver jdbc de Postgresql no implementa aún setQueryTimeout(), al menos hasta la versión disponible en el momento de escribir este artículo (versión Version 9.0-801 de 2010-09-20).
Establecimiento de propiedades de la conexión en el pool de conexiones de Glassfish |
Para nuestros scripts PL/pgSQL, podemos establecer directamente el parámetro statement_timeout al inicio:
SET statement_timeout TO 5000; -- para 5 segundos <...resto del PL> RESET statement_timeout; -- reset
En general, establecer un valor de timeout en operaciones síncronas es una buena práctica para evitar sorpresas desagradables.
No hay comentarios :
Publicar un comentario