Brouillon : cet article n'est pas publié. Il n'apparaît pas dans le blog et n'est pas indexé.
· Par Équipe HeyData
Série : Concevoir des jobs Talend : les bonnes pratiques de Dale Anderson, relues pour la version 8 de Talend Studio · article 2 sur 7
Structurer un job Talend : canevas, jobs parent et enfants, tPreJob et tPostJob
Un job Talend se lit d’abord comme un dessin. Avant d’ouvrir le moindre composant, on voit sur le canevas d’où viennent les données, où elles vont et ce qui se passe en cas d’erreur. Encore faut-il que le dessin soit clair. Un job de quarante composants posés en zigzag, aux liens qui se croisent, se comprend mal et se corrige plus mal encore. La structure d’un job est donc la première bonne pratique.
Cet article synthétise et adapte à Talend Studio 8 les parties 1 et 3 de « Talend Job Design Patterns and Best Practices » de Dale Anderson (Talend, 2015-2016). Ses conseils restent des références : nous les relisons avec les outils d’aujourd’hui.
Le canevas se lit de haut en bas
Dale Anderson propose une règle simple : on construit de haut en bas, puis de gauche à droite. Le chemin normal du traitement va vers la droite et vers le bas ; un chemin qui part vers la gauche signale une erreur. Les liens ne doivent pas se croiser.
Initialiser, traiter, terminer : l’erreur se repère d’un coup d’œil parce qu’elle part vers la gauche.
L’auteur reconnaît que la disposition en zigzag est tentante, parce qu’elle est rapide à écrire : on aligne les composants vers la droite, puis on revient à gauche une ligne plus bas. Mais si un job a besoin de plusieurs lignes pour tenir à l’écran, c’est souvent qu’il en fait trop. Dans la même veine, il conseille d’aérer le canevas : des composants serrés les uns contre les autres se lisent mal.
Des jobs atomiques, orchestrés par un parent
Un gros job est difficile à comprendre, à déboguer et à maintenir. La solution consiste à le découper en jobs enfants qui ont chacun un seul objectif, et à les faire appeler par un job parent avec le composant tRunJob. Le parent orchestre, les enfants travaillent. Le découpage facilite aussi la gestion des erreurs : le parent décide de ce qui se passe quand un enfant échoue.
Le parent enchaîne les enfants ; le joblet, lui, fait partie du job qui l’utilise.
Jusqu’où découper ? Dale Anderson fixe un repère : pas plus de trois niveaux d’appels tRunJob imbriqués, cinq au grand maximum. Au-delà, la mémoire consommée, les paramètres à transmettre et le débogage deviennent difficiles à maîtriser. Le guide de cycle de développement de Qlik Talend donne un repère complémentaire : éviter plus de vingt composants par job.
Deux options de tRunJob méritent d’être connues :
- Transmit whole context transmet au job enfant tous les contextes du parent, ce qui évite de les passer un à un ;
- Use an independent process to run subjob lance l’enfant dans une JVM distincte, avec sa propre mémoire. Ce n’est pas du parallélisme à proprement parler, mais cela isole un enfant gourmand.
tRunJob ou joblet ?
La différence tient en deux mots : un job enfant est appelé, un joblet est inclus. Le job enfant reste un job à part, exécuté par tRunJob, avec ses propres contextes et son propre code généré. Le joblet est un fragment de job que Studio intègre au code du job qui l’utilise : il partage son contexte et son exécution.
Pour Dale Anderson, les bons cas d’usage des joblets sont peu nombreux, mais précieux quand on les trouve. Il en distingue deux formes :
- le joblet classique reçoit des lignes par son entrée, les traite (mise à jour d’une table, écriture d’un log) et les renvoie par sa sortie ;
- le joblet sans entrée ni sortie se contente d’agir dans son coin. L’exemple type est un
tLogCatcherqui capte les erreurs du job pour les journaliser : nous le détaillons dans l’article sur les erreurs.
Pour qu’un joblet serve à plusieurs projets, rangez-le dans un projet de référence. Nous y revenons dans l’article sur l’industrialisation.
Des points d’entrée et de sortie explicites
Tout job commence et finit quelque part. Studio fournit deux composants pour le marquer :
tPreJobs’exécute avant le reste du job. C’est l’étape d’initialisation : charger les contextes, ouvrir les connexions, écrire un premier message de log ;tPostJobs’exécute après, même si le job s’est arrêté sur untDieou sur une erreur avec l’option Die on error. C’est l’étape de clôture : fermer les connexions, faire le ménage, journaliser la fin.
tPreJob et tPostJob ne se relient pas au reste du job : Studio les exécute d’office, au début et à la fin. On relie seulement leurs propres composants, par exemple tPreJob vers un tDBConnection.
Les composants tWarn et tDie complètent ce dispositif. tWarn envoie un avertissement sans interrompre le job ; tDie l’arrête avec un code et un message. Tous deux alimentent tLogCatcher, ce qui en fait les outils naturels d’une gestion d’erreurs maîtrisée.
Identifier la boucle principale
Dans presque tout job, un traitement fait l’essentiel du travail : la boucle principale, généralement le premier flux de données dont la fin marque la fin du job. Des boucles secondaires peuvent s’y imbriquer, par exemple une itération sur des fichiers. Dale Anderson recommande d’identifier explicitement la boucle principale et d’ajouter un tWarn et un tDie sur le composant qui la pilote. On contrôle ainsi la sortie du job, au lieu de la subir.
Ce qui a changé depuis 2016
- Les liens du canevas sont courbes depuis Talend 6. Ils évitent mieux les croisements, mais ne dispensent pas d’une disposition claire.
- Le repère des vingt composants par job vient du guide de cycle de développement de Qlik Talend. Il complète la règle des trois niveaux d’appels.
- Les jobs enfants se gèrent aujourd’hui avec Git, ce qui rend le découpage plus simple à suivre : chaque job a son historique.
- Le module Job Script décrit par Dale Anderson est toujours livré avec Talend Studio 8 : un job peut s’écrire en texte, puis se générer.
À retenir
- De haut en bas, puis de gauche à droite. Le chemin normal va à droite et en bas, l’erreur part à gauche.
- Un job, un objectif. Découpez en jobs enfants appelés par un parent.
- Trois niveaux d’appels. Cinq au plus, et vingt composants par job comme repère.
- Appelé ou inclus.
tRunJobappelle un job à part ; un joblet est intégré au job qui l’utilise. - Entrée et sortie explicites.
tPreJobinitialise,tPostJobclôt, même après untDie. - La boucle principale sous contrôle. Un
tWarnet untDiesur le composant qui la pilote.
Essayer dans Talend Studio
Pour vérifier que tPostJob s’exécute même après un arrêt :
- Créez un job
job_demo_enfant: untPreJobrelié par OnComponentOk à untJavaqui afficheSystem.out.println("Initialisation");. - Ajoutez un
tFixedFlowInputproduisant une ligne, relié à untDie(message « Arrêt volontaire », code 4). - Ajoutez un
tPostJobrelié à untJavaqui afficheSystem.out.println("Clôture");. - Exécutez le job : la console affiche « Initialisation », l’arrêt du
tDie, puis « Clôture ». - Créez un job
job_demo_parentcontenant untRunJobqui appellejob_demo_enfant, avec Die on child error décoché. Ajoutez untJavarelié par OnSubjobOk qui affiche le code retour de l’enfant :
System.out.println("Code retour de l'enfant : " + globalMap.get("tRunJob_1_CHILD_RETURN_CODE"));
Le parent se termine normalement et lit le code 4 renvoyé par l’enfant.
Variante : partir d’un Job Script
Un Job Script est la description d’un job sous forme de texte : composants, réglages et liens. Studio sait générer un job à partir de ce texte, et produire le texte d’un job existant. Voici les deux jobs de la recette, prêts à coller. D’abord job_demo_enfant :
SCRIPT_VERSION=4.2,
JobType= Standard,
DEFAULT_CONTEXT: DEV,
ContextType { NAME: DEV }
addComponent {
setComponentDefinition { TYPE: "tPrejob", NAME: "tPrejob_1", POSITION: 64, 64 }
setSettings { LABEL : "Initialiser" }
}
addComponent {
setComponentDefinition { TYPE: "tJava", NAME: "tJava_1", POSITION: 256, 64 }
setSettings {
CODE : "System.out.println(\"Initialisation\");",
LABEL : "Initialisation"
}
}
addComponent {
setComponentDefinition { TYPE: "tFixedFlowInput", NAME: "tFixedFlowInput_1", POSITION: 64, 192 }
setSettings {
NB_ROWS : "1",
USE_SINGLEMODE : "true",
VALUES { SCHEMA_COLUMN : "message", VALUE : "\"demo\"" },
LABEL : "Une ligne"
}
addSchema {
NAME: "tFixedFlowInput_1", CONNECTOR: "FLOW"
addColumn { NAME: "message", TYPE: "id_String", NULLABLE: true }
}
}
addComponent {
setComponentDefinition { TYPE: "tDie", NAME: "tDie_1", POSITION: 256, 192 }
setSettings {
MESSAGE : "\"Arrêt volontaire\"",
CODE : "4",
EXIT_JVM : "false",
LABEL : "Arrêt volontaire"
}
}
addComponent {
setComponentDefinition { TYPE: "tPostjob", NAME: "tPostjob_1", POSITION: 64, 320 }
setSettings { LABEL : "Clôturer" }
}
addComponent {
setComponentDefinition { TYPE: "tJava", NAME: "tJava_2", POSITION: 256, 320 }
setSettings {
CODE : "System.out.println(\"Clôture\");",
LABEL : "Clôture"
}
}
addConnection {
TYPE: "COMPONENT_OK", NAME: "OnComponentOk", LINESTYLE: 3,
METANAME: "tPrejob_1", SOURCE: "tPrejob_1", TARGET: "tJava_1"
}
addConnection {
TYPE: "FLOW", NAME: "row1", LINESTYLE: 0,
METANAME: "tFixedFlowInput_1", SOURCE: "tFixedFlowInput_1", TARGET: "tDie_1"
}
addConnection {
TYPE: "COMPONENT_OK", NAME: "OnComponentOk", LINESTYLE: 3,
METANAME: "tPostjob_1", SOURCE: "tPostjob_1", TARGET: "tJava_2"
}
Puis job_demo_parent. Son tRunJob désigne l’enfant par son nom, mais surtout par PROJET:ID_DE_L_ENFANT : le nom technique du projet, en majuscules, et l’identifiant interne que Studio a donné à l’enfant à sa création. Sans cet identifiant, le tRunJob ne trouve pas le job à lancer.
SCRIPT_VERSION=4.2,
JobType= Standard,
DEFAULT_CONTEXT: DEV,
ContextType { NAME: DEV }
addComponent {
setComponentDefinition { TYPE: "tRunJob", NAME: "tRunJob_1", POSITION: 64, 64 }
setSettings {
PROCESS : "job_demo_enfant",
PROCESS:PROCESS_TYPE_PROCESS : "PROJET:ID_DE_L_ENFANT",
PROCESS:PROCESS_TYPE_CONTEXT : "DEV",
PROCESS:PROCESS_TYPE_VERSION : "Latest",
DIE_ON_CHILD_ERROR : "false",
TRANSMIT_WHOLE_CONTEXT : "true",
LABEL : "Appeler l'enfant"
}
}
addComponent {
setComponentDefinition { TYPE: "tJava", NAME: "tJava_1", POSITION: 64, 192 }
setSettings {
CODE : "System.out.println(\"Code retour de l'enfant : \" + globalMap.get(\"tRunJob_1_CHILD_RETURN_CODE\"));",
LABEL : "Lire le code retour"
}
}
addConnection {
TYPE: "SUBJOB_OK", NAME: "OnSubjobOk", LINESTYLE: 1,
METANAME: "tRunJob_1", SOURCE: "tRunJob_1", TARGET: "tJava_1"
}
Pour les déployer, dans un projet de test :
-
Dans le référentiel, Code › Job Scripts, clic droit Create JobScript (« Créer un script de Job ») ; nommez-le
job_demo_enfant. -
Collez le script dans l’éditeur et enregistrez (Ctrl+S) : les erreurs de syntaxe sont soulignées.
-
Cliquez sur Generate Job (« Générer un Job ») dans la barre d’outils : le job apparaît sous Job Designs.
-
Relevez l’identifiant de l’enfant. Dans le workspace de Studio, ouvrez
<PROJET>/process/job_demo_enfant_0.1.properties: c’est l’attributidde la balise<TalendProperties:Property …>, de la forme_aBc123…(pasxmi:id). En ligne de commande :grep -o '<TalendProperties:Property [^>]*' job_demo_enfant_0.1.properties | grep -o ' id="[^"]*"' -
Dans le script du parent, remplacez
PROJET:ID_DE_L_ENFANT, par exemple parDEMO:_aBc123…, puis créez et générezjob_demo_parentcomme l’enfant. -
Ouvrez le parent, vérifiez dans le
tRunJoble job appelé et le contexte DEV, puis lancez-le. Le champ Job est vide ? L’identifiant est faux : resélectionnez l’enfant avec le bouton …. -
Si le menu n’apparaît pas, créez un job vide, ouvrez l’onglet Jobscript de son éditeur, collez le script et enregistrez.
La syntaxe des Job Scripts n’a pas de documentation publique complète. Pour connaître le nom exact d’un réglage, construisez le composant à la main, puis faites clic droit Export Job Script sur le job et comparez. L’identifiant change à chaque création de l’enfant : si vous le régénérez, régénérez aussi le parent. Enfin, un Job Script ne doit jamais contenir de mot de passe : les connexions passent par les métadonnées et les contextes du référentiel.
Sources
- Dale Anderson, Talend Job Design Patterns and Best Practices, partie 1 et partie 3, Talend (version archivée).
- Aide Qlik Talend : Software Development Lifecycle Best Practices Guide, conception des jobs et What is a Job design?.
Tout le mérite du contenu original revient à Dale Anderson ; l’adaptation à Talend Studio 8 et ses éventuelles erreurs sont de HeyData. Cet article fait partie de notre série sur les bonnes pratiques de conception de jobs Talend : découvrir la série.