Escalando aplicaciones Rails con memcached.
February 8th, 2010 Posted in UncategorizedSi alguna vez han leido acerca de escalibilidad en grandes sitios, probablemente has escuchado acerca de memcached. memcached (pronunciado mem-cash-dee ) es un sistema distribuido de caché de objetos en memoria creado originalmente por Danga Interactive para LiveJournal pero que ahora es usado por grandes páginas como Twitter, Youtube, Flickr, Wikipedia, Digg, Wordpress o el gigantesco Facebook (que se mantiene en pie gracias a los mas de 800 servidores con memcached que usa).
A groso modo memcached almacena en memoria objetos usando el par llave/valor (valor = datos guardados, llave = identificador para acceder a los datos) que son resultados de consultas a base de datos, llamadas a API o render de paginas.
En aplicaciones donde se realiza muchas veces la misma consulta, ¿valdria realmente la pena que se haga 1000 veces la misma consulta obteniendo el mismo resultado?. Imaginen por ejemplo si Twitter consultara en una base de datos los tweets de cada uno de los usuarios incluso cuando estos lleven dias, semanas o meses sin escribir ni una palabra ¿cuanto tiempo puede haberse perdido haciendo dichas consultas? En este caso indudablemente resulta mejor almacenar los resultados de la primera consulta y luego
Entrando ya en la parte interesante, muchos estaran impacientes de saber como hacer que nuestro framework favorito se integre con memcached.
Integrando Rails con memcached
La tarea de integrar Rails con memcached resulta bastante facil gracias a que existen multiples gems que se encargan de la tarea, uno de los mas interesantes que he encontrado es el Cache Money, hecho por Nick Kallen (ingeniero de Twitter) y que ya se encuentra corriendo en produccion en los servidores de Twitter (of course)
Puesta en marcha
0. Instalar memcached
$ sudo aptitude install memcached
1. Instalar Cache Money
$ gem sources -a http://gems.github.com
$ sudo gem install nkallen-cache-money
2. Configurar memcached
$ vim config/memcached.yml
test:
ttl: 604800
namespace: ...
sessions: false
debug: false
servers: localhost:11211
development:
....
3. Hacer un initializer para el gem
$ config/initializers/cache_money.rb
require 'cache_money'
config = YAML.load(IO.read(File.join(RAILS_ROOT, "config", "memcached.yml")))[RAILS_ENV]
$memcache = MemCache.new(config)
$memcache.servers = config['servers']
$local = Cash::Local.new($memcache)
$lock = Cash::Lock.new($memcache)
$cache = Cash::Transactional.new($local, $lock)
class ActiveRecord::Base
is_cached :repository => $cache
end
4. Agregar indices a nuestros modelos ActiveRecord
cache-money almacena en el cache las consultas del tipo User.find(1), pero es necesario configurar indices para que lo haga con consultas mas complejas como User.find(:first, :conditions => {:screen_name => ‘anteseraotro’}), o User.direct_messages. Los indices se agregan facilmente:
class User < ActiveRecord::Base
index :screen_name
end
class DirectMessage < ActiveRecord::Base
index :user_id
index [:user_id, :id]
end
El ultimo ejemplo [:user_id, :id] es para el caso en que la consulta involucre varios parametros como User.find(:first, :conditions => {:id => 1, :user_id => 20})
5. Beber cerveza mientras ves como el rendimiento de la página mejora magicamente
Sin mas que decir, cito al autor
A version of this code is in production use at Twitter and is one part of the reason Twitter’s uptime has improved so much over the last several months. This is real, pragmatic, unmagical, production-ready code that can be a big part of your Rails scaling strategy. It is designed with massive datasets and real-world operational challenges in mind. And it’s almost effortless to use, since it requires no changes to how you use ActiveRecord.