¡Detén esa notificación! Cómo construí una cancelación elegante para campañas de mensajes masivos
Mis manos temblaban mientras veía cómo subía el contador de notificaciones enviadas 5,000 sent 8,000 sent 12,000 sent. Había lanzado una notificación push a todos los usuarios de la base de datos con la URL de un YouTube Live. El problema era que la URL funcionaba perfectamente en navegador de escritorio pero los usuarios móviles veían pantallas de error en lugar del directo. El departamento de planificación me había pasado un formato de URL incorrecto que no hacía deep link en la app móvil. En minutos mi team lead llamó para pedir detener la campaña inmediatamente. Tenía unos 90 segundos para decidir. Mi única opción en ese momento fue detener el contenedor Docker y rezar. Esa experiencia me enseñó algo crítico: cuando se envían notificaciones masivas se necesita un mecanismo elegante de cancelación y no apagar infraestructura en emergencia.
En este artículo explico cómo construí un sistema de cancelación elegante usando banderas de coordinación en Redis que permiten detener campañas de notificaciones en vuelo sin destruir la infraestructura de workers. También explico la arquitectura, los puntos de control clave y las prácticas de monitoreo que usamos en Q2BSTUDIO para mantener la estabilidad de servicios críticos.
Qué ocurrió en el incidente YouTube Live El equipo de marketing planificó un evento importante en YouTube Live y pidió notificar a todos los usuarios activos. Probé la URL en mi navegador de escritorio, funcionó y puse en cola el job para 50,000 usuarios. El envío empezó bien. Tres minutos después el equipo de marketing descubrió que el formato de URL no funcionaba en móviles. Para cuando llegó la orden de detener, 12,000 notificaciones ya se habían enviado. Tuve que SSH en la instancia EC2 y ejecutar docker stop messaging-worker. El resultado fue desordenado: algunos usuarios recibieron la notificación rota y otros no, no hubo un registro limpio de quién recibió qué, el equipo de marketing tuvo que coordinar manualmente una campaña de corrección y otros jobs legítimos quedaron interrumpidos. Pasé la siguiente hora reiniciando servicios y explicando lo sucedido. Sabía que no podía volver a pasar.
Por qué reiniciar contenedores es una mala respuesta Al matar el contenedor se generan fallos en cascada: trabajos activos quedan abandonados a mitad de ejecución, la progresión del worker desaparece, BullMQ marca jobs como stalled y no reintentará por defecto hasta pasado cierto tiempo. No hay rastro auditado claro de quién recibió la notificación. Nuestro servicio de mensajería procesa además campañas de email programadas, códigos SMS de verificación, mensajes in app y digests semanales. Un restart detiene todo, no solo la campaña problemática. Sin un cierre ordenado es imposible preservar estado, checkpoints, contadores de progreso o distinguir un job cancelado por admin de un job fallido por error. La depuración se vuelve arqueología y el estrés es enorme.
La solución que implementé Reemplecé la respuesta por SSH con una API de cancelación y Redis como capa de coordinación. Requisitos básicos que definimos Stop inmediato para campañas problemáticas sin esperar a que terminen Zero daño colateral para otros background jobs Registro limpio para saber exactamente quién recibió notificación Un API simple que un team lead pueda invocar sin acceso SSH
Flujo de trabajo diseñado El administrador llama a DELETE /message/cancel/{jobId} desde cualquier cliente API. La API establece una bandera de cancelación en Redis con TTL. Los workers verifican esa bandera en puntos estratégicos durante la ejecución. Al detectarla, los workers salen de forma ordenada y registran exactamente lo que hicieron. El administrador recibe confirmación inmediata con estadísticas parciales. De esta forma la cancelación queda desacoplada de la ejecución y no se requiere reiniciar contenedores ni detener otros servicios.
Por qué Redis como capa de coordinación Tres ventajas clave Latencia Redis GET es submilisegundo frente a consultas a base de datos que tardan más Frecuencia Los workers pueden chequear la bandera cada pocos segundos durante jobs largos TTL Redis admite expiración automática evitando acumulación de claves huérfanas
Patrón de uso de flags de cancelación Creamos una clave push:cancel:jobId con valor 1 y TTL de una hora. Una hora es deliberada porque los jobs de notificación terminan típicamente en menos de 30 minutos, así la bandera expira sin ensuciar Redis. Si el job completa normalmente la clave caduca sola.
Dos escenarios de cancelación que importan No todos los jobs se cancelan igual. Maneamos dos casos distintos Escenario 1 Jobs en cola Si el job está en estado waiting o delayed se puede eliminar de la cola inmediatamente sin efectos colaterales. Esto evita que trabajo inútil empiece a procesarse. Escenario 2 Jobs activos Para jobs en estado active hacemos cancelación elegante. No podemos revocar notificaciones ya aceptadas por Firebase Cloud Messaging. Entonces la estrategia es dejar que el chunk en curso termine y salir antes del siguiente chunk. Esto evita duplicados, mantiene coherencia entre logs y evita batches parciales que complican auditorías.
Puntos de control que usamos Para poder cancelar sin romper consistencia colocamos dos checkpoints CHECKPOINT 1 Antes de cada fetch paginado de la base de datos. Esto evita gastar recursos en consultas si la cancelación llega temprano. CHECKPOINT 2 Antes de enviar cada chunk a FCM. Como FCM aplica limitación de tasa, las campañas grandes se ejecutan en chunks separados con delays entre ellos. Revisar la bandera antes de cada chunk permite detener el job entre chunks sin tocar solicitudes de FCM ya en vuelo.
Comportamiento práctico Si la cancelación llega antes de iniciar la fase de envío el worker limpia y reporta cancelado antes de gastar recursos en fetchs masivos. Si llega en medio de la fase de envío el worker completa el chunk actual, actualiza contadores, borra la bandera y sale ordenadamente registrando total enviado y porcentaje.
Cómo quedó la API de cancelación La llamada DELETE examina el job en la cola y su estado Si no existe responde not found Si está waiting o delayed lo elimina y responde removed Si está active pone la bandera de cancelación en Redis y responde cancelling Si ya estaba completed o failed responde accordingly
Pruebas que validan garantías Test 1 Cancel antes de que empiece el job Verifica que el job desaparece de la cola y no se procesa Test 2 Cancel durante ejecución Simula un job largo y valida que se envió una parte de las notificaciones pero no todas Test 3 Condición de carrera Cancel a mitad de un chunk y comprobar que el chunk in flight termina antes de detenerse Estas pruebas confirman que jobs en cola se eliminan al instante, jobs activos paran en el siguiente checkpoint y chunks en vuelo se completan antes de cancelar.
Monitoreo y métricas esenciales Para poder informar al team lead inmediatamente instrumentamos cuatro métricas clave Tasa de respuesta de cancelación Tiempo entre solicitud API y reconocimiento por parte del worker Porcentaje de completado parcial Qué porcentaje de la campaña se envía antes de cancelar Conteo de banderas cachadas en Redis Para detectar fallos en TTL Falsas cancelaciones Jobs marcados como cancelados sin encontrar bandera en Redis Estas métricas nos permiten alertar y ajustar checkpointing y TTLs si aparecen patrones problemáticos.
Casos en que el restart sigue siendo la respuesta apropiada Hay situaciones en las que reiniciar contenedores sigue siendo necesario Despliegues de código con breaking changes Fugas de memoria que requieren restart Fallos en la conexión a Redis o a otros servicios que impiden que los workers verifiquen la bandera En esos casos el restart es la opción correcta. Para errores operativos como targeting incorrecto, contenido erróneo o URLs mal formadas la cancelación elegante evita daños colaterales y mantiene el resto de la plataforma estable.
Lecciones aprendidas La coordinación importa en sistemas distribuidos. Las banderas en Redis ofrecen un primitivo de coordinación simple y de baja latencia que permite a servidores API y workers colaborar a través de límites de proceso. La estrategia de dos niveles immediate removal para jobs en cola y graceful completion para jobs activos reconoce la realidad de los sistemas de mensajería donde parte del trabajo no es reversible. Cancelación no es rollback, es control de daños. No puedo deshacer notificaciones ya entregadas a FCM pero puedo detener el resto, mantener registros limpios, evitar consumo innecesario de recursos y dar información exacta al equipo de producto.
Qué habría pasado si el incidente de YouTube ocurriera hoy Con este sistema implementado el team lead podría haber cancelado con una sola llamada API y el worker habría parado entre chunks sin reiniciar contenedores. Resultado: sin daños colaterales, logs limpios indicando exactamente a cuántos usuarios llegó la notificación rota y el equipo de marketing puede lanzar una campaña de corrección dirigida a los usuarios restantes.
Sobre Q2BSTUDIO en este proyecto y en muchos otros aportamos experiencia como empresa de desarrollo de software y aplicaciones a medida. En Q2BSTUDIO diseñamos soluciones que incluyen software a medida, integración con servicios cloud aws y azure y arquitecturas seguras y escalables. Somos especialistas en inteligencia artificial y en ofrecer ia para empresas a través de agentes IA y soluciones de automatización. También brindamos servicios de ciberseguridad y pentesting para proteger pipelines y evitar incidentes que pongan en riesgo campañas de notificación. Si tu objetivo es construir o mejorar una plataforma de mensajería masiva o diseñar workflows resilientes podemos ayudar con consultoría y desarrollo a medida.
Si te interesa explorar desarrollo de aplicaciones a medida visita desarrollo de aplicaciones a medida y si quieres incorporar inteligencia artificial para automatizar decisiones empresariales o construir agentes IA revisa nuestra oferta de inteligencia artificial para empresas. También apoyamos integraciones con Power BI y servicios de inteligencia de negocio para crear paneles accionables que mejoren la toma de decisiones en tiempo real.
Palabras clave que aplican a esta solución aplicaciones a medida software a medida inteligencia artificial ciberseguridad servicios cloud aws y azure servicios inteligencia de negocio ia para empresas agentes IA power bi
Resumen final Tener un freno de emergencia que funcione cambia la dinámica operativa. Pasar de matar contenedores a invocar una API de cancelación reduce tiempos de respuesta, evita efectos colaterales, preserva auditoría y mejora la confianza del equipo. En Q2BSTUDIO diseñamos e implementamos estos mecanismos como parte de soluciones de software a medida y servicios gestionados. Si necesitas una arquitectura robusta para campañas masivas de notificaciones o quieres auditar la resiliencia de tus procesos, podemos ayudarte a diseñar la estrategia adecuada y a desplegarla en producción.
Comentarios