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.
This commit is contained in:
Johan LEROY
2026-09-14 14:19:25 +02:00
parent 20e7374d90
commit f4d05a8ca9
3 changed files with 89 additions and 3 deletions
+23 -3
View File
@@ -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.
View File
+66
View File
@@ -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.