Mitigando memory leaks en acts_as_solr

Solr logo Sigo encontrando problemas al procesar volúmenes de información importantes con ruby y su ecosistema. Estamos hablando de algo más de dos millones y medio de registros... no es para tanto ¿no?

En este caso el síntoma es la muerte de ruby por falta de memoria durante la reconstrucción del índice de solr (un servidor para búsquedas de texto) con acts_as_solr (un plugin de rails para utilizar solr con active record).

AbelBook:triptance amuino$ rake solr:reindex
(in /Users/amuino/dev/triptance/svn/trunk/triptance)
Clearing index for City...
Rebuilding index for City...
ruby(7890,0xa0398720) malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ruby(7890,0xa0398720) malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ruby(7890,0xa0398720) malloc: *** mmap(size=1052672) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ruby(7890,0xa0398720) malloc: *** mmap(size=1052672) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
rake aborted!
failed to allocate memory

Un poco de google me descubre que no soy el primero, aunque sí que soy muy raro...

Como tengo bastante más soltura con java que con ruby, vuelvo a ejecutar la tarea con jruby y a analizar el consumo de memoria con jconsole. Un beneficio añadido es que la JVM me permite limitar la memoria y puedo reproducir los errores mucho más rápido (ruby sólo falla cuando el operativo no puede asignar más memoria... después de asignar los 64Gb de memoria virtual).

Tras mucho ensayo y error, parece que el consumo de memoria está estabilizado...

Estos son los cambios:

  • Leer los modelos como :readonly
  • Evitar crear un nuevo array con la versión solrizada de los modelos
  • Organizar el código para que todo siga funcionando

Es decir:

acts_as_solr/lib/class_methods Línea 204 y siguientes...

   def rebuild_solr_index(batch_size=0, &finder)
      finder ||= lambda { |ar, options| ar.find(:all, options.merge({:order => self.primary_key})) }
      start_time = Time.now

      if batch_size > 0
        items_processed = 0
        limit = batch_size
        offset = 0
        begin
          iteration_start = Time.now
          items = finder.call(self, {:limit => limit, :offset => offset, :readonly => true})
          items_processed += items.size
          last_id = items.last.id if items.last
          offset += items.size

          items.collect! { |content| content.to_solr_doc }
          if items.size > 0
            solr_add items
            solr_commit
          end
    
          time_so_far = Time.now - start_time
          iteration_time = Time.now - iteration_start         
          logger.info "#{Process.pid}: #{items_processed} items for #{self.name} have been batch added to index in #{'%.3f' % time_so_far}s at #{'%.3f' % (items_processed / time_so_far)} items/sec (#{'%.3f' % (items.size / iteration_time)} items/sec for the last batch). Last id: #{last_id}"
        end while items.nil? || items.size > 0
      else
        items = finder.call(self, {})
        items.each { |content| content.solr_save }
        items_processed = items.size
      end
      solr_optimize
      logger.info items_processed > 0 ? "Index for #{self.name} has been rebuilt" : "Nothing to index for #{self.name}"
    end

De nuevo, espero que esto le ahorre a alguien un poco de tiempo

Esta es la línea de comandos final...

jruby --server -J-Xmx1024m -w -S rake solr:reindex CLEAR=true BATCH=500

Y este, el consumo de memoria...

Consumo de memoria después de modificar acts_as_solr

2 comentarios

Tal vez deberias hacer un fork en github?

Hecho!
http://github.com/amuino/acts_as_solr

Y pull requests enviadas al autor original, mattmatt y onemorecloud.

(Aunque el proyecto parece un poco muerto, viendo los comentarios en el newsgroup...
)

Sigo buscando un hueco para migrarme a sunspot (que incluso lo recomienda mattmatt y está bastante activo... el último commit es de ayer)

Escribir un comentario

Cerrar