jueves, 20 de diciembre de 2012

Paralelización de bucles en Ruby

En ocasiones, tenemos que realizar un proceso Batch en Ruby. Por ejemplo, una tarea rake que tenga que hacer una carga de datos.
Si el volumen de datos es elevado, el proceso puede llegar a tardar demasiado. Por esto, hay veces que , dependiendo del tipo de proceso, se puede considerar la paralelización de alguno de los bucles de la tarea.

La forma más simple de hacerlo es la siguiente:

Incluimos la librería de hilos de Ruby:

require 'thread'

namespace :nstyarea do
  task :tarea_pesada => :environment do

#...


end

Ahora, vamos al bucle que queremos correr en paralelo. Por ejemplo, el procesado de un fichero con muchas líneas. Preparamos un array donde iremos guardando los hilos, y partiremos las líneas del fichero en tantos trozos como hilos queramos generar.

    NUM_HILOS = 4
    my_threads = []
    lineas_fichero = (fichero_entrada.readlines)
    lineas_fichero.in_groups_of(lineas_fichero.count / NUM_HILOS, false) do |lineas|

        my_threads << Thread.new do
            lineas.each do |line|
               begin

               # cuerpo del bucle


               rescue Exception => ex
                 logger.error ex
                 logger.error ex.backtrace
                 logger.error "Error en #{line}"
               end
            end
        end
    end

Con esto, el bucle se ejecutará en paralelo con 4 hilos. Para finalizar, los hilos deben esperarse para continuar con el proceso:

    my_threads.each do |t|
      t.join
    end

Y con esto se realiza la paralelización. Es posible que si manejamos objetos dentro, en algún caso nos de un error, diciendo que no encuentra el tipo de dato o la clase. Para ello, podemos incluir directamente el fichero al principio. Ejemplo:

require_relative "../../app/models/empresa"

Así evitaremos este error.