¿rails no escala?

35
¿Rails no escala? A veces no es suficiente que funcione...

Upload: fernando-calatayud

Post on 11-Jul-2015

70 views

Category:

Software


0 download

TRANSCRIPT

Page 1: ¿Rails no escala?

¿Rails no escala?

A veces no es suficiente que funcione...

Page 2: ¿Rails no escala?

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...

Page 3: ¿Rails no escala?

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

Page 4: ¿Rails no escala?

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.

Page 5: ¿Rails no escala?

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.

Page 6: ¿Rails no escala?

Optimización del backend

- Update_all

- n+1 consultas

- No instanciar para contar

- Índices

- Desnormalización

- Cachés

- Configuración de servidores

Page 7: ¿Rails no escala?

Update_all

Hay que guardar la URL de los posts de blog

en la BD; son 200 blogs y 30.000 postsBlogs.all.each do |blog|

blog.posts.each do |post|

url = ”/blog/#{ blog.url }/#{ post.id }-#{ post.permalink }”

post.update_attribute(:url, url)

end

end

Page 8: ¿Rails no escala?

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...

Page 9: ¿Rails no escala?

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...

Page 10: ¿Rails no escala?

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

Page 11: ¿Rails no escala?

n+1 consultas

http://www.rankia.com/foros/bolsa/[email protected](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

Page 12: ¿Rails no escala?

n+1 consultas

http://www.rankia.com/foros/bolsa/[email protected](ultima_respuesta: :desc)

.includes(:autor, ultima_respuesta: :autor)

.each do |tema|

Consultas: 1 temas, 1 últimas respuestas, 2 autores -> total 4

Page 13: ¿Rails no escala?

No instanciar para contar

Es importante conocer la diferencia entre .count y .length

Y ante la duda, el comodín .size

Page 14: ¿Rails no escala?

Í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)

Page 15: ¿Rails no escala?

Antes

Gráfico de skylight:

Page 16: ¿Rails no escala?

Después

Page 17: ¿Rails no escala?

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!!

Page 18: ¿Rails no escala?

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

Page 19: ¿Rails no escala?

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’)

Page 20: ¿Rails no escala?

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)

Page 21: ¿Rails no escala?

Desnormalización

- Es un “mal necesario” en ocasiones

- NoSQL resuelve mejor estos problemas

- Buscar alternativas antes de desnormalizar

Page 22: ¿Rails no escala?

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 …

Page 23: ¿Rails no escala?

Cachés

Rankia con y sin caché (fallo de Memcache en un takeover):

Page 24: ¿Rails no escala?

Configuración de servidores

Page 25: ¿Rails no escala?

Optimización del frontend

- La escalabilidad es un problema de backend

- Pero la velocidad es un problema de front:

Page 26: ¿Rails no escala?

Optimización del frontend

- Imágenes comprimidas

- Reducir peticiones

- CSS sin excesivo anidamiento

- Páginas ligeras

- CDN

- ¡Medir!

Page 27: ¿Rails no escala?

Imágenes comprimidas

- ImageOptim / PngGauntlet

- Nunca usar imágenes grandes reducidas por

CSS -> Ojo con Responsive Design!

- En su lugar: media queries, HTML5 picture

Page 28: ¿Rails no escala?

Imágenes comprimidas

Show me the code:

http://www.html5rocks.com/en/tutorials/responsive/picture-element/

Page 29: ¿Rails no escala?

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, …

Page 30: ¿Rails no escala?

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

Page 31: ¿Rails no escala?

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.

Page 32: ¿Rails no escala?

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

Page 33: ¿Rails no escala?

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

Page 34: ¿Rails no escala?

¡Medir!

- YSlow

- WebPageTest.org

- WooRank

- NewRelic, Skylight...

Page 35: ¿Rails no escala?

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.