domingo, 18 de septiembre de 2011

Batch processing en rails

En aplicaciones web con cierta complejidad, es común que aparte de responder a las peticiones de los clientes, se necesite realizar trabajos batch o simplemente migrar la base de datos a una nueva estructura.

Cuando la base de datos crece, aparecen nuevos problemas, uno de ellos es que las tablas de la base de datos pueden no caber en memoria. En hostings baratos este problema se acentúa ya que no se suele contar con demasiada RAM. Con ruby on rails es muy fácil solventar esta dificultad.

Tomando como ejemplo una tabla muy grande de usuarios en la que hay que recalcular un campo para cada usuario.
User.find_each{|u| u.karma = calculate_karma(:user => u)}
find_each toma bloques de 1000 usuarios y va devolviendo de uno en uno de forma transparente, se utilizan 1000 entradas por defecto pero se puede cambiar mediante el parámetro batch_size.
User.find_each(:batch_size => 200){|u| u.karma = calculate_karma(:user => u)}
find_each se implementa usando find_in_batches, se puede llegar a mayor nivel de control usando esta función que devuelve directamente los grupos de entradas.
User.find_in_batches(:batch_size => 2000){|group| group.each{|u| u.karma = calculate_karma(:user => u)}}
Puede que este último ejemplo parezca más complejo sin ningún tipo de beneficio, pero sería muy útil en caso de querer paralelizar el proceso.
User.find_in_batches(:batch_size => 2000){|group| Process.fork{calculate_karma(:group => group)}}

domingo, 4 de septiembre de 2011

Temporizadores en javascript: PollJS

Realizar tareas regulares en el tiempo en una web con javascript es una tarea algo pesada. PollJS es una librería muy pequeña y sencilla que facilita algo esta tarea.

Un ejemplo de una tarea regular con PollJS.
Poll.start({
    name: "refresh_list",
    interval: 1000,
    action: function(){}
});
Esa es la sintaxis básica para crear una tarea con PollJS, evidentemente nos deja establecer un intervalo de espera entre repeticiones y definir la acción que queremos ejecutar. Además se puede dar nombre a cada tarea para referenciarla posteriormente, por ejemplo, para detenerla.
Poll.stop("refresh_list");
Existe otra forma de detener una tarea, devolviendo false en la función que se pasa como acción se detiene el temporizador.

Con esta librería podemos además definir un número de reintentos máximo y una función de fallback si en ninguno de los intentos se ha detenido el temporizador.
Poll.start({
    name: "refresh_list",
    action: function(){},
    interval: 1000, 
    attempts: 5,
    fallback: function(){}
});