Algunas aplicaciones requieren tiempos de respuesta reducidos y su flujo transaccional no depende de información o lógica de negocio obtenida desde el motor de base de datos. Para este escenario la solución que encontré fue implementar una cola con el objetivo de manejar la inserción hacia la base de datos sin impactar el tiempo de respuesta de una transacción.

Para este ejemplo supongamos que por regla de negocio se deben registrar todos los requerimientos en un log hacia la base de datos, sin embargo si la base de datos no esta disponible o tiene tiempos de respuesta altos no se debe impactar el tiempo de procesamiento del requerimiento. Para resolver este caso acudiremos a la clase LinkedBlockingQueue que aparece a partir de la versión 1.5 de Java.

Inicialmente creamos la clase AsyncDataWriter que se encargará de mantener una cola de objetos que implementan la interfaz EnqueuableData.

package pro.fware.samples;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class AsyncDataWriter extends Thread {

private BlockingQueue<EnqueueableData> queue =
new LinkedBlockingQueue<EnqueueableData>();

/**
* Envia un nuevo elemento request a la cola.
* @param request
*/
public void accept(EnqueueableData request) {
queue.add(request);
}

/**
* Metodo que obtiene los elementos de la cola
* y los procesa.
*/
public void run() {
while (true)
try {
execute(queue.take());
}
catch (InterruptedException e) {
//Report to log4j
}
}

/**
* Metodo que se encarga de procesar cada elemento
* obtenido de la cola.
* @param request
*/
private void execute(final EnqueueableData request) {
request.execute();
}
}

La interfaz EnqueuableData declara el método execute a ser invocado cuando los objetos son obtenidos de la cola.

package pro.fware.samples;

public interface EnqueueableData {
/*
* Metodo a ser implementado por los objetos
* que implementen esta interfaz.
*/
public void execute();
}

La clase que implementa este método es la clase DataToBeInserted que implementa el método execute, simulando un retraso bloqueante de 500 milisegundos.

package pro.fware.samples;

public class DataToBeInserted implements EnqueueableData{

private int field_1;
private String field_2;
private String field_n;

public DataToBeInserted(int field_1, String field_2, String field_n) {
super();
this.field_1 = field_1;
this.field_2 = field_2;
this.field_n = field_n;
}

/**
* @return the field_1
*/
public int getField_1() {
return field_1;
}

/**
* @param field_1 the field_1 to set
*/
public void setField_1(int field_1) {
this.field_1 = field_1;
}

/**
* @return the field_2
*/
public String getField_2() {
return field_2;
}

/**
* @param field_2 the field_2 to set
*/
public void setField_2(String field_2) {
this.field_2 = field_2;
}

/**
* @return the field_n
*/
public String getField_n() {
return field_n;
}

/**
* @param field_n the field_n to set
*/
public void setField_n(String field_n) {
this.field_n = field_n;
}

@Override
public void execute() {
// TODO Implementar un metodo que permita almacenar los datos.

/**
* A continuacion se simula un retraso bloqueante de 500
* milisegundos, simulando asi un tiempo de procesamiento alto.
*/
long current = System.currentTimeMillis();
int milisecondsToDelay = 500;
long future = current+milisecondsToDelay;
while(System.currentTimeMillis()<future)
{
//Dejar pasar el tiempo
}

System.out.println("Almacenando registro:"+field_1);
}

}

Finalmente creamos la clase Requester que será la encargada de crear datos con información aleatoria y ponerlo en la cola. A través de esta evidenciaremos que cada objeto se procesa rápidamente y se pone en la cola para ser atendido por el hilo de AsyncDataWriter.

package pro.fware.samples;

import java.util.UUID;

public class Requester {

/**
* Clase que crea datos aleatorios y los envia a la cola para
* escribirlos en la base de datos.
* @param args
*/
public static void main(String[] args) {
//Crea la cola y la inicializa.
AsyncDataWriter async_writer = new AsyncDataWriter();
async_writer.start();

//Datos aleatorios de prueba.
System.out.println("Preparando un conjunto de 100 datos para probar.");
for(int i = 0;i<100;i++)
{
System.out.println("Generando dato["+i+"].");
String field_2 = UUID.randomUUID().toString();
String field_n = UUID.randomUUID().toString();
DataToBeInserted data = new DataToBeInserted(i, field_2, field_n);
System.out.println("Agregando dato a la cola:");
async_writer.accept(data);
}
System.out.println("Finaliza el proceso de los 100 registros.");
}

}