Couche de chargement : upload des payloads bruts vers Google Cloud Storage puis déclenchement de jobs de chargement BigQuery.
Upload d'un payload JSON vers un bucket GCS selon une convention de nommage stable.
gs://<bucket>/<table>/<YYYY-MM-DD>/<HH-MM-SS>.json
Le payload est encapsulé dans une enveloppe structurée avant upload :
{
"table_name": "station_status",
"ingested_at": "2026-04-17T09:25:00Z",
"record_count": 1410,
"size_bytes": 482315,
"payload": { ... JSON natif de la source ... }
}Cette enveloppe :
- Est directement compatible avec le schéma BigQuery cible (même colonnes, même types).
- Permet de distinguer les payloads côté data lake sans ouvrir leur contenu.
- Rend le fichier GCS auto-documenté (table d'origine, timestamp, taille).
Déclenche un LoadJob BigQuery par fichier GCS fraîchement uploadé.
Configuration :
source_format = NEWLINE_DELIMITED_JSONschemaexplicite (pas d'autodetect) — garantit la stabilité du schéma malgré les variations éventuelles du payload sourcecreate_disposition = CREATE_IF_NEEDED— bootstrap automatique au premier runwrite_disposition = WRITE_APPEND— append pur, aucune mutationtime_partitioning = DAYsuringested_at— partitionnement au jour pour les requêtes et la rétention
Les trois tables raw_velib.station_information / station_status / station_details partagent le même schéma :
| Colonne | Type | Description |
|---|---|---|
table_name |
STRING | Nom logique de la source (redondant, utile en cas d'union). |
ingested_at |
TIMESTAMP | Horodatage UTC du run d'extraction. Clé de partition. |
record_count |
INT64 | Nombre d'éléments dans le payload (dérivé côté extract). |
size_bytes |
INT64 | Taille du payload brut en octets. |
payload |
JSON | Payload intégral de la source, typé JSON natif BigQuery. |
Dissociation storage / compute. Le chargement BigQuery n'accède pas à la source : il lit depuis GCS. L'extraction et le chargement sont deux étapes distinctes qui peuvent être relancées indépendamment.
Idempotence par horodatage. La combinaison (table_name, ingested_at) identifie de manière unique un payload. Un rejeu crée une nouvelle ligne avec un nouveau timestamp — aucun conflit, aucun écrasement silencieux.
Partitionnement natif. Le partitionnement sur ingested_at permet de borner les requêtes aval (staging dbt filtre ingested_at > max(...)) et d'appliquer une politique de rétention via partition_expiration_days sans requalifier la donnée.
Payload en JSON natif. Le type JSON de BigQuery permet de requêter le payload via JSON_VALUE et JSON_QUERY_ARRAY sans reparsing côté client. La couche staging dbt exploite ces fonctions pour extraire les champs utiles.
Schéma explicite. L'injection explicite du schéma au LoadJob élimine la dépendance à l'autodetect BigQuery : une variation de structure côté source ne provoque pas un schéma erroné côté raw.
Couverts dans tests/test_gcs.py et tests/test_bigquery.py avec mocking des clients Google Cloud.