From f4d05a8ca9c473923ecdcf3e8a6b8d471d8093a8 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Mon, 14 Sep 2026 14:19:25 +0200 Subject: [PATCH] docs: ADR du choix PostgreSQL TimescaleDB Acte le choix de l'extension plutot qu'un second SGBD, celui de l'image -ha et celui de PG17. Fixe surtout la frontiere db/init contre db/migrations contre apps/backend/alembic, qui n'est deductible d'aucun fichier. --- README.md | 26 ++++++++-- docs/adr/.gitkeep | 0 docs/adr/0001-postgresql-timescaledb.md | 66 +++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) delete mode 100644 docs/adr/.gitkeep create mode 100644 docs/adr/0001-postgresql-timescaledb.md diff --git a/README.md b/README.md index b6ab9c8..12342ac 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,14 @@ series temporelles energetiques, deployee sur une machine on-premise. |------------|-------------------------------------|---------------------|---------------| | Backend | FastAPI, Python 3.14 | `apps/backend` | Initialise | | Frontend | Angular, Node 24 LTS | `apps/frontend` | A initialiser | -| Base | PostgreSQL + TimescaleDB | `db` | A initialiser | +| Base | PostgreSQL 17 + TimescaleDB | `db` | Initialise | | ETL | Apache Airflow | `etl/airflow` | A initialiser | | Infra | Terraform | `infra/terraform` | A initialiser | | CI/CD | GitHub Actions | `.github/workflows` | A initialiser | | Monitoring | Prometheus, Grafana, Alertmanager | `monitoring` | A initialiser | -Seul le backend est initialise a ce stade. Les autres dossiers portent l'arborescence et -un README de cadrage, leur contenu fait l'objet d'un ticket dedie. +Le backend et la base sont initialises a ce stade. Les autres dossiers portent +l'arborescence et un README de cadrage, leur contenu fait l'objet d'un ticket dedie. ## Arborescence @@ -50,13 +50,33 @@ un README de cadrage, leur contenu fait l'objet d'un ticket dedie. Prerequis : uv, Docker. Le poste doit disposer de Python 3.14, que `uv` installe seul. ```bash +cp .env.example .env # variables de docker-compose +cp apps/backend/.env.example apps/backend/.env # variables du backend hors conteneur + +make db-up # PostgreSQL + TimescaleDB, publie sur le port 5433 make install # dependances du backend +make migrate # applique les migrations Alembic make dev # API sur http://localhost:8000, docs sur /docs make check # lint + typage + tests ``` `make help` liste les cibles disponibles. +Deux fichiers d'environnement, deux usages : `.env` a la racine alimente `docker-compose.yml`, +`apps/backend/.env` alimente le backend lance sur le poste. Le port 5433 est publie plutot que +5432, souvent deja pris par une autre base. + +La boucle de developpement est `make db-up` puis `make dev` : seule la base tourne en +conteneur. Le service `backend` du `docker-compose.yml` sert la stack complete et la recette, +et n'embarque pas le source, donc toute modification y demande un +`docker compose up -d --build backend`. + +Verifier que la base repond et que l'extension est chargee : + +```bash +curl -s localhost:8000/api/v1/health/ready +``` + ## Conventions - Branches : `feat/`, `fix/`, `chore/`, `docs/` suivi d'un libelle court. diff --git a/docs/adr/.gitkeep b/docs/adr/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/adr/0001-postgresql-timescaledb.md b/docs/adr/0001-postgresql-timescaledb.md new file mode 100644 index 0000000..4ed562b --- /dev/null +++ b/docs/adr/0001-postgresql-timescaledb.md @@ -0,0 +1,66 @@ +# 0001 - PostgreSQL avec l'extension TimescaleDB + +- Statut : accepte +- Date : 2026-09-14 + +## Contexte + +EnerVision collecte, stocke et restitue des series temporelles energetiques sur une machine +on-premise. La charge est dominee par des insertions horodatees en flux et par des lectures +agregees sur des fenetres de temps. Airflow produira des agregations continues, Grafana lira +les memes donnees, et l'API FastAPI les exposera. + +Un SGBD relationnel generaliste sait faire, mais degrade a mesure que la table de mesures +grossit : les index se fragmentent, les balayages de fenetre deviennent couteux, et il faut +ecrire a la main le partitionnement, la retention et les agregats pre-calcules. + +## Decision + +PostgreSQL 17 avec l'extension TimescaleDB, servie en local par l'image +`timescale/timescaledb-ha:pg17`. + +PostgreSQL reste une base relationnelle standard : un seul SGBD pour les donnees metier et +les mesures, un seul dialecte SQL, un seul pilote (`asyncpg`), et l'outillage habituel. +TimescaleDB ajoute le partitionnement automatique, les agregations continues et les +politiques de retention sans changer de moteur. + +L'image `-ha` plutot que l'image alpine : elle embarque `timescaledb_toolkit`, `postgis` et +`pgvector`. Le toolkit porte les fonctions de comblement de trous et d'analyse de series dont +l'ETL aura besoin, et changer d'image plus tard imposerait une reinitialisation du volume. + +PG17 plutot que PG18 : c'est la version la mieux couverte par Airflow et Grafana a ce jour. + +## Frontiere entre `db/` et `apps/backend/alembic/` + +C'est la regle que ce document existe surtout pour fixer. + +- `db/init/` : bootstrap joue **une seule fois**, a la premiere initialisation du conteneur. + Extensions, bases annexes. Ne rejoue jamais sur un volume existant. +- `db/migrations/` : SQL versionne qui ne decoule pas du schema applicatif, typiquement les + politiques de retention et de compression TimescaleDB. +- `apps/backend/alembic/` : le schema expose par l'API, et lui seul. C'est `Base.metadata` + qui fait foi. + +Une hypertable relevera des deux : Alembic cree la table, et le `create_hypertable()` vit +dans la meme revision Alembic, parce que separer les deux rendrait le schema irreproductible +depuis un seul `alembic upgrade head`. + +## Consequences + +- Le projet se lie a une extension, donc a un hebergement qui l'autorise. C'est acquis + puisque le deploiement est on-premise. +- `CREATE EXTENSION` demande le superutilisateur : cela reste un acte de bootstrap, pas une + migration applicative. +- Un bootstrap saute ne se voit pas au demarrage de l'API. Deux gardes couvrent ce cas : + `/api/v1/health/ready` repond 503 si l'extension est absente, et la premiere revision + Alembic refuse de s'appliquer. +- L'image `-ha` pese environ 1 Go, a telecharger une fois par poste. + +## Alternatives ecartees + +- **PostgreSQL nu, partitionnement manuel** : faisable, mais il faudrait reecrire ce que + TimescaleDB fournit, et le maintenir. +- **InfluxDB** : tres bon sur la serie temporelle, mais imposerait un second SGBD pour le + relationnel, donc deux dialectes, deux sauvegardes et des jointures applicatives. +- **ClickHouse** : taille pour un volume analytique que le projet n'atteindra pas, et moins + a l'aise sur les ecritures unitaires frequentes du flux d'ingestion.