Resultado de etiquetas “rails”

Aunque ejecutar los tests de un proyecto Rails en Hudson es muy sencillo y no necesita nada especial, hacer que Hudson entienda lo que Rails (Test::Unit, en realidad) está haciendo es otra cosa.

Para poder ver un gráfico como el que tienen nuestros proyectos Java (JUnit, en realidad), hace falta un poco de grasa para suavizar la relación.

Para esto, existe el plugin de rails ci_reporter, que traduce los resultados de Test::Unit a algo que Hudson puede entender... el formato XML de junit.

Para una aplicación Rails de toda la vida habrá que hacer tres cosas:

  • Instalar el plugin
gem install ci_reporter

(Si no usas rvm entonces seguramente tengas que poner un sudo delante).

  • Añadir 3 líneas a nuestro Rakefile
require 'rubygems'
gem 'ci_reporter'
require 'ci/reporter/rake/test_unit'
  • Ejecutar la tarea ci:setup:testunit antes de las pruebas
rake ci:setup:testunit test

Estos comandos son por defecto, para pruebas con Test::Unit. Si usamos otro framework, cambian. La documentación original lo explica bien.

Por defecto, los informes de las pruebas se generan en test/reports/ dentro del proyecto. Sólo hay que indicarle a Hudson que los lea tocando la configuración del proyecto.

Y acordarse de cambiar el comando para que invoque a ci_reporter en cada ejecución:

Listo, ejecuta un par de builds para poder ver el gráfico... ¡y a otra cosa!

Cada vez estoy más metido en el mundo del desarrollo con ruby on rails y hasta empiezo a disfrutarlo (¡en contra de mi intuición inicial!). Y cualquier cosa con la que haga proyectos necesita estar bajo un sistema de integración continua... yo uso hudson.

Para los no iniciados, un sistema de integración continua (como hudson) no es más que una forma de ejecutar tareas sobre el código, normalmente sobre la versión más reciente en el sistema de control de versiones. Tareas típicas son compilar, pasar pruebas, empaquetar o desplegar la aplicación en un entorno de QA (o incluso producción).

Por otra parte, ruby on rails no necesita compilación, así que lo único que necesitamos es saber que no hemos roto nada... es decir, que las pruebas pasan.

Fácil, creamos un nuevo proyecto de estilo libre y rellenamos los campos habituales para el SCM (dónde está el código fuente) y los disparadores (cuando se ejecuta la build). Yo suelo elegir el polling frecuente del SCM, para que se ejecuten las builds después del commit.

Y la parte interesante:

Añadir un nuevo paso de ejecución de tipo linea de comandos con el mismo contenido que usamos en nuestra máquina. Por ejemplo:

Recuerda que el checkout del proyecto crea un directorio, de ahí que sea necesario el cd trunk ... habrá que ajustarlo si usas otro nombre.

Hasta aquí sólo hemos conseguido que hudson se queje cuando hacemos un commit de algo que rompe las pruebas. Es un comienzo, pero estaría bien tener algo más de información, alguna gráfica... Eso, en el próximo capítulo.

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

1

Sobre mi

No hay sorpresas, mi nombre es Abel Muiño. Soy un apasionado del desarrollo de software desde que cayó en mis manos un ZX Spectrum 48K... si no recuerdo mal, tendría unos 7 años. Han pasado bastantes años, varias empresas y...

Comentarios recientes

  • @Manuel: tienes toda la razón sobre el "efecto contagio". Durante estas vacaciones hablaba del tem...

  • Germán: creo que tengo el mismo joystick de la foto en algún cajón en casa de mi padre ;-) Sobre tu...

  • Hola Abel: Está claro que Dios los cría y ellos se juntan. De ahí que siga tu blog, porque comparto...

    Manuel Jesús Recena Soto
    My most authentic self
  • Uff da miedo conocer tanta gente parecida, hace poco publiqué esta foto: http://twitpic.com/2c90t7...

  • No hombre! Gracias a ti que le subes el nivel a este pobre blog! Estoy de acuerdo en que no siempre...

Cerrar