Detectar fuga de conexiones JDBC en Weblogic

Funcionalidad del servidor Weblogic que permite conocer el estado y traza de creación de cada conexión JDBC

Recientemente resolvimos un caso de rendimiento en una aplicación ADF, la cual llenaba el pool de conexiones del servidor Weblogic cada 3 ó 4 horas.

Después de revisar el código fuente y aplicar mejores prácticas para la apertura/cierre de conexiones JDBC y de Application Modules (si, la aplicación usaba Configuration.createRootApplicationModule(String, String) para crear instancias de Application Modules…) el comportamiento mejoró pero el pool continuaba llenándose.

La aplicación era lo suficientemente grande como para encontrar la razón de este comportamiento solo con revisar el código fuente, así que decidimos usar una de las funcionalidades que ofrece el servidor Weblogic, la cual te permite conocer la traza de creación de cada una de las conexiones JDBC encontradas en el pool en determinado momento. Esto y los logs de la aplicación nos ayudaron a resolver el caso.

El siguiente procedimiento se encuentra documentado en la nota Doc ID 1502054.1 del soporte de Oracle (disponible desde WLS 10 en adelante)  y se recomienda ejecutarlo en varios intervalos de tiempo para poder comparar resultados.

  1. Activar el perfil para detectar fuga de conexiones
    Navegar a la opción: Services->Data Sources.
    Seleccionar el data source que se desea monitorear.
    Ir a la pestaña Configuration->Diagnostics.
    Activar la casilla “Profile Connection Leak”.

    jdbcleakwls-01

  2. Obtener imágenes diagnósticas
    Navegar a la opción Diagnostics->Diagnostics Images.
    Seleccionar el servidor que presenta el problema.
    Presionar el botón “Capture Image”.
    Ingresar la ruta donde se desea guardar el archivo, por defecto se almacena en:

    $DOMAIN_HOME/servers/<SELECTED_SERVER>/logs/diagnostic_images
    

    jdbcleakwls-02

Una vez completado el anterior proceso, se generará un archivo .zip en la ruta seleccionada. Al descomprimirlo se pueden visualizar varios archivos con información de diagnóstico, pero el que nos interesa en este caso es el del pool de conexiones JDBC: JDBC.img.

Dicho archivo puede ser abierto con un editor de texto y encontraremos información como la que se muestra a continuación:

Dumping Resource Pool:MyDS
Resource Pool:MyDS:dumpPool Current Capacity = 28
Resource Pool:MyDS:dumpPool dumping available resources, #entries = 2
Resource Pool:MyDS:dumpPool available[0] = groupId=bdstprd1,autoCommit=true,enabled=true,...
Resource Pool:MyDS:dumpPool available[1] = groupId=bdstprd1,autoCommit=true,enabled=true,isXA=false,isJTS=false,vendorID=100,connUsed=false,doInit=false...
Resource Pool:MyDS:dumpPool reserved[0] = groupId=bdstprd1,autoCommit=false,enabled=true,isXA=false,isJTS=false,vendorID=100,connUsed=false,...,
currentUser=java.lang.Exception
at weblogic.jdbc.common.internal.ConnectionEnv.setup(ConnectionEnv.java:356)
at weblogic.common.resourcepool.ResourcePoolImpl.reserveResource(ResourcePoolImpl.java:364)
at weblogic.common.resourcepool.ResourcePoolImpl.reserveResource(ResourcePoolImpl.java:330)
at com.acelopez.db.Connector.open(Connector.java:125)
...
Resource Pool:MyDS:dumpPool reserved[1] = groupId=bdstprd2,autoCommit=false,enabled=true,isXA=false,isJTS=false,vendorID=100,connUsed=false...,
currentUser=java.lang.Exception
at weblogic.jdbc.common.internal.ConnectionEnv.setup(ConnectionEnv.java:356)
at weblogic.common.resourcepool.ResourcePoolImpl.reserveResource(ResourcePoolImpl.java:364)
at weblogic.common.resourcepool.ResourcePoolImpl.reserveResource(ResourcePoolImpl.java:330)
at com.acelopez.db.Connector.open(Connector.java:125)
...

Si nos fijamos bien, podemos ver que el archivo nos muestra información de cada una de las conexiones JDBC que se encuentran actualmente en el pool de conexiones y además nos indica la clase y método en la cual fue creada, en nuestro ejemplo: com.acelopez.db.Connector.open.
Si después de un tiempo se genera nuevamente este archivo de diagnóstico y se continua observando la misma información, entonces hay una gran probabilidad de que dicho método esté generando conexiones que no son cerradas.

Como mejores prácticas para abrir/cerrar recursos, no solo tienen que ser conexiones JDBC, se recomienda usar la estructura try con recursos propios, disponible a partir de Java SE 7:

try(Connection conn=...)
{
   ...
}

Si tu versión de Java no permite el uso de esta estructura o si tus recursos no implementan java.lang.Autoclosable, entonces un bloque try/finally será suficiente:

Connection conn = null;
try
{
   conn = ...
   ...
}
finally
{
   if(conn != null)
   {
      try{conn.close();}catch(Exception ex){/*Hacer nada.*/}
   }
}

Espero esto sea de ayuda y puedas encontrar esas fugas de conexiones JDBC, en futuras entradas buscaré hacer lo mismo con otros servidores donde se puedan desplegar aplicaciones ADF.