- Oban.py est une version portée en Python, basée sur PostgreSQL, du framework de traitement de jobs Oban d’Elixir, permettant d’insérer et de traiter des jobs uniquement avec la base de données
- Les jobs sont créés et annulés dans des transactions de base de données, avec la prise en charge de nombreuses fonctions comme la gestion des files, le stockage des résultats et la planification cron
- La version open source comporte des limites, comme une exécution asyncio monothread et des insertions et confirmations individuelles, tandis que la version Pro offre le traitement parallèle, les workflows et une concurrence intelligente
- Son fonctionnement interne se compose de cinq étapes :
Insert → Notify → Fetch → Execute → Ack, et s’appuie surFOR UPDATE SKIP LOCKEDde PostgreSQL pour éviter les conflits de concurrence - L’élection du leader, la récupération des jobs orphelins et les nouvelles tentatives avec backoff sont également gérées via la base de données, ce qui permet un traitement distribué fiable sans broker externe
Présentation d’Oban.py
- Oban.py est un framework de traitement de jobs basé sur une base de données, issu d’un portage en Python d’Oban pour Elixir
- Il insère et traite les jobs au sein de transactions de base de données, et en cas d’échec, l’ensemble de la transaction est annulé
- Il inclut diverses fonctions de contrôle comme les limites de file, la conservation des jobs terminés, la rétention des résultats et la planification cron
- Deux versions sont proposées
- Open source (OSS) : exécution asyncio monothread, insertion et confirmation individuelles, récupération simple
- Version Pro : traitement parallèle basé sur un pool de processus, avec prise en charge des workflows, relais, jobs uniques et concurrence intelligente
- La version OSS convient aux projets personnels ou à l’évaluation, tandis que la version Pro est recommandée pour les environnements à grande échelle
Chemin de traitement des jobs
- Après insertion, les jobs sont enregistrés dans la table
oban_jobsavecstate='available', puis une notification est envoyée à chaque nœud via NOTIFY de PostgreSQL - Le Stager de chaque nœud détecte la file concernée, réveille le Producer, puis le Producer récupère et exécute les jobs
- Lors de la sélection des jobs, l’instruction SQL
FOR UPDATE SKIP LOCKEDpermet un traitement parallèle sans exécution en double- Les lignes déjà verrouillées sont ignorées, ce qui permet à d’autres producteurs de récupérer immédiatement d’autres jobs
- Les jobs sont dispatchés comme async tasks et, une fois terminés, un callback gère l’acknowledgement
- La version Pro utilise un dispatcher avec pool de processus à la place d’asyncio pour permettre une exécution parallèle sur plusieurs cœurs
Processus d’arrière-plan
- Élection du leader (Leader Election)
- Le leader est déterminé via
INSERT ... ON CONFLICTde PostgreSQL et un bail (lease) basé sur un TTL - Sans protocole de consensus séparé, un leader unique se charge du nettoyage et de la récupération des jobs
- Le leader est déterminé via
- Lifeline (récupération des jobs orphelins)
- Si un job en cours d’exécution dépasse une certaine durée (
rescue_after, 5 minutes par défaut), il est restauré à l’état available - La version Pro vérifie si le Producer est toujours vivant, tandis que l’OSS ne se base que sur le temps écoulé
- Si un job en cours d’exécution dépasse une certaine durée (
- Pruner (nettoyage des jobs)
- Supprime les jobs terminés, annulés ou abandonnés datant de plus de
max_age(1 jour par défaut) - Limite la portée de suppression avec
LIMITafin d’éviter une surcharge de la base de données
- Supprime les jobs terminés, annulés ou abandonnés datant de plus de
Nouvelles tentatives et backoff
- Lorsqu’un job lève une exception, l’Executor décide s’il faut le relancer
- Si le nombre maximal de tentatives (
max_attempts) n’est pas atteint, le job est relancé, sinon il est abandonné
- Si le nombre maximal de tentatives (
- Le backoff par défaut suit une croissance exponentielle avec jitter
- Cela évite les nouvelles tentatives simultanées lors de défaillances massives et atténue les pics de charge (Thundering Herd)
- Exemple : environ 17 secondes à la 1re tentative, 47 secondes à la 5e, 17 minutes à la 10e
- Les classes de workers peuvent implémenter une logique de backoff personnalisée via la méthode
backoff()
Principales caractéristiques et évaluation
- PostgreSQL joue un rôle central
FOR UPDATE SKIP LOCKED,LISTEN/NOTIFYetON CONFLICTgèrent ensemble le contrôle de la concurrence, la signalisation et l’élection du leader- Une seule base de données suffit à constituer la couche de coordination, sans Redis ni broker externe
- Pas parallèle, mais compatible avec la concurrence
- La base asyncio convient bien aux tâches I/O-bound, tandis que la version Pro est recommandée pour les tâches CPU-bound
- Clarté de la structure du code
- Une nomenclature cohérente et une séparation nette des responsabilités donnent une base de code facile à lire
- Répartition claire des rôles entre OSS et Pro
- L’OSS vise l’expérimentation et les petits déploiements, la version Pro les environnements à grande échelle et à hautes performances
- Conclusion : un portage Python propre et bien structuré qui implémente une file de jobs complète uniquement avec PostgreSQL, adapté aux utilisateurs d’Elixir comme aux développeurs recherchant un système de jobs sans infrastructure externe
1 commentaires
Avis sur Hacker News
Je suis la personne qui a créé Sidekiq, et je félicite Shannon et Parker pour ce lancement.
J’ai déjà eu la même réflexion — me concentrer sur Ruby, ou étendre Sidekiq à d’autres langages. J’ai réalisé qu’il était impossible d’être expert de tous les langages, alors j’ai créé Faktory à la place. C’est une architecture où un serveur central gère le cycle de vie de la file, tandis que les clients propres à chaque langage restent simples. Par exemple, il existe un client comme faktory-rs. L’inconvénient, c’est qu’en ne se concentrant pas sur une communauté de langage en particulier, il est difficile de fournir des exemples adaptés à ce langage.
Il est possible qu’une approche centrée sur une seule communauté donne de meilleurs résultats. Le temps le dira.
Le cœur d’Oban, c’est qu’on peut insérer et traiter des tâches uniquement avec la base de données. On peut ajouter une tâche d’envoi d’e-mail dans une transaction de création d’utilisateur, avec rollback complet en cas d’échec.
Beaucoup de gens disent qu’il ne faut pas utiliser une base relationnelle comme file de tâches, mais ils négligent l’importance des transactions. L’article de Brandur Leach, Job Drain, explique très bien ce concept.
Mais aujourd’hui, plus personne ne se souvient de cette douleur. Le pattern Transactional Outbox est indispensable, et je préfère une approche qui bénéficie des mêmes garanties ACID que mes données.
Même sans connaître les entrailles d’une base, consacrer une semaine à apprendre les niveaux d’isolation et l’ordre des commits peut faire économiser un an de debug de systèmes distribués.
À une époque où les longs processus IA sont nombreux, cette durabilité est indispensable. Dans d’autres écosystèmes, il faut payer pour avoir ce genre de fonctionnalités, alors qu’avec Oban c’est inclus par défaut.
L’équipe d’Oban est connue dans l’écosystème Elixir pour son ingénierie raffinée. En revanche, le fait d’avoir verrouillé le pool de processus dans la version Pro est déroutant.
Par exemple, le forfait à 135 $/mois inclut l’exécution multiprocessus, les workflows, les limites globales, les tâches uniques, les opérations en masse, les sources chiffrées et un support dédié.
Mon projet Chancy est entièrement gratuit, et permet de mélanger librement asyncio, processus, threads et sous-interpréteurs.
Je me demande s’il ne vaudrait pas mieux faire passer ces fonctionnalités en OSS, et réserver le payant surtout au support entreprise. L’écosystème Python a bien plus de concurrents.
Un modèle consistant à ne vendre que du support ne nous a pas vraiment réussi, mais en Python ce sera peut-être différent.
Dans l’écosystème Python, il y a vraiment de tout en abondance.
Ajouter le support Django Tasks à Chancy, ou créer un package
django-chancy, permettrait probablement une adoption rapide.La version OSS d’Oban ne prend en charge que l’exécution asyncio mono-thread, donc les tâches CPU-bound bloquent la boucle d’événements.
C’est pour ça que j’ai eu l’impression que ça ne valait pas la peine d’essayer. L’interface de Celery ne me plaît pas vraiment, mais j’y suis habitué, et on peut faire du scale vertical comme horizontal sans limite.
Cela dit, savoir qu’on peut lancer plusieurs nœuds worker m’a fait un peu changer d’avis.
La distinction OSS/Pro ne me dérange pas, mais le fait de dire que « la version Pro suit la survie des producteurs avec des heartbeats plus intelligents » est regrettable.
Le fait que des fonctionnalités liées à la fiabilité soient payantes rend l’adoption plus difficile pour les projets OSS.
La version de base devrait être la meilleure, et les fonctionnalités additionnelles devraient être payantes. La frontière semble placée à un endroit un peu étrange.
La phrase citée est légèrement inexacte — le suivi de la survie des producteurs est identique, la différence ne porte que sur la méthode de récupération des tâches orphelines.
J’aimerais que les workflows BI/ML/DS en Python basculent vers Elixir.
Je pense qu’Elixir, avec son côté fonctionnel, tolérant aux pannes et fortement concurrent, constitue une base bien plus naturelle pour ce type de travail.
Cette vidéo et le guide Elixir Genius sont de bonnes références.
Dans notre entreprise, nous utilisons aussi Celery, et ce n’est pas formidable. Temporal est trop lourd, alors qu’Oban est léger et me plaît bien.
J’aimerais avoir un comparatif de quelqu’un qui a utilisé les deux.
Temporal convient aux organisations qui ont besoin de garanties sur les workflows et peuvent assumer cette complexité, comme les banques.
Oban est une file basée sur une base de données, dont il faut soi-même renforcer la fiabilité.
À mon avis, il est très bien d’avoir les deux dans un même système.
Nous utilisons une combinaison simple de ProcessWorker et de workers ECS.
Je me demande si Celery est devenu moins stable ou plus difficile à gérer ces derniers temps.
Projet intéressant. Cela dit, le fait qu’une partie des fonctionnalités centrales soit réservée à la version Pro saute aux yeux.
Parmi les projets antérieurs qui implémentent des workflows durables basés sur Postgres en OSS, on peut citer DBOS et Absurd.
C’est une bonne chose de voir les approches centrées sur la base de données se multiplier.
Un modèle entièrement open source, financé uniquement par la vente de support, c’est un modèle de rêve. J’espère qu’un jour ce sera possible.
Je me demande si Postgres offre des performances suffisantes pour traiter des centaines de millions de tâches. À une époque, j’ai vu un gros gain de performances en migrant vers Redis + Sidekiq.
Il est dit que dans la version OSS, si une tâche est longue, elle peut être récupérée à tort même si le producteur est toujours vivant.
Cela veut-il dire qu’il faut se limiter à des tâches courtes ?
Le timing de récupération ne change que lorsqu’on n’a pas pu attendre un arrêt propre.