BKT en Producción: Pre escalando Ruby

Una de las experiencias más gratificantes que puede vivir un desarrollador de software es ver su producto siendo usado por el público objetivo.

De nada sirve gastar meses y meses de trabajo en algo que nadie va a usar. Sí, queda la satisfacción de haber escrito una pieza de software, los aprendizajes del camino y la experiencia. Pero si no llega a usarse, es solo algo más.

Empecé a trabajar profesionalmente como desarrollador de software en el año 2012. El producto en el que trabajaba ocasionalmente tenía un número de usuarios decente, crecía poco a poco pero mi intervención era bastante escasa.

Otros proyectos que sí tenían más participación mía no eran tan desafiantes en términos de un plan a largo plazo.

Y así pasé tres años trabajando en proyectos pequeños, de poco alcance. No me sentía mal pero quería más. Quería saber qué era tener un producto andando en producción.

Con el tiempo pasé a otra empresa y ahí la cosa mejoró. Si bien los proyectos no fueron muy exitosos, el haber recorrido el camino con ellos me fue formando para lo que venía. Y fue así como llegado el año 2016 empecé a trabajar en un proyecto ambicioso y con un panorama claro.

Empecé a trabajar en Bucket.

¿Es Rails Escalable?

La respuesta corta es que eso ni importa. Muchos proyectos mueren incluso antes de lograr un número de usuarios que haga que tu servidor web se caiga.

Cuando un proyecto inicia, preocuparse por escoger una tecnología escalable es algo poco relevante. Lo importante es el proceso y todo lo que rodea el usar un lenguaje de programación o un framework web.

Sí hay que informarse bien al escoger la tecnología según los requerimientos, el proyecto, la visión.

Pero que tan es escalable pasa a un segundo plano.

Es así como antes de iniciar en este proyecto tuve la dicotomía entre usar un framework NodeJS y Rails.

Al final me decanté por Rails porque se sabía que los tiempos iban a ser muy estrictos(lo fueron) y era mejor profundizar(lo hice) que aprender otra cosa nueva a la marcha y con estrés(no estudié ni tampoco sé NodeJS).

El proyectó empezó, los sprints corrieron y llegó el momento de salir a producción. El momento esperado y anhelado por tantos años en mi carrera.

No hay dicha mayor que saber que el software que escribiste es usado y mayor es la dicha cuando el cliente está satisfecho con el trabajo.

Como el tema de si Rails es escalable(ya sabía que sí pero había que profundizar) o no ya no podía dejarse a un lado, opté por empaparme más al respecto de todo lo que involucra hacer escalable un servicio, un software.

Sí, Rails puede escalar

Así que sí, Rails sí puede escalar. Pero no precisamente por defecto o porque esté pensado para ello. Rails puede escalar cuando la arquitectura web que comprende al software está pensada para ello.

En todo caso, mientras averiguaba al respecto, salieron otras dudas con respecto a unos temas nuevos:

  • Multihilo
  • Multiproceso
  • CPU bound
  • I/O bound

Cada uno de esos temas ayuda a facilitar la escalabilidad de un producto. Hay que entenderlos y buscar la forma de adecuar la arquitectura con respecto a ellos también.

Ruby, ¿Multihilo o multiproceso?

Este tema es complicado. Primero toca tener claro que es que un sistema sea multihilo o multiproceso.

proceso vs hilo

Primero que nada, ¿qué es un hilo? Según la Wikipedia, un hilo(thread) de ejecución es la secuencia más pequeña de instrucciones que ejecutará el sistema operativo.

Los hilos son un subconjunto de un proceso por lo que un sistema puede ser multihilo incluso en un ambiente uniproceso.

Por su parte, un proceso es:

Una unidad de actividad que se caracteriza por la ejecución de una secuencia de instrucciones, un estado actual, y un conjunto de recursos del sistema asociados.

Wikipedia

En su momento, Tender Love explicaba en un artículo sobre si permitir una configuración para entornos multihilo o multiproceso. En el texto explica que Rails inserta un middleware llamado Rack::Lock que proteje a las aplicaciones cuando se escribe código que no es thread safe, es decir, código expuesto a fallas en su ejecución al no tener en cuenta otros hilos de ejecución.

Sea cómo sea, es claro que el asunto de multihilo y multiproceso no es algo trivial. Sobre todo en Ruby donde en el interprete(ya que Ruby es un lenguaje interpretado y no compilado) viene con algo llamado GIL.

El GIL o Global Interpreter Lock, es una pieza de software/middleware que se yo, que impide que en un proceso corran a la vez muchos hilos. Ya que ejecutar muchos hilos al mismo tiempo no es algo que se debe tomar a la ligera cuando se escribe un programa.

Y poco a poco uno se va a adentrando más y más en temas muy por debajo de solo escribir un endpoint o hacer un commit. Y aún hay más 😀

Limitado por la CPU o por Lecto-escritura

Otro tema crucial es si el programa hará uso intensivo del procesador o del disco.

En resumidas cuentas, se es limitado por la CPU si, al tener una más rápida, el programa irá más rápido. Por su parte, se es limitado por lecto-escritura si al sistema de lecto-escritura ir más rápido, el programa a su vez irá más rápido.

Ejemplo de limitado por CPU son los programas que hacen muchos cálculos, en el caso de aplicaciones web, responder a peticiones HTTP.

Ejemplo de limitado por lecto-escritura son los programas que tienen que leer y/o escribir mucho del disco duro o están sometidos a bastantes procesos de red.

La importancia de determinar esto es que te permitirá saber por donde optimizar los recursos de los servidores y arquitectura o para usar técnicas en la programación que simplifiquen o reduzcan la dependencia en una de estas dos limitantes.

Otros puntos importantes

Ahora, antes de matarse la cabeza pensando si la app va a escalar hay que tener claro que, para saber si escalará o no, se necesitan sistemas de medición para identificar los procesos más lentos y que se puede ir optimizando y mejorando.

Las métricas son las que le indican a uno donde hay fallas, donde los procesos son lentos, donde se puede mejorar, donde hay consultas pesadas y lentas, etc.

Servicios como Skylight o New Relic ayudan mucho en esto. Puede que creamos que X parte de la aplicación es poco optima y que hay que refactorizarla hasta que se ejecute en un milisegundo pero en la realidad, lo que los usuarios usan es otra parte de la aplicación y el refactor sirvió para nada.

Para temas de alto desempeño, escalabilidad, concurrencia y disponibilidad, hay que disponer de herramientas que permitan identificar cuellos de botella y partes lentas para mejorarlas. Ir al ojo optimizando porque sí no es una buena ruta.

Finalmente, una de las cosas que hice antes de salir a producción fue poner a prueba unos cuantos endpoints usando Flood.io. Un servicio para hacer pruebas de carga contra un sistema.

También hay otras alternativas como JMeter, Apache Bench, Vegeta, Siege, etc.

La idea de estas pruebas es hacerse a una idea de bajo que parámetros fallarían los servidores o si la arquitectura es lo suficientemente buena para salir al mercado.

En mi caso, bajo condiciones muy fuertes los endpoints fallaban pero nunca supe determinar si hice mal las pruebas porque durante la salida a producción los fallos generalmente fueron por las tareas en segundo plano y no por peticiones.

Autor: cesc1989

Ingeniero de Sistemas que le gusta escribir y compartir sobre recursos que considera útiles, además que le gusta leer manga y ver anime.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.