Mit einer Remote Function lassen sich externe Funktionen in BigQuery direkt im SQL-Code einbinden/ausführen. Das hat viel Charme aber einen direkten Einfluss auf die Architektur.
Ausgangslage
Wir schauen uns dies an einem konkreten Beispiel an. Wir haben zwei Tabellen, welche neben einer Kunden-Id und weiteren Feldern beide ein Namensfeld haben. Wir wollen nun jeweils die beiden Namen derselben Kunden-Id bezüglich der Ähnlichkeit vergleichen. Das Resultat wollen wir in eine dritte Tabelle speichern:
Id | NameA | NameB | Similarity
Die Berechnung der Ähnlichkeit ist ein komplexer Vorgang und kann nicht direkt mit SQL berechnet werden. Wir brauchen dazu eine spezifische Softwarebibliothek mit einem entsprechenden Algorithmus.
Die Resultat-Tabelle wollen wir drei Mal am Tag, also alle 8 Stunden neu erstellen.
Wir gehen hier weiter davon aus, dass die beiden Tabellen bestehend sind, respektive von unterschiedlichen Applikationen zu unterschiedlichen Zeitpunkten erstellt werden. Die Berechnung der Ähnlichkeit kann somit nicht direkt im Prozess der Erstellung dieser Tabellen erfolgen.
Für die Ähnlichkeitsberechnung verwenden wir diese Software-Bibliothek: https://github.com/asderix/Esc
Lösungsvariante A: Eine Funktion mit Cloud Scheduler
Eine klassische Architektur wäre, dass wir eine Funktion schreiben, welche die folgenden Aufgaben wahrnimmt:
- ESC-Bibliothek einbinden
- Verbindung zu BigQuery herstellen
- Tabellen mittels JOIN abfragen
- Durch das Record-Set iterieren und für jeden Record von ESC die Ähnlichkeit berechnen lassen
- Die Resultate in die neue Tabelle schreiben
Für einzelne Aufgaben gibt es wiederum mehrere Varianten, wo es einen Architektur-Entscheid bräuchte. So gibt es mehrere Möglichkeiten, Daten in BigQuery effizient zu speichern. Wir beschränken uns hier erstmal mit der mittleren Flughöhe. Diese Funktion wird nun mit einem GCP Cloud Scheduler alle 8 Stunden aufgerufen. Die Architektur würde wie folgt aussehen:
Wenn wir die Ähnlichkeitsfunktion wirklich nur einmal in diesem Use Case, brauchen, ist die direkte verwenden zweckmässig. Brauchen wir sie in mehr als einen Use Case, ist eine Kapselung im Sinne einer Microservice-Architektur sinnvoll. Damit kommen wir zur Lösungsvariante B.
Lösungsvariante B: Zwei Funktionen mit Cloud Scheduler
Diese Lösungsvariante teilt Funktionen in Microservices auf und lagert daher die Ähnlichkeitsberechnung in eine eigene Funktion aus. Der Rest der eigentlichen Funktion bleibt soweit unverändert:
- Verbindung zu BigQuery herstellen
- Tabellen mittels JOIN abfragen
- Durch das Record-Set iterieren und für jeden Record vom ESC-Service die Ähnlichkeit berechnen lassen
- Die Resultate in die neue Tabelle schreiben
Die ESC-Function nimmt als eigenständiger Service die folgenden Aufgaben wahr:
- ESC-Bibliothek einbinden
- Von ESC die Ähnlichkeit der Inputs (Namen) berechnen lassen
- Ähnlichkeitswert zurück geben
Die Hauptfunktion wird ebenfalls mit einem GCP Cloud Scheduler alle 8 Stunden aufgerufen. Die Architektur würde wie folgt aussehen:
Der Nachteil dieser Variante ist, dass wir zwei Funktionen brauchen und mit dem Cloud Scheduler (analog Variante A) ein weiteres GCP Produkt einsetzen müssen. Damit kommen wir zur Lösungsvariante C.
Lösungsvariante C: Eine generische Funktion mit BigQuery
Bei dieser Variante übernehmen wir die generische Funktion der Ähnlichkeitsberechnung von Variante B und machen den Rest direkt in BigQuery. In BigQuery machen wir entsprechend folgendes:
- Die Ähnlichkeits-Function als Remote Function (User Defined Function, UDF, mit einem Remote-Call) anbinden
- Eine SQL-Query schreiben, welche die beiden Tabellen joint und den Ähnlichkeitswert direkt mittels Funktionsaufruf berechnen lässt
- Die gespeicherte Abfrage terminieren wir direkt in BigQuery auf alle 8 Stunden und legen fest, dass das Resultat in einer neue Tabelle (überschreiben) gespeichert werden soll
Das Schedulen einer Abfrage in BigQuery kann sehr einfach vorgenommen werden:
Die Architektur sieht in dieser Lösungsvariante folgendermassen aus:
Das Schöne an dieser Variante ist das Einfache. Wir haben eine generische Funktion als Microservice und der Rest der Logik haben wir direkt in BigQuery mit nur einer einzigen Abfrage umgesetzt.
Outside in
Sie haben sicher festgestellt, dass die Remote Function in BigQuery die Architektur umkehrt. Klassisch ist die Logik und Steuerung in einer externen Anwendung (Funktion) und die Datenbank dient nur als Speicher, welcher abgefragt wird und dann wieder die Ergebnisse speichert. Mit der Remote Function steht nun die zentrale Business Logik in BigQuery und die spezifische Funktion wird von aussen zu BigQuery hinein gezogen. Damit eröffnen sich neue Architektur-Patterns.
Was ist besser?
It depends sucks. Aber es kommt darauf an. Wenn aber die eigentliche Funktion nur darin besteht, Daten zu laden, diese Record für Record zu iterieren und irgend eine Logik darauf anzuwenden und die Resultate dann wieder zu speichern, ist die Outside in Variante eleganter. Insbesondere dann, wenn die spezifische Logik, die auf die Records angewendet wird, von generischer Natur ist und wiederverwendet werden kann. Dann kann man sich einen Microservice sparen und muss Business-Logik nicht imperativ programmieren. Eine simple SQL-Query - auch wenn sie einen Remote Function Call beinhaltet - ist meist verständlicher und besser wartbar.
Wie immer gibt es eine Kehrseite der Medaille. Individuelle Funktionen und gespeicherte Queries und insbesondere geplante Abfragen können unübersichtlich werden und bedürfen einer guten (Architektur-) Dokumentation. Achten Sie darauf, dass Sie auch in BigQuery modularisieren und Tabellen und Abfrage als Schnittstelle betrachten und so isoliert wie möglich umsetzen. Ansonsten laufen Sie Gefahr, hier einen Monolithen zu etablieren.
Betreffend die Übersicht und Dokumentation empfehle ich, solche Definitionen in BigQuery mit Terraform oder einem anderen geeigneten IaaC-Tool zu definieren. Keines Falls sollten Sie in der Produktion solche Dinge von Hand in der Konsole zusammenklicken. Mit einer gut strukturierten IaaC-Definition behalten Sie auch Outside in im Griff.
Implementierung Lösungsvariante C
Ich habe den Microservice mit ESC und die Remote Function in BigQuery implementiert und für die Abfrage eine Beispiel-Query erstellt. Sie finden den kompletten Source-Code inkl. Terraform Open-Source auf GitHub: https://github.com/asderix/ESC-GCP-BQ-Remote-Function--Demo
Das Resultat der Abfrage in BigQuery sieht wie folgt aus:
Die Werte in der Spalte "Similarity" kommen von der Remote Function, welche die Cloud Run function aufruft, welche die ESC-Bibliothek verwendet.
Ressource: BigQuery ruft bei der Nutzung einer Remote Function diese mit einem Http-Post mit einem bestimmten JSON als Body auf. Das JSON-Format dieses Aufrufes finden Sie hier.
Keine Kommentare:
Kommentar veröffentlichen