Cuando te dedicas a crear sitios web que van a soportar tráficos de cientos de miles de usuarios únicos al mes con millones de impactos durante esos periodos, cualquier parte de la aplicación en el que consigas reducir milisegundos redundará en un mejor aprovechamiento de la infraestructura y por tanto en una reducción de los costes de la misma, al ser capaz de incrementar el número de peticiones por segundo que puede atender tu servidor. Hoy vamos a hablar de una técnica ya usada en otros ámbitos pero que pocas veces la he visto utilizada en desarrollos web, y es el uso de números primos en la expiración de las cachés.
A la hora de identificar las cachés que conforman una página, nos podemos encontrar con dos tipos distintos de caché:
- Permantentes: No tienen tiempo de expiración. Normalmente o no son borradas o en caso de ser borradas se realiza desde código (por ejemplo, al añadir un nuevo artículo borramos la caché que contiene el listado de los últimos n artículos).
- Temporales: Tienen tiempo de expiración. Dentro de este tipo de caché entran aquellos módulos que no merece la pena borrar desde código, como por ejemplo datos que muestres en la cabecera o el pie que con que se refresquen cada x minutos es más que suficiente.
Dentro de los cachés temporales es frecuente encontrarse con módulos que expiran transcurrida la misma cantidad de tiempo (2 horas), o módulos que expiran transcurrida una fracción del tiempo de los módulos más tardíos (10 minutos). En un caso que tuviésemos 4 módulos que caducan a las 2 horas y 3 módulos que caducan a los 10 minutos nos encontraríamos con que realmente cada 2 horas tenemos que estar regenerando 7 módulos.
Una manera de evitar una frecuencia tan alta de regenerar todos los módulos de caché es utilizar algo tan sencillo como números primos de minutos en vez de tiempos más “redondos”. En el caso que comentábamos antes cada dos horas se producía la regeneración ya que el mínimo común múltiplo de 10 (2 x 5) y 120 (2^3 x 3 x 5) es 120 (2^3 x 3 x 5). Sin embargo, si para los 3 módulos que caducaban a los 10 minutos hubiésemos escogido los números primos que rodean a 10 (7, 11 y 13) y para los 4 módulos que caducan a las dos horas hubiésemos escogido los primos que rodean a 120 (109, 113, 127, 131), nos encontraríamos con que nunca (o más estrictamente, cada 7 x 11 x 13 x 109 x 113 x 127 x 131 = 205.122.846.929 minutos) tendríamos que regenerar las 7 cachés a la vez, y no solo eso, sino que la gran mayoría de tiempo estaríamos sirviendo o de caché todo o como mucho regenerando un módulo o dos a la vez. Al fin y al cabo, los datos seguirían siendo aproximadamente igual de actuales que si tuvieran los valores más “redondos”.
Esto mismo que hemos explicado para cachés temporales se podría aprovechar para transformar cachés permanentes en temporales. Solo habría que cambiar la escala de “minutos” a “segundos”, y tendríamos datos tan en tiempo real como un usuario se podría dar cuenta ;) Y con estos pequeños cambios simplificaríamos de gran manera nuestros controladores, algo que a su vez implica que reducimos la cantidad de errores posibles en nuestro código y a la postre reducción también en el coste de mantenimiento del mismo :)
Cientos de miles de usuarios únicos al mes ?
Eso no es nada del otro mundo, con o sin números primos.
El tema de esta entrada no es ver quien la tiene más larga, sino dar consejos que puedan valerle a gente que se tenga que enfrentar a desarrollos que tengan que crecer mucho en poco tiempo.
Cualquier cosa que quieras aportar a la gente que quiera aprender y lea esto será bienvenida, ya que de tu comentario se desprende que eso de cientos de miles se te queda pequeño :)
Interesante, una buena forma de hacer que no coincidan todas esas tareas a la vez :)
Incluso se podría pensar en que en un mismo server puedes tener varios sitios con cientos de miles de usuarios al mes, lo que sumarían bastantes usuarios el mes… y organizar los tiempos no solo para un solo sitio sino teniendo en cuenta también el resto
Muy bueno, si señor. En efecto no me suena haberlo visto anteriormente, y es muy buena idea evitar así los solapamientos.
Hola!
Lo que dices no tiene sentido si las cachés temporales se regeneran bajo demanda, es decir cuando, al pedirlas, se detecta que han caducado, ya que los tiempos ya no son exactos. Defines un tiempo de expiración de X unidades, pero realmente es X + Y unidades, siendo el valor Y un valor indefinido que depende de cuando se solicite el objeto cacheado.
Un saludo!
cacheman: Buena observación :)
Este método efectivamente es más indicado para módulos que se comparten a lo largo de todas las páginas (por ejemplo, un últimas noticias, o la cabecera de los últimos usuarios en iniciar sesión, cosas así). Para módulos que no se muestran en todas las páginas ocurre lo que comenta cacheman, que ya no sería un tiempo X, sino X+Y.
Hola!
En cuanto uno de los módulos se utilice de forma aislada en otra página dejará de ser válido. Yo sigo sin verlo útil salvo que el tiempo de generación de los módulos sea alto e interese hacerlo de forma programada (pe. con el crontab en linux).
Creo que este post no aplica el principio KISS (Keep It Simple, Stupid), que debería ser una de las bases de los programadores.
Un saludo!
Al comentario de cacheman ” Defines un tiempo de expiración de X unidades, pero realmente es X + Y unidades, siendo el valor Y un valor indefinido que depende de cuando se solicite el objeto cacheado. “, y su aceptación por parte del autor (gran idea por cierto la de las primas ;D) discrepo: cron si es en servidor dedicado, pseudocron en php o similar desde un servidor externo si es hosting compartido, y el factor Y desaparece de la ecuación :)
Se me ha solapado el comentario con el segundo de cacheman, que va por el mismo camino :D
De todas formas, si estamos hablando del suficiente número de pageviews el factor Y debería ir tendiendo a 0 a mayor carga (incluso llegados a un punto extremo deberíamos tirar más por Proxy caché Apache/Squid/nginx/similares) con lo cual, atendiendo al “público objetivo” del tutorial, como indica el autor, “Cuando te dedicas a crear sitios web que van a soportar tráficos de cientos de miles de usuarios únicos al mes con millones de impactos durante esos periodos” yo si veo el método válido.
Por ejemplo, con tan sólo ~30000 usuarios únicos al dia, algo bastante habitual, y a una relación media de tan sólo 3 pageviews por visita única, estariamos hablando al mes de 3 millones de pageviews, y ya estaríamos en una “afinación” al segundo.
Just my 2 cents.
MarcosBL: En realidad esta idea es una adaptación de algo que en su momento leí sobre técnicas que se aplicaban con problemas similares con la caché en las CPUs actuales. Relacionado con desarrollos web no había leído nada parecido, por eso me animé a publicarlo en la bitácora.
Buen apunte el relativo a que a mayor número de impactos mayor probabilidad de que en el caso X+Y la Y tienda a ser cero :)
A ver si consigo sacar tiempo para hablar de servidores web, ESI, proxys y demás familia, que también se encuenta uno en el día a día con cosas de lo más curiosas.
Te he seguido via Twitter via @r0sk por un twitt suyo en el que comentabais sobre nginx … a mi el High Avaliability (balanceo, reducción de peticiones, concurrencia, etc…) me apasiona, hasta he empezado a desarrollar mi propio (y por ahora infuncional) servidor-web-minimal-de-la-muette simplemente por entender a bajo nivel los drawbacks de unos y otros, asi que posts en esa linea, siempre serán muy bien recibidos. :-)