4 points par GN⁺ 2023-09-24 | 1 commentaires | Partager sur WhatsApp
  • Pour transmettre en temps réel les changements de Postgres à d’autres systèmes, il faut du CDC (Change Data Capture) ; selon l’option choisie, de la simple notification à la réplication basée sur le WAL, la fiabilité et la charge opérationnelle varient fortement
  • Listen/Notify est le moyen le plus léger pour démarrer, mais avec une livraison at-most-once, des notifications éphémères et une limite de payload de 8 000 octets, il relève davantage d’un signal auxiliaire que d’un CDC central
  • Le polling de tables et les tables d’audit (outbox pattern) peuvent être implémentés uniquement avec des tables et triggers standards, mais il faut gérer soi-même la détection des suppressions, les diffs, l’ordre des commits, l’amplification des écritures et la backpressure
  • La réplication logique (logical replication) est une approche puissante qui streame les insert/update/delete depuis le WAL, mais l’application doit gérer les replication slots, les ack, les redémarrages et la montée en débit
  • Sequin s’appuie sur la réplication logique de Postgres pour transmettre les changements vers SQS, Kafka, Elasticsearch, Redis, des endpoints HTTP, etc., et réduit la charge liée à la gestion directe des replication slots

Quand on a besoin de CDC avec Postgres

  • Postgres est très efficace pour manipuler les données stockées, mais si l’on veut déclencher des workflows à partir de changements de tables ou streamer en temps réel vers d’autres stores de données, systèmes ou services, il faut concevoir séparément le déplacement des données
  • Le Change Data Capture (CDC) consiste à identifier et capturer les changements de la base de données, puis à les transmettre en temps réel aux systèmes downstream
  • Il existe plusieurs façons de capturer les changements dans Postgres, avec des niveaux de difficulté d’implémentation, de fiabilité et de charge opérationnelle différents

Listen/Notify : le pub-sub le plus simple

  • Listen/Notify de Postgres est une fonctionnalité de communication inter-processus qui fonctionne selon le pattern publish-subscribe
  • Une session peut listen sur un canal donné, et l’activité de la base de données ou d’autres sessions peuvent envoyer un notify sur ce canal
  • Pour capturer les changements, on peut l’utiliser avec un trigger
    • Un exemple de trigger consiste, au moment d’un after insert or update or delete, à construire en JSON le table, l’id et l’action de l’enregistrement modifié, puis à appeler pg_notify('table_changes', payload::text)
  • Les limites sont nettes
    • La sémantique de livraison est at-most-once : le listener doit être connecté au moment où la notification est publiée
    • Le listener ne reçoit que les notifications émises après son abonnement ; une brève coupure réseau peut donc faire perdre des notifications
    • La taille du payload est limitée à 8 000 octets ; au-delà, la commande notify échoue
    • Le nom du canal est inclus dans la taille du payload et, comme les identifiants Postgres, il peut faire jusqu’à 64 octets
  • C’est utilisable pour une détection basique des changements ou pour optimiser le polling de tables, mais cela peut mal convenir à des besoins CDC complexes

Polling de tables : simple, mais faible sur les suppressions et les diffs

  • La façon robuste la plus simple de capturer les changements est de poller directement les tables
  • Chaque table doit disposer d’une colonne comme updated_at, mise à jour à chaque modification de ligne, et éventuellement créée via un trigger
  • On utilise la combinaison de updated_at et id comme curseur, et la logique applicative stocke et gère ce curseur
  • En ajoutant un abonnement Notify, on peut informer l’application des insertions et modifications d’enregistrements afin de réduire la fréquence du polling
    • Les notifications Postgres étant éphémères, elles conviennent surtout comme optimisation au-dessus du polling
  • Les principaux inconvénients sont au nombre de trois
    • Les lignes supprimées ne restant pas dans la table, la détection des suppressions est impossible
    • Comme contournement, un trigger delete peut stocker l’id et les colonnes nécessaires dans une table séparée comme deleted_contacts, que l’application pourra poller
    • On peut savoir qu’un enregistrement a été mis à jour, mais pas ce qui a changé
    • Les datetime et sequences de Postgres pouvant ne pas respecter l’ordre des commits, on peut manquer des lignes encore en cours de commit pendant la lecture d’un bloc basé sur updated_at
  • C’est une option raisonnable pour un suivi simple des changements lorsque les suppressions, les diffs et les oublis occasionnels ne sont pas de gros problèmes

Tables d’audit : stocker le journal des changements avec l’outbox pattern

  • L’approche par table d’audit (audit table) enregistre les changements dans une table changelog séparée ; on l’appelle aussi outbox pattern
  • changelog peut contenir les colonnes liées au changement
    • action : indique insert, update ou delete
    • old : jsonb de l’enregistrement avant changement, vide pour un insert
    • values : jsonb des champs modifiés, vide pour un delete
    • inserted_at : heure à laquelle le changement s’est produit
  • Pour l’implémenter, il faut une fonction trigger qui insère dans changelog à chaque changement, ainsi qu’un trigger par table à surveiller
  • On peut aussi consommer changelog comme une file
    • Un worker applicatif récupère les changements depuis la table
    • Pour un traitement approximativement exactly-once, on peut utiliser for update skip locked de Postgres
    • Le worker peut ouvrir une transaction, verrouiller un lot avec order by timestamp limit 100 for update skip locked, le traiter, supprimer les enregistrements traités, puis committer
  • Il existe des inconvénients opérationnels
    • Une écriture sur une table unique entraîne plusieurs écritures dans la table d’audit : c’est de l’amplification d’écriture (write amplification)
    • En général, il y a au minimum trois écritures : l’insert initial dans la table d’audit, un update pendant le traitement, puis un delete après traitement
    • Le fan-out via workers doit être conçu sur mesure pour l’application
    • Avant un déploiement à l’échelle de la production, il faudra probablement ajuster les fonctions trigger et le design des tables
    • On peut aussi définir des politiques détaillées, par exemple une limite de temps pendant laquelle un worker peut garder un changement check-outé
    • Même si un worker ne parvient pas à traiter les changements, la table d’audit continue de se remplir, ce qui limite la gestion de la backpressure

Foreign Data Wrapper : une option plutôt proche de la synchronisation entre Postgres spécifiques

  • Foreign Data Wrapper (FDW) est une fonctionnalité qui permet à une base Postgres de lire et d’écrire dans des sources de données externes
  • L’extension basée sur FDW la plus largement prise en charge est postgres_fdw
    • Elle permet de connecter deux bases Postgres et de créer, depuis une base, une structure proche d’une view qui référence les tables de l’autre base
    • En interne, une base Postgres devient cliente et l’autre serveur
    • Lorsqu’on interroge une foreign table, la base cliente envoie une requête à la base serveur via le wire protocol de Postgres
  • FDW n’est pas une méthode courante de capture des changements et il est difficile de le recommander en dehors de situations très spécifiques
  • Si l’on veut écrire les changements d’une base Postgres dans une autre base Postgres, FDW peut être approprié
    • Par exemple dans une situation où l’on utilise séparément une base de données comptable et une base de données applicative
    • On peut sauter l’étape intermédiaire de capture des changements et répliquer directement entre bases avec postgres_fdw
  • Il est aussi possible d’écrire son propre FDW pour POSTer les changements vers une API interne
    • Comme l’écriture vers l’API se fait dans le commit, l’API peut rejeter le changement et provoquer un rollback du commit
  • FDW est puissant, mais il est rarement le meilleur choix pour du CDC, et écrire soi-même un FDW fait partie des travaux les plus lourds parmi les méthodes de capture des changements
    • L’écriture d’un FDW personnalisé est devenue plus simple avec des outils comme Supabase wrappers, mais cela reste un gros chantier

Réplication logique directe : un CDC puissant basé sur le WAL

  • Postgres dispose de protocoles pour la réplication de bases de données, dont la réplication logique (logical replication)
  • La réplication logique est construite au-dessus du WAL (write-ahead log) de Postgres
    • Tous les insert, update et delete de la base sont suivis
    • Les changements sont streamés vers un subscriber
  • L’utilisateur commence par créer un replication slot sur le primary
    • On utilise une forme comme pg_create_logical_replication_slot('<your_slot_name>', '<output_plugin>')
  • output_plugin désigne le plugin qui décodera les changements du WAL
    • pgoutput est le plugin par défaut et produit une sortie au format binaire attendu par le serveur client
    • test_decoding est un plugin de sortie simple qui fournit les changements du WAL sous une forme lisible par un humain
    • Bien qu’il ne soit pas intégré à Postgres, wal2json est un plugin populaire ; JSON est plus facile à manipuler comme point de départ applicatif que le format binaire de Postgres
  • Après avoir créé le replication slot, on peut démarrer et consommer
    • Un replication slot utilise une zone du protocole Postgres différente de celle des requêtes standards
    • Plusieurs bibliothèques clientes fournissent des fonctions pour travailler avec les replication slots
    • Dans un exemple avec psycopg2, on consomme les messages WAL avec cursor.start_replication(...) et cursor.consume_stream(...), puis on envoie un ack avec cursor.send_feedback(flush_lsn=msg.wal_end)
  • Le client doit ack les messages WAL reçus, et le replication slot fonctionne un peu comme Kafka avec un offset
  • La réplication logique est une méthode robuste conçue pour le CDC, mais elle est complexe
    • Les replication slots et le replication protocol sont moins familiers aux développeurs que les tables et requêtes classiques
    • Il faut une stratégie pour ne pas perdre de messages pendant les redémarrages
    • Il faut concevoir le système pour absorber le volume important de messages provenant de Postgres

Sequin : un outil CDC qui encapsule la réplication logique

  • Sequin est un outil CDC qui transmet les changements et les lignes Postgres vers des files, streams, index de recherche, caches, endpoints HTTP, etc.
  • Les destinations incluent SQS, Kafka, Elasticsearch, Redis, des HTTP endpoints, etc.
  • En interne, Sequin utilise la réplication logique de Postgres, mais abstrait la complexité du protocole low-level
  • Il peut capturer les insert, update et delete ; pour les update et delete, il capture à la fois les valeurs new et old de la ligne
  • Les conditions dans lesquelles envisager Sequin sont les suivantes
    • Vous avez besoin de CDC en temps réel
    • Vous voulez streamer directement vers une destination comme SQS ou un webhook, sans système intermédiaire
    • Vous avez besoin de fonctionnalités comme le backfill de données historiques et le filtrage des changements basé sur des clauses SQL where
    • Vous cherchez une alternative plus simple que la gestion directe des replication slots
    • Vous avez besoin de garanties de traitement exactly-once
  • Il existe aussi des inconvénients
    • Sequin n’est pas une extension interne de Postgres, mais un outil tiers qui s’exécute à côté de la base de données
    • Comme ce n’est pas une extension, il offre une large compatibilité avec toutes sortes de bases Postgres, mais si vous n’utilisez pas Sequin Cloud, vous devez mettre en place vous-même une infrastructure supplémentaire

Critères de choix

  • Au début, Listen/Notify et le polling de tables conviennent bien
    • Listen/Notify est utile pour capturer des événements non critiques, prototyper et optimiser le polling
    • Le polling est une solution correcte et directe pour des cas d’usage simples
  • À un stade un peu plus sérieux, une table d’audit peut être un choix intermédiaire
    • Elle permet de capturer les payloads new et old des lignes
    • Bien conçue, elle peut fournir un système de traitement exactly-once
    • À l’échelle, l’amplification des écritures et l’absence de backpressure deviennent problématiques, et une configuration manuelle erronée peut faire perdre des messages
  • À l’échelle, la réplication logique est ce qui se rapproche le plus d’une solution robuste
    • Il est toutefois recommandé d’utiliser un outil comme Sequin plutôt que de lire directement depuis un slot
  • FDW est une fonctionnalité intéressante, mais elle a peu de chances de répondre aux besoins CDC courants

1 commentaires

 
GN⁺ 2023-09-24
Avis Hacker News
  • Les triggers + tables d’historique (tables d’audit) sont la bonne réponse dans 98 % des cas. Si vous ne les utilisez pas déjà, vous pouvez commencer dès aujourd’hui. C’est une technique éprouvée depuis plus de 30 ans.
    Un exemple simple d’implémentation générique se trouve ici : https://gist.github.com/slotrans/353952c4f383596e6fe8777db5d.... C’est une approche qui renonce à l’efficacité en espace au profit d’une « implémentation facile ».
    Pouvoir stocker des données immuables serait vraiment idéal, mais votre base contient probablement énormément de données mutables, et il est fort possible que vous oubliiez beaucoup de choses chaque jour. N’oubliez pas : utilisez des tables d’historique.
    Référence : https://github.com/matthiasn/talk-transcripts/blob/master/Hi...
    Mieux vaut éviter les bibliothèques ou techniques de suivi d’historique au niveau applicatif, comme Papertrail. Elles sont lentes, sujettes aux erreurs, et ne capturent pas les modifications de la DB qui contournent la stack applicative. Essayer de renseigner un timestamp updated depuis l’application est aussi fondamentalement erroné, car chaque serveur web a sa propre horloge. Il faut utiliser l’horloge de la DB, et c’est la seule horloge correcte.

    • Pour garantir la cohérence, il faut éviter de générer l’heure côté client et utiliser l’horloge de la DB via un appel comme now() dans la requête.
      Mais synchroniser uniquement sur la base de ce timestamp ne suffit pas. En effet, le timestamp est généré au début de la transaction, et non au moment du commit.
      Si vous pollez une table en filtrant sur les timestamps récents, vous pouvez manquer une partie des transactions dont l’ordre de commit est entremêlé. On peut prévoir une fenêtre tampon en remontant quelques minutes plus loin dans le passé et en dédupliquant, mais dans PostgreSQL la durée des transactions n’est pas bornée, et remonter trop loin devient très coûteux. Si l’exactitude et l’efficacité comptent, cette approche n’est pas la bonne.
    • Estuary (https://estuary.dev, j’en suis le CTO) crée, sans configuration supplémentaire sur la DB de production, un journal de changements de data lake en temps réel de toutes les modifications de la base dans du stockage cloud.
      Avec le numéro de séquence du journal, l’heure de la DB et REPLICA IDENTITY FULL, on obtient même les états avant/après des changements. Ensuite, en matérialisant des collections dans un système comme Snowflake, on obtient par défaut des tables synchronisées qui suivent les mises à jour de la DB source.
      Depuis le même data lake sous-jacent, on peut aussi transformer ou matérialiser l’historique complet des tables à des fins d’audit, sans devoir rattacher une nouvelle capture ou un lecteur WAL à la DB source.
    • En référençant des variables de session dans les triggers, on pouvait ajouter à l’historique des informations supplémentaires, comme un commentaire sur la raison du changement. Je ne l’ai fait que sur un petit projet personnel, mais jusqu’ici cela fonctionne bien.
    • J’ai porté l’exemple vers SQLite et fait une démonstration de son fonctionnement : https://chat.openai.com/share/b5113cb1-10df-4a38-adde-5ec0e7...
      J’ai aussi décrit séparément une approche SQLite qui implémente un motif similaire avec des colonnes plutôt qu’avec du JSON : https://simonwillison.net/2023/Apr/15/sqlite-history/
    • Cette approche est bonne, et c’est d’ailleurs ainsi que je construis le fil d’activité de l’application. En revanche, elle ne résout pas en soi le problème consistant à « pousser les changements vers l’extérieur ». Bien sûr, en écoutant les changements WAL de la table d’audit, on peut obtenir les deux avantages.
  • Cet article résume de façon concise les différentes approches possibles avec les fonctionnalités de base de Postgres.
    Pour la partie « capturer les changements dans une table d’audit », nous avons bien utilisé le motif des Temporal Tables dans mon entreprise précédente. Contrairement à d’autres grands SGBDR, Postgres ne l’intègre pas nativement, mais il existe un motif simple exploitable via des fonctions SQL : https://github.com/nearform/temporal_tables
    Il permet de voir l’état d’une table à un instant donné, et donc de répondre à des questions comme : « quels étaient les paramètres de cet utilisateur le 12 août ? », « combien d’enregistrements en attente y avait-il hier soir à 23 h 55 ? », ou « montrez-moi les différences de feature flags entre aujourd’hui et il y a une semaine ».

  • J’ai déjà fait du conseil dans une entreprise qui avait un très gros SQL Server monolithique. Ce n’était pas Postgres, mais on peut supposer que ce serait similaire avec Postgres.
    Il avait été exploité pendant des dizaines d’années et servait à toutes sortes d’usages dans l’entreprise ; en pratique, presque toutes les applications et tous les processus métier de l’entreprise stockaient leurs données dans cette base.
    Le problème, c’est qu’il y avait beaucoup d’applications qui interrogeaient cette DB, et un nombre énorme de processus et procédures qui inséraient ou modifiaient des données ; quand des processus amont d’insertion ou de modification changeaient, ou que de nouveaux étaient ajoutés, ils pouvaient casser des invariants au niveau applicatif. Même des processus normaux se comportaient différemment en présence de mauvaises données.
    Il était très difficile d’en retrouver la cause, parce que ce qu’on inspectait datait souvent de 10 ans et que les employés qui l’avaient écrit avaient déjà quitté l’entreprise.
    Je me demande s’il serait possible de capturer les changements d’une base Postgres sous forme de DAG, pour savoir quels processus insèrent, modifient et suppriment des données, comment ils se sont comportés historiquement, comment plusieurs applications interrogent ces données et comment les statistiques de requêtes évoluent au fil du temps.
    Je ne sais pas s’il existe des précédents, ni quelle approche permettrait de construire un tel outil. J’avais déjà envisagé de faire quelque chose de similaire, mais j’ai l’impression que c’est un domaine où il faut une compréhension du niveau d’un ingénieur core Postgres pour faire les bons choix.

    • La réplication logique de Postgres contient toutes les instructions de changement nécessaires pour recréer logiquement le même état dans une autre base de données, c’est-à-dire les informations d’insertion, de modification et de suppression.
      On n’obtient pas, pour chaque changement, les données d’origine au niveau client.
      Mais il existe des contournements. Le flux de réplication logique peut aussi inclure les messages d’information de la fonction pg_logical_emit_message, donc le client peut y insérer lui-même des métadonnées. Il serait peut-être possible de configurer l’émission d’un identifiant client au début de chaque transaction.
    • Je ne sais pas comment traiter les requêtes, mais pour les insertions et les mises à jour, on utilise une colonne qui suit la source de l’événement (last updated by). C’est peut-être un antipattern, donc une solution plus robuste serait préférable.
    • Techniquement, la réplication de logs contient toutes les actions effectuées par tous les acteurs, et avec des triggers utilisés prudemment, on peut aussi tout suivre dans une table de capture DDL/DML. Si le DCL vous inquiète, on peut aussi l’inclure.
      Cette approche fonctionne avec presque toutes les solutions de type SQL qui utilisent le WAL ou des triggers.
      J’ai utilisé plusieurs fois l’approche par triggers avec SQL Server, mais journaliser toutes les requêtes a tendance à ralentir le système. Concevoir un mécanisme d’insertion qui ne bloque pas la production n’est pas parfait, et un échantillonnage peut être nécessaire.
    • Le simple fait que chaque application ait son propre utilisateur DB donne déjà pas mal d’informations.
    • Il y avait l’idée de parcourir tous les scripts et programmes qui envoient des requêtes à la DB, et d’ajouter à chaque requête un commentaire avec un ID unique pointant vers le script ou le programme correspondant. Si ce commentaire et cet ID apparaissent dans les logs de requêtes, on devrait pouvoir en retracer l’origine.
  • Si vous partez sur la voie des « tables d’audit », autant utiliser simplement pgaudit. C’est une extension éprouvée en production, et si vous utilisez AWS, elle est aussi disponible sur RDS.
    https://github.com/pgaudit/pgaudit/blob/master/README.md
    https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appen...

  • Ce n’est pas forcément nécessaire. Vouloir cela revient à transformer les relations Postgres en contrats. Aucun service ne peut plus persister son état interne.
    C’est peut-être possible si l’on s’engage vraiment dans le domain-driven design, mais il vaut mieux utiliser un système événementiel léger et pragmatique.

    • Les relations de la base de données sont déjà des contrats, qu’on le veuille ou non.
      Un truc événementiel est 1000 fois plus complexe.
  • Interroger une colonne updated_at par polling n’est pas robuste dans sa forme la plus simple, car rien ne garantit que les transactions soient commit dans cet ordre.

    • C’est l’auteur. Bonne remarque. Par exemple, la transaction A démarre, un trigger before s’exécute et le updated_at de la ligne 1 est défini à 2023-09-22 12:00:01.
      Peu après, la transaction B démarre, le updated_at de la ligne 2 est défini à 2023-09-22 12:00:02, et B est commit en premier.
      La requête de polling s’exécute, voit la ligne 2 comme dernier changement et met à jour le curseur à 2023-09-22 12:00:02 ; si A est commit ensuite, la ligne 1 sera manquée.
      Une façon simple d’éviter ce problème est de ne pas faire de polling quasiment en temps réel. L’ordre finit par devenir cohérent.
      Une proposition plus robuste pourrait être d’utiliser une séquence. Par exemple, avoir une colonne updated_at_idx incrémentée à chaque modification d’une ligne.
    • Je ne savais pas ça. Est-ce aussi le cas si l’on utilise un trigger pour mettre à jour la colonne ?
      Je me demande si, même avec un trigger before qui met now(), les timestamps updated_at de deux lignes peuvent être différents de l’ordre de commit des transactions. updated_at et le timestamp de commit n’ont pas besoin d’être identiques, mais updated_at doit représenter précisément l’ordre des commits à la milliseconde ou à la microseconde près.
    • Pour le polling, on utilise, au lieu de updated_at, une colonne _txid que le trigger définit avec l’ID de transaction courant. Ensuite, lors du polling, on utilise txid_current() pour vérifier quelles transactions ont été commit et lesquelles ne le sont pas encore.
      C’est un peu limite et très facile à rater avec des erreurs de borne, mais ça tourne bien en production depuis plusieurs années.
  • Excellent article.
    Si vous utilisez Elixir et Postgres, j’ai créé une petite bibliothèque qui écoute les changements du WAL avec une approche similaire : https://github.com/cpursley/walex

  • Ces approches sont toutes un peu médiocres, et personnellement je pense que le polling est la plus pratique
    J’aimerais que Postgres innove dans ce domaine

    • Il y a eu des tentatives pour intégrer plusieurs formes de temporalité comme fonctionnalités de première classe dans le standard SQL
      Tant que ce n’est pas dans le standard SQL, je pense qu’il sera difficile de créer une dynamique dans l’espace noyau des SGBD relationnels. Les options sont nombreuses et complexes, et les solutions réussies côté utilisateur ne sont pas non plus excessivement coûteuses en performances
      À noter que les personnes qui travaillent sur ce domaine penchent généralement pour l’approche des tables d’audit. Parce qu’elle conserve des propriétés ACID cohérentes dans la base de données, et maintient Postgres comme point de défaillance unique au lieu d’ajouter un proxy ou des tâches de polling
    • Un intervalle de polling d’une seconde est-il pratique ?
  • Il y a un grand manque dans le monde de la donnée. Au lieu de demander les résultats à un magasin de données, ce serait bien que les résultats de requêtes soient poussés de manière incrémentale
    On fait beaucoup d’analytique temps réel et streaming : on peut faire du traitement de flux, et aussi en traiter une partie dans le magasin de données avec des vues matérialisées. Mais une fois que les données sont entrées dans une DB ou un data lake, pour observer les changements en aval on revient en pratique au polling
    Il existe peu de solutions propres pour réagir lorsqu’une situation se produit dans les données, ou pour mettre à jour l’écran sans rechargement de page. Les solutions de cet article ressemblent davantage à des contournements qu’à des fonctionnalités de première classe
    Si l’on veut créer un rapport qui se met à jour en temps réel sans rechargement de page, on finit généralement par charger les données depuis la DB, puis par faire circuler les changements vers l’interface graphique avec Kafka et WebSocket. On se retrouve alors à exploiter une étrange architecture lambda, où certaines analyses sont traitées dans le code et d’autres dans la DB
    Il y a de l’innovation dans ce domaine. KSQL et Kafka Streams peuvent émettre des changements, Materialize propose des abonnements, et ClickHouse a des vues live. Mais beaucoup de fonctionnalités sont récentes ou en préversion, et ne conviennent pas parfaitement. Je les ai toutes essayées, et j’ai eu l’impression qu’elles reportaient trop de travail sur les développeurs
    J’aimerais qu’il existe une bibliothèque permettant de recevoir directement un flux de changements avec une option du type [select * from orders with suscribe]. C’est un domaine suffisamment important, mais qui a reçu trop peu d’attention

  • Il y a un gros piège de la réplication que l’article n’aborde pas, et c’est pour cela que je ne l’utilise pas
    Postgres cherche à garantir très fortement que les consommateurs des slots de réplication ne manquent pas de données. Donc si un consommateur ne consomme pas les données de son slot, Postgres conserve gentiment les données manquées, jusqu’à ce que le disque finisse par être plein et que la DB tombe. Cela m’est arrivé pendant du prototypage sur deux DB SaaS différentes, et la seule façon de récupérer était d’ouvrir un ticket au support
    Si un consommateur de slot de réplication cesse de lire, une alerte doit impérativement se déclencher
    Une autre raison est que le chemin de code pour récupérer le snapshot initial d’une table et celui pour lire les changements sont complètement différents. Initialiser la lecture d’un slot de réplication sans manquer le moindre changement n’a rien de trivial
    Malheureusement, du point de vue de la capture de changements, la réplication est la solution la moins bricolée
    J’utilise le polling, mais je stocke le txid au lieu de updated_at

    • On peut définir une limite de taille pour qu’un slot soit marqué comme invalide lorsqu’il dépasse une certaine taille, au lieu de continuer à retenir de l’espace : https://www.postgresql.org/docs/current/runtime-config-repli...
      Je serais curieux de savoir quel comportement vous préféreriez
      Si vous traitez de gros volumes de données, vous aurez envie de gérer différemment le snapshot initial et la lecture des changements. Parce qu’il faut pouvoir faire des choses comme une initialisation parallèle ou une initialisation basée sur une sauvegarde physique. Cela dit, je comprends qu’une fonctionnalité permettant de streamer sélectivement les données existantes après la création du slot pourrait être utile
      La partie consistant à initialiser la lecture du slot de réplication sans manquer de changements ne devrait pas être difficile, me semble-t-il ; je serais curieux de savoir où vous avez bloqué
    • Une des astuces pour gérer le premier problème consiste à s’envoyer à soi-même des messages de décodage logique. Cela permet de maintenir la quantité de WAL conservée à un niveau bas
      Quand on n’a pas besoin de tous les changements, les slots de réplication temporaires, qui se nettoient d’eux-mêmes lorsque la connexion est coupée, sont aussi utiles. Il existe aussi une configuration pour fixer un maximum de WAL conservés afin de ne pas tuer le serveur
    • Je suis déjà tombé dans ce piège. C’est vraiment subtil. Quand on supprime le consommateur, on a l’impression que cela ne devrait avoir aucun effet sur la DB principale, mais en réalité cela crée une bombe à retardement
      J’aimerais en savoir plus sur la manière dont vous utilisez txid au lieu de updated_at