Software Guru Conference & Expo 2014

Universal Connection Pool para Driver JDBC de Oracle

Al construir aplicaciones web en java, es muy importante manejar las conexiones a base de datos mediante connection pooling para hacer un uso adecuado de los recursos de la base de datos.

Tomcat cuenta con su propia implementacion de connection pooling que se puede aplicar a cualquier driver JDBC llamado DBCP, pero, desgraciadamente esta implentacion de pooling a menudo falla al cerrar las conexiones por lo que acaba desperdiciando recursos del servidor.

El Driver JDBC de Oracle cuenta con 2 modos de pooling que si bien son mas confiables a la hora de recuperar recursos tampoco estan libres de problemas:

  1. Pooling Explicito (oracle.jdbc.pool.OracleConnectionCacheImpl)Obsoleto. Desventajas: inestabilidad por que no cuenta con capacidad intrinseca para validar conexiones
  2. Pooling Implicito (oracle.jdbc.pool.OracleDataSource) :Obsoleto, Ventajas: Compatible con supervisión mediante LambdaProbe. Desventajas: Sigue sufriendo de inestabilidad,  por sigue sin contar con capacidad intrinseca para validar conexiones

Recientemente aparecio un tercer modo:

UCP: Universal Connection Pool (oracle.ucp.jdbc.PoolDataSourceImpl) El modo recomendado por Oracle, supuestamente estable, puede validar conexiones, cuenta con una arquitectura unificada y portable que puede utilizarse no solo para pooling con Oracle si no tambien con otras bases de datos.

Este modo de pooling cuenta con una documentacion mas o menos decente, pero con la importante carencia de no contar con ejemplos concretos de como configurarla, por ejemplo, para Tomcat, asi que me di a la tarea de encontrar el modo:

Ejemplo de la configuracion requerida para tomcat:

<Resource name="jdbc/nombreRecursoJNDI"

auth="Container"

scope="Shareable"

factory="oracle.ucp.jdbc.PoolDataSourceImpl"

type="oracle.ucp.jdbc.PoolDataSource"

connectionFactoryClassName="oracle.jdbc.pool.OracleDataSource"

url="jdbc:oracle:thin:@direccionip:puerto:sid"        

user="*****" 

password="****" 

minPoolSize="10" maxPoolSize="20"

validateConnectionOnBorrow="true"
/>

Lo interesante de esta nueva configuracion es la opcion “validateConnectionOnBorrow” que, de acuerdo con la documentacion de UCP deberia encargarse de evitar los errores tipicos de los 2 modos originales de pooling: “Conexion Cerrada” para una conexion que acabamos de obtener del pool, o “El cache de conexiones ha caducado” igualmente para cuando reclamamos una conexion del pool que no se habia utilizado desde hace un buen rato.

Aun no he tenido oportunidad de probar UCP en condiciones de produccion, por lo que no estoy seguro de que efectivamente sea una mejoria, pero pienso hacerlo durante las proximas semanas (siempre que la severa limitacion del ClasspathHell de Tomcat me permita hacerlo). Una ves efectuadas las pruebas, las publicare aqui mismo.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

DBCP

El Commons DBCP de Jakarta, que es el que usa Tomcat como bien mencionaste, también puede validar conexiones al tomarlas del pool y también antes de devolverlas al pool. Es cosa de activar las propiedades testOnBorrow y testOnReturn de la clase BasicDataSource.

Pero pues habrá que probar este UCP, es bueno tener una tercera opción (la primera es DBCP, la segunda es C3P0, y ahora UCP de Oracle). Qué licencia tiene UCP?

Imagen de luxspes

Al principio yo tambien

Al principio yo tambien usaba DBCP, pero empece a notar algo curioso -que no me ocurria mi gracias a yo uso Spring-JDBC o Spring-ORM y esto simplifica mi manejo de aperturas y cierres de conexion- pero que si le ocurria a algunas otras aplicaciones aqui desarrolladas usando el API de JDBC directamente:

1) Apertura de Conexion
2) Apertura de Statement
3) Obtencion de RowSet
4) Cierre del Statement
5) Cierre de la Conexion
6) El RowSet sigue abierto!!!!!

Tal ves ya se corrigio (esto lo experimente con Tomcat 5.5 hace 3 años), al pasar a los pooling de Oracle, el problema se resolvio (pero empezaron lo problemas de conexiones caducadas o invalidas).
Desgraciadamente hasta donde se UCP es propietario de Oracle.

Imagen de ezamudio

C3P0

Y no probaste con C3P0? yo hice pruebas para comparar performance de ambos y DBCP es un poquitititititiittiito más rápido, pero dicen que C3P0 es más robusto, porque maneja mejor las desconexiones (desecha conexiones inválidas y crea nuevas conexiones de forma más rápida y transparente que DBCP)

Re: Universal Connection Pool para Driver JDBC de Oracle

Este modo de pooling cuenta con una documentacion mas o menos decente

¡Guau! Sí que tienes estándares muy altos. Si más de 50 páginas de la documentación de este pool se te hacen más o menos decentes (y no me refiero sólo a la extensión), no me imagino lo que has de pensar de la documentación de los otros pobres pools.

pero con la importante carencia de no contar con ejemplos concretos de como configurarla, por ejemplo, para Tomcat, asi que me di a la tarea de encontrar el modo:

Bueno, a mi en lo personal me parece que el lugar idóneo para encontrar cómo configurar un pool de conexiones en Tomcat es... la documentación de recursos JNDI de Tomcat. Sabiendo que los archivos de configuración de Tomcat se utilizan de manera similar a los archivos de contexto de Spring, es decir, siguen la convención JavaBeans y determinan cuáles "setters" son invocados (con excepción de las propiedades auth, description, name, scope y type), entonces la documentación del UCP explica cómo configurar el pool en otros ambientes:

Third-party products can instantiate these data source implementation classes. In addition, the methods of these interfaces follow the JavaBean design pattern and can be used to set connection pool properties on the class using reflection. For example, a UCP data source that uses an Oralce JDBC connection factory and database might be defined as follows and loaded into a JNDI registry:

<data-sources>
<data-source
name="UCPDataSource"
jndi-name="jdbc/UCP_DS"
data-source-class="oracle.ucp.jdbc.PoolDataSourceImpl">
<property name="ConnectionFactoryClassName"
value="oracle.jdbc.pool.OracleDataSource"/>
<property name="URL" value="jdbc:oracle:thin:@//localhost:1521:oracle"/>
<property name="User" value"user"/>
<property name="Password" value="password"/>
<property name="ConnectionPoolName" value="MyPool"/>
<property name="MinPoolSize" value="5"/>
<property name="MaxPoolSize" value="50"/>
</data-source>
</data-sources>

(aplicar las convenciones del elemento en Tomcat)

Saludos

Javier

Precisión técnica pools de Oracle

El Driver JDBC de Oracle cuenta con 2 modos de pooling que si bien son mas confiables a la hora de recuperar recursos tampoco estan libres de problemas:
1. Pooling Explicito (oracle.jdbc.pool.OracleConnectionCacheImpl)Obsoleto. Desventajas: inestabilidad por que no cuenta con capacidad intrinseca para validar conexiones
2. Pooling Implicito (oracle.jdbc.pool.OracleDataSource) :Obsoleto, Ventajas: Compatible con supervisión mediante LambdaProbe. Desventajas: Sigue sufriendo de inestabilidad, por sigue sin contar con capacidad intrinseca para validar conexiones

En realidad en ambos casos mencionas sólo un problema con el pool: su incapacidad de probar conexiones antes de entregar una conexión cuando se le solicita. La razón es que OracleDataSource implementa un framework propietario de pool de conexiones, el cual no tiene esta capacidad. Este es el framework subyacente para el caching de conexiones como el de OracleConnectionCacheImpl, que no es más que un pool de conexiones "JDBC 3.0 compliant". Es decir, el driver de Oracle provee dos tipos de pool de conexiones: propietario y estándar JDBC 3.0.

¿Qué tan inestable es esto? Quizá no demasiado. Por default, los pools de conexiones como los de WebSphere, WebLogic e inclusive C3P0 no efectúan "testing" de las conexiones, ya que generalmente esto es lo mejor, pues el "testing" tiene implicaciones negativas de rendimiento. Así que ¡a usar sin miedo cualquiera de los dos sabores del pool de conexiones de Oracle!

Por supuesto, siempre que se pueda, debería usarse el nuevo UCP.

Saludos

Javier Castañón

Imagen de luxspes

La calidad de una

La calidad de una documentación no va en funcion de su volumen, si no de la utilidad de su contenido, tomemos por ejemplo, tu afirmación: "la documentación del UCP sí explica cómo configurar el pool en otros ambientes" y la mia "no contar con ejemplos concretos de como configurarla, por ejemplo, para Tomcat, " y lo que dice la documentacion de UCP: "a UCP data source that uses an Oracle JDBC connection factory and database might be defined as follows and loaded into a JNDI registry".

Lo que copiaste en tu comentario (que por cierto yo ya habia leido antes, pero gracias por hacerlo notar para otros lectores) no es un ejemplo de como configurar un datasource que sirva para ningún java enterprise application server en concreto, es solo una "configuración general" , si no me crees, observa las diferencias que tiene con respecto a la configuración que puse en mi blog post, en donde explícitamente menciono que lo lo que yo quisiera es que tuviera ejemplos concretos:

name en tomcat contra jdni-name en la configuracion general
factory en tomcat contra data-source-class en la configuracion general
type en tomcat contra ... ni si quiera tiene equivalente en la configuracion general!

A eso es con lo que me referia, otra ves, con "no contar con ejemplos concretos de como configurarla, por ejemplo, para Tomcat" de hecho no cuenta con un ejemplo, que sirva para ningun application server en concreto. Desgraciada (o afortunadamente) cosas como las definiciones de los datasource no estan estandarizadas entre distintos application servers jee, por lo que resulta como contar con ejemplos concretos para diversos application servers (o algunas veces, de plano, mejor utilizar Spring para tener una configuración verdaderamente portable)

Imagen de luxspes

La inestabilidad, va en

La inestabilidad, va en funcion de la situacion... desgraciadamente para mi, la red y los servidores sobre los que tiene que ejecutarse mi sistema deja mucho que desear en cuanto a estabilidad, por supuesto, que mejor que corregir esos problemas subyacentes (ya ando gestionado que eso se corrija, pero mientras tanto...) pero a veces uno no cuenta con el poder o los recursos para hacerlo, en esos casos, el sistema tiene que ajustarse a las condiciones del ambiente y verificar las conexiones antes de usarlas. Por supuesto, si tu red y servidores son estables, es posible que no lo necesites.

Re: La calidad de una

Entonces estamos más o menos de acuerdo ¿no?: en mi post aclaré que no me refería sólo a la extensión de la documentación, afirmé que había que conocer el funcionamiento de la configuración de Tomcat para poder interpretar el ejemplo de acuerdo a las convenciones JavaBean, también aclaré que el atributo type (junto con otros tres) salen de la regla. Por último escribí que "quizá" y "generalmente" no hace falta probar conexiones.

Saludos

Javier Castañón

Imagen de luxspes

Supongo que tal vez estamos

Supongo que tal vez estamos en "Acuerdo Violento" ;-)

Imagen de luxspes

No, la verdad no he probado

No, la verdad no he probado C3P0, voy a darle una ojeada a ver si me puede ayudar con estos problemas, aunque igual me preocupa que como va "encima" de los drivers tenga problema con el codigo JDBC mal escrito.

Re: Supongo que tal vez estamos

Para nada, definitivamente es un Heated Agreement ;-)

Perdón, no pude resistirlo, hace tiempo que no tenía la oportunidad de una buena discusión técnica.

Saludos cordiales

Javier

Imagen de ezamudio

me consta

hace tiempo que no tenía la oportunidad de una buena discusión técnica

Eso me consta, creo que nunca te había visto postear tanto en un foro jaj

Imagen de benek

Vaya

Vaya... hasta que alguien sacó a Javier del Vampire Wars :-O

Jaja, no te creas tocayito.

Re: me consta

Eso me consta, creo que nunca te había visto postear tanto en un foro jaj

No creo, por ahí hay algo muy interesante e ilustrativo (al menos para mí) de Execution Framework, timers y pools de threads ;-)

Imagen de ingscjoshua

pregunta

Pregunta tengo tomcat 6 y oracle XE puedo configurar un conection pool? en tomcat y si es asi q necesito actualmente uso weblogic 8.1 asi es la arquitectura yo no lo defini y bueno asi las cosas el chsite q con esta roña de lap q me dieron se muere despues de 3 dploys y no exageron en decirlo y me dijeironq si uso tomcat por lo menos no moria ya qeste solo es un web container y no un server de apliacciones pro ende mas lijegro alguna sujerencia?

Imagen de ezamudio

Tomcat conn pool

Imagen de ingscjoshua

duda pues despues del primer intento salto esto

GRAVE: Exception processing Global JNDI Resources
javax.naming.NamingException: Could not load resource factory class [Root exception is java.lang.ClassNotFoundException: oracle.ucp.jdbc.PoolDataSourceImpl]
at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:81)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)
at org.apache.naming.NamingContext.lookup(NamingContext.java:793)
at org.apache.naming.NamingContext.lookup(NamingContext.java:140)
at org.apache.naming.NamingContextBindingsEnumeration.nextElementInternal(NamingContextBindingsEnumeration.java:113)
at org.apache.naming.NamingContextBindingsEnumeration.next(NamingContextBindingsEnumeration.java:71)
at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:137)
at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:144)
at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:109)
at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.lifecycleEvent(GlobalResourcesLifecycleListener.java:81)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:703)
at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
Caused by: java.lang.ClassNotFoundException: oracle.ucp.jdbc.PoolDataSourceImpl
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:79)
... 18 more
12-jul-2010 19:16:51 org.apache.catalina.core.StandardService start

Alguien me puede iluminar acerca de que esta mal?

pro cierto esto ocurre al momento de arrancar tomcat

Imagen de ezamudio

RTFS

Las primeras lineas del stack trace te dicen el problema:

javax.naming.NamingException: Could not load resource factory class [Root exception is java.lang.ClassNotFoundException: oracle.ucp.jdbc.PoolDataSourceImpl]

Si estas definiendo el datasource en Tomcat, debes tener los drivers JDBC de Oracle en el classpath de Tomcat. Me parece que hay un directorio "lib" dentro del directorio de Tomcat donde pones los JARs son clases que deben ser accesibles a Tomcat al momento de iniciarlo.

Lee este articulo te puede ser muy util.