Guzmán D. Darío Senior Python Developer Español Hire me
Taller autoguiado

De bloqueante a tiempo real: un pipeline de video multi-cámara con asyncio

Paso 13 de 23
Paso 13 de 23

Bloquear o descartar: elegir tu política

Terminaste el paso anterior con una tensión sin resolver: el backpressure frena al productor, pero una cámara en vivo no se puede frenar. Va a seguir mandando 25 frames por segundo aunque tu cola esté llena y aunque tu proceso esté ahogado.

Así que cuando la cola se llena solo hay dos salidas honestas. Y la palabra clave es elegir.

Las dos políticas

Bloquear al productor. await q.put(item) espera a que haya espacio. Nada se pierde, y el sistema entero se ajusta al ritmo del eslabón más lento. Es lo correcto cuando perder un dato es inaceptable: una transacción, un mensaje, una fila de trabajos por lotes.

Descartar lo viejo. Cuando la cola está llena, botas el elemento más antiguo y metes el nuevo. Pierdes datos a propósito, y a cambio lo que procesas siempre es reciente. Es lo correcto en tiempo real, donde un frame de hace treinta segundos no le sirve a nadie aunque lo analices perfectamente.

Lo importante es que esto se elige por etapa, no de forma global. En el mismo pipeline puedes querer una cosa en un sitio y la contraria en otro.

Analogía · La sala de urgencias

En una sala de urgencias hay dos tipos de fila. La del papeleo administrativo es de bloquear: si hay diez personas por delante, esperas tu turno, porque perder tu formulario no es una opción.

La de los monitores de signos vitales es de descartar: la pantalla muestra el pulso de ahora. Si el sistema se atrasa, a nadie le sirve ver el latido de hace dos minutos como si fuera el actual. Ahí lo viejo se tira sin dudar.

Mismo hospital, políticas opuestas, y las dos correctas.

Descartar, en código

Bloquear ya sabes cómo se escribe, es el await q.put(item) de siempre. Descartar hay que escribirlo a mano, y ocupa doce líneas. Ábrelo en pipeline.py:

pipeline.py python
def put_or_drop(q: asyncio.Queue, item) -> bool:
    try:
        q.put_nowait(item)
        return True
    except asyncio.QueueFull:
        try:
            q.get_nowait()
        except asyncio.QueueEmpty:
            pass
        q.put_nowait(item)
        return False

Sigue la lógica, porque cada línea está por una razón:

Ese valor de retorno es la diferencia entre perder frames y perderlos a ciegas. Con él puedes contar los descartes y enseñarlos, que es justo lo que harás en el paso 16.

Una línea contra doce

Vale la pena mirar las dos políticas juntas, porque el contraste dice algo:

Bloquear es await q.put(item). Una línea.

Descartar son doce líneas con dos try anidados, porque tienes que hacer el espacio a mano y decidir qué se pierde.

Esa asimetría no es casualidad: Python te da gratis la política conservadora, y te obliga a escribir explícitamente la que pierde datos. Está bien que sea así. Botar información debería costar unas líneas de pensar.

Ojo

El error clásico aquí es elegir descartar porque "es más rápido". No lo es: procesas los mismos frames por segundo en los dos casos, porque el límite lo pone el consumidor. Lo que cambia es qué frames procesas, los recientes o los viejos, y si el productor se frena o no.

Checkpoint

Antes de seguir, contesta esto para el pipeline que estás construyendo:

  • La cola de ingesta a inferencia: ¿bloquear o descartar? Las cámaras no se pueden frenar y un frame viejo no sirve. Descartar.
  • La cola de inferencia a publicación: ¿bloquear o descartar? Ese resultado ya costó una ida y vuelta al modelo. Sería absurdo tirarlo ahora. Bloquear.

Si llegaste a esas dos respuestas por tu cuenta, ya tienes el diseño del pipeline en la cabeza. En el siguiente paso lo vas a montar entero.