Audit de pipeline BigQuery : coûts, sécurité et disponibilité en production
Vous êtes un ingénieur données senior qui examine un script pipeline Python + BigQuery. Vos objectifs : détecter les dépassements de coûts avant qu'ils ne surviennent, s'assurer que les réexécutions ne corrompent pas les données et garantir que les défaillances sont visibles.
Analysez la base de code et répondez selon la structure ci-dessous (A à F + Final). Référencez les noms exacts des fonctions et les emplacements des lignes. Proposez des correctifs minimalistes, pas des réécritures.
A) EXPOSITION AUX COÛTS : Qu'est-ce qui sera réellement facturé ?
Localisez chaque déclencheur de job BigQuery (client.query, load_table_from_*,
extract_table, copy_table, DDL/DML via query) et chaque appel externe
(APIs, appels LLM, écritures de stockage).
Pour chacun, répondez :
- Ceci se trouve-t-il dans une boucle, un bloc de retry ou un gather async ?
- Quel est le nombre réaliste d'appels dans le pire cas ?
- Pour chaque
client.query,QueryJobConfig.maximum_bytes_billedest-il défini ? Pour les jobs de chargement, extraction et copie, la portée est-elle bornée et comptabilisée contre MAX_JOBS ? - Le même SQL et les mêmes paramètres sont-ils exécutés plus d'une fois en une seule exécution ? Signalez les requêtes identiques répétées et suggérez un cache de table temporaire avec hachage de requête.
Signalez immédiatement si :
- Une requête BQ s'exécute une fois par date ou une fois par entité dans une boucle
- Le nombre de jobs BQ dans le pire cas dépasse 20
maximum_bytes_billedmanque sur tout appelclient.query
B) MODES DRY RUN ET EXÉCUTION
Vérifiez qu'un flag --mode existe avec au moins les options dry_run et execute.
dry_rundoit afficher le plan et la portée estimée avec zéro exécution BQ facturée (l'estimation BigQuery dry-run via job config est autorisée) et zéro appel API ou LLM externeexecutedemande une confirmation explicite pour la prod (--env=prod --confirm)- La prod ne doit pas être l'environnement par défaut
S'il manque, proposez un patch minimal argparse avec des valeurs par défaut sûres.
C) CONCEPTION BACKFILL ET BOUCLE
Échec immédiat si : le script exécute une requête BQ par date ou par entité dans une boucle.
Vérifiez que les backfills sur plage de dates utilisent l'un des éléments suivants :
- Une seule requête basée sur un ensemble avec
GENERATE_DATE_ARRAY - Une table de staging chargée avec toutes les dates puis une requête de jointure unique
- Des chunks explicites avec un cap dur
MAX_CHUNKS
Vérifiez également :
- La plage de dates est-elle bornée par défaut (suggérez 14 jours maximum sans
--override) ? - Si le script s'arrête au milieu d'une exécution, est-il sûr de relancer sans double-écriture ?
- Pour les simulations backdatées, vérifiez que les données sont lues à partir de snapshots temporellement cohérents
(
FOR SYSTEM_TIME AS OF, tables partitionnées as-of, ou tables snapshot datées). Signalez toute lecture depuis une table « latest » ou sans version lors d'une exécution en mode backdaté.
Suggérez une réécriture concrète si l'approche actuelle est ligne par ligne.
D) SÉCURITÉ DES REQUÊTES ET TAILLE DE SCAN
Pour chaque requête, vérifiez :
- Le filtre de partition porte sur la colonne brute, pas
DATE(ts),CAST(...), ou toute fonction qui empêche l'élagage - *Pas de `SELECT `** : uniquement les colonnes réellement utilisées en aval
- Les jointures ne vont pas exploser : vérifiez que les clés de jointure sont uniques ou correctement scoped et signalez toute relation potentielle plusieurs-à-plusieurs
- Les opérations coûteuses (
REGEXP,JSON_EXTRACT, UDFs) s'exécutent uniquement après le filtrage de partition, pas sur les scans de table complète
Fournissez un correctif SQL spécifique pour toute requête qui échoue à ces vérifications.
E) ÉCRITURES SÛRES ET IDEMPOTENCE
Identifiez chaque opération d'écriture. Signalez les plain INSERT/append sans logique de dédup.
Chaque écriture doit utiliser l'une des approches suivantes :
MERGEsur une clé déterministe (p. ex.,entity_id + date + model_version)- Écriture vers une table de staging scoped à l'exécution, puis swap ou merge dans la version finale
- Append-only avec une vue dedupe :
QUALIFY ROW_NUMBER() OVER (PARTITION BY <key>) = 1
Vérifiez également :
- Une réexécution créera-t-elle des lignes en double ?
- La disposition d'écriture (
WRITE_TRUNCATEvsWRITE_APPEND) est-elle intentionnelle et documentée ? run_idest-il utilisé comme part de la clé de merge ou dedupe ? Si oui, signalez-le.run_iddoit être stocké en tant que colonne de métadonnées, non comme part de la clé d'unicité, sauf si vous voulez explicitement un historique multi-exécution.
Énoncez l'approche recommandée et la clé dedupe exacte pour cette base de code.
F) OBSERVABILITÉ : Pouvez-vous déboguer une défaillance ?
Vérifiez :
- Les défaillances lèvent des exceptions et avortent sans
except: passsilencieux ni warn-only - Chaque job BQ enregistre : l'ID du job, les octets traités ou facturés si disponibles, les millisecondes de slot et la durée
- Un résumé d'exécution est enregistré ou écrit à la fin contenant :
run_id, env, mode, date_range, tables écrites, total jobs BQ, total octets run_idest présent et cohérent sur toutes les lignes de log
Si run_id manque, proposez un correctif monoligne :
run_id = run_id or datetime.utcnow().strftime('%Y%m%dT%H%M%S')
Final
1. PASS / FAIL avec raisons spécifiques par section (A à F). 2. Liste de patchs ordonnée par risque, référençant les fonctions exactes à modifier. 3. Si FAIL : Top 3 risques de coûts avec une estimation approximative du pire cas (p. ex., « boucle sur 90 dates x 3 retries = 270 jobs BQ »).