Implementar BoneCP

Hola foro
estoy trabajando con pool conections y he probado el conocido DBCP que al parecer con las ultimas versiones y actualizaciones presenta mejorias en comparacion con por ejemplo C3P0.
Pero he escuchado sobre BoneCP que es ucho mejor, pero no encuentro por ningun lado descargar las librerias necesarias ni ejemplo de codigo para ver como funciona.
Alguien tiene alguna implementacion de BoneCP y que la pudiera explicar?
Se agradece la cooperacion.
Saludos a todos.

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

google

Invertí la asombrosa cantidad de 10 segundos en buscar en Google, solamente tecleé BoneCP y el primer resultado fue la página oficial del proyecto. Ahí viene una liga a unos benchmarks que ellos hicieron.

El principio es el mismo con los tres, desde el punto de la vista de la aplicación: Tienes un DataSource al que le pides conexiones y luego las cierras. El DataSource en realidad no las cierra sino que el método close() de la conexión realmente la devuelve al pool.

Sus benchmarks no dicen cuándo se hicieron o con qué versiones de cada pool, solamente dice the latest stable. Yo hice un benchmark entre BoneCP, c3p0 y DBCP pero creo que no estuvo muy bien hecho. Era entre c3p0 0.9.2, DBCP 1.4 y BoneCP 0.6.4. La prueba consiste en correr varias veces una tarea en un thread pool, para que se haga de manera concurrente en repetidas ocasiones. La tarea siempre es la misma, el programa no cambia; solamente hay que ejecutarlo 3 veces, una vez con cada pool. Bueno y para ser un poco más exactos, hay que correrlo varias veces con cada pool, para sacar un promedio, porque puede haber variaciones.

La tarea que se corre repetidas veces es un UPDATE a una tabla y si no funciona porque no hay registro, se inserta a la tabla. Antes de comenzar la prueba, se borra la tabla. Esto hace que se inserte un registro por cada hilo del thread pool, el cual se actualizará muchas veces seguidas.

Los pools están configurados lo más similar posible: máximo de 6 conexiones activas, con 2 conexiones activas inicialmente.

El problema es que las variaciones me salieron muy grandes. Lo estoy corriendo otra vez y por ejemplo con DBCP 1.4 (y commons-pool 1.5.5) me salen estos tiempos:

Comienza 09:52:51 (1296489171044)
Termina  09:53:00 (1296489180748) o sea 9704 milis

Comienza 09:54:58 (1296489298500)
Termina  09:55:06 (1296489306557) o sea 8057 milis

Comienza 09:55:31 (1296489331082)
Termina  09:55:38 (1296489338654) o sea 7572 milis

Comienza 09:55:53 (1296489353092)
Termina  09:56:00 (1296489360691) o sea 7599 milis

Comienza 09:58:23 (1296489503121)
Termina  09:58:30 (1296489510688) o sea 7567 milis

Como puedes ver, el tiempo va bajando casi predeciblemente. La prueba la estoy corriendo con PostgreSQL en el mismo equipo que corre el programa (para ser más precisos habría que tener un servidor dedicado separado que no tenga más que la base de datos, pero bueno, las pruebas con los 3 pools se hacen bajo las mismas circunstancias).

Con c3p0 me da estos tiempos:

Comienza 10:00:58 (1296489658328)
Termina  10:01:05 (1296489665932) o sea 7604 milis

Comienza 10:01:25 (1296489685882)
Termina  10:01:33 (1296489693021) o sea 7139 milis

Comienza 10:01:39 (1296489699702)
Termina  10:01:46 (1296489706315) o sea 6613 milis

Comienza 10:03:23 (1296489803576)
Termina  10:03:31 (1296489811161) o sea 7585 milis

Comienza 10:03:59 (1296489839414)
Termina  10:04:06 (1296489846617) o sea 7203 milis

Considero que las primeras 2 corridas con DBCP fueron más lentas por alguna cosa con PostgreSQL y tener todo en el mismo equipo, que por el pool mismo. Las siguientes pruebas dan tiempos muy similares que con c3p0.

Finalmente, con BoneCP me da estos tiempos:

Comienza 10:07:13 (1296490033094)
Termina  10:07:22 (1296490042157) o sea 9063 milis

Comienza 10:08:08 (1296490088069)
Termina  10:08:17 (1296490097130) o sea 9061 milis

Comienza 10:08:49 (1296490129989)
Termina  10:08:59 (1296490139071) o sea 9082 milis

Comienza 10:12:35 (1296490355864)
Termina  10:12:44 (1296490364472) o sea 8608 milis

Comienza 10:12:54 (1296490374715)
Termina  10:13:03 (1296490383378) o sea 8663 milis

BoneCP resultó ser consistentemente el pool más lento. Solamente para estar seguros, otras dos pruebas con DBCP:

Comienza 10:14:04 (1296490444462)
Termina  10:14:11 (1296490451592) o sea 7130 milis

Comienza 10:14:27 (1296490467696)
Termina  10:14:35 (1296490475358) o sea 7662 milis

Eso confirma mis sospechas de que las primeras dos pruebas fueron más lentas por causas ajenas a DBCP.

El código de la prueba es muy sencillo. Utilizo Spring para alambrar algunas cosas, y estoy usando un SimpleJdbcTemplate para las pruebas, porque ya hace por mí todo el rollo de obtener la conexión, utilizarla, cerrarla, etc y además es lo que uso en el sistema en producción que me interesaba para ver si cambiaba de pool. Los resultados que puse aquí fueron configurando 10 hilos en el threadpool y corriendo 10mil procesos.

public class DbPoolTest implements Runnable {

    private ThreadPoolExecutor tpool;
    @Resource private SimpleJdbcTemplate jdbc;
    @Resource private DataSource dataSource;
    private int procs;
    private int threads;

    @Required
    public void setProcesos(int value) {
        procs = value;
    }
    @Required
    public void setThreadPoolSize(int value) {
        tpool = (ThreadPoolExecutor)Executors.newFixedThreadPool(value);
        threads = value;
    }

    public void run() {
        System.out.printf("Corriendo %d procesos en %d hilos con %s%n", procs, threads, dataSource.getClass().getName());
        jdbc.update("DELETE FROM pool_test");
        long ini = System.currentTimeMillis();
        System.out.printf("Comienza %TT (%d)%n", new Date(ini), ini);
                //Aqui echamos a andar todos los procesos en el thread pool
        for (int i = 0; i < procs; i++) {
            tpool.execute(new Minitest());
        }
                //Lo apagamos porque ya no vamos a meter nada mas
        tpool.shutdown();
                //Esperamos a que termine de correr todo lo que hay encolado
        try {
            tpool.awaitTermination(2, TimeUnit.MINUTES);
        } catch (InterruptedException ex) {
            //nada
        }
        long fin = System.currentTimeMillis();
        System.out.printf("Termina  %TT (%d) o sea %d milis%n", new Date(fin), fin, fin-ini);
        tpool.shutdownNow();
    }

    private class Minitest implements Runnable {
        public void run() {
            String tname = Thread.currentThread().getName();
            if (jdbc.update("UPDATE pool_test SET cuenta=cuenta+1, actualizada=localtimestamp WHERE thread=?", tname) == 0) {
                jdbc.update("INSERT INTO pool_test (thread, cuenta, creada) VALUES (?, 0, localtimestamp)", tname);
            }
        }
    }
}

La tabla en la base de datos se crea con un simple CREATE TABLE pool_test(thread VARCHAR(20),cuenta INT NOT NULL DEFAULT 0, creada TIMESTAMP, actualizada TIMESTAMP). No tiene llave primaria ni índices pero no importa, no estamos probando performance de la base de datos sino de los pools de conexiones (y además vamos a meter muy pocos registros como para que importe).

Faltan muchas otras pruebas como lo de BoneCP con dos particiones, usar pools mucho más grandes, etc. Es probable que el comportamiento cambie y que por ejemplo con 200 conexiones (como los benchmarks de BoneCP) ya no funcione tan bien DBCP o c3p0.

interesante las pruebas, las

interesante las pruebas, las ejecutare ahora mismo.
No busque en google porq kice preguntar de inmediato en este foro que me resulta mas confiable sobretodo cuando programo en java, y con tu respuesta ratifico eso.
Cualquier cosa que me salga mal escribo,
gracias por la ayuda.

solicitud

Yo soy el autor de BoneCP (usando Google Translate lo siento por mi español).

Por favor enviar su fichero de configuración, estoy seguro de que hay un error de configuración en alguna parte. BoneCP es consistentemente más rápido en todas las pruebas que he hecho.

Uno de los problemas que tiene: se presentó la base de datos que es un factor desconocido. Lo que hice en mi punto de referencia es burlarse de un controlador JDBC de modo que no hay reales PP pide por lo que se mide sólo la piscina.

También última versión BoneCP es 0.7.1-rc3 que es más rápido, el 0.6.4 es muy viejo.

Wallace

y como podria hacer un test

y como podria hacer un test por ejemplo para DBCP para asegurarme de que las conexiones no se caigan en ningun momento (o mas bien ver en que momento se cayeron)

Imagen de ezamudio

wwadge

Hi wwadge. I assume you're talking the test code I posted. If this is the case, the config for each datasource was this (in a Spring Application Context) - I only enabled one of the three at a time:

    <bean id="dataSource" name="bonecp" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="jdbcUrl" value="jdbc:postgresql://localhost/blabla" />
        <property name="username" value="blabla" />
        <property name="password" value="blabla" />
        <property name="acquireIncrement" value="2" />
        <property name="releaseHelperThreads" value="2" />
        <property name="partitionCount" value="1" />
        <property name="maxConnectionsPerPartition" value="6" />
        <property name="minConnectionsPerPartition" value="2" />
    </bean>

    <bean id="dataSource" name="dbcp" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost/blabla" />
        <property name="username" value="blabla" />
        <property name="password" value="blabla" />
        <property name="maxActive" value="6" />
        <property name="maxIdle" value="2" />
        <property name="minIdle" value="1" />
        <property name="initialSize" value="2" />
        <property name="maxWait" value="1000" />
        <property name="validationQuery" value="SELECT localtimestamp" />
    </bean>

    <bean id="dataSource" name="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="jdbcUrl" value="jdbc:postgresql://localhost/blabla" />
        <property name="user" value="blabla" />
        <property name="password" value="blabla" />
        <property name="acquireIncrement" value="2" />
        <property name="minPoolSize" value="2" />
        <property name="maxPoolSize" value="6" />
        <property name="automaticTestTable" value="c3p0test" />
        <property name="numHelperThreads" value="2" />
    </bean>

The test was done with DBCP 1.4 (and commons-pool 1.5.5), c3p0 0.9.2, and BoneCP 0.7.0 (with google-collect-1.0). The JDBC driver is postgresql-8.4-702.jdbc4.jar; the test uses the exact same setup in every case, the only thing that changes is the dataSource.

Imagen de ezamudio

prueba desconexiones

Para probar desconexiones pues solamente que tumbes la base de datos a la mitad de la prueba; o si está en un server separado, la desconectes de la red. Y luego reconectarla (o levantarla de nuevo) para ver cómo se reconecta la aplicación.

claro, es justamente la idea

claro, es justamente la idea que tengo, quiero tumbar la base de datos y mostrar a traves de un ciclo cuantas conexiones se hicieron (probe un for pero no me resulto), por ejemplo si tengo una simple implementacion de una clase q implementa una consulta a traves de un pool como esta:

public class QuerySimple{

    private DataSource dataSource=null;
       
    public void setDataSource (DataSource dataSource) {
        this.dataSource = dataSource;
        }

        public void Consulta () {      
                Connection conexion = null;
                try {
                    conexion = dataSource.getConnection();     
                    Statement sentencia = conexion.createStatement();

 /*********   Por ejemplo aqui necesito conocer la efectividad del pool si tumbo la base de datos o se me cae la red"*************/

                    ResultSet resultado = sentencia.executeQuery("SELECT algo FROM unaTabla"); 

                    }  
                }
                catch (Exception e) {
                 e.printStackTrace();
                }
                finally {
                   if (null != conexion)
                         conexion.close();
           }
        }
       
        public static void main(String[] args) {
                new QuerySimple();
            }

}

no se si quedo claro la prueba que quiero hacer.

hay un atributo llamado Pool

hay un atributo llamado Pool State pero no se como implementarlo,
no es que este desesperada pero no me agrada cuando no me funciona un codigo :/
cualquier sugerencia sera bienvenida :)

Algunas de las

Algunas de las preguntas:

¿Cuántas conexiones estás golpeando la piscina? ¿Puedo ver el código?
También releaseHelperThreads = 2 en bonecp pero no en el DBCP.

+ Sólo tiene 6 conexiones!
+ Recomendar a prevenir el crecimiento de las piscinas en las pruebas es decir, establecer el tamaño mínimo tamaño máximo ==
+ ¿Estás haciendo JIT calentamiento primero para manejar en tiempo de ejecución de optimización?

Imagen de ezamudio

wwadge

All the code is here already. The code I posted earlier is configured to make a fixed thread pool of 10 threads and I queue 10 thousand tasks on it (the MiniTarea class, which updates/inserts on a very simple test table).

Yes, the pools (all of them) have only 6 connections. I don't think DBCP has helper threads (it can't have any, there's no such property in the BasicDataSource).

All of the pools have min and max connection parameters set. Not that it matters, because the test will always be using all the connections available.

No JIT warmup, I just run the task. Maybe the google translation did a lousy job, but the point is: I wanted to test all 3 pools and so I wrote a very simple test consisting of a simple task: updating a row, or inserting it if it doesn't exist. I created a task that does this and I run 10 thousand instances of that task in a thread pool of 10 threads. Each thread will cause a row to be inserted; after that, it's just updates to the database. I'm using Spring's SimpleJdbcTemplate because that's what I use in a real app and I want the test to be similar to the production environment.

So I run this test each of the 3 pools, one at a time. That is, I configure the Spring application context with 1 pool and run the test (5 times, 5 separate runs). Then I do the same thing with a different pool, and then the last one. It's all separate runs; JIT warmup won't give advantage to one of the pools because they're not all tested during the same run. Maybe the JIT warmup can make a difference, but it will make a difference for all 3 pools.

This is not meant to be an authoritative benchmark of any kind. I just posted the code here because someone asked about it and I had already done this test.

librerias necesarias

Hola, estoy aprendiendo de esto tambien y viendo ese minitest que hiciste trate de probarlo pero no pude porq me arroja un error al declarar la variabl jdbc como SimpleJdbcTemplate, ¿cuales son todas las librerias que utilizaste para realizar e test?? o quizas mi problema es otro pero no alcanzo a percatarme.

Imagen de ezamudio

Spring

Necesitas una clase que eche a andar el programa (bootstrap):

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext appctxt = new ClassPathXmlApplicationContext("spring-config.xml");
        Runnable main = (Runnable)appctxt.getBean("main");
        main.run();
    }
}

Y necesitas los siguientes módulos de Spring 3.0 (usé 3.0.5 que es lo más reciente): asm, expression, core, beans, context, context-support, jdbc, transaction.

De la versión 1.6.1 de SLF4J: jcl-over-slf4j, slf4j-api, slf4j-simple

Para probar con DBCP: commons-dbcp-1.4, commons-pool-1.5.5.

Para probar con c3p0: c3p0-0.9.1.2

Para probar con BoneCP: bonecp-0.7.0, google-collect-1.0

Y obviamente el driver JDBC de la base de datos que vayas a utilizar. Necesitas una tabla llamada pool_test que tenga las siguientes columnas:
thread (tipo VARCHAR), cuenta (tipo INTEGER), creada (fecha/hora, en PostgreSQL se llama TIMESTAMP), actualizada (también fecha/hora).

Para lo del warmup de la JVM, debe ser suficiente con poner las siguientes líneas justo antes de la línea que hace el DELETE a la tabla:

tpool.execute(new MiniTest());
try { Thread.Sleep(2000); } catch (InterruptedException ex) { /* nada */ }

Con eso se inicializa el thread pool, se compila la clase MiniTest, y se tiene todo listo para que no se afecte el tiempo de la prueba.

En el application context, además de los 3 pools (de los cuales solamente uno debe estar definido cada vez, los otros dos los comentas), debes tener estos beans:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans <a href="http://www.springframework.org/schema/beans/spring-beans.xsd
" title="http://www.springframework.org/schema/beans/spring-beans.xsd
">http://www.springframework.org/schema/beans/spring-beans.xsd
</a>            <a href="
http://www.springframework.org/schema/context" title="http://www.springframework.org/schema/context">http://www.springframework.org/schema/context</a> <a href="http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/>

" title="http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/>

">http://www.springframework.org/schema/context/spring-context-3.0.xsd">

...</a>    <bean id="main" class="com.solab.benchmark.dbpools.DbPoolTest">
        <property name="threadPoolSize" value="10" />
        <property name="procesos" value="10000" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
        <constructor-arg ref="dataSource"/>
    </bean>

<!-- uno de los tres dataSource aquí -->
</beans>

Y con eso ya queda para correr.

excelente brother! tengo otra

excelente brother!
tengo otra pregunta aprovechando este thread y los comentarios que se han hecho:
en teoria la la finalidad de los pool es reutilizar las conexiones manteniendolas siempre abiertas y entregandola a quien la solicite,
entonces como podria obtener una reconexion?
basicamente quiero hacer un breve codigo dentro de mi clase que implementa el pool y que me reconecte a la base de datos antes algun problema (tal como se menciono en la discusion de arriba)

Imagen de ezamudio

info

validationQuery! no sabia de

validationQuery! no sabia de su existencia.
voy a probar.

una pregunta: ¿puedo

una pregunta:
¿puedo solicitar una nueva conexion (si hay algun fallo en la conexion e independiente si se obtiene o no) de esta forma:

if(dataSource.getValidationQuery()==null)
     conexion = dataSource.getConnection();    
Imagen de ezamudio

no

el validationQuery es una propiedad específica de DBCP (no recuerdo si los otros pools tienen algo similar). Hay dos de hecho: uno es para ejecutarlo cuando se toma una conexión del pool para prestarla a otro proceso, y el otro es para ejecutarlo antes de devolver una conexión al pool. Esto lo maneja el pool automáticamente, de modo que no es necesario que tú manejes nada.

Es decir, si por ejemplo el pool tiene algunas conexiones abiertas, pero das de baja la base de datos y luego la vuelves a levantar, entonces esas conexiones son inválidas; si le pides una a tu dataSource (que es un BasicDataSource de DBCP) y tiene el validationQuery configurado, lo va a ejecutar en la conexión (una de las que tiene ya en el pool); al ver que es inválida (cacha la excepción), desecha esa conexión y crea una nueva (bueno no estoy muy seguro si crea una nueva o si toma otra del pool y vuelve a hacer pruebas hasta terminárselas). La cosa es que para ti es transparente; al final vas a obtener una conexión válida... a menos que el pool se tarde mucho tiempo en validar esa conexión, entonces puede que llegue el timeout que le hayas configurado y si no pudo obtener una conexión en ese tiempo, ahí sí te arroja una excepción.

gracias por la

gracias por la aclaracion,
aunq estaba tan seguro de lo que pretendia hacer.... igual seguire jeje
por ejemplo pretendo trabajar con la variable que tiene la conexion... para que se entienda:

...
Connection con = null;
con = dataSource.getConnection();

if (!ValidarConexion(con)) { //este metodo lo uso para validar la conexion, es booleano, si  la variable "con" es null retorno falso y si no, reconecto

Connection newcon = dataSource.newConnection(true);
con = newcon;
...
...

no se si estaria bien la logica o quizas estoy siendo muy redundante, pero tenia un solo error al crear la nueva conexion, el problema es con el newConnection().

y no puedo solucionar ese error para saber si lo q estoy haciendo esta bien o mal.

Imagen de ezamudio

wow

O sea que ValidarConexion es una función "enterprise"? a nivel empresarial, o sea que hay que hacer mucho código con funciones de una línea... por qué no simplemente if (con == null)?

Y por lo visto no has entendido el punto. No es necesario el newConnection. Te remito al JavaDoc de BasicDataSource para que veas las opciones, leas la documentación del API, y en la documentación oficial hay una sección de configuración donde puedes ver todas las opciones de DBCP y también algunos ejemplos.

no se a q te refieres con eso

no se a q te refieres con eso de a nivel empresarial, pero bueno, ahora tengo una ultima duda,
Creé un metodo para ver las propiedades del pool y el problema q tengo es q cuando deseo obtener el numero de conexiones e estado idle (ocioso) del pool con getNumIdle() siempre me arroja el mismo valor, independiente del numero inicial que establezca yo en setInitialSize(), lo probe con 10,15 y 20 conexiones iniciales, pero siempre que muestro en la consola el numero de conexiones ociosas me dice q son 7, siendo que si yo defino 20 conexiones setInitialSize(20) y si muestro las conexiones activas deberia ser 1 y las conexiones ociosas 19, pero eso no sucede, siempre me dice q las ociosas son 7.
por qué pasa eso?

Imagen de ezamudio

en qué momento

Los valores de numIdle y numActive dependen de lo que estés haciendo con el pool. Si no estás usando conexiones cuando consultas esos valores, te va a dar siempre lo mismo. Tienes que pedir una conexión al pool y pedir los valores para obtener que hay 1 activa y las otras inactivas.

Tal vez el initialSize no lo pela y solamente inicializa 8 conexiones por eso te dice que hay 1 activa y 7 idle.

El RDBMS con el que estás probando esto, acepta 20 conexiones? tal vez solamente se pueden crear 8 y por eso no hay más.

Lo de la función a nivel empresarial era burla. Esa función que tienes de ValidarConexion me parece totalmente innecesaria. Por lo que describes, creo que el código es solamente boolean ValidarConexion(Connection conn) { if (conn == null) { return false; } else { return true; } }. Esa función realmente podría llamarse isObject o isNotNull, y recibir un Object como parámetro, devolviendo false cuando el objeto es null. Pero esa función realmente es completamente superflua, no hay una justificación para su existencia, porque es muy fácil validar que un objeto sea null en cualquier condición para un if, for, while, etc. Entonces tu código podría decir simplemente if (conn != null) {...} y no tienes que tener esa función (que además los métodos en Java deberían empezar con minúscula; solamente es una convención pero es muy aconsejable seguirla).

Lo de función empresarial es una traducción de "enterprise code", inspirado en cosas como esta o esta.

aaaah jeje bueno es que esa

aaaah jeje
bueno es que esa funcion tenia otra finalidad o funcionalidad tambien para las querys a la base de datos.
y sobre lo otro parece que hay que tomar en cuenta todas las propiedades al momento se hacerles el set, ya que estoy viendo que estableciendo un valor a setMaxIdle() muestra las conexiones perdidas siempre y cuando sea consecuente con el numero inicial de conexiones.