Direkt zum Hauptinhalt
Branchen

Eclipse Aerospace vereinfacht die Erfassung von Flugdaten mit Databricks

Schnellerer Datenzugriff mit einem auf Rust basierenden Apache Arrow Connector

von Jack Yallop

  • Optimierter Datenzugriff: Eclipse hat fast 20 Jahre Flugtelemetrie modernisiert, damit Servicezentren und Ingenieure schneller auf wichtige Informationen zugreifen können.
  • Verbesserte Leistung und Geschwindigkeit: Die Datenverarbeitungsgeschwindigkeit wurde deutlich erhöht, sodass große Flugverläufe in wenigen Minuten analysiert werden können.
  • Geringere Kosten und Komplexität: Redundante Speicher und mehrstufige Verarbeitungssysteme wurden abgeschafft, was den Betriebsaufwand senkt und die Verwaltung vereinfacht.

Da Unternehmen in der Luft- und Raumfahrt, der Fertigung, den Life Sciences und anderen datenintensiven Branchen ihre Abläufe modernisieren, bringen sie immer mehr Sensor-, Telemetrie- und wissenschaftliche Daten in Datenplattformen ein. Ein Großteil dieser Daten ist jedoch nach wie vor schwer im großen Maßstab zu nutzen, da sie in spezialisierten oder proprietären Formaten vorliegen, die gängige Analyse-Engines nicht direkt lesen können. Dies führt zu einer praktischen Herausforderung für die Branche: Teams müssen sich entscheiden, ob sie langsame, speicherintensive benutzerdefinierte Transformationen entwickeln, spezielle Konnektoren pflegen oder zusätzliche Dekodierungsschritte einfügen, bevor die Daten analysiert werden können.

PySpark-Benutzerdefinierte Datenquellen bieten einen einfacheren Weg nach vorn. Sie ermöglichen es Teams, spezialisierte Formate mithilfe von Python in Spark einzubinden, ohne einen separaten Konnektor von Grund auf neu zu erstellen. Für Eclipse Aerospace, einen Hersteller und Betreiber sehr leichter Jets, bot die Partnerschaft mit Databricks eine Möglichkeit, fast zwei Jahrzehnte an proprietären Flugdaten für Servicezentren und Ingenieure leichter zugänglich zu machen, ohne kostspielige, ständig aktive Infrastruktur hinzuzufügen. Databricks stellte die serverlose Datenplattform und die Spark-Integration bereit, während Eclipse Aerospace seine bestehenden Flugdatendekoder und seine Expertise in der Luft- und Raumfahrt einbrachte.

Gemeinsam entwickelten Eclipse Aerospace und Databricks einen direkteren Weg von den Flugdaten zur Analyse, wodurch die Latenz, Duplizierung und der operative Aufwand des vorherigen mehrstufigen Prozesses reduziert wurden, während die Flexibilität von Databricks Serverless erhalten blieb.

Die Herausforderung von Eclipse Aerospace bei der Datenaufnahme

Eclipse Aerospace nutzt Databricks zum Speichern von fast 20 Jahren Flugdaten mit einem Gesamtvolumen von mehr als 3 TB. Das Unternehmen unterstützt seine Servicezentren und Ingenieure mit Daten aus über einer Million Flugstunden, die aus mehreren Quellen bezogen und verifiziert wurden, darunter: Satellitentelemetrie fast in Echtzeit; große Massen-Uploads vollständiger Flugverläufe; und externe Datenquellen. Die gesamte Telemetrie wird in proprietären Dateiformaten gespeichert, die benutzerdefinierte Dekodierer erfordern, die bereits in mehreren Sprachen, einschließlich Rust und Scala, entwickelt wurden.

Bevor die PySpark-benutzerdefinierten Datenquellen in Databricks Runtime 15.4 LTS verfügbar waren, hatte das Team von Eclipse Aerospace drei praktikable Optionen zum Dekodieren und Laden seines proprietären Dateityps in Databricks:

  1. Laden der Dateien als Binärtypen und Aufteilung mithilfe von Python-benutzerdefinierten Funktionen (UDF)
  2. Verwenden eines benutzerdefinierten Dekodierers in Scala auf dedizierten Rechenressourcen
  3. Dekodieren von Dateien in einen intermediären Dateityp vor dem Laden in Databricks

Für einfache benutzerdefinierte Dateitypen sind UDFs eine naheliegende Option. Sie opfern Leistung zugunsten der Lesbarkeit. Wenn das Nachrichtenformat jedoch eine variable Länge hat, bergen UDFs das reale Risiko, dass bei langen Nachrichten der Arbeitsspeicher knapp wird, weshalb sie nicht verwendet werden konnten. Der bestehende Scala-Dekodierer hätte zur Implementierung einer nativen Datenquelle v1/v2 verwendet werden können, aber die Verwendung in Databricks erforderte dedizierte Rechenressourcen. Die Benutzer erwarteten, dass Daten schnell hochgeladen und angezeigt werden können, was erfordern würde, diese Rechenressourcen ständig aktiv zu halten, was höhere Kosten als die anderen serverlosen Optionen bedeutete. Dies ließ dem Team nur die letzte Option, die Dateien über einen Rust-basierten Dekodierer auf AWS Lambda in ein intermediäres Dateiformat – in diesem Fall Avro – zu dekodieren.

Diese Lösung war kostengünstig, aber sie:

  • führte zu Latenzen durch das Multi-Hop-Aufnahmemuster
  • duplizierte die Schemalogik zwischen dem Lambda-Dekodierer und den nachgelagerten Spark-Jobs, die die Avro-Ausgabe verbrauchten
  • kostete das Team die Beobachtbarkeit der Pipeline; die Spark-Benutzeroberfläche und die Job-Metriken sahen die Welt immer nur ab der Avro-Landing-Zone, sodass Ingenieure, die eine langsame oder fehlgeschlagene Aufnahme debuggten, Lambda-Protokolle in einem völlig anderen Teil der Plattform suchen mussten
  • bedeutete, für die Speicherung sowohl der originalen Binärdateien als auch deren Avro-Kopien zu bezahlen

Warum Databricks und PySpark-benutzerdefinierte Datenquellen wichtig sind

Databricks war bereits die Plattform von Eclipse Aerospace zum Speichern und Verarbeiten von Flugdaten. Der entscheidende Vorteil war die Kombination aus Spark-nativer Ausführung, der PySpark-benutzerdefinierten Datenquelle und Databricks Serverless. Databricks Serverless weist bedarfsgesteuerte Rechenressourcen automatisch zu und verwaltet sie, sodass Eclipse Aerospace keine dedizierte Infrastruktur bereitstellen oder betriebsbereit halten musste. Die PySpark-benutzerdefinierte Datenquelle machte es möglich, den bestehenden Rust-Dekodierer zu umschließen, ohne einen JVM-Konnektor zu erstellen. Da die Dekodierung nun innerhalb von Spark läuft, bietet dieselbe Plattform die Spark-Benutzeroberfläche, Job-Metriken und nachgelagerte Delta-Workflows, anstatt den kritischen Aufnahmeschritt an eine separate Lambda-Pipeline zu übergeben.

Die meiste Datentechnik basiert auf gängigen Datenformaten, aber einige der wertvollsten Daten bleiben in Formaten verborgen, die PySpark nicht direkt zugänglich sind, wie HDF5 für wissenschaftliche Daten und Industriestandards wie ARINC 429 für Luft- und Raumfahrtdaten. Databricks Serverless bietet Teams eine flexible Möglichkeit, diese Daten im großen Maßstab nutzbar zu machen, während Rust-basierte Implementierungen eine Leistung liefern können, die über einen reinen Python-Ansatz hinausgeht.

Mit PySpark-benutzerdefinierten Datenquellen können Sie ein les-/schreibbares Dateiformat in reinem Python definieren, das wie jede andere Datenquelle verwendet werden kann. Ihr Hauptvorteil ist die Einfachheit, die es Benutzern ermöglicht, Funktionen zum Lesen und Schreiben in reinem Python zu definieren sowie wichtige Merkmale wie die Parallelisierung festzulegen. Das Team von Eclipse Aerospace ging noch einen Schritt weiter: Durch die Entwicklung eines auf Rust basierenden Python-Pakets erzielte es die doppelte Leistung im Vergleich zu einer ähnlichen PyArrow-basierten, reinen Python-Datenquelle. Verglichen mit einer nativen Python-Implementierung ohne Arrow lieferte es fast die 20-fache Leistung.

Das folgende Beispiel-Code-Repository führt durch eine benutzerdefinierte Datenquelle für ARINC 429. Der begleitende Code enthält Designentscheidungen, die über den Rahmen dieses Artikels hinausgehen, sowie Benchmarks, die Sie selbst ausführen können.

Wie dies mit den PySpark-benutzerdefinierten Datenquellen lösbar wurde

Apache Spark 4.0 hat eine Python Data Source API veröffentlicht (in der Databricks-Dokumentation als PySpark-benutzerdefinierte Datenquellen bezeichnet), mit der Sie ein benutzerdefiniertes Format für Batch- oder Streaming-Daten registrieren können wie:

Die in Apache Spark 4.0 eingeführte und auf Databricks verfügbare API ist ab Databricks Runtime 15.4 LTS allgemein verfügbar und wird auch in der Serverless-Umgebung 2 und später unterstützt. Da die API in Python implementiert ist, erfordert sie keinen separaten JVM-Konnektor oder JAR.

Die API ist eine vereinfachte Schnittstelle zum Erstellen neuer nativer Datenquellen, bei der der Benutzer einige Klassen implementiert und diese registriert mit:

Diese Dekodierer gibt es in zwei Ausführungen: als natives Python und im Apache Arrow-Format, wobei letzteres die wesentlich leistungsstärkere Wahl ist. Apache Arrow ist ein „sprachunabhängiges spaltenorientiertes Speicherformat für flache und verschachtelte Daten“ und damit eine ideale Schnittstelle zwischen verschiedenen Sprachen, die Datenobjekte gemeinsam nutzen. Die API macht sich dies zunutze, indem sie Benutzern erlaubt, pyarrow.RecordBatch-Objekte direkt zurückzugeben, und Spark verarbeitet sie als Batches, anstatt für jede Zeile ein InternalRow zu materialisieren.

Dies warf die Frage auf: Könnte Python als Steuerungsschicht dienen, während Rust die rechenintensive Dekodierung übernimmt und dabei durchgehend das Apache Arrow-Format beibehält?

Übersicht über die Lösungsarchitektur

Die Architektur dahinter ist ein einzelner logischer Konnektor mit einem Dekodierungspfad, der fast vollständig aus kompiliertem Code besteht, und einer Python-Schicht, die so schlank ist, dass sie minimale Änderungen am bestehenden Rust-SDK von Eclipse Aerospace erfordert. Diese schlanke Python-Schicht wurde mit PyO3/Maturin als eigenständiges Paket erstellt, in Pipelines importiert und genau wie jede andere Quelle verwendet. Um die beste Leistung und Trennung der Belange zu gewährleisten, folgte das Team einigen architektonischen Prinzipien.

image1.png

Der Dekodierer weiß nicht, dass Spark existiert. Die Rust-Kern-Crate hat keine Abhängigkeit von PyO3, Arrow oder Spark, nimmt lediglich Bytes entgegen und gibt strukturierte Datensätze zurück. Dadurch konnte Eclipse Aerospace die bestehende Dekodierungslogik weitgehend unverändert portieren, da das SDK dafür nicht neu geschrieben, sondern nur umschlossen werden musste.

Arrow ist das Einzige, das die FFI-Grenze (Foreign Function Interface) überschreitet, und es überschreitet sie ohne Kopieren. Die Rust-Glue-Schicht baut ein Arrow RecordBatch mit arrow-rs auf und übergibt es dann über die Arrow C Data Interface als pyarrow.RecordBatch an Python. Dabei gibt es keinen Serialisierungsschritt, da keine Bytes re-enkodiert und keine zeilenbasierten Python-Objekte erstellt werden. Die von Rust zugewiesenen Puffer sind die Puffer, die Python liest.

Die PySpark-Schicht ist bewusst schlank gehalten. Sie analysiert Optionen, legt die Partitionierung fest (ein InputPartition pro Eingabe im Referenzbeispiel) und definiert das Schema. Bei benutzerdefinierten PySpark-Datenquellen müssen DataSource, DataSourceReader und deren Streaming-Äquivalente serialisierbar sein, damit Spark die Reader-Instanz per Pickle serialisieren und an einen Worker senden kann. Indem die PySpark-Schicht schlank gehalten wird, enthält die DataSourceReader-Instanz, die Spark per Pickle an einen Worker sendet, ausschließlich einfachen Python-Zustand (Dateipfade, ein Integer für die Batch-Größe). Das auf PyO3 basierende Rust-Objekt wird in read() erstellt, nachdem der Reader bereits auf dem Worker angekommen ist. Es ist selbst niemals Teil des serialisierten Inhalts.

Der native Code wird als Python-Wheel bereitgestellt, das mit Maturin erstellt wurde. Auf Databricks ist dieses Wheel ein Build-Artefakt, das als normale Python-Abhängigkeit an einen Job oder eine Notebook-Umgebung angehängt wird. Da die Implementierung kompiliert ist, erfordern Versions- und Architekturenkompatibilität Sorgfalt, obwohl ein ordnungsgemäßer CI/CD-Prozess dies wie im Beispiel-Repository gezeigt verwalten kann.

Wie der Connector in Databricks ausgeführt wird

Auf Databricks Serverless ist der Laufzeitpfad unkompliziert:

  1. Eine proprietäre Telemetriedatei landet im Cloud-Speicher.
  2. Ein Databricks Spark-Lesevorgang ruft die registrierte benutzerdefinierte PySpark-Datenquelle für das benutzerdefinierte Format auf.
  3. Spark, das auf Databricks ausgeführt wird, erstellt einen serialisierbaren Reader, der den Eingabepfad und Lesepfadoptionen enthält, und plant dann Eingabepartitionen auf Workern.
  4. Jeder Worker erstellt den Rust-Decoder in read() und übergibt die Dateibytes an den kompilierten Decoder.
  5. Rust gibt Arrow RecordBatches über die Arrow C Data Interface zurück, wodurch Serialisierung und Python-Objekte pro Zeile vermieden werden.
  6. Spark, das auf Databricks ausgeführt wird, verarbeitet die Batches und schreibt die decodierten Daten in nachgeschaltete Tabellen wie Delta.
  7. Decodierungsfehler, fehlerhafte Datensätze, Spark UI-Stufen und Job-Metriken bleiben im Databricks-Workflow sichtbar.

Dies ist der wesentliche Unterschied zum vorherigen Design: Die Originaldatei wird im Rahmen des Spark-Lesevorgangs decodiert, anstatt sie zuerst in eine intermediäre Avro-Landing-Zone zu kopieren.

Welche Auswirkungen hatte dies für Eclipse Aerospace?

image2.png

Durch das Ersetzen des mehrstufigen Lambda- und Avro-Workflows durch einen einzelnen Spark-nativen Lesevorgang hat Eclipse Aerospace Flugdaten schneller verfügbar gemacht und gleichzeitig Infrastruktur-, Speicher- und Betriebskosten reduziert. Der neue Arrow-native Connector für benutzerdefinierte PySpark-Datenquellen bringt zudem das Decodieren in Spark, was die Transparenz der Pipeline-Leistung verbessert und die Verwaltung von Schema- und Datenqualitätslogik an einem zentralen Ort ermöglicht.

Die Implementierung der benutzerdefinierten Datenquelle hat die Infrastruktur von Eclipse Aerospace erheblich vereinfacht, und die Effekte zeigen sich in vier verschiedenen Bereichen: Latenz, Kosten, Observability und Engineering-Aufwand.

Latenz. Das Zusammenführen einer Multi-Hop-Pipeline (Upload, Lambda, Decodierung, Avro schreiben, Avro lesen, in Delta laden) in einen einzigen Spark-nativen Lesevorgang eliminiert mehrere Netzwerk-Hops und Stellen, an denen eine Datei in einer Warteschlange auf die nächste Stufe wartet. Der neue Connector kann Datensätze innerhalb einer Minute nach dem Datei-Upload einschließlich der Startzeit verarbeiten und bewältigt dank Parallelisierung große, mehrere Gigabyte große Batches in der Regel parallel in unter zwei Minuten.

Kosten. Das Entfernen des Lambda-Hops und des Avro-Zwischenspeichers hat eine gesamte Infrastrukturebene eliminiert, die Eclipse Aerospace betreiben, überwachen und bezahlen musste. Das Unternehmen zahlt nicht mehr für die Speicherung von zwei Kopien jedes Fluges – der binären Originaldatei und ihrer Avro-Konvertierung –, was bei einer fast zwei Jahrzehnte langen Historie gespeicherter Flüge eine beträchtliche Menge an dupliziertem Speicher ausmacht. Dies bedeutet, dass die Mehrheit der Dateien im Glacier-Speicher auf AWS verbleibt, mit problemlos wiederholbaren Decodierungsvorgängen, falls die Originaldaten jemals wieder benötigt werden.

Observability. Da der Decodierungsschritt nun direkt in Spark stattfindet, wird er in der Spark UI und in den Standard-Job-Metriken wie jede andere Job-Stufe angezeigt. Eclipse Aerospace verwendet benutzerdefinierte „badRecordsPath“-Einstellungen, die den CSV/JSON-Loader imitieren, um fehlerhaft formatierte Daten zu erfassen, und betreibt umfangreiche Datenqualitäts-Pipelines, die Statistiken direkt vom Decoder erfassen.

Engineering-Aufwand. Schemalogik wurde früher doppelt definiert: einmal im Lambda-Decoder, der Avro erzeugte, und erneut in den nachgeschalteten Spark-Jobs, die diesen konsumierten. Jede Änderung des Nachrichtenformats bedeutete, dass beide in unterschiedlichen Codebasen aktualisiert werden mussten, mit dem Risiko, dass die beiden Definitionen auseinanderlaufen. Mit einem einzigen, versionierten Connector wird das Schema einmal direkt neben der Decodierungslogik definiert. Dies bedeutet auch, dass dasselbe architektonische Muster – ein schlanker Python-DataSource, der einen Rust-Decoder umschließt – nun für die anderen proprietären Formate wiederverwendet werden kann, die Eclipse Aerospace unterstützt, anstatt es für jedes Format neu erfinden zu müssen.

Die Erfahrung von Eclipse Aerospace zeigt, wie spezialisierte Datenformate in Databricks integriert werden können, ohne eine separate Ingestion-Plattform darum herum aufzubauen. Indem der Decoder in Rust belassen, Python als Integrationsschicht verwendet und Datensätze über Apache Arrow geleitet werden, liest die benutzerdefinierte Datenquelle Originaldateien direkt in Spark und bleibt mit der Flexibilität von Databricks Serverless kompatibel.

Für Teams, die mit proprietären oder branchenspezifischen Formaten arbeiten, ist das wiederverwendbare Muster einfach. Engineerings-Teams können die domänenspezifische Decodierungslogik nah am Quellformat halten, sie über eine benutzerdefinierte PySpark-Datenquelle bereitstellen und Spark die Partitionierung, Ausführung, Observability und die nachgeschaltete Verarbeitung übernehmen lassen.

Möchten Sie loslegen? Werfen Sie einen Blick auf Folgendes:

Häufig gestellte Fragen

Was ist eine benutzerdefinierte PySpark-Datenquelle?

Eine benutzerdefinierte PySpark-Datenquelle ist eine benutzerdefinierte Spark-Datenquelle, die in Python implementiert ist. Sie ermöglicht es Teams, spezialisierte Dateiformate als Teil eines Standard-Spark-Workflows zu lesen, ohne einen separaten JVM-Connector erstellen zu müssen.

Welches Problem bei der Datenaufnahme löst ein benutzerdefinierter Arrow-nativer Connector für PySpark-Datenquellen?

Der Arrow-native Connector für benutzerdefinierte PySpark-Datenquellen nimmt proprietäre Flugtelemetriedaten in Databricks auf, ohne auf eine mehrstufige Pipeline, dedizierte, ständig aktive Compute-Ressourcen oder doppelte Avro-Kopien der Originaldateien angewiesen zu sein.

Was ist Databricks Serverless?

Databricks Serverless ist ein vollständig verwalteter Compute-Dienst, mit dem Sie Workloads ausführen können, ohne Infrastruktur in Ihrem Cloud-Konto bereitstellen oder verwalten zu müssen. Databricks weist die Compute-Ressourcen automatisch zu, skaliert und optimiert sie für Sie.

Wie verbindet sich Databricks Serverless mit dem Arrow-nativen PySpark-Connector für benutzerdefinierte Datenquellen?

Der Connector verwendet benutzerdefinierte PySpark-Datenquellen, um die Dateileselogik innerhalb von Spark auf serverlosem Compute auszuführen. Python steuert den Kontrollfluss, während Rust und Apache Arrow für eine performante Decodierung und Datenübertragung sorgen. Dadurch können proprietäre Dateien direkt gelesen werden, ohne dedizierte, ständig aktive Compute-Ressourcen oder eine intermediäre Avro-Pipeline. Benutzerdefinierte PySpark-Datenquellen sind in Databricks Runtime 15.4 LTS und höher oder in der Serverless-Umgebung Version 2 verfügbar.

(Dieser Blogbeitrag wurde mit KI-gestützten Tools übersetzt.) Originalbeitrag

Verwandte Tags:

Fertigung

Erhalten Sie die neuesten Beiträge in Ihrem Posteingang

Abonnieren Sie unseren Blog und erhalten Sie die neuesten Beiträge direkt in Ihren Posteingang.