Notas sobre lo que uno puede aprender en la DjangoCon 2014

Este fin de semana he tenido más tiempo libre de lo habitual y me he dedicado a ver algunos de los vídeos de la DjangoCon 2014, la conferencia anual sobre Django más importante a nivel mundial. En mi opinión, cualquier desarrollador con la intención de programar seriamente debería asistir a conferencias de este tipo, aunque sea a nivel nacional. Allí van los mejores a hablar de las tecnologías más punteras, las mejores prácticas, los errores más frecuentes. Un par de días de asistir a charlas (o unas horas de ver vídeos, en su defecto) te pueden aportar más que decenas de días trabajando tú solo o buscando aleatoriamente por la red.

Ir presencialmente a las conferencias tiene el punto añadido de que conoces gentecilla del mundillo, a los pros y a los no tan pros, una comunidad cercana a la que acudir cuando tienes dudas y crear un ambiente de colegueo súper chulo, que te anima a seguir trabajando duro para hacer las cosas bien. Cosas del software libre… :-)

Escribo estas líneas para recordarme a mi yo futuro qué es lo que aprendido en algunas de las charlas que he visto y, ya de paso, que le sea útil a alguien más. Todo el mundo sabe que tengo memoria de pez, y además me va muy bien escribir las cosas para fijarlas en mi cabeza. Here we go!

Development with Ansible and VMs

Últimamente estoy muy interesado en el tema DevOps, porque he visto verdaderos problemas en algunas de las estrategias de desarrollo y deployment de algunos clientes con los que he trabajado. De esto habla Jeff Schenk en Development with Ansible and VMs. ¿A quién no le ha pasado lo típico de tener que esperar a que los 7 planetas del sistema solar se alineen para que un proyecto arranque en su ordenador porque la información de configuración no está en ningún sitio o está desactualizada?

Aquí viene “The holy grail” aka “la madre del cordero” que son las máquinas virtuales que se parecen lo más posible a la máquina de producción y que se configuran/instalan (provisionan) con un sistema automático. Las tecnologías clave se llaman vagrant y ansible. Vagrant es “simplemente” un sistema que te permite escribir la definición de una máquina virtual en un archivo de texto llamado “Vagrantfile” y viene con la batería añadida de que configura automáticamente una carpeta compartida entre la máquina virtual que creas y tu propia máquina (la anfitriona). Así puedes editar lo que quieras con tu propio IDE en vez de con vim/emacs, y correr las aplicaciones desde la máquina virtual. Si uno se quiere flipar, puede incluso lanzar varias máquinas enlazadas simulando la estructura de producción, pero quizá sea pasarse un poco.

El otro ingrediente, ansible, es un sistema similar a Chef y Puppet (¡pero más moderno y escrito en Python!) que permite especificar una lista de instrucciones para instalar y configurar los servicios que queremos en la máquina virtual. Esto incluye, por ejemplo, descargar e instalar Nginx, Gunicorn y Django con archivos de configuración basados en una plantilla que escribimos nosotros, crear usuarios, cambiar permisos de carpetas y un largo etcétera.

Con estas dos herramientas, ejecutando un simple “vagrant up” tendremos un entorno de desarrollo listo en la máquina de cualquier desarrollador. ¿Que viene un nuevo miembro al equipo técnico? vagrant up y a correr. Quien piense que esto no es la leche, es que no se entera de nada.

DjangoCon 2014- Performant Django

Otra rama que me está molando mucho actualmente es el tema de performance y escalabilidad. No es un secreto que trabajo en Lead Ratings, y aquí tenemos que enfrentarnos a cargas de cientos de miles de leads (clientes en potencia) cada dos por tres. O uno hace el proceso eficiente o no hay máquina que lo aguante.

La primera de las charlas que he visto sobre este tema y probablemente la más general es Performant Django, por Ara Anjargolian. Este chico tartamudeante dice cosas básicas pero muy importantes, que diría que muchos desarrolladores de Django out there no conocen.

En primer lugar, cómo optimizar la parte del front-end de la aplicación. Resumen: cachear recursos estáticos y reducir el número de requests a éstos concatenándolos y minificándolos. Herramientas: django-pipeline, webassets. También existe la posibilidad más avanzada de dejar que el cliente renderice las plantillas enviándoselas como handlebars y los datos como JSON y hala.

Luego pasa a la parte del back-end, es decir, el código Python. Cosas generales: usar cached sessions (en lugar de en la base de datos) y compilar las plantillas. Empezar siempre por hacer un profiling del código para ver qué partes se deben mejorar. Dicen por ahí que un 20% del código es responsable de un 80% del tiempo de proceso. Hay que identificarlo y optimizar ese 20% en vez de lanzarnos a optimizar sin saber dónde. Herramientas: django-debug-toolbar y django-devserver.

Dicho esto, el principal culpable de que una aplicación de Django funcione lentamente es un excesivo número de queries SQL. Esto viene en parte motivado por la abstracción que añade el ORM de Django, que nos oculta un poco cuándo y qué queries SQL se hacen. Para hacer menos queries en sitios obvios, utilizar select_related() y prefetch_related().

Para mejorar la eficiencia de las queries y recuperación de datos: values/values_list() evita el overhead de crear el objeto de Python y evita tener que recuperar la fila entera, si no la necesitamos. Añadir db_index=True en las columnas que utilizamos para filtrar más a menudo.

Cuando no podemos optimizar más el número de queries o su calidad: usar denormalización. Esto significa repetir en más de un sitio de la base de datos una información. El ejemplo canónico es contar el número de comentarios en un post. Si añadimos un contador al post con este número que se actualice cada vez que se añada uno, nos evitamos tener que hacer un count() en la base de datos.

Finalmente, cache. Es imposible ir más allá de un cierto nivel de escalabilidad sin usar caches. A nivel de función: django-cache-utils, django-cache-helper. A nivel de query SQL: django-cache-machine, django-cacheops.

A Nice Problem to Have: Django Under Heavy Load

Este tío tiene una dicción mucho más segura y va más al grano. Tiene pinta de DB Analyst duro.

Empieza por lo de siempre: menos SQL queries. Ya sea con prefetch_related/select_related o cache.

Toca un punto muy importante a considerar cuando tienes cientos de miles de filas: sharding. Sharding consiste en poner las filas de un mismo modelo en distintas bases de datos. Por ejemplo, los tweets de cada grupo de usuarios en una base de datos distinta.

De nuevo, profiling. Sin profiling no hay revolución. New Relic is awesome. Eso dice. Si lo dice este tipo con esa dicción, me lo creo. Tengo que probarlo aunque haya que pagar. 😀  Statsd también tiene muy buena pinta, aunque parece que tiene curro de montarlo y eso me aburre un poco.

En realidad, aquí Joshua habla de un profiling más avanzado, no sólo mirar el número de queries y tal, sino monitorizar también la carga de los procesadores, memoria, I/O… ¡El bottleneck puede estar en cualquier sitio!

Para aliviar los cuellos de botella, muy importante: hacer algo más que cambiar el cuello de botella a otro sitio. Si pones el doble de servidores web puedes sobrecargar la base de datos, etc.

Lo siguiente tiene todo que ver con “getting cozy with your database”. Y es que, como ya hemos dicho más arriba

Mejorar las queries SQL mediante generics. Menos queries dentro de for-loops por favor.

PostgreSQL rocks. Apunte mental: ver qué bases de datos puedo migrar a PostgreSQL. Desarrollar estrategias adecuadas de índices en las tablas.

Usar SQL EXPLAIN con las instrucciones SQL para ver qué es lo que está haciendo el motor y detectar índices. Para sacar el SQL de un QuerySet en Django: print qs.query.

Usar un disco duro de tipo SSD para los archivos temporales de la base de datos. Considerar sharding para conjuntos de datos grandes que le vengan al pelo.

A veces conviene tratar de utilizar tecnologías que no sean SQL para ciertas tareas. Por ejemplo SQL sucks para agregación de datos (mirar Red Shift o Vertica), operaciones de búsqueda avanzadas (Solr, Lucene), acceso rápido a un registro específico (Redis), datos multi-dimensionales (MongoDB) o grafos (Neo4J, Titan). Elegir la herramienta adecuada para cada tarea en vez de siempre el martillo. 😀  Pero tener cuidado y mantener siempre una única fuente de verdad (single source of truth) en la base de datos para no liarse.

Finalmente, downstream caching. Esto es, usar la cache de los navegadores de los clientes devolviendo 304 (not modified) en lugar de 200 (ok). Para esto, lo mejor es utilizar nuestro propio conocimiento sobre nuestras vistas y devolver 304 cuando no han cambiado.

High Performance Django: From Runserver to Reddit Hugs

La siguiente y última charla sobre performance que he visto ha sido ésta. En este caso el foco no se pone en cómo optimizar el código de nuestra aplicación sino en la diferencia al utilizar distintos servidores.

Las configuraciones que prueba Peter Baumgartner son las siguientes (¡todas en EC2 bajo Docker!):

  • runserver
  • uwsgi
  • nginx + 2*uwsgi
  • varnish + 2*uwsgi

Para hacer los benchmarks utiliza Apache JMeter, una herramienta gráfica que yo no he probado, pero que parece bastante flexible y… ¡pinta gráficos! Las demostraciones las hace en vivo y en directo.

Como es de esperar, a medida que va probando stacks más abajo de la lista, los resultados van mejorando. Más peticiones por segundo, menor tiempo de respuesta a cada petición.

El resumen es que es increíble lo que se puede ganar en performance simplemente variando el stack de servidores de nuestro sistema. Y si podemos poner una downstream cache como Varnish, increíble. Sin tener que tocar una línea de código de nuestra aplicación.

Los cuellos de botella pueden estar en cualquier parte. Consideraré comprar el libro de este tipo, que cuesta en PDF 39$. Me parece carillo, ¿pero quién sabe lo bien que puede estar y lo útil que me puede ser? Ansia de conocimiento… :-)

Inheriting a Sloppy Codebase: A Practical Guide to Wrangling Chaotic Code

Slides

Este es el último vídeo que he visto que quería comentar. Todos nos hemos encontrado alguna vez con un proyecto que hemos mirado el código y hemos dicho puffffffffffff. Pero en serio: pufffff. Facepalms, gota que nos cae por la sien, caquita, lo que queráis. Ésto es lo que Casey Kinsey llama “sloppy code”.

Como él es muy políticamente correcto, dice que una mierda de código no se debe sólamente a inexperiencia, sino también a hacer las cosas (demasiado) rápido (rapid prototyping) o a no tener intención de mantener el código que escrito en un futuro cercano. Que los “protitipos” que creamos muchas veces los subimos a producción sin ningún cuidado y ahí se quedan hasta que a alguien se le tiene que caer la gotita por la sien.

La primera parte de la charla creo que se podría resumir en dos cosas. Antes de ponerte a poner un poco de orden en el código:

  • Limpiar la lista de dependencias instalando una por una y viendo qué falla, para eliminar las que no se necesiten y congelarlas en un bonito requirements.txt.
  • Escribir tests funcionales que cubran cerca de un 95% de código y tests unitarios para tareas críticas.

Yo también cometo el error a diario de ponerme a cambiar cosas de un código que no tengo controlado sin escribir los tests correspondientes. Y también lo lamento cada día. No es broma. Cuando uno tiene que lidiar con un buen trozaco de “sloppy code” la probabilidad de que cambies una cosa en apariencia inocua y que te cargues funcionalidades importantes no es nada pequeña.

La segunda parte de la charla versa sobre chapucillas típicas que uno se puede encontrar trabajando con el “sloppy code” e ideas sobre cómo arreglarlas. Pero las chapuzas que muestra en sus slides no son nada comparado con lo que te puedes encontrar por ahí. No kidding. Code horror.

Bueno, esto es todo por el momento amigos. Seguro que vosotros encontráis muchas otras charlas interesantes en la lista. Desde luego que seguro que las hay.

¿Hay un DjangoCon en España? :-)

Leave a Reply

Your email address will not be published. Required fields are marked *