- Oban.py es una versión portada a Python basada en PostgreSQL del framework de procesamiento de jobs Oban de Elixir, y permite insertar y procesar jobs usando solo la base de datos
- Los jobs se crean y se revierten dentro de transacciones de base de datos, y admite varias funciones como gestión de colas, almacenamiento de resultados y programación cron
- La versión de código abierto tiene limitaciones como ejecución asyncio de un solo hilo y inserción y confirmación individuales, pero la versión Pro ofrece procesamiento en paralelo, workflows y concurrencia inteligente
- Su funcionamiento interno se compone de cinco etapas:
Insert → Notify → Fetch → Execute → Ack, y usa FOR UPDATE SKIP LOCKED de PostgreSQL para evitar conflictos de concurrencia
- La elección de líder, la recuperación de jobs huérfanos y los reintentos con backoff también se realizan sobre la base de datos, lo que permite procesamiento distribuido confiable sin brokers externos
Resumen de Oban.py
- Oban.py es un framework de procesamiento de jobs basado en base de datos que porta Oban de Elixir a Python
- Inserta y procesa jobs dentro de transacciones de base de datos, y si falla, toda la transacción se revierte
- Incluye varias funciones de control como límites de cola, almacenamiento de jobs completados, retención de resultados y programación cron
- Ofrece dos versiones
- Código abierto (OSS): ejecución asyncio de un solo hilo, inserción y confirmación individuales, recuperación simple
- Versión Pro: procesamiento en paralelo basado en pool de procesos, con soporte para workflows, relays, jobs únicos y concurrencia inteligente
- La versión OSS es adecuada para proyectos personales o evaluación, mientras que para entornos a gran escala se recomienda la versión Pro
Ruta de procesamiento de jobs
- Tras insertar un job, se guarda en la tabla
oban_jobs con state='available', y se envía una notificación a cada nodo mediante NOTIFY de PostgreSQL
- El Stager de cada nodo detecta esa cola y despierta al Producer, y el Producer toma el job para ejecutarlo
- Al seleccionar jobs se usa
FOR UPDATE SKIP LOCKED de SQL, lo que permite procesamiento paralelo sin ejecución duplicada
- Las filas ya bloqueadas se omiten para que otros producers puedan tomar de inmediato otros jobs
- Los jobs se despachan como async task y, al completarse, se procesa el acknowledgement mediante callback
- La versión Pro usa un dispatcher con pool de procesos en lugar de asyncio, lo que permite ejecución paralela en múltiples núcleos
Procesos en segundo plano
- Elección de líder (Leader Election)
- Determina al líder con
INSERT ... ON CONFLICT de PostgreSQL y un lease basado en TTL
- Sin un protocolo de consenso aparte, un único líder se encarga de la limpieza y recuperación de jobs
- Lifeline (recuperación de jobs huérfanos)
- Si un job en ejecución dura más de cierto tiempo (
rescue_after, 5 minutos por defecto), se restaura al estado available
- La versión Pro verifica si el producer sigue vivo, pero OSS decide solo con base en el tiempo
- Pruner (limpieza de jobs)
- Elimina jobs completados, cancelados o descartados cuya antigüedad supere
max_age (1 día por defecto)
- Limita el alcance del borrado con
LIMIT para evitar sobrecargar la base de datos
Reintentos y backoff
- Si un job lanza una excepción, el Executor decide si debe reintentarse
- Si está por debajo del máximo de intentos (
max_attempts), se reintenta; si lo supera, se descarta
- El backoff por defecto es un incremento exponencial con jitter
- Esto evita reintentos simultáneos ante fallas masivas y reduce el aumento brusco de carga (Thundering Herd)
- Ejemplo: espera de unos 17 segundos en el intento 1, unos 47 segundos en el intento 5 y unos 17 minutos en el intento 10
- La clase worker puede implementar lógica de backoff personalizada con el método
backoff()
Características principales y evaluación
- PostgreSQL cumple un rol central
- Con
FOR UPDATE SKIP LOCKED, LISTEN/NOTIFY y ON CONFLICT resuelve control de concurrencia, señalización y elección de líder
- Construye una capa de coordinación con una sola base de datos, sin Redis ni brokers externos
- Sin paralelismo, pero con soporte de concurrencia
- Al estar basado en asyncio, es adecuado para tareas I/O-bound; para trabajos CPU-bound se recomienda la versión Pro
- Claridad en la estructura del código
- Con nombres consistentes y responsabilidades separadas, tiene una base de código fácil de leer
- Separación clara entre OSS y Pro
- OSS está orientado a experimentación y pequeña escala; Pro, a entornos grandes y de alto rendimiento
- Conclusión: es un port limpio y bien estructurado a Python que implementa una cola de jobs completa usando solo PostgreSQL, ideal para usuarios de Elixir o desarrolladores que buscan un sistema de jobs sin infraestructura externa
1 comentarios
Comentarios de Hacker News
Yo soy quien creó Sidekiq, y quiero felicitar a Shannon y Parker por este lanzamiento.
Antes tuve la misma duda: enfocarme en Ruby o expandir Sidekiq a otros lenguajes. Me di cuenta de que no podía ser experto en todos, así que en su lugar creé Faktory. La idea es que un servidor central gestione el ciclo de vida de la cola, mientras que los clientes de cada lenguaje se mantienen simples. Por ejemplo, existe un cliente como faktory-rs. La desventaja es que, al no enfocarse en una comunidad de lenguaje específica, es difícil ofrecer ejemplos adaptados a ese lenguaje.
Puede que enfocarse en una sola comunidad dé mejores resultados. El tiempo lo dirá
Lo esencial de Oban es que puedes insertar y procesar trabajos usando solo la base de datos. Puedes meter un trabajo de envío de email dentro de una transacción de creación de usuario, y si falla, todo se revierte.
Mucha gente dice que no deberías usar una BD relacional como cola de trabajos, pero pasan por alto la importancia de las transacciones. El artículo de Brandur Leach, Job Drain, también explica muy bien esta idea
Pero ahora ya nadie recuerda esa incomodidad. El patrón transactional outbox es indispensable, y yo prefiero un enfoque que tenga las mismas garantías ACID que mis datos.
Aunque no conozcas el internals de la BD, si inviertes una semana en aprender niveles de aislamiento y orden de commit, te ahorras un año depurando sistemas distribuidos
En una época con tantos procesos largos de IA, este tipo de durabilidad es indispensable. En otros ecosistemas de lenguaje pagas por tener esto, pero en Oban viene incluido de base
El equipo de Oban es conocido en el ecosistema Elixir por su ingeniería sofisticada. Pero me desconcierta que hayan dejado el pool de procesos bloqueado en la versión Pro.
Por ejemplo, el plan de $135/mes incluye ejecución multiproceso, flujos de trabajo, límites globales, trabajos únicos, operaciones en lote, fuentes cifradas, soporte dedicado, etc.
Mi proyecto Chancy es completamente gratis, y permite mezclar libremente asyncio, procesos, hilos y subintérpretes.
Creo que sería mejor mover estas funciones a OSS y dejar lo de pago más centrado en soporte enterprise. En el ecosistema Python hay muchos más competidores
El modelo de vender solo soporte no nos funcionó muy bien, aunque en Python podría ser distinto.
En el ecosistema Python de verdad hay de todo
Si agregas soporte de Django Tasks a Chancy o creas un paquete
django-chancy, creo que tendría adopción rápidaEl Oban OSS solo soporta ejecución asyncio de un solo hilo, así que los trabajos CPU-bound bloquean el event loop.
Por eso sentí que no valía la pena probarlo. La interfaz de Celery no me encanta, pero me resulta familiar y puede escalar vertical y horizontalmente sin límite.
Aun así, al enterarme de que sí se pueden levantar varios nodos worker, cambié un poco de opinión
La separación entre funciones OSS y Pro está bien, pero me decepciona que “la versión Pro rastrea la vida del producer con heartbeats más inteligentes”.
Que las funciones relacionadas con la confiabilidad sean de pago hace que sea más difícil adoptar el proyecto en OSS
La versión base debería ser la mejor posible, y lo adicional debería ser lo de pago. La línea divisoria parece estar en un lugar un poco extraño
La frase citada es un poco inexacta: el rastreo de vida del producer es igual; la diferencia está solo en cómo se recuperan los trabajos huérfanos
Ojalá los flujos de trabajo de BI/ML/DS en Python se mudaran a Elixir.
Creo que Elixir, con su naturaleza funcional, tolerante a fallos y concurrente, es una base mucho más natural para ese tipo de tareas
Este video y la guía Elixir Genius son buenas referencias
En nuestra empresa también usamos Celery, y la verdad no nos gusta mucho. Temporal es demasiado pesado, mientras que Oban se siente ligero y atractivo.
Me interesa una comparación de alguien que haya usado ambos
Temporal encaja mejor en organizaciones que pueden asumir la complejidad a cambio de garantías de workflow, por ejemplo un banco.
Oban es una cola basada en BD, y la confiabilidad tienes que reforzarla tú mismo.
A mí me parece bien que ambos estén presentes dentro de un sistema
Usamos una combinación de ProcessWorker simple y workers en ECS
Me pregunto si últimamente Celery se ha vuelto menos estable o más difícil de manejar
Proyecto interesante. Pero sí destaca que algunas funciones clave estén solo en Pro.
Como proyectos previos que implementan workflows durables basados en Postgres en OSS, están DBOS y Absurd.
Da gusto ver que el enfoque centrado en la base de datos vaya ganando terreno
Un modelo totalmente open source y sostenido solo por venta de soporte es un modelo soñado. Ojalá algún día sea posible
Me pregunto si Postgres da rendimiento suficiente para procesar cientos de millones de trabajos. En el pasado vi una gran mejora de rendimiento al migrar a Redis + Sidekiq
Dicen que en la versión OSS, si hay trabajos largos, pueden recuperarse incorrectamente aunque el producer siga vivo.
Entonces, ¿solo sirve para trabajos cortos?
El momento de recuperación solo cambia cuando no se pudo esperar una terminación normal