Utiliser dbt State
dbt State est un mécanisme de réutilisation piloté par serveur. Il ne faut pas le confondre avec le sélecteur state:modified ou le report --state de dbt.
Avant de construire chaque nœud sélectionné, dbt demande au serveur dbt State si l'objet peut être ignoré (réutilisé depuis le schéma cible), cloné (réutilisé depuis un autre schéma) ou doit être construit. C'est le successeur de State-Aware Orchestration, mais il fonctionne dans dbt Core, en développement et en CI — pas seulement dans Fusion en production.
dbt State est un produit payant, mais il ne nécessite pas d'abonnement à la plateforme dbt (anciennement dbt Cloud).
Idées fausses courantes
| Idée fausse | Réalité |
|---|---|
« dbt State est juste state:modified / --state » |
Non. state:modified hache les contenus de fichiers par rapport à un manifest que vous gérez et reconstruit state:modified+ (tous les descendants). dbt State gère l'état automatiquement sur un serveur, analyse le SQL en arborescence syntaxique et compare les hashes sémantiques, considère la fraîcheur des données en amont, et reconstruit un descendant uniquement s'il dépend réellement du changement (pas toute la sous-arborescence +). |
| « C'est exclusif à Fusion / production » | Fonctionne dans dbt Core, la plateforme dbt et Fusion, sur dev, CI et production, avec n'importe quel orchestrateur. |
| « Les utilisateurs de dbt Core ne peuvent pas l'utiliser » | Ils le peuvent. dbt Core 1.7–1.11 requiert pip install dbt-state. C'est intégré dans dbt Core 1.12 / v2.0 et Fusion. |
| « C'est gratuit / c'est local » | Il appelle le serveur dbt State et nécessite une authentification via un compte de plateforme dbt ou un compte dbt State autonome (app.state.dbt.com). La réutilisation est mesurée en DATTs — tables cibles actives quotidiennement (voir Facturation ci-dessous). |
| « Il envoie mes données à dbt Labs » | Il envoie les horodatages de dernière modification et le texte SQL. Le SQL est hashé puis supprimé — dbt Labs ne peut pas lire le contenu des requêtes après hachage et ne peut jamais accéder aux données brutes. |
Comment fonctionne la décision de réutilisation
Pour chaque nœud sélectionné, dbt State choisit l'option la moins coûteuse valide :
- Ignorer — l'objet existe dans le schéma cible, son hash sémantique est inchangé, et aucun parent n'a des données plus fraîches au-delà de
lag_tolerance. Ne fait rien. - Cloner — un objet correspondant (même hash, données fraîches) existe dans un autre schéma (par exemple production ou le schéma dev d'un collègue). Le clone est marqué Réutilisé. Utilise un clone sans copie s'il est supporté par l'entrepôt, ou exécute une instruction CTAS pour copier les données transformées d'ailleurs sinon. Les résultats des tests sont aussi réutilisés — un test échoué apparaît toujours même s'il n'a pas été réexécuté.
- Construire — pas de réutilisation valide. Construit normalement, en différant automatiquement les nœuds en amont non sélectionnés.
Si un nœud est sélectionné pour exécution mais ses entrées n'existent pas dans le schéma cible, dbt State utilise le report comme d'habitude. Si un manifest.json est présent, il l'utilisera, sinon il fera une meilleure supposition possible du FQN correct basée sur les macros generate_*_name. Le report ne consomme pas de DATTs. La config defer_to_target dans profiles.yml peut être utilisée pour spécifier le schéma vers lequel différer pour les utilisateurs auto-gérés. Ce n'est pas nécessaire pour les utilisateurs de plateforme dbt.
Pour obtenir la fraîcheur, dbt récupère les métadonnées de l'entrepôt (ou loaded_at_field/loaded_at_query) pour chaque relation en entrée. Pour les vues sans config loaded_at, il parcourt en amont jusqu'à trouver une vraie table.
Normalisation des requêtes et raison pour laquelle les modèles se reconstruisent
dbt State hache une arborescence syntaxique analysée, il ignore donc les changements cosmétiques — espaces, commentaires, alias de tables, reformatage par dbt lint --fix. Un modèle se reconstruit seulement quand sa logique ou ses données changent.
SQL volatil (current_timestamp(), getdate(), random()): traité par défaut comme de la logique — le hash utilise le nom de la fonction, pas sa valeur d'exécution, donc il n'invalide pas le modèle à chaque exécution (sinon rien en aval de getdate() ne pourrait jamais être réutilisé). Pour que un modèle se reconstruise quand la valeur change :
- Définir
evaluate_volatile_sql: true(préféré — couvre toutes les fonctions du modèle, héritable comme n'importe quelle config). dbt State émule la valeur de la fonction dans le hash. - Ou utiliser un équivalent Jinja (par exemple
{{ run_started_at }}) — Jinja s'affiche avant l'analyse, donc il change le SQL compilé à chaque exécution.
Jinja non-déterministe (par exemple dbt_utils.get_relations_by_pattern retournant les relations dans un ordre variable) produit un hash compilé différent et déclenche des reconstructions même quand la logique est inchangée.
Changements de config : seules les configs pertinentes pour la construction affectent le hash (materialized, on_schema_change, severity, …). Les configs cosmétiques (meta, tags) sont ignorées. Si un hook post-exécution mute les tables basées sur des champs ignorés (par exemple appliquer meta comme tags d'entrepôt), définir execute_hooks_on_any_reuse: true pour que les hooks s'exécutent à la réutilisation.
Référence rapide des configs
À définir sous models: +state: dans dbt_project.yml, dans la config schema.yml config.state, ou dans {{ config(state={...}) }}.
| Config | Défaut | Objectif |
|---|---|---|
lag_tolerance |
45m |
Jusqu'où les données peuvent être obsolètes avant qu'un nœud soit admissible à la reconstruction. Fraîcheur des données uniquement — les changements SQL se reconstruisent toujours. |
require_fresh_data_from |
any |
Si any ou all les parents directs ont besoin de données fraîches pour déclencher une reconstruction. |
evaluate_volatile_sql |
false |
Hasher la valeur d'exécution des fonctions volatiles au lieu du nom. |
pre_clone |
if_missing |
Pré-remplir les modèles incrémentels/snapshots en clonant la prod avant une exécution (never / if_missing / always). |
execute_hooks_on_any_reuse |
false |
Exécuter les hooks pré/post-exécution même quand un nœud est réutilisé. |
defer_to_target |
prod |
(Auto-géré uniquement, profil) Quel profil cible utiliser pour différer/cloner. |
metadata_warehouse |
profile warehouse |
(Snowflake uniquement, profil) Entrepôt séparé pour les recherches de métadonnées. |
Entrepôts supportés : Snowflake, Databricks, BigQuery, Redshift.
Facturation : tables cibles actives quotidiennement (DATT)
L'utilisation de dbt State est mesurée en DATTs (tables cibles actives quotidiennement), pas par « modèles construits ».
- Une table cible est un objet de base de données géré par votre projet (par base de données + schéma) : seeds, snapshots, modèles (incl. incrémentels), et chaque test distinct — même les tests non stockés en base de données (
store_failuresoff). Exemple :dim_customersavecnot_nulletuniquesurid= 3 tables cibles (le modèle + 2 tests). - Une table cible devient un DATT quand dbt State effectue au moins une omission, clone ou réutilisation de test dessus un jour donné (UTC). Toutes les réutilisations de la même table cible en un jour comptent comme un seul DATT. Une construction complète n'est pas une réutilisation.
- Les vues ne sont jamais facturées comme DATTs, même si réutilisées ou clonées. Les tests attachés à une vue seront facturés normalement.
Si vous avez des questions sur les tarifs, consultez https://www.getdbt.com/product/dbt-state.
Optimisations pour de meilleurs résultats
lag_tolerancepar environnement — en dev, réglez-la élevée (par exemple une semaine) pour que dbt ne fasse rien quand les données sont légèrement obsolètes ; le clonage est pas cher mais ne rien faire est moins cher. Exemple :# dbt_project.yml models: +state: lag_tolerance: "{{ '4h' if target.name == 'prod' else '7d' }}"- Continuez à utiliser les sélecteurs en développement. Toute table cible que dbt State réutilise compte comme un DATT pour ce jour (même une à l'intérieur de sa fenêtre lag-tolerance). Sélectionnez seulement les nœuds sur lesquels vous travaillez pour que le report simple gère le reste — les nœuds non modifiés et non sélectionnés n'entraînent aucune utilisation de dbt State.
- Réduisez l'utilisation de sélecteurs complexes en production. dbt State rend la plupart des jobs proches d'un simple
dbt build; laissez-le décider ce qu'il faut reconstruire au lieu de peaufiner manuellement la sélection par job. Spécifiez lag_tolerance pour éviter la reconstruction excessive. - *Spécifiez les colonnes au lieu de `select
pour augmenter la probabilité de réutilisation**. Si dbt State ne peut pas prouver qu'untable.*` ou similaire a le même ensemble de colonnes, il reconstruira pour être sûr. C'est particulièrement pertinent pour les vues. L'analyse statique de Fusion n'est actuellement pas utilisée pour cela.
Diagnostiquer les comportements déroutants
| Symptôme | Cause / correction |
|---|---|
Un modèle avec current_timestamp() se reconstruit continuellement |
Probablement evaluate_volatile_sql: true quelque part, ou une valeur Jinja (par exemple run_started_at) changeant le SQL compilé. Si vous voulez la réutilisation, laissez le SQL volatil par défaut (logique). |
| Le modèle se reconstruit malgré « pas de changement » | Le changement cosmétique n'en est pas la cause (ceux-ci sont normalisés). Cherchez du Jinja non-déterministe (sortie de macro non ordonnée), un changement de config pertinent pour la construction ou des données en amont plus fraîches au-delà de lag_tolerance. Les tables de métadonnées peuvent considérer une table modifiée par une commande insert même si aucune ligne n'a été ajoutée. Envisagez d'utiliser loaded_at_field, mais cela peut être plus coûteux en entrepôt — les requêtes de métadonnées sont souvent gratuites mais loaded_at_field sera une requête payante standard. |
| Les post-hooks n'ont pas s'exécuté sur un modèle réutilisé | Les hooks ne s'exécutent pas à la réutilisation par défaut — définir execute_hooks_on_any_reuse: true. |
| Voulez savoir pourquoi un nœud a été réutilisé/reconstruit | Utilisez la commande dbt-state explain (dbt v1.7–1.12) pour inspecter la décision. |
| Besoin d'authentification / accès | Connectez-vous via votre compte plateforme dbt ou un compte dbt State autonome. Pour une organisation, définir state-org-id sous dbt-cloud: dans dbt_project.yml. |
v1 (Python) vs v2 (Rust/Fusion)
| dbt Core 1.7–1.11 | dbt Core 1.12 / v2.0 | Fusion | |
|---|---|---|---|
| Installation | pip install dbt-state requis |
Intégré | Intégré |
- Les utilisateurs de dbt v1.7-1.11 doivent installer le package
dbt-stateséparé pour utiliser dbt State. - Les utilisateurs de dbt v1.12+ ont le package
dbt-stateinclus automatiquement. - Les utilisateurs de dbt v2.0+ (distributions Core ou Fusion) ont l'implémentation Rust de la logique client intégrée, donc aucune installation séparée n'est nécessaire.
Le comportement de réutilisation, les configs et la normalisation des requêtes sont côté serveur et se comportent de manière cohérente sur tous les moteurs. La principale différence v1 est l'installation sépaée dbt-state pour 1.7–1.11. Le diagnostic dbt-state explain n'est pas disponible dans dbt v2.
Docs associées
- Aperçu :
/docs/deploy/dbt-state-about - Configuration :
/docs/deploy/dbt-state-setup· Exemples :/docs/deploy/dbt-state-examples - Monitorer l'activité :
/docs/deploy/dbt-state-interface· Report :/docs/deploy/dbt-state-deferral· CI/CD :/docs/deploy/dbt-state-cicd - Configs :
/reference/resource-configs/dbt-state-configs·lag_tolerance·defer_to_target