Nouveau module app.etl.reading_retention : pour chaque chunk de `reading` entièrement plus vieux que APP_READING_RETENTION_DAYS (1095 jours), export CSV gzip reproductible vers Garage (SSE-C, sha256 en métadonnées), relecture et comparaison, puis drop_chunks ciblé sur ce seul chunk dans une transaction dédiée. --dry-run. Réglages APP_S3_* optionnels, jamais exigés par l'API. DAG Airflow `retention` quotidien à 03h20. 44 tests unitaires sans réseau ni base, tests d'intégrité du DAG, vérification --help dans l'image Airflow en CI. Docs 40-data et 20-backend. Closes #36
46 lines
1.8 KiB
Python
46 lines
1.8 KiB
Python
"""DAG de rétention de l'hypertable `reading` : export vers Garage puis suppression (issue #36).
|
|
|
|
La nuit, parce que `drop_chunks` pose un verrou exclusif sur `reading`, `site` et `dataset`
|
|
jusqu'au COMMIT : un chunk est supprimé dans une transaction courte, mais hors des heures où
|
|
l'API et les DAGs horaires écrivent. À :20 pour se glisser entre `ml_score` (à l'heure pile),
|
|
`alertes` (à :15) et `mock_api_import` (à :45), bien avant `derive` (05:30). Une reprise est sans
|
|
risque : le module est idempotent, un objet déjà exporté avec le même sha256 n'est pas réécrit et
|
|
un chunk déjà supprimé n'est plus éligible. La borne vient de `APP_READING_RETENTION_DAYS`
|
|
(1095 jours), posée par le compose sur `airflow-scheduler` avec les réglages `APP_S3_*`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from airflow.providers.standard.operators.bash import BashOperator
|
|
from airflow.sdk import DAG
|
|
|
|
# Le backend a son propre environnement uv dans l'image (ADR 0008). `--no-sync` et
|
|
# `env -u VIRTUAL_ENV` : cf. `ml_train.py`, même raisonnement.
|
|
COMMANDE_BACKEND = "cd /opt/backend && env -u VIRTUAL_ENV uv run --no-sync python -m"
|
|
|
|
TENTATIVES = 1
|
|
DELAI_ENTRE_TENTATIVES = timedelta(minutes=5)
|
|
PLAFOND = timedelta(minutes=20)
|
|
|
|
with DAG(
|
|
dag_id="retention",
|
|
description=(
|
|
"Exporte vers Garage puis supprime les chunks de reading plus vieux que la borne de "
|
|
"rétention (app.etl.reading_retention)."
|
|
),
|
|
schedule="20 3 * * *",
|
|
start_date=datetime(2026, 1, 1),
|
|
catchup=False,
|
|
max_active_runs=1,
|
|
tags=["etl", "retention"],
|
|
) as dag:
|
|
BashOperator(
|
|
task_id="archiver",
|
|
bash_command=f"{COMMANDE_BACKEND} app.etl.reading_retention",
|
|
retries=TENTATIVES,
|
|
retry_delay=DELAI_ENTRE_TENTATIVES,
|
|
execution_timeout=PLAFOND,
|
|
)
|