martes, 9 de noviembre de 2010

Cierre de conexiones olvidadas


En esta entrada quiero comentar un problema muy común que puede darse en un proyecto de gran envergadura, estamos hablando de millones de líneas de código, centenares de métodos que acceden a las bases de datos, y en el que puede ser bastante común, que debido a una mala práctica de programación, estos métodos solicitan o abren una conexión a la base de datos y por despiste del programador, no se cierra.
Ante esta situación nuestro proyecto puede tener grandes problemas cuando todas estas conexiones abiertas no han sido correctamente cerradas o devuelta a un POOL de conexiones, provocando que nuestro servidor caiga debido a que pueda quedarse en un momento dado sin conexiones disponibles para ser usadas.
Según la documentación de Java, sabemos que el recolector de basura es capaz de cerrar estas conexiones una vez que detecta que estas conexiones dejaron de ser usadas.
Resumiendo las cuentas, estamos ante el escenario de que nuestro proyecto tiene un único punto donde se crean las conexiones (Pool de conexiones) y que existe código que no devuelve estas conexiones al pool, por lo que pueden quedar zombies en cualquier lugar del código. Además, para mala fortuna, no sabemos detectar en que parte del código no se devuelven al pool dichas conexiones. Este escenario se dio en un proyecto de la Consejería de Gobernación de la Junta de Andalucía, y que resolví con la solución que voy a aportar ahora mismo.
Aunque no voy a colocar el código, voy a comentar la idea para que puedan considerarla y ponerla en práctica.
Como sabemos que el punto común de la aplicación para crear u obtener conexiones es el pool de conexiones, sabemos que será aquí uno de los lugares candidatos a tener que modificar. En este punto, en vez de devolver una conexión específica (instancia de la clase que implementa java.sql.Connection), devolveremos una conexión específica nuestra que crearemos (decorador).
1.       Creamos una clase que implemente la interfaz java.sql.Connection
2.       Creamos un atributo delegado de tipo java.sql.connection (decorado) que alberga el delegado o instancia de conexión que crea el pool de conexciones. Es decir, nuestro pool devuelve una instancia de la clase que estamos creando y obligamos que la conexión real esté en este atributo delegado.
3.       Implementamos todos los métodos de la interfaz delegando al delegado cada llamada implementación.
4.       Reimplementamos el método close de nuestra clase, para que devuelva el delegado al pool.
5.       Implementamos el método finalize, que devolverá la conexión al pool si no se hizo antes esta operación. Esto es sencillo de saber, basta con colocar un atributo booleano para saber si se invocó el método close.
6.       En el constructor de nuestra clase, obligamos a llamar al recolector de basura con System.gc() cada ciertas conexiones abiertas o creadas, de tal forma que el recolector de basura, llamará al método finalize de cada conexión que no se esté usando, y esta a su vez devolverá el delegado al pool.
Como veis, esta solución es un poco rebuscada, de hecho en mi empresa se plantearon de crear herramientas para detectar cuantas conexiones no se cerraban. Sin embargo, con esta solución ahorré dinero y tiempo, y conseguimos el objetivo deseado (no más caídas del sistema por falta de conexiones).
Disculpad por no aportar el código, pero creo que es más importante conocer la idea, que el código en sí, puesto que para cada proyecto, el código puede variar. Lo importante es conocer el diseño de la solución.
Si tenéis cualquier tipo de dudas, no tengáis reparo en poneros en contacto con migo para que os ayude en esta solución.

No hay comentarios:

Publicar un comentario