Anything that can go wrong will go wrong.
El viejo Murphy tenía razón (no es la primera vez 🤭). Un Lunes nuestra plataforma amaneció abajo debido a un abrazo de la muerte o deadlock entre hilos tratando de escribir en caché al mismo tiempo.
Buscando la solución me sorprendí al descubrir que Dalli — la implementación del cliente memcache que usamos — había deprecado hacía mucho tiempo el uso de race_condition_ttl, que la documentación de Rails promete evita que hilos compitan por quien refresca el contenido de la cache una vez que este expira. El cambio había sido avisado en el changelog de la gema que no siempre leemos cuando hacemos cambio de versión 😬.
Sin embargo también aprendí que ActiveSupport::Cache::MemCacheStore — desde la versión 4.0 de Rails — es un thin wrapper de Dalli que mantiene el soporte de race_condition_ttl. Entonces para seguir beneficiándonos del feature basta con declarar el uso de mem_cache_store en vez de dalli_store en en la configuración del ambiente config/environments/production.rb:
# BEFORE config.cache_store = :dalli_store, { pool_size: ENV.fetch(“RAILS_MAX_THREADS”, 5) } # AFTER (with support for race conditions) config.cache_store = :mem_cache_store, { race_condition_ttl: 10.seconds, pool_size: ENV.fetch(“RAILS_MAX_THREADS”, 5) }
El snippet de configuración de paso muestra el soporte de Dalli que permite usar un pool de conexiones para evitar que la instancia singleton del cliente memcache se convierta en cuello de botella en ambientes multi-thread (dígase servidores Puma o Unicorn).