docs: spec schéma en cache seul (#78)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -70,28 +70,43 @@ touche l'API. On ne paie pas cette complexité tant qu'on n'en a pas la preuve.
|
|||||||
|
|
||||||
## Décisions
|
## Décisions
|
||||||
|
|
||||||
1. **Schéma** : URL primaire, fallback local. (confirmé)
|
1. **Schéma** : URL primaire, **cache seul** en fallback (on supprime
|
||||||
|
`DATA_SCHEMA_LOCAL`). (confirmé)
|
||||||
2. **DuckDB** : réutiliser le dernier DuckDB construit en cas d'échec. (confirmé)
|
2. **DuckDB** : réutiliser le dernier DuckDB construit en cas d'échec. (confirmé)
|
||||||
3. **Last-known-good réel du schéma** : après un fetch distant réussi, persister
|
3. **Last-known-good réel du schéma** : après un fetch distant réussi, persister
|
||||||
le schéma localement pour que le fallback soit toujours le _dernier schéma
|
le schéma dans un cache local pour que le fallback soit toujours le _dernier
|
||||||
distant fonctionnel_. (confirmé)
|
schéma distant fonctionnel_. (confirmé)
|
||||||
|
|
||||||
### Nuance sur le chemin de persistance du schéma
|
### Chemin de persistance du schéma : `DATA_SCHEMA_CACHE` seul
|
||||||
|
|
||||||
L'utilisateur a demandé « écrire dans `DATA_SCHEMA_LOCAL` ». Mais en dev,
|
On remplace `DATA_SCHEMA_LOCAL` (qui pointait, en dev, vers
|
||||||
`DATA_SCHEMA_LOCAL = ../decp-processing/dist/schema.json` — un fichier d'un **autre
|
`../decp-processing/dist/schema.json` — un fichier cross-repo qu'on ne veut pas
|
||||||
repo**. L'écraser au boot salirait ce repo.
|
écraser) par un **cache unique possédé par l'app**.
|
||||||
|
|
||||||
**Choix retenu (à confirmer en relecture) :** introduire un **chemin de cache
|
|
||||||
dédié que l'app possède**, distinct du fichier statique de secours.
|
|
||||||
|
|
||||||
- `DATA_SCHEMA_PATH` (URL) — source primaire.
|
- `DATA_SCHEMA_PATH` (URL) — source primaire.
|
||||||
- `DATA_SCHEMA_CACHE` (nouveau, ex. défaut `./schema.cache.json`) — écrit après
|
- `DATA_SCHEMA_CACHE` (nouveau, ex. défaut `./schema.cache.json`) — écrit après
|
||||||
chaque fetch distant réussi ; lu en fallback n°1 (dernier distant fonctionnel).
|
chaque fetch distant réussi, lu en fallback.
|
||||||
- `DATA_SCHEMA_LOCAL` — graine statique de secours (fichier `decp-processing`),
|
|
||||||
**lue mais jamais écrite**.
|
|
||||||
|
|
||||||
Chaîne de résolution : `URL → cache → local statique → RuntimeError`.
|
Chaîne de résolution : `URL → cache → RuntimeError`.
|
||||||
|
|
||||||
|
**Pourquoi c'est suffisant.** Le déploiement est en place sur un VM persistant
|
||||||
|
(`ssh → cd /var/www/APP_NAME → git pull → restart systemd`), donc le fichier de
|
||||||
|
cache survit aux déploiements — **même garantie de persistance que le DuckDB
|
||||||
|
réutilisé**. Tous les incidents constatés (env oubliée, parquet KO, URL schéma en
|
||||||
|
erreur) surviennent sur un **redéploiement** d'un hôte déjà chaud, où le cache a
|
||||||
|
déjà été écrit par un boot précédent réussi ⇒ couvert.
|
||||||
|
|
||||||
|
**Seul cas non couvert (assumé) :** le _cold start absolu_ — un hôte qui n'a jamais
|
||||||
|
booté avec succès **et** URL distante down au même instant. Étroit, non-récurrent.
|
||||||
|
Fermable plus tard par une graine commitée in-repo si jamais il se matérialise
|
||||||
|
(YAGNI).
|
||||||
|
|
||||||
|
**Contraintes :**
|
||||||
|
|
||||||
|
- `DATA_SCHEMA_CACHE` (`./schema.cache.json`) doit être **`.gitignore`** — sinon le
|
||||||
|
`git pull` du déploiement entrerait en conflit. (Comme `decp.duckdb` aujourd'hui.)
|
||||||
|
- En dev, plus de fallback vers le schéma frais de `decp-processing` : on bascule
|
||||||
|
sur le cache (dernier schéma data.gouv). Acceptable, l'URL restant primaire.
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
@@ -135,20 +150,19 @@ def _ensure_database() -> Path:
|
|||||||
|
|
||||||
### Invariant 2 — Schéma (`src/utils/data.py`)
|
### Invariant 2 — Schéma (`src/utils/data.py`)
|
||||||
|
|
||||||
> Un schéma valide non-vide est toujours retourné si une source (distant, cache,
|
> Un schéma valide non-vide est toujours retourné si une source (distant ou cache)
|
||||||
> ou local statique) en fournit un. Échec dur seulement si aucune.
|
> en fournit un. Échec dur seulement si aucune.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_data_schema() -> dict:
|
def get_data_schema() -> dict:
|
||||||
|
cache_path = os.getenv("DATA_SCHEMA_CACHE", "./schema.cache.json")
|
||||||
raw = _fetch_remote_schema(os.getenv("DATA_SCHEMA_PATH")) # dict valide | None
|
raw = _fetch_remote_schema(os.getenv("DATA_SCHEMA_PATH")) # dict valide | None
|
||||||
if raw is not None:
|
if raw is not None:
|
||||||
_persist_schema_cache(raw, os.getenv("DATA_SCHEMA_CACHE", "./schema.cache.json"))
|
_persist_schema_cache(raw, cache_path)
|
||||||
else:
|
else:
|
||||||
raw = _load_schema_file(os.getenv("DATA_SCHEMA_CACHE", "./schema.cache.json"))
|
raw = _load_schema_file(cache_path)
|
||||||
if raw is None:
|
if raw is None:
|
||||||
raw = _load_schema_file(os.getenv("DATA_SCHEMA_LOCAL", ""))
|
raise RuntimeError("Aucun schéma disponible (ni distant ni cache).")
|
||||||
if raw is None:
|
|
||||||
raise RuntimeError("Aucun schéma disponible (distant, cache ni local).")
|
|
||||||
return OrderedDict((c["name"], c) for c in raw["fields"])
|
return OrderedDict((c["name"], c) for c in raw["fields"])
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -173,11 +187,15 @@ Couvrir chaque branche de fallback. Sans dépendre du réseau réel.
|
|||||||
2. URL renvoie une erreur HTTP (mock 500) ⇒ fallback cache.
|
2. URL renvoie une erreur HTTP (mock 500) ⇒ fallback cache.
|
||||||
3. URL renvoie un JSON malformé (sans `"fields"`) ⇒ fallback cache.
|
3. URL renvoie un JSON malformé (sans `"fields"`) ⇒ fallback cache.
|
||||||
4. URL KO + cache présent ⇒ schéma du cache.
|
4. URL KO + cache présent ⇒ schéma du cache.
|
||||||
5. URL KO + cache absent + local statique présent ⇒ schéma local.
|
5. URL KO + cache absent ⇒ `RuntimeError` claire.
|
||||||
6. Toutes sources KO ⇒ `RuntimeError` claire.
|
6. Échec d'écriture du cache ⇒ schéma quand même retourné (non bloquant).
|
||||||
7. Échec d'écriture du cache ⇒ schéma quand même retourné (non bloquant).
|
|
||||||
|
|
||||||
**Bootstrap DuckDB (`_ensure_database`) :** 8. `should_rebuild` lève + DuckDB existant ⇒ réutilisé, pas d'exception. 9. `build_database` lève + DuckDB existant ⇒ réutilisé, pas d'exception. 10. Échec + **aucun** DuckDB (cold start) ⇒ ré-lève. 11. Cas nominal : rebuild nécessaire et possible ⇒ build effectué.
|
**Bootstrap DuckDB (`_ensure_database`) :**
|
||||||
|
|
||||||
|
7. `should_rebuild` lève + DuckDB existant ⇒ réutilisé, pas d'exception.
|
||||||
|
8. `build_database` lève + DuckDB existant ⇒ réutilisé, pas d'exception.
|
||||||
|
9. Échec + **aucun** DuckDB (cold start) ⇒ ré-lève.
|
||||||
|
10. Cas nominal : rebuild nécessaire et possible ⇒ build effectué.
|
||||||
|
|
||||||
Mocker `get_last_modified` / `build_database` / `httpx.get` ; utiliser des fichiers
|
Mocker `get_last_modified` / `build_database` / `httpx.get` ; utiliser des fichiers
|
||||||
DuckDB et schéma temporaires (`tmp_path`).
|
DuckDB et schéma temporaires (`tmp_path`).
|
||||||
@@ -190,12 +208,16 @@ DuckDB et schéma temporaires (`tmp_path`).
|
|||||||
|
|
||||||
## Variables d'environnement
|
## Variables d'environnement
|
||||||
|
|
||||||
| Variable | Rôle | Changement |
|
| Variable | Rôle | Changement |
|
||||||
| ------------------------ | --------------------------------------- | -------------------- |
|
| ------------------------ | --------------------------------------- | ------------ |
|
||||||
| `DATA_FILE_PARQUET_PATH` | Source parquet (URL ou chemin) | inchangé |
|
| `DATA_FILE_PARQUET_PATH` | Source parquet (URL ou chemin) | inchangé |
|
||||||
| `DATA_SCHEMA_PATH` | URL schéma (primaire) | inchangé |
|
| `DATA_SCHEMA_PATH` | URL schéma (primaire) | inchangé |
|
||||||
| `DATA_SCHEMA_LOCAL` | Fichier schéma de secours statique | **lu, jamais écrit** |
|
| `DATA_SCHEMA_LOCAL` | Ancien fichier de secours statique | **supprimé** |
|
||||||
| `DATA_SCHEMA_CACHE` | Cache last-known-good du schéma distant | **nouveau** |
|
| `DATA_SCHEMA_CACHE` | Cache last-known-good du schéma distant | **nouveau** |
|
||||||
| `DUCKDB_PATH` | Fichier DuckDB | inchangé |
|
| `DUCKDB_PATH` | Fichier DuckDB | inchangé |
|
||||||
|
|
||||||
Mettre à jour `.template.env` avec `DATA_SCHEMA_CACHE`.
|
À faire côté config :
|
||||||
|
|
||||||
|
- Ajouter `DATA_SCHEMA_CACHE` à `.template.env`, retirer `DATA_SCHEMA_LOCAL` de
|
||||||
|
`.template.env` / `.env`.
|
||||||
|
- Ajouter `schema.cache.json` (ou la valeur de `DATA_SCHEMA_CACHE`) au `.gitignore`.
|
||||||
|
|||||||
Reference in New Issue
Block a user