Sincronizar caché (redis) con almacenamiento persistente (mysql)

84 views
Skip to first unread message

Noel Garcia

unread,
Nov 30, 2015, 6:42:15 AM11/30/15
to Symfony Madrid
Buenas! 

Es mi primer tema en este grupo y me gustaría empezar planteando un problema al que todavía no he encontrado una solución que me satisfaga. A ver si alguno puede echar una manita.
Lo que busco es una forma de planificar tareas individuales en el tiempo sin necesidad de usar crones, tareas que pueden ser a corto o a largo plazo, y con garantía de que se ejecuten.
Voy a poner un par de ejemplos de tareas a corto plazo y a largo plazo.

CORTO PLAZO
En una aplicación queremos contar las llamadas al api de cada usuario (llamadas totales a lo largo de la historia). 
Para eso tenemos una columna en la tabla users de mysql con un contador.
Un listener del kernel.request se ocupa de contar, pero para evitar los masivos updates tenemos una key en redis por cada usuario a la que le hacemos un incr y le seteamos el ttl en time()+3600
Hay que tener en cuenta el detalle de que si un usuario accede a las 11:00 y programo la sincronización de sus accesos para las 12:00, si accede a las 11:15 debería ser capaz de obtener el evento programado y actualizar su hora de ejecución.

LARGO PLAZO
Un usuario A se suscribe al premium de Armazon hoy a las 13:32:23UTC (suscripción anual) y queremos que el próximo cargo en su tarjeta se haga exactamente en un año
Un usuario B se suscribe al premium de Armazon hoy a las 11:02:02UTC (suscripción trimestral) y queremos que el próximo cargo en su tarjeta se haga exactamente en tres meses
Hay que tener en cuenta que si el usuario B decide cancelar su suscripción el sistema tendría que ser capaz de eliminar el evento programado.


Lógicamente, el ejemplo de largo plazo se podría resolver con un cron sin problemas, pero el de corto plazo, con miles de usuarios concurrentes ya no es tan sencillo. 
A ver si hay suerte y alguien responde con un mensaje del estilo "tontolaba, eso exactamente es lo que hace anticron666.io", en ese caso aceptare gustoso mi derrota y deberé una cerveza al vencedor.


Un saludo 

daniel....@freelancemadrid.es

unread,
Nov 30, 2015, 7:23:23 AM11/30/15
to symfony...@googlegroups.com

2015-11-30 12:42 GMT+01:00 Noel Garcia <noelgarc...@gmail.com>:
CORTO PLAZO
En una aplicación queremos contar las llamadas al api de cada usuario (llamadas totales a lo largo de la historia). 
Para eso tenemos una columna en la tabla users de mysql con un contador.
Un listener del kernel.request se ocupa de contar, pero para evitar los masivos updates tenemos una key en redis por cada usuario a la que le hacemos un incr y le seteamos el ttl en time()+3600
Hay que tener en cuenta el detalle de que si un usuario accede a las 11:00 y programo la sincronización de sus accesos para las 12:00, si accede a las 11:15 debería ser capaz de obtener el evento programado y actualizar su hora de ejecución.

Hola Noel.

Creo que aqui falta algo de información primero quieres contar las llamadas y luego hablas de eventos programados.


--
-------------------------------------------------------------------------------------
Daniel González Cerviño
Developer http://desarrolla2.com
Tel (+34) 653 96 50 48
Mail daniel....@freelancemadrid.es
Twiter: http://twitter.com/desarrolla2
Linkedin: http://www.linkedin.com/in/desarrolla2
Github: https://github.com/desarrolla2
-------------------------------------------------------------------------------------

Noel Garcia

unread,
Nov 30, 2015, 8:05:21 AM11/30/15
to Symfony Madrid
Sí, a ver si consigo explicarme mejor.

Las llamadas al api se cuentan en redis, usando el evento kernel.request de symfony se incrementa el contador de redis. 
El problema viene en la sincronización de redis y mysql, es ahí donde me gustaría usar algún tipo de evento programado de forma que se pudiera sincronizar el valor para cada usuario de forma individual 1h después de su último acceso.
A ver si con este esquema se entiende un poco mejor 

Noel Garcia

unread,
Nov 30, 2015, 8:05:42 AM11/30/15
to Symfony Madrid


daniel....@freelancemadrid.es

unread,
Nov 30, 2015, 10:53:04 AM11/30/15
to symfony...@googlegroups.com

2015-11-30 14:05 GMT+01:00 Noel Garcia <noelgarc...@gmail.com>:
El problema viene en la sincronización de redis y mysql, es ahí donde me gustaría usar algún tipo de evento programado de forma que se pudiera sincronizar el valor para cada usuario de forma individual 1h después de su último acceso.


1.- Para que necesitas tenerlo en mysql? No es suficiente con tenerla en redis?
2.- No acabo de entenderlo del todo, pero por ejemplo puedes poner un cron que se ejecute cada minuto y que actualize aquellos usuarios que cumplen las condiciones, que creas oportunas. En lugar de programar para dentro de una hora, puedes ejecutar la query sobre los que llevan más de una hora sin ser actualizados.

Noel Garcia

unread,
Nov 30, 2015, 11:45:57 AM11/30/15
to symfony...@googlegroups.com
Gracias Daniel por las respuestas. 

Se podría resolver confiando en redis al 100% (aunque no es persistente y podrías perder la info)
También se podría hacer la sincronización con crones

Pero el reto es precisamente ese, programar tareas individuales (no de ejecución por lotes como suele ser un cron) sin usar crontab. For fun.




--
Has recibido este mensaje porque estás suscrito a un tema del grupo "Symfony Madrid" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/symfony_madrid/EEWk1If8FsQ/unsubscribe.
Para anular la suscripción a este grupo y a todos sus temas, envía un correo electrónico a symfony_madri...@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.



--

Nacho Martín

unread,
Nov 30, 2015, 11:58:14 AM11/30/15
to symfony...@googlegroups.com
Hola!

Pues un cron sería lo más fácil, pero si te has propuesto no usar cron, supongo que podrías hacerlo con alguna cola de mensajes que persista los mensajes, como RabbitMQ.

Aquí tienen algo de info sobre como retrasar mensajes https://www.rabbitmq.com/blog/2015/04/16/scheduling-messages-with-rabbitmq/ , aunque hablan de milisegundos, que será el caso más normal, habría que ver si se adapta bien a tus retrasos de largo plazo. Imagino que sí porque se guarda todo en la BD Mnesia.

Supongo que si en algún momento quieres forzar el ejecutar el job, (en tu ejemplo cuando el tipo se conecta a las 11:15), mandas un mensaje sin delay que fuerce a que se ejecute el job en el momento.

--
Has recibido este mensaje porque estás suscrito al grupo "Symfony Madrid" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a symfony_madri...@googlegroups.com.

Noel Garcia

unread,
Dec 1, 2015, 12:26:13 PM12/1/15
to Symfony Madrid
Gracias Nacho por la info, parece muy prometedora la solución.

Efectivamente, la idea es no usar un cron, todas las aplicaciones hoy en día tiene sus sistemas replicados para facilitar la escalabilidad y evitar los puntos únicos de fallo, pero en el tema de los crones parece que muchas veces nos pasamos todos esos principios por la piedra y confiamos en procesos por lotes sin tener en cuenta lo mal que escalan (es muy fácil que un cron que carga datos de doctrine se vaya de memoria o que se solapen ejecuciones cuando sube el volumen de trabajo del cron).

He encontrado un par de repos interesantes para implementar esto https://github.com/schmittjoh/JMSJobQueueBundle https://github.com/michelsalib/BCCResqueBundle será cuestión de probarlos...
Reply all
Reply to author
Forward
0 new messages