Este artículo del blog de Inngest explora un problema común en Python asyncio: la coordinación de tareas concurrentes que comparten estado. La biblioteca estándar ofrece asyncio.Event y asyncio.Condition, pero ambos tienen limitaciones que se manifiestan bajo presión de concurrencia real. El artículo ilustra estas limitaciones a través de un escenario de ejemplo: una aplicación asíncrona que gestiona el estado de una conexión (desconectado, conectando, conectado, cerrando, cerrado) y necesita coordinar tareas para drenar solicitudes pendientes durante el cierre de la conexión.
Inicialmente, se intenta la solución más obvia: el polling (comprobar el estado en un bucle). Esto es ineficiente debido a la latencia-eficiencia y la duplicación de código. Luego, se prueba asyncio.Event, que es útil para despertar tareas cuando ocurre un evento, pero se vuelve engorroso cuando se necesitan múltiples estados, ya que requiere un evento por cada estado y una lógica compleja para coordinarlos. asyncio.Condition mejora la situación al permitir predicados arbitrarios, pero introduce el problema del 'lost update'. Este problema ocurre cuando el estado cambia rápidamente entre dos tareas, y una tarea que se ha despertado con notify_all() puede reevaluar su predicado con un estado obsoleto, perdiendo la actualización original.
El artículo presenta una solución alternativa: el uso de colas per-consumer. En lugar de despertar a los consumidores y verificar el estado actual, cada transición de estado se encola para cada consumidor individualmente. Esto garantiza que ningún consumidor pierda una transición de estado, incluso si el estado cambia rápidamente. La implementación propuesta utiliza una clase ValueWatcher que mantiene una lista de colas y notifica a cada cola con las transiciones de estado. Finalmente, se discuten las mejoras necesarias para una implementación de producción, incluyendo seguridad de subprocesos, registro atómico, tipado genérico, predicados, tiempos de espera, y una API de callback más completa. La solución propuesta aborda la fragilidad de las primitivas estándar de asyncio al garantizar que cada consumidor reciba y procese cada transición de estado de manera confiable.
