Der Blog-Beitrag Data Engineering mit Talend aus dem letzten Jahr war dem Talend-Ökosystem und den allgemeinen Möglichkeiten für das Data Engineering gewidmet. An dieser Stelle wird nun beschrieben, wie mit zwei grundlegenden Designentscheidungen die Erfolgswahrscheinlichkeit von Talend-Projekten erhöht werden kann. Diese Designkonzepte können die Entwicklungsarbeit und die Fehleranfälligkeit der Datenstrecken deutlich reduzieren und weiterhin die Anwendungsszenarien einzelner Datenstrecken und Jobketten erweitern bzw. generalisieren. Der Anspruch dabei ist es die Möglichkeiten von Talend so einzusetzen, dass die Entwicklung so simpel wie möglich ist, eine hohe Konsistenz aufweist und die Jobs gut wartbar sind. Insbesondere die herausstechende Eigenschaft von Talend, eigenen Java-Code einzubinden, führt zu neuen Herausforderungen, die in der Entwicklung berücksichtigt werden müssen. Einerseits ergibt sich zwar eine hohe Flexibilität in der Entwicklung, da nicht nur auf vorgefertigte Funktionen und Datenbankfähigkeiten gesetzt werden muss. Andererseits entsteht dadurch auch die Anforderung Standards in der Entwicklung einzuführen, die zu wiederverwendbaren Code und einer konsistenten Entwicklung beitragen. Des Weiteren kann die Fähigkeit von Talend genutzt werden, um während der Job-Ausführung auf unterschiedliche Variablen zuzugreifen und somit dynamisch generierten SQL-Code zu nutzen, welcher eine hohe Flexibilität der Verwendbarkeit von einzelnen Jobs ermöglicht.
Das Java-Repository
In Talend kann komplexer und häufig gebrauchter Java-Code in einem gemeinsamen Repository abgelegt werden, den Code-Routinen. Diese Java-Routinen können dann in sämtlichen Jobs direkt verwendet werden. Auch wenn Talend bereits von Haus aus die Vielzahl an nativen Java-Funktionen unterstützt und diese auch um eigene Funktionen erweitert, bietet ein gut gepflegtes Java-Code-Repository in Talend viele Vorteile. Der größte Mehrwert von eigenen Code-Routinen bietet die Wiederverwendbarkeit des Codes. So müssen häufig genutzte Transformationen nur einmal entwickelt und Änderungen lediglich einmalig vorgenommen werden, da alle Talend-Jobs die geänderte Logik automatisch übernehmen. Dies kann den Aufwand in der Entwicklung, die Fehleranfälligkeit und Wartung in den Datenstrecken deutlich reduzieren.
Braucht man zum Beispiel regelmäßig Substrings bis zu einem bestimmten Trennzeichen, bietet es sich an dafür eine Java-Klasse zu erstellen, da somit nicht bei jeder Anwendung eine Transformation mit den Talend nativen Methoden geschrieben werden muss. Diese Java-Funktion würde dann den vollständigen String und das gewünschte Trennzeichen als Eingabe benötigen und schließlich den gewünschten Substring als Ergebnis liefern:
package routines;
public class MyStringUtil {
public static String getStringToDelim(String text, String delimiter) {
String result = null;
if (text != null && !text.trim().equals("")
&& text.indexOf(delimiter) != -1) {
result = text.substring(0, text.indexOf(delimiter));
} else {
result = text;
}
return result;
}
}
Das Code-Beispiel macht dabei von den Java-Methoden “substring()” und “indexOf()” Gebrauch, um einen neuen String zu erzeugen. Bei der Entwicklung mit Talend ist es wichtig zu berücksichtigen, dass nur von den Java-Methoden unterstützte Inputs verarbeitet werden. Wird bei der Entwicklung auf native Java-Funktionen direkt in Talend-Komponenten gesetzt, muss der Entwickler bei jeder Transformation die möglichen Probleme, zu denen ungewöhnliche Input-Werte führen können, berücksichtigen. Nutzt man hingegen die Code-Routinen lässt sich direkt im Code sicherstellen, dass nur gültige Werte mit den jeweiligen Funktionen verarbeitet werden. In dem Beispiel wird dabei zum einen darauf geachtet, dass bei einem null-Wert als Eingabe die Funktion null zurückgibt, da sonst der Talend-Job mit einer „NullPointerException“ als Fehlermeldung abbrechen würde. Der letzte Teil der If-Anweisung verhindert “IndexOutOfBoundsExceptions” als Fehlermeldung, wenn der eingegebene String leer ist oder aber das Trennzeichen nicht in dem String enthalten ist. Im ersten Fall wird ein null-Wert zurückgegeben im zweiten Fall der vollständige String.
Diese Funktion lässt sich anschließend an jeder möglichen Stelle in den Datenstrecken aufrufen:
MyStringUtil.getStringToDelim(row1.exampleString,".")
Der Code aus dem eigenen Java-Repository bietet somit eine Lösung für häufige Aufgaben an, die bereits ausführlich getestet ist und daher den Aufwand wie die Fehleranfälligkeit in der Entwicklung reduzieren kann.
Dynamischer SQL-Code
Werden bestimmte SQL-Fragmente in der Entwicklung der Talend-Jobs öfter verwendet, kann es sinnvoll sein diese ebenfalls als Java-Code im Repository abzulegen, da diese dann auch für alle Jobs simultan geändert werden können. Soll der SQL-Code hingegen während der Ausführung des Jobs erstellt werden, müssen andere Eigenschaften von Talend angewandt werden. Denn in diesem Fall kann die SQL-Syntax nicht direkt in die jeweiligen Komponenten eingetragen werden, sondern muss dynamisch beim Ausführen des Jobs erzeugt werden. Talend bietet zwei Möglichkeiten, um Variablen dynamisch beim Ausführen eines Jobs zu erzeugen, welche dann für einen dynamischen SQL-Code genutzt werden können:
- Kontextvariablen: Können vor der Ausführung des Jobs durch Auswählen einer bestimmten Kontext-Umgebung oder durch einen impliziten Kontextload gesetzt werden. Auch eine tJava-Komponente ermöglicht das Setzen von Kontextvariablen während der Ausführung. Sie eignen sich vor allem zum Setzen der Datenbank-Ebene, um das Ausführen der Jobs in unterschiedlichen Umgebungen zu ermöglichen.
- globalMap: Diese zentrale Java HashMap ermöglicht es innerhalb von Talend-Jobs Daten zu speichern, auf welche im weiteren Verlauf zugegriffen werden können. So kann die globalMap genutzt werden, um relevante Informationen, die für das Erstellen von SQL-Statements benötigt werden, aus anderen Quellen zu extrahieren. Die globalMap wird nicht nur vom Entwickler aktiv genutzt, sondern auch von Talend selbst. So werden für viele Komponenten die Anzahl der verarbeiteten Spalten sowie Fehlermeldungen in die globalMap geschrieben. Diese Informationen lassen sich gut für eine frühzeitige Fehlererkennung mit Hilfe von tWarn oder tDie Komponenten nutzen.
In folgender Abbildung ist ein Beispiel für die Anwendung von dynamischen SQL-Code in Talend gegeben:
Dabei wird zum einen von der Kontextvariable „DB_Ebene“ Gebrauch gemacht, um die Umgebung, auf welche die Datenbank-Komponente (5.) zugreift, zu definieren. Zum anderen sind notwendige Einschränkungen für das SQL-Statement in einer Textdatei abgelegt, welche in der Konstruktion der Datenbank-Quelle (5.) genutzt werden sollen. Dies kann sinnvoll sein, wenn die benötigten Einschränkungen für unterschiedliche Datenbank-Ebenen abweichen oder aber durch Business-Analysten geändert werden sollen. Dieser Ansatz ermöglicht eine Steuerung der Jobs außerhalb der Talend-Umgebung, da lediglich durch Änderungen dieser Dateien die Talend-Jobs direkt beeinflusst werden.
Für das beschriebene Szenario wird die Anwendung der globalMap notwendig, da die benötigten Informationen nicht mehr extrahiert werden können sobald die Datenbankkomponente (5.) aktiv wird. Daher muss ein vorgelagerter Subjob erstellt werden, der die benötigten Where-Bedingungen aus der Datei ausliest und diese zur Weiterverwendung in die globalMap schreibt. Dateien zur dynamischen Konstruktion von SQL-Statements können unterschiedliche Formen aufweisen, im Beispiel wird angenommen, dass die Datei zwei Zeilen enthält, eine für den Namen der betroffenen Spalte und eine mit den relevanten Einschränkungen. Nun kann mit einer tFixedFlowInput-Komponenten (1.), welche alle Spaltennamen der Quelltabelle enthält ein Lookup auf die Datei (2.) erfolgen. Damit lassen sich die relevanten Spalten und ihre vorgesehenen Einschränkungen ermitteln, die im nächsten Schritt mit der tMap (3.) in die endgültigen Where-Klauseln mit SQL konformer Schreibweise konkateniert werden. Anschließend wird eine tJavaRow-Komponente (4.) genutzt, um so viele Bedingungen wie aus dem Lookup hervorgehen in die globalMap zu schreiben. Abschließend kann dann aus der Verbindung der Einträge in der globalMap und der Kontextfähigkeiten von Talend ein dynamisches SQL erstellt werden, für welches erst während der Jobausführung alle notwendigen Informationen erzeugt werden.
Fazit
Talend bietet mit seiner offenen Art viele Freiheiten, die es zu einer sehr flexiblen Entwicklungsumgebung für ETL-Strecken macht. Allerdings sollten vor Projektbeginn grundsätzliche Überlegungen getroffen werden, wie der optimale Entwicklungsprozess aussehen soll, damit diese Freiheiten nicht ein unnötig kompliziertes Ergebnis nach sich ziehen. In diesem Beitrag wurden fortgeschrittene Konzepte vorgestellt, die dazu führen sollen, dass die Entwicklungsarbeit vereinfacht wird, die Jobs eine höhere Konsistenz aufweisen und vor allem auch besser wartbar werden und eine effizientere Fehlersuche ermöglichen. Insbesondere das gute Pflegen des Talend Java-Repositories und der Kontextgruppen ermöglicht es Änderungen, welche eine Vielzahl von Talend-Jobs betreffen, an zentraler Stelle vorzunehmen. Das Verwenden von dynamischen SQL-Code hingegen ermöglicht einen vielfältigen Einsatz der Jobs in unterschiedlichen Ebenen, mit abweichenden Schema, Tabellen oder Einschränkungen und kann somit den Entwicklungsaufwand weiter reduzieren. Die angesprochenen Fähigkeiten von Talend können zu einer besseren Entwicklungsarbeit beitragen, wenn andere logische Schritte wie klare Namenskonventionen, simpel gehaltene Jobketten und dass sinnvolle Erstellen von Joblets im Entwicklungsprozess Berücksichtigung finden.