ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Migrer les modèles incrémentiels vers Microbatch

Comment convertir les modèles incrémentiels dbt traditionnels vers la stratégie microbatch — migration étape par étape, exemples de code côte à côte et considérations pour la première exécution.

Planté
dbtincremental processingdata engineering

La conversion d’un modèle incrémentiel existant vers microbatch est généralement simple. Les modifications mécaniques sont directes : supprimer le bloc is_incremental(), ajouter la configuration event_time et batch_size, et laisser dbt gérer le filtrage. La partie plus difficile est de savoir quand la conversion est pertinente et de gérer la première exécution après la migration.

Quand la migration est pertinente

Tous les modèles incrémentiels ne bénéficient pas de microbatch. Avant de convertir, vérifiez que le modèle remplit ces critères :

  1. Colonne horodatage claire : Le modèle a besoin d’une colonne qui définit proprement à quel moment chaque enregistrement appartient dans le temps. C’est ce qui devient event_time. Les modèles qui mettent à jour des enregistrements sur des plages de temps arbitraires — comme SCD Type 2 ou des patterns CDC complexes — ne s’adaptent pas au modèle de batch.

  2. Granularité horaire ou plus grossière : Le batch_size minimum de microbatch est hour. Si le modèle actuel traite des fenêtres inférieures à l’heure (intervalles de 15 minutes ou 5 minutes), microbatch ne fonctionnera pas. Voir la note sur les compromis pour les détails.

  3. Traitement borné dans le temps : La logique is_incremental() actuelle doit déjà filtrer par une colonne temporelle. Si le filtre incrémentiel est basé sur une colonne non temporelle (comme un ID auto-incrémentiel ou un flag de statut), microbatch n’est pas la bonne stratégie.

  4. Valeur de backfill ou relance : Microbatch ajoute une surcharge (exécution séquentielle des batchs) en échange d’une relance par batch et d’un backfill intégré. Si le modèle ne tombe jamais en erreur et qu’il n’est jamais nécessaire de retraiter des plages de dates spécifiques, ce compromis n’en vaut pas la peine.

Côte à côte : avant et après

Voici un modèle incrémentiel traditionnel typique et son équivalent microbatch.

Incrémentiel traditionnel (delete+insert)

{{ config(
materialized='incremental',
incremental_strategy='delete+insert',
unique_key='date_day'
) }}
SELECT
event_id,
user_id,
event_occurred_at,
event_name,
event_properties
FROM {{ ref('base__app__events') }}
{% if is_incremental() %}
WHERE date_day >= (
SELECT {{ dbt.dateadd("day", -3, "max(date_day)") }}
FROM {{ this }}
)
{% endif %}

Équivalent microbatch

{{ config(
materialized='incremental',
incremental_strategy='microbatch',
event_time='event_occurred_at',
batch_size='day',
lookback=3,
begin='2020-01-01'
) }}
SELECT
event_id,
user_id,
event_occurred_at,
event_name,
event_properties
FROM {{ ref('base__app__events') }}

Les différences :

  • Bloc is_incremental() supprimé : dbt gère le filtrage temporel automatiquement en fonction de event_time et batch_size.
  • event_time ajouté : Pointe vers la colonne horodatage qui définit les limites des batchs. Remplace la logique manuelle WHERE date_day >= ....
  • lookback remplace la fenêtre manuelle : Le paramètre lookback=3 indique à dbt de retraiter les 3 batchs précédents, exactement comme le pattern dateadd("day", -3, ...) dans l’original.
  • Date begin ajoutée : Requise pour microbatch. Définit la date la plus ancienne que dbt traitera lors d’un full build.
  • unique_key supprimé : Non requis pour la plupart des entrepôts. Sur BigQuery, microbatch utilise insert_overwrite en coulisses (remplacement de partition, pas de matching de lignes). Sur Snowflake, il utilise delete+insert. Seul le microbatch PostgreSQL nécessite unique_key car il délègue au merge.

Migration étape par étape

1. Cartographier la logique existante

Avant de modifier le code, identifier :

  • Quelle colonne est le filtre temporel ? C’est ce qui devient event_time. C’est la colonne dans la clause WHERE qui filtre les enregistrements par temps.
  • Quelle est la fenêtre de lookback actuelle ? Si 3 jours sont soustraits dans le bloc is_incremental(), définir lookback=3.
  • Quelle granularité convient ? Si le traitement est quotidien, utiliser batch_size='day'. Si le modèle tourne toutes les heures et traite la dernière heure, utiliser batch_size='hour'.
  • Quelle est la date la plus ancienne dans la table ? C’est ce qui devient begin. Vérifier via SELECT MIN(event_time_column) FROM your_table.

2. Ajouter la configuration spécifique à l’entrepôt

Microbatch utilise des stratégies sous-jacentes différentes selon l’entrepôt. Certains nécessitent une configuration supplémentaire :

BigQuery nécessite partition_by correspondant à event_time :

{{ config(
materialized='incremental',
incremental_strategy='microbatch',
partition_by={
"field": "event_occurred_at",
"data_type": "timestamp",
"granularity": "day"
},
event_time='event_occurred_at',
batch_size='day',
begin='2020-01-01'
) }}

La granularité de partition_by doit correspondre au batch_size. Les incompatibilités ne cassent pas le modèle mais réduisent l’efficacité — une partition mensuelle avec des batchs quotidiens signifie que plusieurs batchs écrivent dans la même partition, perdant le bénéfice du remplacement atomique de partition.

Snowflake et Databricks ne nécessitent pas de configuration supplémentaire. Les stratégies sous-jacentes (delete+insert et replace_where respectivement) fonctionnent sans paramètres additionnels.

PostgreSQL nécessite unique_key car microbatch délègue au merge :

{{ config(
materialized='incremental',
incremental_strategy='microbatch',
unique_key='event_id',
event_time='event_occurred_at',
batch_size='day',
begin='2020-01-01'
) }}

3. Configurer event_time en amont

Si les modèles en amont (ceux référencés via ref()) ont une colonne horodatage, configurez également event_time sur eux. Cela active le filtrage automatique en amont — dbt filtrera les tables source pour correspondre à la fenêtre du batch courant, évitant les scans de tables complètes.

schema.yml
models:
- name: base__app__events
config:
event_time: event_occurred_at

Ce n’est pas requis pour la migration, mais c’est une optimisation significative des performances. Sans cela, chaque batch lit la table en amont complète.

4. Supprimer la logique redondante

Supprimez entièrement le bloc is_incremental(). Tout filtrage temporel manuel, calcul de lookback ou logique conditionnelle liée au traitement incrémentiel est désormais géré par la configuration microbatch.

Supprimez également :

  • Les calculs de lookback dateadd / DATE_SUB manuels
  • Les références à {{ this }} pour déterminer la date maximale traitée
  • Toute configuration unique_key (sauf sur PostgreSQL)
  • Les overrides de variables de backfill personnalisés (microbatch a un backfill intégré)

5. Valider avec des tests unitaires

Avant le déploiement, validez que la version microbatch produit les mêmes résultats que la version traditionnelle. Exécutez les deux en parallèle et comparez les sorties avec des tests unitaires ou des requêtes de comparaison manuelles :

-- Comparer les comptages de lignes par jour
SELECT
DATE(event_occurred_at) AS event_date,
COUNT(*) AS row_count
FROM traditional_model
GROUP BY 1
EXCEPT DISTINCT
SELECT
DATE(event_occurred_at) AS event_date,
COUNT(*) AS row_count
FROM microbatch_model
GROUP BY 1;

Si les résultats divergent, les causes les plus courantes sont :

  • Différences de fuseau horaire : Microbatch utilise UTC pour les limites de batch. Si le modèle original utilisait l’heure locale, les enregistrements proches de minuit tomberont dans des batchs différents.
  • Incompatibilité de lookback : Vérifier que lookback correspond exactement à la taille de fenêtre originale.
  • Changements de déduplication : Si le modèle original s’appuyait sur unique_key pour la déduplication lors du merge, et que le nouveau microbatch délègue à insert_overwrite (BigQuery), une déduplication explicite dans le SELECT peut être nécessaire.

Gérer la première exécution

Lors de la première exécution après la conversion vers microbatch, deux options s’offrent à vous :

Option A : Full refresh

L’approche la plus simple. Exécutez dbt run --full-refresh --select your_model pour reconstruire la table de zéro en utilisant la logique microbatch. dbt traite chaque batch de begin à maintenant, séquentiellement.

Pour les tables petites à moyennes, c’est correct. Pour les très grandes tables, c’est lent — une table avec 5 ans de données quotidiennes signifie 1 825 requêtes de batch séquentielles. Envisagez l’option B.

Option B : Reconstruction bornée par morceaux

Utilisez --event-time-start et --event-time-end pour reconstruire par morceaux gérables :

Terminal window
# Reconstruire les données 2024
dbt run --full-refresh --select int__sessions_aggregated \
--event-time-start "2024-01-01" --event-time-end "2024-07-01"
dbt run --full-refresh --select int__sessions_aggregated \
--event-time-start "2024-07-01" --event-time-end "2025-01-01"

C’est particulièrement utile sur BigQuery où le coût de calcul de chaque batch est proportionnel aux données scannées. Le traitement par morceaux plus petits maintient les coûts de requêtes individuelles à un niveau gérable et permet de relancer des morceaux spécifiques en cas d’échec.

Option C : Conserver les données existantes

Si la structure de table existante est compatible (mêmes colonnes, même partitionnement), un full refresh n’est peut-être pas nécessaire du tout. La prochaine exécution incrémentielle utilisera la logique microbatch pour traiter les nouveaux batchs depuis l’endroit où la table s’est arrêtée. dbt détermine le point de « dernier traitement » depuis les données existantes dans la table.

Cela fonctionne mieux quand :

  • La table est déjà partitionnée par la même colonne utilisée comme event_time
  • Aucune logique de transformation n’a changé, seulement la stratégie incrémentielle
  • On est à l’aise avec les données historiques existantes telles qu’elles sont

Pièges courants de migration

Oublier begin : Sans begin, dbt ne sait pas jusqu’où remonter lors d’un full refresh. Le modèle génère une erreur.

Surprises aux limites UTC : Le modèle original peut avoir utilisé des limites de batch en heure locale (minuit heure de l’Est, minuit CET). Microbatch utilise exclusivement UTC. Les enregistrements proches de la fin de la journée locale tomberont dans des batchs différents qu’avant. Si cela importe pour le reporting, il faut convertir les horodatages en UTC avant de les définir comme event_time.

Exigence unique_key PostgreSQL : Tous les autres entrepôts fonctionnent sans unique_key pour microbatch, mais PostgreSQL en a besoin car microbatch délègue au merge. L’oublier cause des erreurs, pas des échecs silencieux.

full_refresh=false trop agressif : Définir full_refresh=false immédiatement après la migration empêche de faire facilement une reconstruction complète si quelque chose se passe mal. Attendez d’avoir validé le comportement microbatch avant d’ajouter cette protection.