Dieser Artikel ist der erste Teil einer vierteiligen Serie über die Funktionsweise der Envision-Virtual-Machine: der Software, die Envision-Skripte ausführt. Siehe Teil 2, Teil 3 und Teil 4. Diese Serie behandelt nicht den Envision-Compiler (vielleicht ein anderes Mal), daher nehmen wir einfach an, dass das Skript irgendwie in den Bytecode umgewandelt wurde, den die Envision-Virtual-Machine als Eingabe akzeptiert.

Eine Supply Chain Optimization-Pipeline deckt eine Vielzahl von Datenverarbeitungsanforderungen ab: Datenübernahme und -erweiterung, Merkmalsextraktion, probabilistische Prognose, Erzeugung optimaler Entscheidungen unter Einschränkungen, Datenexport, Analyse und Dashboard-Erstellung. Jede Pipeline eines Unternehmens ist unterschiedlich, mit eigenen Eingaben, Regeln, Einschränkungen und Ausgaben. Das Datenvolumen ist groß: Nach Erfahrungen von Lokad müssen selbst unsere kleinsten Konten täglich Gigabyte an Daten verarbeiten, und unsere größeren Konten erreichen weit über ein Terabyte pro Tag. Da die Optimierungspipeline normalerweise auf tägliche Eingaben aus der restlichen internen Datenverarbeitung des Unternehmens wartet, hat sie nur wenige Stunden Rechenzeit, um Entscheidungen für morgen basierend auf den heutigen Daten zu optimieren.

Beyond time-series

Es ist nicht schwer. Wirklich nicht. Die Verarbeitung mehrerer Terabyte in wenigen Stunden ist ein Leistungsziel, das ein Team erfahrener Softwareingenieure problemlos erreichen kann.

Die Herausforderung von Lokad besteht darin, solche Pipelines ohne Softwareingenieure aufzubauen. Natürlich arbeiten bei Lokad Softwareingenieure, aber sie entwickeln Tools und Infrastruktur für das gesamte Unternehmen, nicht für einzelne Kunden. Stattdessen haben wir unsere Supply Chain Scientists - ein Team von Supply Chain-Experten, die die spezifischen Probleme jedes Kunden verstehen und die Lösungen entwerfen. In einer traditionelleren Unternehmensstruktur wären dies die Produktmanager, diejenigen, die den Kunden zuhören und dann den Softwareingenieuren genau mitteilen, was implementiert werden muss. Unsere Entwicklungsphilosophie und der Grund für die Erstellung von Envision, unserer eigenen Programmiersprache, ist, dass es für einen Supply Chain Scientist schneller sein sollte, die Lösung selbst zu implementieren, als die Spezifikationen für andere zu schreiben.

Um dies zu erreichen, ohne Skalierbarkeit oder Leistung einzubüßen, gibt es drei Funktionen, die Envision kostenlos1 und ohne Aufwand des Supply Chain Scientists bereitstellen muss.

  • Das Speichermanagement muss automatisch erfolgen. Dies beinhaltet natürlich einen Garbage Collector, bedeutet aber auch, dass Envision transparent mit großen Datensätzen umgehen sollte. Das Erstellen eines zehn Gigabyte großen Arrays ist Standard: Das sind noch nicht einmal drei Milliarden Zahlen! Die Fähigkeit, mit einem Datensatz zu arbeiten, der größer ist als der Speicher einer einzelnen Maschine, wird erwartet. Tatsächlich unterstützt Envision Datensätze, die größer sind als der Speicher des gesamten Clusters, indem es geschickt auf NVMe-Laufwerke ausweicht.
  • Mehrkern- und Mehrmaschinen-Parallelverarbeitung muss automatisch erfolgen. Parallelisierbare Operationen sollten auf so viele Kerne wie möglich im Cluster verteilt werden, ohne menschliches Eingreifen. Ein Skript sollte den Ausfall einer einzelnen Maschine im Cluster überstehen, ohne von vorne beginnen zu müssen. Ein Skript sollte auch dann abgeschlossen werden können, wenn der Cluster auf eine einzige Maschine reduziert wird. Und natürlich sollten zwei Envision-Ausführungen gleichzeitig auf demselben Cluster ausgeführt werden können.
  • Wir erwarten, dass Envision wenig Hardware zum Ausführen benötigt. Viele Leistungsprobleme können durch Ausgeben von Millionen von Dollar für hochwertige Hardware und/oder Serverlizenzen gelöst werden, aber wir möchten eine Situation vermeiden, in der das Klicken auf die Schaltfläche “Ausführen” eines Skripts Hunderte von Dollar kostet.

Allgemeine Programmiersprachen bieten diese Funktionen nicht, und obwohl sie in der Regel mit Frameworks kombiniert werden können, die dies tun (Scala + Spark, Python + Dask und andere), werden dabei zu viele scharfe Kanten für den Benutzer freigelegt. In diesem Sinne ähnelt Envision mehr SQL, das auf BigQuery ausgeführt wird.

Umgebung

Envision kann nicht lokal installiert oder ausgeführt werden. Stattdessen verbinden sich alle Benutzer mit der Online-Plattform von Lokad, die eine browserbasierte IDE zum Bearbeiten und Ausführen von Skripten bereitstellt. Die Daten und Dashboards werden ebenfalls online gespeichert und über eine Web-Schnittstelle sowie über SFTP und FTPS abgerufen.

Wenn ein Benutzer ein Skript über die Web-Schnittstelle ausführt, wird eine Mission erstellt, die an einen Envision-Cluster zur Ausführung gesendet wird.

Envision wird im Batch-Modus ausgeführt: Jede Ausführung liest die gesamten Eingabedaten und erzeugt eine vollständige Ausgabe. Dies kann zwischen 5 Sekunden für ein sehr einfaches Skript, das auf wenigen Daten läuft, und 30-40 Minuten für ein großes Skript zur Datenanreicherung dauern, und sogar mehrere Stunden für einige Machine Learning Aufgaben.

Andere Ausführungsmodi wie Stream-Verarbeitung (das Hören auf neue Eingaben und das Erzeugen der entsprechenden Ausgabe in Echtzeit) oder transaktionaler Zugriff (das Lesen nur weniger Zeilen Daten und das Schreiben weniger Zeilen zurück) werden nicht unterstützt: Die Sprachprimitiven und die Details der Low-Level-Implementierung, die für diese Modi mit vernünftiger Leistung erforderlich sind, stehen im Widerspruch zu denen des Batch-Modus.

Cluster-Struktur

Ab 2021 läuft Envision vollständig auf .NET 5 und wird auf Ubuntu 20.04-Virtual Machines in der Microsoft Azure Cloud gehostet. Ein Cluster besteht aus 2 bis 6 Standard_L32s_v2-Instanzen: 32× AMD EPYC 7551-Kerne, 256GiB Arbeitsspeicher und 4× NVMe-Laufwerken mit insgesamt 7,68 TB Speicherplatz. Diese Maschinen werden als Worker bezeichnet.

Der Cluster hat eine M-zu-N-Verbindung zwischen Workern und Missionen: Ein einzelner Worker kann mehrere Envision-Skripte gleichzeitig ausführen, und wenn ein Skript mehreren Workern zugewiesen ist, arbeiten sie zusammen, um es schneller abzuschließen.

Jeder Cluster hat auch einen Scheduler, einen kleineren Standard_B2ms mit 2× Kernen und 8GiB Arbeitsspeicher. Der Scheduler stellt die API-Endpunkte für externe Anwendungen bereit, um neue Missionen einzureichen und die Ergebnisse abgeschlossener Missionen zu sammeln. Er ist auch dafür verantwortlich, Missionen an eine oder mehrere Maschinen im Cluster zu verteilen. Abhängig von der Auslastung und dem Grad der Parallelität, der jedem Skript zu einem bestimmten Zeitpunkt zur Verfügung steht, kann der Scheduler Worker einer Mission hinzufügen oder entfernen.

Das gesamte System wurde so konzipiert, dass es widerstandsfähig ist: Sobald einer Mission ein Worker zugewiesen wurde, kann dieser die Mission auch dann abschließen, wenn alle anderen Worker im Cluster sowie der Scheduler offline gehen. Die Zusammenarbeit mehrerer Worker und die erneute Zuweisung von Missionen durch den Scheduler sind daher Leistungsoptimierungen, die nicht für den Abschluss der Mission erforderlich sind.

Blob-Speicher

Lokad verwendet keine SQL-Datenbanken für Kundendaten. Die gehosteten Lösungen können die Datensätze unserer größeren Kunden nicht einfach aufnehmen (sie erreichen oft ihre Grenzen zwischen 4 TB und 16 TB), und das Betreiben eigener Server würde einen Aufwand erfordern, den wir lieber anderweitig einsetzen würden.

Auf der anderen Seite wird Envision im Batch-Modus ausgeführt, was die Notwendigkeit von Abfragen, die komplexer sind als “Lese Spalte X zwischen den Zeilen L und M”, eliminiert: Sobald die Eingabedaten geladen wurden, kann der Worker sie bei Bedarf indizieren und erneut verarbeiten.

Aus diesem Grund verwenden wir Azure Blob Storage als unseren primären Speicher. Es ermöglicht uns, mehr als ein Petabyte zu speichern, und das zu weniger als 1% der Kosten von gehosteten SQL-Datenbanken. Die Leistung der Leseabfragen liegt zuverlässig zwischen 30 MB/s und 60 MB/s.

Wir haben auch unsere Blobs unveränderlich gemacht: Es ist möglich, neue Blobs zu erstellen, aber nicht vorhandene zu ändern. Dadurch wird sichergestellt, dass die Eingaben eines Skripts während der Ausführung nicht geändert werden können und dass die Ausgaben eines Skripts erst gesehen werden können, wenn die Ausführung abgeschlossen ist und die Kennungen der neuen Blobs zurückgegeben werden.

Genauer gesagt hat Lokad einen Content-Addressable Store auf Azure Blob Storage aufgebaut. Es ist Open Source und als ein Paar von NuGet-Paketen Lokad.ContentAddr und Lokad.ContentAddr.Azure verfügbar. Das Wissen um den Hash-Wert einzelner Dateien ermöglicht es Envision festzustellen, dass sich einige seiner Eingaben nicht geändert haben, so dass es berechnete Werte, die im Cache von einem vorherigen Lauf gespeichert sind, wiederverwenden kann.

Bereitstellung

Envision verwendet keine Containerisierung (wie Docker), da die Vorteile von Containern den zusätzlichen Aufwand nicht rechtfertigen.

Erstens erfordert High-Performance Computing den vollen Einsatz von CPU, RAM und Speicher unserer Worker, sodass es nicht möglich ist, mehrere Anwendungen auf demselben Rechner auszuführen.

Zweitens haben wir festgestellt, dass dotnet publish ausreicht, um eine Anwendung zusammen mit allen Abhängigkeiten plattformunabhängig zu verpacken (und tatsächlich schneller als docker build). Mit .NET 5 bietet Microsoft herausragende plattformübergreifende Unterstützung, und es genügt, die Ergebnisse eines Builds von einer Windows-Maschine auf einen Linux-Host zu kopieren.

Schließlich vermeiden wir es aktiv, neue Instanzen von Grund auf zu erstellen. Während wir Worker herunterfahren können, um Kosten zu senken, ist das Erstellen neuer Cluster oder das Hinzufügen weiterer Worker zu bestehenden Clustern eine finanzielle Entscheidung: Wir stellen unseren Kunden keine Rechnungen basierend auf dem Ressourcenverbrauch aus, sodass wir die zusätzlichen Kosten nicht weitergeben können.

Nächste Woche werden wir uns mit dem Ausführungsmodell von Envision und der Darstellung der zu erledigenden Arbeiten im Cluster befassen.

Unverschämte Werbung: Wir suchen Softwareingenieure. Remote-Arbeit ist möglich.


  1. nichts ist wirklich kostenlos, und wir bezahlen dafür, indem wir die Fähigkeit von Envision opfern, als allgemeine Programmiersprache zu fungieren. Zum Beispiel bedeutet automatische Parallelisierung, dass wir niemals eine explizite Kontrolle über Threads in Envision unterstützen werden; automatische Mehrmaschinenverarbeitung bedeutet, dass es in Envision niemals das Konzept einer “lokalen Maschine” geben wird. ↩︎