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:
@@ -9,14 +9,14 @@ series temporelles energetiques, deployee sur une machine on-premise.
|
|||||||
|------------|-------------------------------------|---------------------|---------------|
|
|------------|-------------------------------------|---------------------|---------------|
|
||||||
| Backend | FastAPI, Python 3.14 | `apps/backend` | Initialise |
|
| Backend | FastAPI, Python 3.14 | `apps/backend` | Initialise |
|
||||||
| Frontend | Angular, Node 24 LTS | `apps/frontend` | A initialiser |
|
| 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 |
|
| ETL | Apache Airflow | `etl/airflow` | A initialiser |
|
||||||
| Infra | Terraform | `infra/terraform` | A initialiser |
|
| Infra | Terraform | `infra/terraform` | A initialiser |
|
||||||
| CI/CD | GitHub Actions | `.github/workflows` | A initialiser |
|
| CI/CD | GitHub Actions | `.github/workflows` | A initialiser |
|
||||||
| Monitoring | Prometheus, Grafana, Alertmanager | `monitoring` | A initialiser |
|
| Monitoring | Prometheus, Grafana, Alertmanager | `monitoring` | A initialiser |
|
||||||
|
|
||||||
Seul le backend est initialise a ce stade. Les autres dossiers portent l'arborescence et
|
Le backend et la base sont initialises a ce stade. Les autres dossiers portent
|
||||||
un README de cadrage, leur contenu fait l'objet d'un ticket dedie.
|
l'arborescence et un README de cadrage, leur contenu fait l'objet d'un ticket dedie.
|
||||||
|
|
||||||
## Arborescence
|
## 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.
|
Prerequis : uv, Docker. Le poste doit disposer de Python 3.14, que `uv` installe seul.
|
||||||
|
|
||||||
```bash
|
```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 install # dependances du backend
|
||||||
|
make migrate # applique les migrations Alembic
|
||||||
make dev # API sur http://localhost:8000, docs sur /docs
|
make dev # API sur http://localhost:8000, docs sur /docs
|
||||||
make check # lint + typage + tests
|
make check # lint + typage + tests
|
||||||
```
|
```
|
||||||
|
|
||||||
`make help` liste les cibles disponibles.
|
`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
|
## Conventions
|
||||||
|
|
||||||
- Branches : `feat/`, `fix/`, `chore/`, `docs/` suivi d'un libelle court.
|
- Branches : `feat/`, `fix/`, `chore/`, `docs/` suivi d'un libelle court.
|
||||||
|
|||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user