Resultado de etiquetas “ruby”

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.

Por algún motivo, todos los desarrollos que empiezo con ruby necesitan utilizar imagemagick... Y aunque no es algo nuevo, pero es algo que necesito hacer una y otra vez (buscando en google antes), hoy lo voy a dejar por escrito.

Image magick

Mi plataforma: osx 10.6 (Snow Leopard). Aunque estoy usando rvm para gestionar mis rubies, esta instalación la voy a hacer sobre la versión del sistema (rvm system)... lo que implica usar sudo (si usas rvm con otra versión, omítelo)

Lo malo de ruby con imagemagick es que necesita compilar código nativo para hacer la interfaz entre ambos, lo que a su vez significa que se necesitan los ficheros de cabecera de imagemagick para que el compilador C se quede contento (que tiempos cuando java dominaba el mundo y podías hacer copy&paste incluso entre distintos operativos y plataformas...)

Para instalar imagemagick y sus cabeceras utilizo macports

sudo port install libxml2
sudo port install ImageMagick +no_x11

... tras la segunda línea es un buen momento para tomar un café.

Por último, instalamos la gema rmagick. También tarda lo suyo...

sudo gem install rmagick

Para hacer la prueba de que todo hay ido bien...

$ irb -rubygems -r RMagick
irb(main):001:0> puts Magick::Long_version
This is RMagick 1.15.3 ($Date: 2007/01/20 15:45:29 $) Copyright (C) 2007 by Timothy P. Hunter
Built with ImageMagick 6.3.2 02/23/07 Q8 http://www.imagemagick.org
Built for ruby 1.8.5 (2006-12-25 patchlevel 12) [powerpc-darwin8.0.0]
Web page: http://rmagick.rubyforge.org
Email: rmagick@rubyforge.org
=> nil

Hasta otra!

(Con agradecimientos a este otro blog en inglés que contiene la información crítica y a la guía de instalación de rmagick que descubrí demasiado tarde)

Hace poco que he empezado a evolucionar una aplicación escrita hace más de un año usando ruby on rails.

La aplicación funciona sin problemas en Ubuntu Karmic (en concreto, esta imagen de amazon) con la última versión de ruby disponible mediante apt-get.

El problema

El problema es que en mi máquina local la cosa no iba tan bien. El motivo es una diferencia en el patchlevel de las versiones:

ruby ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux]
ruby ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin9]

Yo uso MacPorts:http://www.macports.org/ para instalar ruby en mi mac y, aunque hay varias versiones de ruby disponibles, sólo está patchlevel más reciente.

La solución

Como siempre, google trae la respuesta. Joe Homs cuenta como crear una fuente local para MacPorts que contenga la versión anterior que queramos.

No es complicado:

  • Crea una carpeta para la fuente local.
mkdir /opt/macports-local-repo
  • Descarga la revisón a tu repositorio
cd /opt/macports-local-repo/
svn co http://svn.macports.org/repository/macports/trunk\
/dports/lang/ruby@62943 lang/ruby
  • Informa a MacPorts de que hay una nueva fuente. Para eso, modifica /opt/local/etc/macports/sources.conf y añade una línea con el protocolo file://. En mi caso:
file:///opt/macports-local-repo/
rsync://rsync.macports.org/release/ports/ [default]
  • Actualiza el índice de MacPorts
sudo portindex /opt/macports-local-repo/
  • Comprueba que todo ha ido bien
port search --exact ruby
ruby @1.8.7-p174 (lang, ruby)
    Powerful and clean object-oriented scripting language

ruby @1.8.7-p249 (lang, ruby)
    Powerful and clean object-oriented scripting language

Found 2 ports.
  • ¡Y a instalar!
sudo port install ruby @1.8.7-p174

Bonus

MacPorts permite activar y desactivar versiones, así que no hay problema en tener unas cuantas versiones instaladas, dependiendo del proyecto:

sudo port installed ruby
The following ports are currently installed:
  ruby @1.8.7-p72_2+thread_hooks
  ruby @1.8.7-p174_2+darwin+thread_hooks (active)
  ruby @1.8.7-p249_0+darwin+thread_hooks

Y para cambiar de una versión a otra...

sudo port activate ruby @1.8.7-p249*
--->  Deactivating ruby @1.8.7-p174_2+darwin+thread_hooks
--->  Activating ruby @1.8.7-p249*
AbelBook:~ amuino$ sudo port installed ruby
The following ports are currently installed:
  ruby @1.8.7-p72_2+thread_hooks
  ruby @1.8.7-p174_2+darwin+thread_hooks
  ruby @1.8.7-p249_0+darwin+thread_hooks (active)

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

Ruby se muere

Al menos, se muere cuando le pido que trabaje duro para mi...

/Users/amuino/.gem/ruby/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:1650: [BUG] Segmentation fault
ruby 1.8.6 (2008-08-11) [universal-darwin9.0]

Abort trap

Lo mejor es que es un error semi-aleatorio: no ocurre siempre en la misma línea ni fichero ni gema... pero ocurre siempre en algún momento de un proceso batch de varias horas.

Es muy posible que no sea culpa de ruby (he probado tanto con la versión instalada por Apple como con Darwin Ports) si  no del adaptador de MySQL... no lo sé.

En cualquier caso... yo ya me había olvidado de todos estos bus error y segmentation fault hace muchos años, al usar Java (aunque sí, también he visto alguna JVM morirse...).

Así que mi siguiente intento ha sido usar JRuby. Instalo el adaptador activerecord jdbc para mysql, un nuevo entorno y a volar. Sencillo ¿no?

Lamentablemente, tengo una dependencia que utiliza la gema libxml-ruby, que necesita una extensión nativa (¿extensiones nativas? ¿aplicaciones interpretadas no portables? ¿no hemos dado un paso atrás?)... algo que jruby no permite.

Por suerte, el proceso batch no usa libxml y puedo desactivarlo al usar jruby:

config.gem 'libxml-ruby', :lib=>"xml/libxml", :version => '1.1.3' unless RUBY_PLATFORM =~ /java/

Sólo queda configurar el acceso a mysql mediante jdbc. Es importante instalar la gema desde linea de comandos.

jruby -S gem install activerecord-jdbcmysql-adapter

Algo que me ha confundido durante horas han sido los errores del tipo:

Missing these required gems:   activerecord-jdbcmysql-adapter  = 0.9.2

... que son causados por declarar la dependencia en environment.rb. No lo hagas :-)

Y el toque final es añadir la compatibilidad del adaptador jdbc con la configuración mysql "de toda la vida" para no tener que tocar el database.yml ni crear un nuevo environment.

jruby script/generate jdbc

(Esto sólo activa el adaptador jdbc cuando se ejecuta sobre jruby).

Han sido unas cuantas horas de experimentos, búsquedas en google y frustración... ¡que espero que le ahorren tiempo a alguien!

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