Tokio ofrece progreso, no orden: cómo programar un millón de tareas

Fuentes: Tokio Gives Progress, Not Ordering: Scheduling 1M Tasks

En servicios escritos en Rust con Tokio, los autores suelen asumir que las tareas lanzadas antes terminarán antes. Esta premisa se rompe cuando se generan ráfagas masivas de tareas cortas, como ocurre en flujos basados en eventos donde cada evento lanza hasta mil tareas para procesar tokens de usuario.

Tokio opera con un número fijo de hilos trabajadores, cada uno con una cola local de 256 tareas y una cola global compartida. Cuando una cola local se desborda, la mitad de sus tareas pasan a la cola global. Los workers priorizan sus propias colas, consultan la global ocasionalmente y roban trabajo de otros cuando están inactivos. Una vez que las tareas se vuelven independientemente programables, Tokio pierde la noción del evento que las originó: pasan a competir como tareas ejecutables anónimas.

El artículo demuestra, mediante logs reales de una ráfaga de mil eventos que generaron un millón de tareas, cómo tareas de eventos tempranos recibían su primer poll mucho después que las de eventos posteriores. El problema no es de corrección sino de uso de memoria: las tareas y el estado del evento padre permanecen vivos hasta que todas las tareas hijas finalizan, inflando el pico de memoria.

La solución pasa por acotar la concurrencia con un Semáforo, limitando el número de eventos procesados simultáneamente. Tokio garantiza planificación equitativa solo bajo un número acotado de tareas; ese límite debe imponerlo la aplicación. Con esa estructura, la ráfaga terminó dentro del tiempo esperado y el pico de memoria se redujo de forma significativa, sin sacrificar el throughput.