Acceso a datos más rápido con un conector de Apache Arrow respaldado por Rust
por Jack Yallop
A medida que las organizaciones de la industria aeroespacial, manufacturera, de ciencias biológicas y otros sectores con uso intensivo de datos modernizan sus operaciones, incorporan una mayor cantidad de datos de sensores, telemetría e información científica en las plataformas de datos. Sin embargo, gran parte de estos datos sigue siendo difícil de utilizar a escala porque se encuentran en formatos especializados o propietarios que los motores de análisis principales no pueden leer directamente. Esto plantea un desafío práctico en la industria: los equipos deben elegir entre crear transformaciones personalizadas lentas y con uso intensivo de memoria, mantener conectores especializados o agregar fases de decodificación adicionales antes de poder analizar los datos.
PySpark custom data sources ofrece una alternativa más sencilla. Permite a los equipos incorporar formatos especializados en Spark utilizando Python, sin necesidad de crear un conector independiente desde cero. Para Eclipse Aerospace, un fabricante y operador de aviones ligeros, asociarse con Databricks ofreció una forma de hacer que casi dos décadas de datos de vuelo propietarios fuesen más accesibles para los centros de servicio y los ingenieros sin necesidad de añadir una infraestructura costosa y siempre activa. Databricks aportó la plataforma de datos sin servidor y la integración con Spark, mientras que Eclipse Aerospace aportó sus decodificadores de datos de vuelo existentes y su experiencia en el sector aeroespacial.
Juntos, Eclipse Aerospace y Databricks desarrollaron una ruta más directa desde los datos de vuelo hasta el análisis, lo que redujo la latencia, la duplicación y la sobrecarga operativa del proceso multietapa anterior, al tiempo que preservó la flexibilidad de Databricks Serverless.
Eclipse Aerospace confía en Databricks para almacenar casi 20 años de datos de vuelo, lo que suma un total de más de 3 TB. Ofrece soporte a sus centros de servicio y a sus ingenieros con datos de más de un millón de horas de vuelo, recopilados y verificados a través de múltiples fuentes, entre las que se incluyen: telemetría satelital casi en tiempo real; cargas masivas de historiales de vuelo completos; y fuentes de datos externas. Toda su telemetría se almacena en formatos de archivo propietarios que requieren decodificadores personalizados, los cuales ya están desarrollados en varios lenguajes, incluidos Rust y Scala.
Antes de que Pyspark custom data sources estuviera disponible en Databricks Runtime 15.4 LTS, el equipo de Eclipse Aerospace tenía tres opciones viables para decodificar y cargar su tipo de archivo propietario en Databricks:
Para los tipos de archivos personalizados sencillos, las UDF son una opción natural. Sacrifican rendimiento a cambio de legibilidad. Sin embargo, cuando el formato del mensaje tiene una longitud variable, las UDF presentan un riesgo real de agotar la memoria en mensajes largos, por lo que no pudieron utilizarse. El decodificador de Scala existente se podía usar para implementar un Data Source v1/v2 nativo, pero emplearlo en Databricks requería un entorno de procesamiento dedicado. Los usuarios esperaban poder cargar y visualizar los datos rápidamente, lo que habría exigido mantener este entorno siempre activo, lo que representaba un costo mayor en comparación con las demás opciones sin servidor. Esto dejó al equipo con la última opción: decodificar los archivos en un formato de archivo intermediario (en este caso, Avro) mediante un decodificador basado en Rust en AWS Lambda.
Esta solución era económica, pero presentaba los siguientes inconvenientes:
Databricks ya era la plataforma elegida por Eclipse Aerospace para almacenar y procesar los datos de vuelo. La ventaja decisiva fue la combinación de la ejecución nativa en Spark, el origen de datos personalizado de Pyspark y Databricks Serverless. Databricks Serverless asigna y administra automáticamente los recursos de procesamiento bajo demanda, por lo que Eclipse Aerospace no necesitó aprovisionar ni mantener una infraestructura dedicada en funcionamiento. El origen de datos personalizado de PySpark permitió empaquetar el decodificador de Rust existente sin necesidad de construir un conector JVM. Dado que la decodificación se ejecuta ahora dentro de Spark, la misma plataforma proporciona la interfaz de usuario de Spark, las métricas de los trabajos y los flujos de trabajo de Delta posteriores, en lugar de delegar el paso crítico de la ingesta en una canalización de Lambda independiente.
La mayor parte de la ingeniería de datos se basa en formatos de datos comunes, pero algunos de los datos más valiosos permanecen ocultos en formatos a los que PySpark no puede acceder directamente, como HDF5 para datos científicos y estándares de la industria como ARINC 429 para datos aeroespaciales. Databricks Serverless ofrece a los equipos una forma flexible de desbloquear esos datos a escala, mientras que las implementaciones respaldadas por Rust pueden ofrecer un rendimiento superior al de un enfoque puramente en Python.
PySpark custom data sources te permite definir un formato de archivo de lectura y escritura en Python puro, utilizable como cualquier otro origen de datos. Su principal beneficio es la sencillez, ya que permite a los usuarios definir funciones para leer y escribir en Python puro, así como establecer características importantes como la paralelización. El equipo de Eclipse Aerospace fue un paso más allá: al crear un paquete de Python respaldado por Rust, logró duplicar el rendimiento evaluado en pruebas comparativas en comparación con un origen de datos en Python puro basado en PyArrow. En comparación con una implementación nativa en Python sin Arrow, alcanzó casi 20 veces su rendimiento.
El siguiente repositorio de código de ejemplo muestra paso a paso un origen de datos personalizado para ARINC 429. El código adjunto incluye decisiones de diseño que van más allá del alcance de este artículo, junto con pruebas de rendimiento que puedes ejecutar por tu cuenta.
Apache Spark 4.0 incorporó una API de origen de datos en Python (mencionada en la documentación de Databricks como PySpark custom data sources) que permite registrar un formato personalizado para datos por lotes o de transmisión, como por ejemplo:
Introducida en Apache Spark 4.0 y disponible en Databricks, la API está disponible de manera general a partir de Databricks Runtime 15.4 LTS y también es compatible con el entorno Serverless 2 y versiones posteriores. Como la API está implementada en Python, no requiere un conector JVM ni un archivo JAR independientes.
La API ofrece una interfaz simplificada para crear orígenes de datos nativos, en la que el usuario implementa unas pocas clases y las registra de la siguiente manera:
Estos decodificadores se presentan en dos modalidades: Python nativo y formato Apache Arrow, siendo esta última opción la que ofrece un rendimiento considerablemente superior. Apache Arrow es un "formato de memoria columnar independiente del lenguaje para datos planos y anidados", lo que lo convierte en una interfaz ideal entre diferentes lenguajes que comparten objetos de datos. La API aprovecha esto al permitir que los usuarios devuelvan objetos pyarrow.RecordBatch directamente, y Spark los consumirá por lotes en lugar de materializar un objeto InternalRow por cada fila.
Esto planteó una pregunta: ¿podría Python actuar como la capa de control, mientras que Rust se encarga de las tareas pesadas de decodificación y mantiene el formato Apache Arrow en todo momento?
La arquitectura detrás de esto consiste en un único conector lógico con una ruta de decodificación compuesta casi en su totalidad por código compilado y una capa de Python lo suficientemente ligera como para realizar cambios mínimos en el SDK de Rust existente de Eclipse Aerospace. Esta capa ligera de Python está construida con PyO3/Maturin como un paquete independiente, que se importa en las canalizaciones y se utiliza exactamente igual que cualquier otro origen. Para garantizar el mejor rendimiento y la separación de responsabilidades, el equipo siguió algunos principios arquitectónicos.

El decodificador no sabe que Spark existe. El paquete central de Rust no tiene dependencias de PyO3, Arrow o Spark; solo procesa bytes y devuelve registros estructurados. Esto permitió a Eclipse Aerospace migrar la lógica de decodificación existente prácticamente sin cambios, ya que no fue necesario reescribir el SDK para este propósito, sino únicamente adaptarlo.
Arrow es el único elemento que cruza el límite de la interfaz de funciones externas (FFI), y lo hace sin realizar copias. La capa de integración de Rust construye un objeto Arrow RecordBatch utilizando arrow-rs y luego se lo entrega a Python como un objeto pyarrow.RecordBatch a través de la interfaz de datos en C de Arrow. Al hacerlo, no hay ningún paso de serialización, ya que no se vuelven a codificar bytes ni se crean objetos de Python por cada fila. Los búferes que asigna Rust son los mismos que lee Python.
La capa de PySpark es intencionalmente ligera. Analiza las opciones, decide cómo particionar (un InputPartition por entrada en el ejemplo de referencia) y define el esquema. En los orígenes de datos personalizados de PySpark, DataSource, DataSourceReader y sus equivalentes de streaming deben ser serializables para que Spark pueda serializar (pickle) la instancia del lector y enviarla a un trabajador. Al hacer que la capa de PySpark sea ligera, la instancia de DataSourceReader que Spark serializa y envía a un trabajador solo contiene estado simple de Python (rutas de archivos, un entero para el tamaño de lote). El objeto Rust respaldado por PyO3 se construye dentro de read(), después de que el lector ya haya llegado al trabajador. Nunca forma parte de lo que se serializa.
El código nativo se distribuye como un wheel de Python, construido con Maturin. En Databricks, ese wheel es un artefacto de compilación, adjunto a un entorno de trabajo o notebook como una dependencia normal de Python. Debido a que la implementación está compilada, la compatibilidad de versión y arquitectura requiere cuidado, aunque un proceso de CI/CD adecuado puede gestionar esto como se demuestra en el repositorio de ejemplo.
En Databricks Serverless, la ruta de ejecución es directa:
read() y pasa los bytes del archivo al decodificador compilado.Esta es la diferencia clave con respecto al diseño anterior: el archivo original se decodifica como parte de la lectura de Spark en lugar de copiarse primero a una zona de aterrizaje intermedia de Avro.

Al reemplazar el flujo de trabajo de Lambda y Avro de múltiples etapas por una sola lectura nativa de Spark, Eclipse Aerospace puso los datos de vuelo a disposición más rápido mientras reducía la infraestructura, el almacenamiento y la carga operativa. El nuevo conector de orígenes de datos personalizados de PySpark nativo de Arrow también incorpora la decodificación en Spark, mejorando la visibilidad del rendimiento de la canalización y permitiendo que la lógica de esquema y calidad de datos se gestione en un solo lugar.
La implementación del origen de datos personalizado ha simplificado significativamente la infraestructura de Eclipse Aerospace, y los efectos se muestran en cuatro áreas distintas: latencia, costo, observabilidad y tiempo de ingeniería.
Latencia. Reducir una canalización de múltiples saltos (carga, Lambda, decodificación, escritura en Avro, lectura de Avro, carga en Delta) a una sola lectura nativa de Spark elimina varios saltos de red y varios puntos donde un archivo se queda en una cola esperando que la siguiente etapa lo recoja. El nuevo conector puede procesar registros en un minuto desde la carga del archivo, incluido el tiempo de inicio, y normalmente puede procesar lotes grandes de varios gigabytes en paralelo en menos de dos minutos debido al paralelismo.
Costo. Eliminar el salto de Lambda y el intermediario de Avro eliminó todo un nivel de infraestructura que Eclipse Aerospace tenía que ejecutar, monitorear y pagar. Ya no pagan por almacenar dos copias de cada vuelo, el binario original y su conversión a Avro, lo que en casi dos décadas de historial de vuelos retenidos representa una cantidad significativa de almacenamiento duplicado. Esto significa que la mayoría de los archivos permanecen en el almacenamiento Glacier en AWS, con decodificaciones fácilmente repetibles si los datos originales se necesitan nuevamente.
Observabilidad. Como el paso de decodificación ahora ocurre dentro de la propia Spark, aparece en la UI de Spark y en las métricas de trabajo estándar como cualquier otra etapa de un trabajo. Eclipse Aerospace utiliza "badRecordsPath" personalizado, que imita al cargador de CSV/JSON, para capturar datos mal formateados, y mantiene amplias canalizaciones de calidad de datos que capturan estadísticas directamente desde el decodificador.
Tiempo de ingeniería. La lógica de esquema solía definirse dos veces: una en el decodificador de Lambda que producía Avro, y otra en los trabajos posteriores de Spark que lo consumían. Cualquier cambio en el mensaje significaba actualizar ambos, en dos bases de código diferentes, con el riesgo de que las dos definiciones se desincronizaran. Con un único conector versionado, el esquema se define una vez, junto a la propia lógica de decodificación. Esto también significa que el mismo patrón arquitectónico, un DataSource delgado de Python que envuelve un decodificador de Rust, ahora es reutilizable para los otros formatos propietarios que admite Eclipse Aerospace, en lugar de ser algo que deba reinventarse por formato.
La experiencia de Eclipse Aerospace muestra cómo se pueden incorporar formatos de datos especializados a Databricks sin crear una plataforma de ingestión separada a su alrededor. Al mantener el decodificador en Rust, usar Python como la capa de integración y pasar registros a través de Apache Arrow, el origen de datos personalizado lee los archivos originales directamente en Spark y sigue siendo compatible con la flexibilidad de Databricks Serverless.
Para los equipos que trabajan con formatos propietarios o específicos de la industria, el patrón reutilizable es simple. Los ingenieros pueden mantener la lógica de decodificación específica del dominio cerca del formato de origen, exponerla a través de un origen de datos personalizado de PySpark y dejar que Spark maneje el particionamiento, la ejecución, la observabilidad y el procesamiento posterior.
¿Quieres empezar? Echa un vistazo a lo siguiente:
Un origen de datos personalizado de PySpark es un origen de datos de Spark definido por el usuario e implementado en Python. Permite a los equipos leer formatos de archivo especializados como parte de un flujo de trabajo estándar de Spark sin construir un conector de JVM separado.
El conector de origen de datos personalizado de PySpark nativo de Arrow ingiere telemetría de vuelo propietaria en Databricks sin depender de una canalización de múltiples etapas, computación dedicada siempre activa o copias de Avro duplicadas de los archivos originales.
Databricks Serverless es un servicio de computación totalmente administrado que le permite ejecutar cargas de trabajo sin aprovisionar ni administrar ninguna infraestructura en su cuenta de la nube. Databricks asigna, escala y optimiza automáticamente los recursos de computación por usted.
El conector utiliza los orígenes de datos personalizados de Pyspark para ejecutar la lógica de lectura de archivos dentro de Spark en computación sin servidor. Python maneja el flujo de control, mientras que Rust y Apache Arrow manejan la decodificación de alto rendimiento y la transferencia de datos. Esto permite leer archivos propietarios directamente sin una computación dedicada siempre activa o una canalización de Avro intermedia. Los orígenes de datos personalizados de PySpark están disponibles en Databricks Runtime 15.4 LTS y superior, o en la versión 2 del entorno sin servidor.
(Esta entrada del blog ha sido traducida utilizando herramientas basadas en inteligencia artificial) Publicación original
Suscríbete a nuestro blog y recibe las últimas publicaciones directamente en tu bandeja de entrada.