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...

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)