Database Engineering

Traçabilité immuable avec JSONB Postgres et réplication logique

Dans les architectures modernes basées sur les événements, l'intégrité et la traçabilité des données sont primordiales. Les bases de données relationnelles traditionnelles peinent souvent à gérer le débit d'écriture élevé et la flexibilité du schéma requis par l'event sourcing. Heureusement, PostgreSQL offre une combinaison puissante de fonctionnalités, notamment le type de données JSONB et le décodage logique, permettant aux ingénieurs de créer des journaux d'audit immuables et en append-only avec des performances et une flexibilité exceptionnelles.

Cet article explore comment exploiter ces outils pour créer un système d'audit robuste qui capture chaque changement d'état sans verrouiller la base de données ni sacrifier les performances.

Le problème fondamental : l'audit dans l'event sourcing

L'event sourcing stipule que l'état d'une application est dérivé d'une séquence d'événements. Pour répondre aux exigences de conformité et de débogage, nous devons garantir l'immuabilité de ces événements. Une fois écrits, un événement ne peut ni être modifié ni supprimé. Ce modèle « append-only » est idéal pour les colonnes JSONB, qui peuvent stocker efficacement des données de charge utile complexes et non structurées au sein d'un cadre relationnel.

Cependant, stocker simplement du JSON dans une table ne suffit pas. Nous avons besoin d'un mécanisme pour capturer ces écritures de manière fiable, potentiellement pour des consommateurs en aval comme des entrepôts de données ou des tableaux de bord en temps réel, sans impacter la latence de la transaction principale.

Conception du schéma pour des événements immuables

Définissons d'abord un schéma qui impose l'immuabilité. Nous utilisons une contrainte d'unicité sur l'ID de l'événement et l'horodatage pour empêcher les doublons, et nous omettons les déclencheurs de mise à jour pour garantir que l'enregistrement est véritablement en append-only.

CREATE TABLE event_store (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    aggregate_id UUID NOT NULL,
    event_type VARCHAR(50) NOT NULL,
    payload JSONB NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Optionnel : Index pour les requêtes par historique d'agrégat
CREATE INDEX idx_event_store_aggregate 
ON event_store (aggregate_id, created_at);

En faisant de payload un type JSONB, nous gagnons la capacité de requêter des champs spécifiques au sein de l'événement sans désérialiser le blob entier, ce qui est crucial pour le débogage ou le filtrage des journaux d'audit.

Exploiter la réplication logique pour le découplage

Si le stockage des événements est la première étape, le partage de ces données avec d'autres systèmes est là où la réplication logique brille. Contrairement à la réplication physique, qui copie l'intégralité de la base de données, la réplication logique vous permet de vous abonner à des tables spécifiques et de diffuser les changements dans un format lisible (souvent JSON ou protocol buffers).

Pour activer cela, vous devez configurer votre postgresql.conf :

wal_level = logical
max_replication_slots = 10
max_wal_senders = 10

Une fois activé, vous pouvez créer une publication qui diffuse les événements vers des consommateurs en aval :

PUBLICATION event_pub FOR TABLE event_store;

Avantages de cette approche

  • Performance : Les consommateurs en aval lisent depuis le WAL (Write-Ahead Log) de manière asynchrone. Cela ajoute une surcharge minimale au chemin d'écriture principal.
  • Tolérance aux pannes : Si le consommateur tombe en panne, il peut rejouer le WAL depuis la dernière position consommée, garantissant qu'aucun événement n'est perdu.
  • Découplage : Vous pouvez avoir plusieurs consommateurs (par exemple, un indexeur Elasticsearch, un producteur Kafka, un chargeur d'entrepôt de données) sans qu'ils n'impactent les autres.

Mise en œuvre pratique : lecture du WAL

Les développeurs peuvent utiliser des bibliothèques comme pgoutput en Java/Go ou psycopg2 avec des plugins de décodage logique en Python pour consommer ces flux. Les données arrivent dans un format qui mape directement votre schéma, rendant la désérialisation triviale.

Par exemple, un consommateur pourrait recevoir un message comme celui-ci :

{
  "operation": "INSERT",
  "table": "event_store",
  "tuple": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "aggregate_id": "b2c1e9f0-...",
    "event_type": "OrderPlaced",
    "payload": {"item": "Widget", "qty": 1},
    "created_at": "2023-10-27T10:00:00Z"
  }
}

Conclusion

Combiner la flexibilité de JSONB de PostgreSQL avec la réplication logique fournit une base éprouvée pour des journaux d'audit immuables dans les systèmes basés sur les événements. Cela vous permet de maintenir une forte cohérence dans vos données principales tout en diffusant facilement les changements vers des systèmes secondaires. Cette architecture est évolutive, résiliente et exploite l'écosystème mature de PostgreSQL pour gérer les complexités de l'ingénierie des données modernes.

Commencez petit en implémentant le schéma, puis intégrez un slot de réplication logique pour alimenter votre premier consommateur. Le résultat est un système à la fois facile à maintenir et suffisamment robuste pour répondre aux exigences d'audit à l'échelle de la production.

Share: