Choisir une technologie de file d’attente avec Postgres
(adriano.fyi)- Avant d’introduire un système de file d’attente séparé, si l’on peut mettre en place une file d’attente de tâches en arrière-plan avec le Postgres déjà utilisé, on peut tirer largement parti de la simplicité opérationnelle et de la familiarité de l’équipe
- Depuis Postgres 9.5,
NOTIFY/LISTENetFOR UPDATE SKIP LOCKEDpermettent à la fois de notifier les nouvelles tâches et d’éviter le traitement en double entre workers - Redis, Kafka, RabbitMQ et Amazon SQS sont des options solides, mais une nouvelle dépendance ajoute aux environnements de développement, de test et d’exploitation des modes de défaillance et des coûts d’apprentissage
- Les files d’attente Postgres ne sont pas non plus une solution universelle : le push/pop peut être plus lent qu’avec Redis, et il peut y avoir des coûts opérationnels comme une base de données ou un serveur de file d’attente séparé, ou des
VACUUMplus fréquents - Il est plus sûr d’abstraire le code des tâches en arrière-plan pour qu’il ne soit pas lié au backend de file d’attente, et de n’examiner d’autres technologies de file d’attente que lorsque la technologie familière ne répond pas aux besoins
Fonctionnement des files d’attente Postgres
- Une technologie de file d’attente Postgres repose sur deux éléments
- un mécanisme pub/sub pour annoncer et recevoir de nouvelles tâches
- des verrous de ligne pour empêcher plusieurs workers de traiter la même tâche
- Ces deux fonctionnalités sont fournies nativement depuis Postgres 9.5, sorti en 2016
- En utilisant
NOTIFYetLISTENensemble, on peut ajouter du pub/sub à une application FOR UPDATE SKIP LOCKEDprend un verrou de ligne sur les enregistrements correspondant aux conditions, et ignore ceux qui sont déjà verrouillés- Appliqué aux enregistrements de tâches, cela permet de construire une requête de traitement de file comme
SELECT * FROM jobs ORDER BY created_at FOR UPDATE SKIP LOCKED LIMIT 1
- Appliqué aux enregistrements de tâches, cela permet de construire une requête de traitement de file comme
SKIP LOCKEDfournit une vue « incohérente » des données, mais cette caractéristique est utile pour une file d’attente- Les tâches déjà en cours de traitement ne sont pas visibles par les autres workers à cause du verrou de ligne
- Ce comportement permet une exclusion mutuelle distribuée
- En utilisant
NOTIFYpour informer les workers enLISTENde l’arrivée d’une nouvelle tâche, on peut mettre en place à la fois du traitement périodique par lots et du traitement en temps réel
Comment Redis est devenu le choix par défaut pour les tâches en arrière-plan
- La manière de traiter les tâches de longue durée est un choix technique comportant de nombreux compromis
- Les options de files d’attente et de courtiers de messages largement utilisées sont les suivantes
- Redis : un magasin de données en mémoire et le backend de nombreuses bibliothèques de tâches en arrière-plan
- Apache Kafka : une plateforme distribuée de streaming d’événements maintenue par l’Apache Foundation
- RabbitMQ : présenté comme un courtier de messages largement déployé
- Amazon SQS : le SaaS d’Amazon qui fournit des files d’attente hautement scalables
- Sur le topic background jobs de GitHub, les 5 bibliothèques les plus populaires reposent toutes sur Redis
- Redis stocke les données en mémoire, ce qui rend les insertions et les lectures rapides, et fournit une API pub/sub ainsi que des structures de données
listetset, ce qui convient bien à l’implémentation de files d’attente - La scalabilité de Redis en fait le choix par défaut pour de nombreux développeurs, et les valeurs par défaut ont un poids important dans les choix techniques
Les critères à examiner avant la scalabilité
- L’obsession de l’industrie tech pour le « scale » peut reléguer au second plan la simplicité, la facilité de maintenance et la réduction de la charge cognitive des développeurs
- Beaucoup d’applications n’exigent pas une montée en charge au niveau de Google, Facebook ou Uber
- Lorsqu’on lance un projet ou une activité, plutôt que d’optimiser dès le départ uniquement pour la scalabilité, il faut d’abord examiner les critères suivants
- une technologie que l’équipe connaît bien
- une technologie suffisamment bonne pour répondre aux besoins des utilisateurs
- la solution qui correspond aux compétences de l’équipe et demande le moins d’efforts
- Postgres passe aussi très bien à l’échelle en pratique, mais ce n’est pas le système de file d’attente spécialisé qui scale le plus pour ce cas d’usage
- En tant que logiciel généraliste, Postgres gère assez bien de nombreuses tâches, et les files d’attente peuvent en faire partie
Juger à l’aune de la « technologie ennuyeuse »
- Lors du choix d’une technologie de file d’attente, la question la plus importante est de savoir quelle technologie on utilise déjà et que l’on comprend bien
- Une technologie déjà utilisée et bien connue ajoute peu de charge à la stack logicielle
- Si l’on utilise déjà une base de données relationnelle et que cette base est Postgres, on peut envisager les files d’attente Postgres avant d’autres logiciels
- La « technologie ennuyeuse » n’est pas une liste fixe : elle dépend des technologies actuellement utilisées
- Pour une application centrée sur la messagerie, RabbitMQ peut être une technologie ennuyeuse
- Pour une application centrée sur le cache, Redis peut être une technologie ennuyeuse
- Pour une application riche en données relationnelles, Postgres peut être une technologie ennuyeuse
- Introduire Redis, Kafka, RabbitMQ ou SQS uniquement pour des tâches en arrière-plan a un coût élevé
- Une nouvelle dépendance système est ajoutée aux environnements de développement, de test et de production
- À l’avenir, les développeurs, DBA et SRE devront connaître les modes de défaillance et la configuration de ce nouveau système
- Des connaissances en reprise après incident, diagnostic de problèmes et monitoring des performances seront nécessaires
- Les unknown unknowns que l’équipe ne connaît pas encore restent un risque
Coûts et critères de choix des files d’attente Postgres
- La technologie ennuyeuse n’est pas une solution universelle, et Postgres ne fait pas exception
- En choisissant une file d’attente Postgres, on gagne en familiarité, en modes de défaillance connus et en répartition des coûts, mais on peut payer les coûts suivants
- Le push/pop d’une file Postgres est sensiblement plus lent qu’avec Redis
- Au lieu d’une seule base de données relationnelle, il peut être nécessaire d’avoir une base de données applicative et une base de données de file d’attente
- Un serveur de base de données séparé peut être nécessaire pour faire évoluer les tâches en arrière-plan indépendamment
- Des
VACUUMplus fréquents peuvent être nécessaires, ce qui peut peser sur les performances
- Aucun de Postgres, Redis, Kafka, RabbitMQ ou SQS ne devrait devenir automatiquement le choix par défaut
- Le choix technique est une succession de compromis, à évaluer selon l’équipe et les besoins de l’application
- En cas de doute, on peut appliquer le critère suivant : « n’envisager une alternative que lorsqu’il est prouvé que la technologie ennuyeuse ne répond pas aux besoins »
Une architecture de tâches en arrière-plan qui permet de changer de file d’attente
- Le code de traitement des tâches en arrière-plan ne doit pas dépendre d’une technologie de file d’attente particulière
- À mesure qu’une application grandit, des technologies comme memcached ou Redis peuvent être ajoutées selon les besoins, et avec le temps elles peuvent aussi devenir des « technologies ennuyeuses » familières à l’équipe
- En abstrayant la technologie de file d’attente, les utilisateurs peuvent choisir la file adaptée à la tâche
- Parmi les bibliothèques de tâches en arrière-plan les plus populaires sur GitHub, à l’exception de Hangfire, aucune ne fournit de sortie de secours vers une technologie de file autre que Redis
- Dans ce type d’architecture, changer de file d’attente impose de réécrire le code applicatif
- Neoq est une bibliothèque Go conçue selon cette philosophie
- Les backends de file d’attente peuvent être en mémoire, Postgres ou Redis
- Les utilisateurs peuvent l’initialiser avec un autre backend de file sans modifier le code applicatif
- Les implémentations en mémoire et Postgres sont fournies en première partie, tandis que l’implémentation Redis utilise asynq
- Redis, Kafka, RabbitMQ et SQS peuvent être d’excellentes technologies, mais elles ne sont pas toujours la file adaptée à la tâche ni le niveau de complexité approprié
1 commentaires
Avis de Hacker News
Jusqu’ici, j’ai construit trois systèmes de tâches distribués, et la règle empirique que je recommande depuis des années est : « concevoir pour 10 fois l’échelle actuelle »
Si vous êtes à 70 requêtes par seconde, dimensionnez pour 700 ; si vous avez besoin de 20 serveurs de jobs batch, partez sur 200. Même une startup qui croît de 100 % par an ne sera qu’à 8 fois l’échelle au bout de 3 ans, donc vous aurez le temps de réécrire en grandissant
Le premier système que j’ai créé évitait SQL pour des raisons de « scalabilité », mais j’ai beaucoup souffert en rencontrant de nombreux cas limites où il fallait de l’intégrité transactionnelle
Les deux systèmes de tâches distribués que j’ai construits ensuite utilisaient PostgreSQL comme coordinateur, et étaient littéralement organisés autour de
SELECT FOR UPDATE SKIP LOCKED. L’un pilote généralement 350 workers, et l’autre applique des priorités complexes à des milliers de tâchesLes deux tiendront sans problème jusqu’à plusieurs millions de dollars de chiffre d’affaires annuel, et par exemple celui à 350 workers pourrait, avec quelques ajustements, monter à environ 2 000 CPU
Les technologies à très grande échelle ont l’air assez bon marché jusqu’au moment où l’on se rend compte qu’on a besoin de transactions. À partir de là, imiter une sémantique transactionnelle au-dessus d’un stockage à cohérence éventuelle devient un cauchemar d’ingénierie
Il faut donc s’asseoir et faire les calculs. Si l’entreprise fait 100 millions de dollars de chiffre d’affaires annuel, quelle taille devra avoir le système distribué, peut-on facilement obtenir une instance PostgreSQL capable d’encaisser cette charge, ou un sharding par client suffit-il ? Si oui, PostgreSQL mérite d’être très sérieusement envisagé. Cent petites choses deviennent plus simples
Les solutions SQL sont en général plus simples, consomment moins de ressources de calcul et sont plus faciles à exploiter
Mais pour les faire fonctionner, il faut assez bien connaître les bases de données pour savoir qu’une fonctionnalité comme
SELECT FOR UPDATE SKIP LOCKEDexiste au départ. Aujourd’hui, beaucoup d’ingénieurs grandissent dans des environnements où de lourds ORM font écran aux vraies capacités du SGBD, et ce savoir devient assez rareLe système doit encaisser les pics de charge, et si vous ne connaissez pas ces pics, il faut prévoir une marge de sécurité ou un moyen de jeter des tâches ou de les reporter quand c’est nécessaire
Tout est affaire de compromis ; il ne faut optimiser que ce qui doit l’être, et savoir déterminer quoi optimiser est l’une des marques d’un bon ingénieur
Si c’est nouveau pour vous, vous venez de rentabiliser une semaine passée à lire HN pendant les heures de travail ; ça vaut le coup de l’écrire sur un papier et de le coller sur votre carte bancaire
Sur plusieurs projets, j’ai choisi une approche plus simple : utiliser une table ordinaire et
SELECT FOR UPDATE SKIP LOCKED, ce qui fonctionne directement avec les ORM/frameworks de DSL de requêtes de tous les langageshttps://www.pgcasts.com/episodes/the-skip-locked-feature-in-...
Ce n’est pas du « web scale », mais d’après mon expérience, cela monte facilement jusqu’à des milliers de tâches en arrière-plan
J’ai utilisé cette approche sans problème pour des tâches dans une grande organisation. Pas besoin de déploiement particulier ni de nouvelle infrastructure : il suffit de lancer quelques threads workers dans l’application. On peut aussi avoir un thread qui réinitialise les tâches abandonnées
Cela dit, en 3 ans, cette situation ne s’est jamais vraiment produite ; tout était dans des
try/catch, donc en cas d’échec la tâche était remise dans la file, et l’application Java était aussi très stabledelete from taskwhere task_id in( select task_idfrom taskorder by random() -- use tablesample for better performancefor updateskip lockedlimit 1)returning task_id, task_type, params::jsonb as params[1] https://taylor.town/pg-task
On parle de « milliers de tâches en arrière-plan », mais une file devrait en général être analysée selon la loi de Little : il faut parler de ratios comme le taux moyen d’arrivée des tâches par seconde et la durée moyenne des tâches. Le nombre brut n’a pas beaucoup de sens à lui seul
Au début, un naïf
UPDATE ... SETpeut suffire, mais il prend trop de verrous. On peut utiliser une sous-requêteSELECTdans l’UPDATE, ou rendre le verrouillage de prélèvement plus efficace avecSELECT FOR UPDATE SKIP LOCKED, mais au final les requêtes de prélèvement peuvent bloquer les verrous les unes des autres et figer la fileÀ ce moment-là, on peut couper l’ingestion pour laisser la DB respirer, mais les entrées perdues provoquent une perte de données, et le cœur du problème est généralement que les prélèvements se bloquent mutuellement
On peut aussi shard en urgence la table des tâches pour éviter les verrous, mais c’est fragile à déployer sur plusieurs workers et cela peut entraîner une perte de données. On peut jeter une partie arbitraire des tâches, mais c’est évidemment une perte de données
Ces options sont extrêmement stressantes en production, et la récupération est très difficile sans repartir d’une conception depuis zéro. À moins d’avoir seulement quelques clients et d’être certain de rester pour toujours à quelques dizaines de tâches par seconde, depuis que j’ai vécu ce genre de situation, je choisis si possible une vraie technologie de file plutôt qu’une base de données
On voit souvent des critiques du type « les ingénieurs copient l’infrastructure des FAANG pour avoir l’air cool, alors qu’elle ne correspond pas du tout à leurs besoins », mais je pense qu’une grande partie du problème relève de la connaissance et de la documentation
Si vous voulez suivre les FAANG ou d’autres startups et construire une architecture à base de files, extensible à l’infini, il existe des dizaines de guides, tutoriels et livres blancs de qualité qui vous permettent de configurer en quelques heures des solutions scalables comme Redis ou SQS
Le coût de maintenance est plus élevé, mais on peut démarrer en copiant-collant des commandes, du code et de la configuration provenant de sources réputées
À l’inverse, si vous voulez utiliser
NOTIFYde PostgreSQL et que vous cherchez « SQLALchemy notify listen postgres », vous ne trouverez guère que quelques questions Stack Overflow sans réponse et des GitHub Gist sans contexteJ’aimerais vraiment essayer cette approche dans un side project, mais je n’ai pas 2 ou 3 jours à y consacrer seul pour comprendre comment faire
Vu de l’extérieur, le choix semble être entre « simple mais non scalable, c’est-à-dire utiliser seulement PostgreSQL » et « complexe mais scalable, c’est-à-dire Redis/SQS, etc. ». On en conclut alors que je suis aveuglé par les technologies à la mode et les FAANG, et que je choisis une scalabilité dont je n’ai pas besoin
Mais si l’on tient compte des guides et des ressources disponibles, les vraies options ressemblent plutôt à « complexe et à la scalabilité imprévisible, parce que je ne connais ni la mise en œuvre ni les pièges » et « simple et scalable, la méthode que tout le monde utilise réellement ». Dans ce cas, le choix de l’ingénieur qui suit les FAANG paraît beaucoup plus rationnel
NOTIFY, il suffit de lire https://www.postgresql.org/docs/current/sql-notify.html, sans avoir besoin de talents particuliers en recherchePar exemple, avec un PostgreSQL peu chargé, les migrations de schéma, l’ajout de nouvelles contraintes, l’analytique, etc. deviennent presque triviaux
Avec SQS, Cassandra, etc., vous gagnez en scalabilité/disponibilité, mais si vous découvrez que la conception initiale ne convient pas, la modifier prendra beaucoup plus de temps. C’est le cas quand le métier demande d’ajouter une contrainte du genre « les utilisateurs de type foo ne doivent pas pouvoir combiner simultanément la valeur bar »
C’est possible à implémenter sans PostgreSQL, mais ce n’est ni facile ni simple, surtout si des changements sont nécessaires
Donc, pour conserver de la flexibilité, je pense qu’il vaut mieux utiliser PostgreSQL, et n’ajouter autre chose par-dessus PostgreSQL que lorsque l’on sait que cela ne changera pas. Bien sûr, cela ajoute alors de l’overhead d’infrastructure et de maintenance
Au final, c’est toujours un compromis, et il faut savoir quoi échanger, contre quoi, et à quel moment
Je ne pense pas qu’il soit idéal que tout le monde implémente lui-même, dans son application, un worker de tâches en arrière-plan sur mesure, qu’il soit basé sur PostgreSQL ou sur une autre file
Les tâches en arrière-plan comportent beaucoup de subtilités et de détails d’implémentation faciles à rater ; en général, il vaut mieux les confier à une bibliothèque ou à un framework dédié plus complet
Si toutes les applications Rails n’avaient pas Sidekiq/Active Jobs et que chacune implémentait son propre worker en arrière-plan sur mesure, les apps Rails auraient probablement une réputation bien pire qu’aujourd’hui en matière de fiabilité
Il faut choisir la solution qui apporte le plus grand bénéfice sans trop s’éloigner de l’objectif principal
J’ai vu une startup où les développeurs passaient environ 80 % de leur temps à se battre avec les outils et l’infrastructure. Il leur restait trois mois de runway, et aujourd’hui il ne reste plus qu’un grand trou au bout de cette piste. Rien que d’y penser, j’en ai encore des frissons
Il me semble qu’il n’y a pas assez de respect pour la complexité
Le principal inconvénient de l’utilisation de PostgreSQL comme bus publish/subscribe avec
LISTEN/NOTIFY, c’est queLISTENest une fonctionnalité de session et ne convient donc pas au pooling de connexions au niveau instructionSi vous utilisez cette approche, il vaut mieux recourir aux verrous consultatifs. Les autres verrous explicites imposent une charge plus importante à la base de données, alors que les verrous consultatifs sont volontairement conçus pour être très légers
Mon exemple d’implémentation préféré est que, qui a été porté dans plusieurs langages
NOTIFY/LISTEN, c’est que c’est difficile à diagnostiquer quand un problème survientRécemment encore, nous avons dû cesser de l’utiliser à cause d’un problème où, au bout d’un certain temps, tous les
NOTIFY/LISTENs’arrêtaient, et seul un redémarrage de la base de données permettait de le résoudre https://dba.stackexchange.com/questions/325104/error-could-n...Il y a des années, Skype utilisait PostgreSQL comme file d’attente avec un petit plugin pour traiter tous ses CDR. Je ne sais pas s’ils l’utilisent encore, mais il y a 10 ans, c’était du « web scale », et pendant que des gens débattaient sur Internet pour savoir si utiliser une base de données comme file d’attente était un anti-pattern, ça tournait très bien
Le fait d’avoir des transactions est assez pratique
https://wiki.postgresql.org/wiki/SkyTools
Comme je l’ai pas mal utilisé au travail, j’ai aussi fait quelques présentations sur ce sujet à Sydpy. Si vous exploitez et supportez déjà bien PostgreSQL, c’est utile
Cela dit, aujourd’hui j’utiliserais probablement une file dédiée, et j’éviterais RabbitMQ à tout prix
Même si l’on s’inquiète du verrouillage fournisseur, les opérations de base d’une file se résument en pratique à push/pop, donc il est relativement facile d’écrire le code de façon à pouvoir migrer si nécessaire
Si vous avez mis la file dans la base de données au motif que vous exploitez déjà cette base, ce raisonnement est à double tranchant. Ce n’est pas réjouissant, mais le bazar de la file peut maintenant entraîner aussi votre stockage principal de données
L’un des plus grands avantages de l’utilisation de PostgreSQL comme file d’attente applicative est que toutes les tâches asynchrones planifiées bénéficient de la transactionnalité
Par exemple, si vous avez une modification backend complexe après une inscription, qui envoie un e-mail, et qu’après avoir ajouté la tâche d’envoi d’e-mail une partie ultérieure de la même transaction échoue et provoque un rollback, cette tâche d’e-mail n’aura jamais été placée dans la file
Dans le cas de l’e-mail, si l’on attend la confirmation d’envoi et que ça échoue, il faut choisir entre faire échouer la transaction au risque d’envoyer l’e-mail en double, ou continuer au risque de perdre l’e-mail
Le gros avantage apparaît dans les chemins de code qui modifient la DB de façon asynchrone. Comme la consommation de la tâche et la mise à jour de la DB sont dans la même transaction, on peut traiter une sémantique exactement une fois de manière entièrement transactionnelle
Dans PostgreSQL, j’ai une table de journal de transactions contenant les événements déclenchés et le
pg_current_xact_id()de la transaction correspondante. On pourrait aussi utiliser lexminintégré de la ligne, mais il faut alors se soucier du wraparound des transactions. L’insertion dans cette ligne déclenche unNOTIFYUn processus en arrière-plan tourne en boucle et sélectionne dans la table de transactions toutes les lignes dont l’ID de transaction est compris entre le
xminde l’exécution précédente et lepg_snapshot_xmin(pg_current_snapshot())actuel. Il mappe ces événements en tâches et les soumet à SQS, enregistre lexmin, puis attend le prochainNOTIFYviaLISTENAvec une file PostgreSQL, il est vrai que si l’on ajoute une tâche d’envoi d’e-mail puis que la transaction échoue plus tard, l’e-mail n’entre pas dans la file
Le code de base de données qui doit être totalement séparé mérite d’être isolé dans des transactions différentes. À l’inverse, si l’utilisateur n’a pas été créé en DB, on peut ne pas vouloir envoyer l’e-mail d’inscription ; cela dépend donc du contexte
Avec une file basée sur Redis, ou pratiquement n’importe quelle autre file, on se retrouve vite dans une situation où une tâche supposant l’existence d’un enregistrement en base s’exécute avant que la transaction ne soit commitée. Le code pour corriger cela devient généralement bancal et complexe
Ce que j’aime dans Kafka, c’est que ce n’est qu’un journal append-only, et que les clients ne portent essentiellement que des offsets
Conceptuellement, c’est très simple à comprendre, persistant et assez robuste face aux pannes, puisqu’on peut revenir à n’importe quel offset et relire
Malheureusement, en raison de sa nature distribuée, Kafka s’accompagne d’une complexité suffisante pour ne finalement pas valoir le coût dans la plupart des cas d’usage
Personnellement, j’aimerais quelque chose de similaire mais plus simple à exploiter. Sur un seul nœud, on pourrait traiter des centaines, voire peut-être des milliers d’événements par seconde, et sans la complexité distribuée ce serait plutôt appréciable
En théorie, on pourrait aussi le faire avec PostgreSQL : il suffirait de ne jamais supprimer les lignes. Peut-être que c’est la bonne réponse
Si l’on n’a pas besoin de toutes les fonctionnalités comme les groupes de consommateurs ou les partitions, la configuration des consommateurs deviendra aussi beaucoup plus simple
Chez Windmill, nous utilisons exactement cette approche. Windmill est une alternative open source à Retool et un outil proche d’un Airflow moderne, et nous lançons des benchmarks tous les jours
Sur une instance GitHub CI correcte, en exécutant un worker Windmill et PostgreSQL dans des conteneurs, le benchmark traite 1 200 tâches par seconde. En ajoutant des workers, il scale de manière stable jusqu’à 5 000 tâches par seconde
Nous envisageons d’utiliser Citus pour dépasser la barrière des 5 000 tâches par seconde sur une instance multitenant
https://github.com/windmill-labs/windmill/tree/benchmarks
À l’époque où l’on était autour de 10 messages par seconde, on utilisait PostgreSQL pour certaines files, et ça a plutôt bien tenu la montée en charge. Mais franchement, sur AWS, GCP ou Azure, configurer SQS ou une autre stack de file est extrêmement simple, ces services sont conçus pour cet usage, et des choses comme les DLQ sont intégrées.
Je ne vois pas pourquoi choisir cette voie en s’inquiétant du risque que ce système s’effondre et affecte l’état du reste de la base de données.
J’aime le principe « utilisez l’outil le plus simple », mais parfois j’ai l’impression que les ingénieurs vont trop loin et finissent par ne garder que l’outil le plus bête, assorti de réserves ambiguës, alors que les alternatives mainstream sont relativement peu coûteuses et simples.
Stocker l’état des tâches en base permet de l’interroger facilement. Même si cela ne reflète pas toujours exactement l’état réel, c’est utile pour analyser des incidents en production. D’autant que la plupart des files de tâches suppriment simplement les enregistrements traités.
Et si l’on rend toutes les tâches en arrière-plan idempotentes, une approche du type « envoyer un message dans la file pour demander le traitement de cette tâche » est presque toujours sûre.
Lorsqu’on dépend uniquement d’une file de messages, il est souvent difficile de comprendre ce qui se passe en cas de problème de performance. Par exemple, RabbitMQ peut indiquer la taille d’une file, mais ses capacités d’inspection des données internes sont quasi inexistantes ou très limitées.
Certains logiciels peuvent être conçus en partant du principe qu’ils ne dépasseront jamais les capacités de PostgreSQL, et même si cela arrive, on peut les rendre faciles à migrer vers un autre service.
Et on peut aussi vouloir construire simplement, dans PostgreSQL, un système de tâches doté de transactions.
Nous utilisons Oban d’Elixir pour traiter chaque jour des centaines de milliers à des millions de tâches dans PostgreSQL.
Le fait d’avoir une sémantique transactionnelle autour des tâches en arrière-plan — par exemple ne planifier un e-mail que si une transaction a réussi — est incroyablement pratique.
Il faut ajuster un peu
autovacuum, mais une fois cela réglé, ça nous a très bien convenu.