Im Jahr 2018 entwickelte die OTTO BI (Team Siggi) in Zusammenarbeit mit dem Online Marketing eine hauseigene Programmatic Advertising Lösung. Hierfür wurde unter anderem eine eigene SSP (Supply Side Platform) und DSP (Demand Side Platform) Lösung entwickelt – genannt Orbidder.
Die Hintergründe rund um die SSP, die DSP und wie diese Services zusammenarbeiten, wurden bereits von unserem Kollegen Dr. Rainer Volk in diesem Blog Post beschrieben.
Seit 2019 ist der Orbidder live und nimmt täglich 1 – 1,2 Milliarden Bid Requests entgegen und steuert, über im Hintergrund laufende Data Science Modelle, mehr als 300 aktive Marketingkampagnen zeitgleich. Anfang 2022 wurde entschieden, dass über die initial mit dem Vertrieb aufgesetzte Lösung, zur Steuerung von vermarkteten Werbekampagnen auf externen Webseiten, nun auch für die Steuerung von otto.de Flächen zum Einsatz kommen soll. Dies führte zu neuen Anforderungen an die Systemlandschaft und die Steuerungslogik. Im nachfolgenden Post möchten wir darauf eingehen, wie wir diese neuen Anforderungen umgesetzt haben.
Grundsätzlich funktioniert die SSP/DSP Lösung wie in Abbildung 1 schematisch dargestellt. Bid Requests für Ad Slots (auch Placement genannt) werden über openRTB bzw. einen prebid.js Adapter an die SSP übermittelt. Von dort werden sie aufbereitet an die DSP weitergeleitet. In der DSP erfolgt die Gebotsermittlung, auf Basis der von einem Data Science Team bereitgestellten Modelle. Das errechnete Gebot wird zurückgegeben und für eine Auktion verwendet. Sollte diese gewonnen werden, wird anschließend ein Werbemittel angezeigt.
Abbildung 1: Grundsätzlicher Aufbau SSP/DSP
Abbildung 2: User Assignment
Was passiert nun, wenn ein Nutzer auf otto.de zum Beispiel auf eine Angebotsseite mit Tablets oder Smartphones kommt?
Abbildung 3: Problem 1
Im Beispiel 1 landet User 1 auf der Seite für Multimedia. Obwohl ihm die Kampagne „Technik“ zugewiesen wurde, passt der Scope in diesem Fall nicht. Hintergrund ist, dass für diesen Kunden die optimale Klickwahrscheinlichkeit auf externen Seiten liegt. Somit würde diesem Kunden kein Banner auf otto.de angezeigt werden. Im Beispiel 2 ruft User 3 die Seite auf. Für ihn wurde zwar ein Onsite Scope berechnet, aber er befindet sich im falschen Kontext, eben Technik und nicht wie berechnet Fashion. Also: wieder keine Ausspielung.
Abbildung 4: Problem 2
Ein Bid Request umfasst üblicherweise immer alle Placements / Ad Slots auf einer Webseite. Gebote werden nun zwar für alle Placements einzeln abgegeben, allerdings erfolgt das bisherige Assignment auf Basis von Batch Prozessen. Dadurch ist ein User immer für 2 Stunden nur genau einer Kampagne zugeordnet. Wenn nun auf einer Webseite 3 Placements zur Verfügung stehen und es werden 2 Placements gewonnen, dann wird auf beiden Placements die gleiche Kampagne und damit auch das gleiche Werbemittel ausgespielt.
In unserem Beispiel wurde für User 1 eine Technik Kampagne berechnet, die auch den richtigen Scope hat. Es fallen also alle Werbeplätze auf der Seite der gleichen Kampagne zu, was dann in der Ausspielung ähnlicher Werbemittel auf allen Placements resultiert.
Wie löst man nun diese Probleme? – Zunächst einmal mit Diskussionen.
In gemeinsamen Runden zwischen den Entwicklern beider Teams wurden die Kernanforderungen zusammengefasst:
Wie schafft man es nun in Echtzeit die optimale Gebotshöhe und Kampagne zu berechnen? Diverse Faktoren wie Budget, relevante Kampagnen und verschiedenste User Features müssen in Millisekunden zur Verfügung stehen und schließlich über eine von den Data Scientisten zur Verfügung gestellte Formel in Relation zueinander gebracht werden. Und das alles mit einer sehr niedrigen Latenz.
Da eins unserer Ziele auch die Unabhängigkeit der Data Scientisten war, wollten wir die Implementierung einer aufwändigen und statischen Funktion direkt in einem Service vermeiden. Das Lesen und Schreiben großer Datenmengen in eine Datenbank scheiterte an der Geschwindigkeit und einzelne Schritte vorberechnen, war wiederum nicht flexibel genug, um den Problemstellungen gerecht zu werden.
Abbildung 5: TensorFlow Logo
Nach einiger Überlegung und Diskussion zeigte sich, dass die beste Möglichkeit die Nutzung eines Maschine Learning Frameworks wäre, was den Export eines kompletten, trainierten Models inkl. den zugehörigen Daten ermöglicht. Dieses exportierte Model müsste dann in einen Service integriert und über eine Schnittstelle aufgerufen werden.
So entstand der Ansatz TensorFlow und TensorFlow Serving zu verwenden, welches von Google entwickelt wurde, um genau diese Art des Use Cases zu vereinfachen. TensorFlow ist ein Framework, mit dem sich Aufgaben des maschinellen Lernens und der künstlichen Intelligenz umsetzen lassen. Durch TensorFlow können die Data Scientisten Modelle und Daten in einem exportierbaren Format kapseln, so die Kontrolle über die Algorithmen behalten und diese unabhängig verändern ohne das in der SSP oder DSP weiterer Entwicklungsaufwand notwendig ist. TensorFlow Serving bietet dabei eine einfache und hoch performante Möglichkeit die Modelle, nach dem Training, operativ zu betreiben. Im Wesentlichen können so die angereicherten Bid Requests per API an TensorFlow Serving geschickt und die Response, bestehend aus dem Gebot und der Kampagne, ausgelesen werden.
Für die Berechnung wurde ein Großteil der Daten bzw. Features schon in die TensorFlow Modelle hinein implementiert und dann als ‚saved model‘ exportiert, wodurch die Modelle mit + 15 GB sehr groß sind. Die Modelle werden durch den Model Server von TensorFlow Serving komplett in den ORBIDDER geladen. Dies ermöglicht eine sehr schnelle Ausführung des Modelcodes und Antwortzeiten im 10 – 20 Millisekunden Bereich.
Für die Berechnung der Gebote und der Kampagnen fehlen dann nur noch die angereicherten Bid Requests. Darin enthalten sind neben der UserId, auch Informationen zu Kampagnen, Kontext und Budget. Der Model Server von TensorFlow Serving stellt out-of-the-box Schnittstellen für gRPC und REST zur Verfügung, über die dann das Model angesprochen werden kann.
Die Response wird dann in der SSP weiterverarbeitet und es wird ein Gebot für ein Placement abgegeben, was dann eventuell zu einer Impression und im allerbesten Fall auch zu einem Klick führt.
Zu den genaueren Inhalten der Gebotsberechnung gibt es von den Data Science Kolleg*innen hier einen Blog Artikel.
In dem anschließenden, vereinfachten Architekturbild ist die SSP/DSP Landschaft einmal aufgezeichnet. Anhand dieser lässt sich auch der Weg des Bid Requests durch die SSP/DSP ablesen. Weiterhin zeigt es einige kritische Komponenten, die im Anschluss kurz erläutert sind.
Abbildung 6: Architecture flow map
Kubernetes
Der Kubernetes Cluster besteht aus einzelnen Node Pools in unterschiedlichen Zonen, um den unterschiedlichen Anforderungen der Services gerecht zu werden (CPU vs. RAM).
Redis
Unser Applikationscache hält die Daten zwischen 1 und 60 Sekunden vor, danach aktualisiert er sich durch die Daten aus der Redis.
Cloud Storage
Für einfachen Dateiaustausch und zur Ablage und Archivierung von Daten, kommt Cloud Storage zum Einsatz.
PubSub
Ein Message Broker für die Übertragung der Bids, Impression und Klickevents, sowie von Kampagnenänderungen.
BigQuery
Data Warehouse zur dauerhaften Speicherung von Daten zur Verbesserung der Modelle und Algorithmen, sowie zur Abrechnung von Erlösen und Kosten.
Filestore
Da in GKE ein Storage nicht gleichzeitig lesend und schreibend in einen Pod gemounted werden kann, nutzen wir Filestore (NFS) um dies zu ermöglichen. Notwendig wird dies um TensorFlow Modelle ohne Downtime zu tauschen.
Prometheus / Grafana
Aus unseren Services werden in Echtzeit Metriken generiert, die in Prometheus gespeichert und mit Grafana visualisiert werden.
TensorFlow Serving
Um die Modelle der Data Scientisten operativ laufen zu lassen, werden diese mit allen notwendigen Daten exportiert und anschließend mit TensorFlow Serving via REST mit Bid Requests angefragt.
• 71 Milliarden Requests wurden von der Orbidder SSP im Oktober 2022 gehandelt
• Im Schnitt verarbeitet der Orbidder zwischen 25.000 und 30.000 Requests pro Sekunde
• 100.000 Request pro Sekunde ist der maximale registrierte Durchsatz der Requests an die Orbidder SSP
• 15 Millisekunden ist die durchschnittliche Latenz bei der Verarbeitung eines Requests
• Etwa 1000 ist die Anzahl der Service Instanzen (Pods) im GCP Kubernetes-Cluster vom Orbidder tagsüber
Otto hat das Ziel bis 2030 klimaneutral zu sein. Die Klimaziel der Otto Group lassen sich am besten hier nachlesen.
In der Otto BI und damit auch beim Orbidder, versuchen wir diese Ziele immer im Blick zu halten. Leider ist es schwierig, den CO2 Ausstoß auf der Frontend Seite z.B. im Internetbrowser bei dem Ausspielen der Online-Werbung zu beeinflussen bzw. überhaupt messbar zu machen, aber auf der SSP/DSP Seite im Backend kann man sich bewusst für nachhaltigen Technologien entscheiden.
Die Google Cloud Platform bietet dabei eine breite Palette von Möglichkeiten, Services nachhaltig zu gestalten, so gibt es z. B. bei der Auswahl der Services immer die Möglichkeit zu sehen, wieviel CO2 durch eine Technologie oder einen Service verursacht wird.
Durchschnittlich produzieren die Komponenten vom Orbidder System etwa 1,6t. CO2 pro Monat. Ist das viel oder wenig? Studien zufolge entspricht dies dem CO2 Ausstoß von 6 Personen je Tag.
Lässt man die Grundsatzdiskussion über die Notwendigkeit von Werbung im Internet einmal komplett außer Acht, sind dies schon vergleichbar gute Werte. Die Rechenzentren in Belgien, wo unsere Services laufen, haben ein paar der niedrigsten Werte in Europa. Trotzdem Orbidder wir stetig daran Rechenoperationen zu verschlanken und somit den Ressourceneinsatz zu verringern.
Willst du Teil des Teams werden?
We have received your feedback.