Aller au contenu principal

PostgreSQL central

Version anglaise : en/postgresql.md.

Connexion​

  • EAI_DATABASE_URL : postgresql://utilisateur@hôte:5432/eai?sslmode=verify-full&sslrootcert=/etc/eai/pg-ca.pem. En prod, le démarrage refuse une URL sans sslmode=require, verify-ca ou verify-full.
  • Pool psycopg_pool (EAI_DB_POOL_MIN, EAI_DB_POOL_MAX), chaque connexion est testée avant usage (remplacée après redémarrage ou bascule), application_name=eai-control-plane dans pg_stat_activity.
  • Isolation : chaque transaction locataire bascule sur le rôle eai_app (RLS forcée, FORCE ROW LEVEL SECURITY).
  • Identifiant runtime séparé (ADR-0156) : EAI_RUNTIME_DATABASE_URL est le login de l'API et du worker, membre du rôle eai_runtime (V165 : membre de eai_app, aucun droit CREATE, propriétaire de rien), créé par l'exploitant : CREATE ROLE eai_api LOGIN PASSWORD '...' IN ROLE eai_runtime;. Les migrations gardent EAI_DATABASE_URL (identifiant d'administration, propriétaire du schéma). Vide : l'API utilise EAI_DATABASE_URL. En prod, la même exigence TLS s'applique aux deux URL.

Migrations​

  • python -m eai.migrate applique sql/postgres/V###__nom.sql sous verrou consultatif ; --status liste applied ou pending. Une migration appliquée puis modifiée est refusée (checksum).

Haute disponibilité​

  • CloudNativePG (deploy/data/postgres/cluster.yaml) : 3 instances réparties par zone, bascule automatique, archivage WAL chiffré vers S3 et sauvegarde de base quotidienne ; PITR par un nouveau Cluster en bootstrap.recovery.

Sauvegarde et restauration logiques (commandes vérifiées)​

La connexion passe uniquement par l'environnement libpq (PGHOST, PGPORT, PGUSER, PGDATABASE, PGSSLMODE, PGPASSFILE en mode 0600) : jamais de mot de passe ni d'URL en argument, les scripts le refusent.

cd services/control-plane
export PGHOST=pg.interne PGUSER=eai_backup PGSSLMODE=verify-full PGPASSFILE=/run/secrets/pgpass
PGDATABASE=eai scripts/pg_backup.sh /backup/eai-$(date +%F).dump # pg_dump --format=custom, fichier 0600, vérifié par pg_restore --list
createdb eai_drill
PGDATABASE=eai_drill EAI_PYTHON=.venv/bin/python \
EAI_DATABASE_URL='postgresql://eai_backup@pg.interne/eai_drill?sslmode=verify-full' \
scripts/pg_restore.sh /backup/eai-2026-10-30.dump # une seule transaction, échec si migration en attente

Les rôles (eai_app, propriétaire) sont globaux au cluster et doivent exister sur la cible. Le test tests/test_core_pg_backup.py exécute ces deux scripts sur un vrai PostgreSQL 16 : locataire et données retrouvés, aucune migration en attente, mêmes tables en RLS forcée et mêmes politiques, eai_app ne voit rien sans portée locataire.