SlideShare una empresa de Scribd logo
1 de 35
¿Rails no escala? 
A veces no es suficiente que funcione...
Rankia 
- 2M+ contenidos 
- 100K+ usuarios registrados 
- 10M páginas/mes 
- 1500 páginas/minuto en las horas punta 
Si algo funciona, pero funciona mal, 
posiblemente el servidor se vendrá abajo...
Escalabilidad no es sobreoptimizar 
- La velocidad es muy importante para la web 
- Pero rascar unas milésimas no justifica sacrificar 
la legibilidad o mantenibilidad del código 
- Escalabilidad no es bajar de 100 a 80, sino bajar 
de 100 a 1 -> eliminar problemas
Sobreoptimizar 
Usuario.select(:nick, :email, :estado) 
- Ganancia despreciable 
- Se está comprometiendo la compatibilidad con métodos 
(presentes o futuros) que se apoyen en otros campos 
- Si alguna validación (presente o futura) mira otros 
campos, p.ej. código postal, no se podrán guardar 
cambios.
Pero las reglas están para romperlas 
http://www.rankia.com/blog/blogs-en-rankia/ultimo 
- Página con mucho tráfico -> las ganancias que aquí 
consiga se multiplican 
- Los posts son contenidos con textos muy largos que 
aquí no voy a mostrar -> la ganancia es menos 
despreciable 
Las reglas hay que conocerlas, y saber por qué se han 
puesto, para saber cuándo romperlas… como en todo.
Optimización del backend 
- Update_all 
- n+1 consultas 
- No instanciar para contar 
- Índices 
- Desnormalización 
- Cachés 
- Configuración de servidores
Update_all 
Hay que guardar la URL de los posts de blog 
en la BD; son 200 blogs y 30.000 posts 
Blogs.all.each do |blog| 
blog.posts.each do |post| 
url = ”/blog/#{ blog.url }/#{ post.id }-#{ post.permalink }” 
post.update_attribute(:url, url) 
end 
end
Update_all 
Behind the scenes… 
- 1 lectura a blogs (200 registros) 
- 200 lecturas a posts (~150 registros c/u) 
- 30.000 escrituras a posts 
Houston, tenemos un problema...
Update_all 
Post.rb 
validates :titulo, presence: true, length: { maximum: 120 } 
validates :descripcion, length: { maximum: 500 }, allow_blank: true 
use_permalink(:titulo) 
before_save :limpia_tag_list 
after_create :limpia_titulo 
after_update :actualiza_cache_actividad 
after_update :carga_fecha_titulares_en_tags 
Y pensabais que las 30.000 escrituras eran el problema...
Update_all 
Blogs.all.each do |blog| 
url = ”CONCAT(‘/blog/#{blog.url}/’, id, ‘-’, permalink)” 
Post.where(blog_id: blog.id).update_all(:url, url) 
end 
- 1 lectura a blogs (200 registros) 
- 200 escrituras a posts (~150 registros c/u) 
- 0 validaciones y hooks
n+1 consultas 
http://www.rankia.com/foros/bolsa/temas 
@foro.temas.order(ultima_respuesta: :desc).each do |tema| 
link_to tema 
link_to tema.autor 
link_to tema.ultima_respuesta 
link_to tema.ultima_respuesta.autor 
Consultas: 1 temas, 36 últimas respuestas, 72 autores -> total 109
n+1 consultas 
http://www.rankia.com/foros/bolsa/temas 
@foro.temas.order(ultima_respuesta: :desc) 
.includes(:autor, ultima_respuesta: :autor) 
.each do |tema| 
Consultas: 1 temas, 1 últimas respuestas, 2 autores -> total 4
No instanciar para contar 
Es importante conocer la diferencia entre .count y .length 
Y ante la duda, el comodín .size
Índices 
http://www.rankia.com/foros/bolsa/temas/510770-pulso-mercado 
~90.000 respuestas, ~11.000 páginas 
Tabla contenidos (+2M registros) 
Índice: tema + publicado + created_at 
Consulta: tema.respuestas.order(:created_at) -> FAIL 
Necesita un índice por tema + created at 
(primero criterio de filtrado y luego el de ordenación)
Antes 
Gráfico de skylight:
Después
Pero el problema no ha acabado... 
http://www.rankia.com/foros/bolsa/temas/510770-pulso-mercado?page=11156 
Gigantesco offset -> Obligo a la BD a recorrer todas las respuestas… OMG!!
Solución (ahora sí) 
- Guardar el nº de página en la tabla contenidos 
- Índice por tema + página (+ created_at) 
tema.respuestas 
.where(pagina: params[:pagina]) 
.order(created_at: :desc) 
-> Accesos instantáneos! 
...pero acabas de romper el will_paginate :-( 
La escalabilidad muchas veces necesita trajes a medida
Desnormalización 
Problema: el filtrado/ordenación afecta a otras tablas 
-> No podemos usar índices 
Ej: Lista depósitos ordenados por nombre del banco 
Deposito.includes(:banco).order(‘banco.nombre’)
Desnormalización 
Solución: Copiar el nombre del banco en el depósito y 
crear un índice por ese campo. 
Deposito.order(:nombre_banco) 
Solución razonablemente buena si el dato desnormalizado 
no va a cambiar; si se permite cambiar el nombre del 
banco, al cambiarlo habrá que actualizar en cascada: 
banco.depositos.update_all(nombre_banco: banco.nombre)
Desnormalización 
- Es un “mal necesario” en ocasiones 
- NoSQL resuelve mejor estos problemas 
- Buscar alternativas antes de desnormalizar
Cachés 
- Deben usarse para hacer que vaya aún mejor, no para 
“barrer bajo la alfombra” un problema 
- Incluir el “updated_at” en la clave de la caché (Rails lo 
hace por defecto) es la mejor forma de expirarlas. 
belongs_to :producto, touch: true 
cache(@producto) do …
Cachés 
Rankia con y sin caché (fallo de Memcache en un takeover):
Configuración de servidores
Optimización del frontend 
- La escalabilidad es un problema de backend 
- Pero la velocidad es un problema de front:
Optimización del frontend 
- Imágenes comprimidas 
- Reducir peticiones 
- CSS sin excesivo anidamiento 
- Páginas ligeras 
- CDN 
- ¡Medir!
Imágenes comprimidas 
- ImageOptim / PngGauntlet 
- Nunca usar imágenes grandes reducidas por 
CSS -> Ojo con Responsive Design! 
- En su lugar: media queries, HTML5 picture
Imágenes comprimidas 
Show me the code: 
http://www.html5rocks.com/en/tutorials/responsive/picture-element/
Reducir peticiones 
- Javascript/CSS: Asset Pipeline se encarga. Uno de 
cada, o quizá dos para cachear agresivamente lo 
menos cambiante. 
- Imágenes: Sprites, imágenes embebidas 
<img src="data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8W 
SLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKc 
ppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/g 
AwXEQA7" width="16" height="14" alt="embedded folder icon"> 
- Banners, DNS, …
Reducir peticiones 
- La petición más rápida es la que no se hace porque el 
archivo ya está cacheado en el cliente: Indicar 
expiraciones... o dejar que el Asset Pipeline lo haga por ti 
- Ojo con los banners, son la muerte necesaria, pero al 
menos cargarlos al final
CSS sin excesivo anidamiento 
- El CSS no necesita emular la estructura del HTML 
.columna 
.bloque_lateral 
.lista_enlaces 
a.link_usuario 
color: red 
NO. Con nombres no demasiado genéricos, dos niveles 
deberían ser suficientes, máximo tres.
Paginas ligeras 
- Limpia el HTML 
- El diseñador debe implicarse: menos es más 
- Ojo con el Responsive Design y cargar 
elementos para ocultarlos por CSS
CDN 
- Se encarga por ti de varias de las técnicas 
de front descritas, y alguna más: 
prefetching. 
- Akamai es la caña… pero caro. 
- Google Pagespeed, en beta, es gratis
¡Medir! 
- YSlow 
- WebPageTest.org 
- WooRank 
- NewRelic, Skylight...
Bonus tip 
Siempre, a la hora de optimizar, buscar el cuello de botella: 
lo más lento, lo más frecuentado... optimizar donde no está 
el problema tiene un retorno bajo.

Más contenido relacionado

Similar a ¿Rails no escala?

Meetup Wordpress Zaragoza - David Ayala
Meetup Wordpress Zaragoza - David AyalaMeetup Wordpress Zaragoza - David Ayala
Meetup Wordpress Zaragoza - David AyalaDavid Ayala Gil
 
Escalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQLEscalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQLLorena Fernández
 
Estándares Web con Chico UI
Estándares Web con Chico UIEstándares Web con Chico UI
Estándares Web con Chico UIGuillermo Paz
 
Web Performance para Magento
Web Performance para MagentoWeb Performance para Magento
Web Performance para MagentoJordi Rosell
 
Front-end: Diseñar webs rápidas, flexibles y potentes
Front-end: Diseñar webs rápidas, flexibles y potentesFront-end: Diseñar webs rápidas, flexibles y potentes
Front-end: Diseñar webs rápidas, flexibles y potentesRomán Hernández
 
Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?
Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?
Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?Martin Siniawski
 
El Ferrocarril Contra Los Arboles Muertos2
El Ferrocarril Contra Los Arboles Muertos2El Ferrocarril Contra Los Arboles Muertos2
El Ferrocarril Contra Los Arboles Muertos2ferdinand13
 
WPO - Murcia Meetup Day #MMD17
WPO - Murcia Meetup Day #MMD17WPO - Murcia Meetup Day #MMD17
WPO - Murcia Meetup Day #MMD17Javier Agudo
 
Posicionamiento SEO, SEM, SMO - Clase 2
Posicionamiento SEO, SEM, SMO - Clase 2 Posicionamiento SEO, SEM, SMO - Clase 2
Posicionamiento SEO, SEM, SMO - Clase 2 Dinamiclerning
 
88 Php. Imagenes En Tablas
88 Php. Imagenes En Tablas88 Php. Imagenes En Tablas
88 Php. Imagenes En TablasJosé M. Padilla
 
CSS, planeando para el futuro
CSS, planeando para el futuroCSS, planeando para el futuro
CSS, planeando para el futuroJose Leiva
 

Similar a ¿Rails no escala? (20)

Meetup Wordpress Zaragoza - David Ayala
Meetup Wordpress Zaragoza - David AyalaMeetup Wordpress Zaragoza - David Ayala
Meetup Wordpress Zaragoza - David Ayala
 
Html guia 1
Html guia 1 Html guia 1
Html guia 1
 
Cache en la Web
Cache en la WebCache en la Web
Cache en la Web
 
Escalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQLEscalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQL
 
Estándares Web con Chico UI
Estándares Web con Chico UIEstándares Web con Chico UI
Estándares Web con Chico UI
 
Splat - Programador PHP
Splat - Programador PHPSplat - Programador PHP
Splat - Programador PHP
 
Textos sqli_mssql
 Textos sqli_mssql Textos sqli_mssql
Textos sqli_mssql
 
Web Performance para Magento
Web Performance para MagentoWeb Performance para Magento
Web Performance para Magento
 
Front-end: Diseñar webs rápidas, flexibles y potentes
Front-end: Diseñar webs rápidas, flexibles y potentesFront-end: Diseñar webs rápidas, flexibles y potentes
Front-end: Diseñar webs rápidas, flexibles y potentes
 
Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?
Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?
Cómo volarle la peluca a tus usuarios con la velocidad de tu sitio?
 
El Ferrocarril Contra Los Arboles Muertos2
El Ferrocarril Contra Los Arboles Muertos2El Ferrocarril Contra Los Arboles Muertos2
El Ferrocarril Contra Los Arboles Muertos2
 
WPO - Murcia Meetup Day #MMD17
WPO - Murcia Meetup Day #MMD17WPO - Murcia Meetup Day #MMD17
WPO - Murcia Meetup Day #MMD17
 
Webperf wordpress
Webperf wordpressWebperf wordpress
Webperf wordpress
 
Funciones
FuncionesFunciones
Funciones
 
Seo Cheat Sheet
Seo Cheat SheetSeo Cheat Sheet
Seo Cheat Sheet
 
Vistiendo a WordPress
Vistiendo a WordPressVistiendo a WordPress
Vistiendo a WordPress
 
Posicionamiento SEO, SEM, SMO - Clase 2
Posicionamiento SEO, SEM, SMO - Clase 2 Posicionamiento SEO, SEM, SMO - Clase 2
Posicionamiento SEO, SEM, SMO - Clase 2
 
Laboratorio 03
Laboratorio 03Laboratorio 03
Laboratorio 03
 
88 Php. Imagenes En Tablas
88 Php. Imagenes En Tablas88 Php. Imagenes En Tablas
88 Php. Imagenes En Tablas
 
CSS, planeando para el futuro
CSS, planeando para el futuroCSS, planeando para el futuro
CSS, planeando para el futuro
 

¿Rails no escala?

  • 1. ¿Rails no escala? A veces no es suficiente que funcione...
  • 2. Rankia - 2M+ contenidos - 100K+ usuarios registrados - 10M páginas/mes - 1500 páginas/minuto en las horas punta Si algo funciona, pero funciona mal, posiblemente el servidor se vendrá abajo...
  • 3. Escalabilidad no es sobreoptimizar - La velocidad es muy importante para la web - Pero rascar unas milésimas no justifica sacrificar la legibilidad o mantenibilidad del código - Escalabilidad no es bajar de 100 a 80, sino bajar de 100 a 1 -> eliminar problemas
  • 4. Sobreoptimizar Usuario.select(:nick, :email, :estado) - Ganancia despreciable - Se está comprometiendo la compatibilidad con métodos (presentes o futuros) que se apoyen en otros campos - Si alguna validación (presente o futura) mira otros campos, p.ej. código postal, no se podrán guardar cambios.
  • 5. Pero las reglas están para romperlas http://www.rankia.com/blog/blogs-en-rankia/ultimo - Página con mucho tráfico -> las ganancias que aquí consiga se multiplican - Los posts son contenidos con textos muy largos que aquí no voy a mostrar -> la ganancia es menos despreciable Las reglas hay que conocerlas, y saber por qué se han puesto, para saber cuándo romperlas… como en todo.
  • 6. Optimización del backend - Update_all - n+1 consultas - No instanciar para contar - Índices - Desnormalización - Cachés - Configuración de servidores
  • 7. Update_all Hay que guardar la URL de los posts de blog en la BD; son 200 blogs y 30.000 posts Blogs.all.each do |blog| blog.posts.each do |post| url = ”/blog/#{ blog.url }/#{ post.id }-#{ post.permalink }” post.update_attribute(:url, url) end end
  • 8. Update_all Behind the scenes… - 1 lectura a blogs (200 registros) - 200 lecturas a posts (~150 registros c/u) - 30.000 escrituras a posts Houston, tenemos un problema...
  • 9. Update_all Post.rb validates :titulo, presence: true, length: { maximum: 120 } validates :descripcion, length: { maximum: 500 }, allow_blank: true use_permalink(:titulo) before_save :limpia_tag_list after_create :limpia_titulo after_update :actualiza_cache_actividad after_update :carga_fecha_titulares_en_tags Y pensabais que las 30.000 escrituras eran el problema...
  • 10. Update_all Blogs.all.each do |blog| url = ”CONCAT(‘/blog/#{blog.url}/’, id, ‘-’, permalink)” Post.where(blog_id: blog.id).update_all(:url, url) end - 1 lectura a blogs (200 registros) - 200 escrituras a posts (~150 registros c/u) - 0 validaciones y hooks
  • 11. n+1 consultas http://www.rankia.com/foros/bolsa/temas @foro.temas.order(ultima_respuesta: :desc).each do |tema| link_to tema link_to tema.autor link_to tema.ultima_respuesta link_to tema.ultima_respuesta.autor Consultas: 1 temas, 36 últimas respuestas, 72 autores -> total 109
  • 12. n+1 consultas http://www.rankia.com/foros/bolsa/temas @foro.temas.order(ultima_respuesta: :desc) .includes(:autor, ultima_respuesta: :autor) .each do |tema| Consultas: 1 temas, 1 últimas respuestas, 2 autores -> total 4
  • 13. No instanciar para contar Es importante conocer la diferencia entre .count y .length Y ante la duda, el comodín .size
  • 14. Índices http://www.rankia.com/foros/bolsa/temas/510770-pulso-mercado ~90.000 respuestas, ~11.000 páginas Tabla contenidos (+2M registros) Índice: tema + publicado + created_at Consulta: tema.respuestas.order(:created_at) -> FAIL Necesita un índice por tema + created at (primero criterio de filtrado y luego el de ordenación)
  • 15. Antes Gráfico de skylight:
  • 17. Pero el problema no ha acabado... http://www.rankia.com/foros/bolsa/temas/510770-pulso-mercado?page=11156 Gigantesco offset -> Obligo a la BD a recorrer todas las respuestas… OMG!!
  • 18. Solución (ahora sí) - Guardar el nº de página en la tabla contenidos - Índice por tema + página (+ created_at) tema.respuestas .where(pagina: params[:pagina]) .order(created_at: :desc) -> Accesos instantáneos! ...pero acabas de romper el will_paginate :-( La escalabilidad muchas veces necesita trajes a medida
  • 19. Desnormalización Problema: el filtrado/ordenación afecta a otras tablas -> No podemos usar índices Ej: Lista depósitos ordenados por nombre del banco Deposito.includes(:banco).order(‘banco.nombre’)
  • 20. Desnormalización Solución: Copiar el nombre del banco en el depósito y crear un índice por ese campo. Deposito.order(:nombre_banco) Solución razonablemente buena si el dato desnormalizado no va a cambiar; si se permite cambiar el nombre del banco, al cambiarlo habrá que actualizar en cascada: banco.depositos.update_all(nombre_banco: banco.nombre)
  • 21. Desnormalización - Es un “mal necesario” en ocasiones - NoSQL resuelve mejor estos problemas - Buscar alternativas antes de desnormalizar
  • 22. Cachés - Deben usarse para hacer que vaya aún mejor, no para “barrer bajo la alfombra” un problema - Incluir el “updated_at” en la clave de la caché (Rails lo hace por defecto) es la mejor forma de expirarlas. belongs_to :producto, touch: true cache(@producto) do …
  • 23. Cachés Rankia con y sin caché (fallo de Memcache en un takeover):
  • 25. Optimización del frontend - La escalabilidad es un problema de backend - Pero la velocidad es un problema de front:
  • 26. Optimización del frontend - Imágenes comprimidas - Reducir peticiones - CSS sin excesivo anidamiento - Páginas ligeras - CDN - ¡Medir!
  • 27. Imágenes comprimidas - ImageOptim / PngGauntlet - Nunca usar imágenes grandes reducidas por CSS -> Ojo con Responsive Design! - En su lugar: media queries, HTML5 picture
  • 28. Imágenes comprimidas Show me the code: http://www.html5rocks.com/en/tutorials/responsive/picture-element/
  • 29. Reducir peticiones - Javascript/CSS: Asset Pipeline se encarga. Uno de cada, o quizá dos para cachear agresivamente lo menos cambiante. - Imágenes: Sprites, imágenes embebidas <img src="data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8W SLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKc ppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/g AwXEQA7" width="16" height="14" alt="embedded folder icon"> - Banners, DNS, …
  • 30. Reducir peticiones - La petición más rápida es la que no se hace porque el archivo ya está cacheado en el cliente: Indicar expiraciones... o dejar que el Asset Pipeline lo haga por ti - Ojo con los banners, son la muerte necesaria, pero al menos cargarlos al final
  • 31. CSS sin excesivo anidamiento - El CSS no necesita emular la estructura del HTML .columna .bloque_lateral .lista_enlaces a.link_usuario color: red NO. Con nombres no demasiado genéricos, dos niveles deberían ser suficientes, máximo tres.
  • 32. Paginas ligeras - Limpia el HTML - El diseñador debe implicarse: menos es más - Ojo con el Responsive Design y cargar elementos para ocultarlos por CSS
  • 33. CDN - Se encarga por ti de varias de las técnicas de front descritas, y alguna más: prefetching. - Akamai es la caña… pero caro. - Google Pagespeed, en beta, es gratis
  • 34. ¡Medir! - YSlow - WebPageTest.org - WooRank - NewRelic, Skylight...
  • 35. Bonus tip Siempre, a la hora de optimizar, buscar el cuello de botella: lo más lento, lo más frecuentado... optimizar donde no está el problema tiene un retorno bajo.