Este artículo es el primero de una serie de cuatro partes sobre el funcionamiento interno de la máquina virtual Envision: el software que ejecuta los scripts de Envision. Consulta la parte 2, parte 3 y parte 4. Esta serie no cubre el compilador de Envision (quizás en otro momento), así que asumamos que el script ha sido convertido de alguna manera al bytecode que la máquina virtual de Envision toma como entrada.

Un pipeline de Optimización de la Cadena de Suministro cubre una amplia gama de necesidades de procesamiento de datos: ingestión y ampliación de datos, extracción de características, pronóstico probabilístico, toma de decisiones óptimas bajo restricciones, exportación de datos, análisis y creación de paneles de control. El pipeline de cada empresa es diferente, con sus propias entradas, reglas, restricciones y salidas. El volumen de datos es grande: según la experiencia de Lokad, incluso nuestras cuentas más pequeñas deben procesar gigabytes de datos cada día, y nuestras cuentas más grandes superan fácilmente el terabyte diario. Como el pipeline de optimización generalmente espera las entradas diarias del resto del procesamiento interno de datos de la empresa, solo tiene unas pocas horas de tiempo de cálculo para optimizar las decisiones del día siguiente en función de los datos actuales.

Más allá de las series de tiempo

No es difícil. En realidad, no lo es. Procesar varios terabytes en unas pocas horas es un objetivo de rendimiento al alcance de un equipo de ingenieros de software capacitados.

El desafío de Lokad es construir dichos pipelines sin ingenieros de software. Por supuesto, hay ingenieros de software trabajando en Lokad, pero están desarrollando herramientas e infraestructura para toda la empresa, no para clientes individuales. En cambio, tenemos a nuestros Supply Chain Scientists - un equipo de expertos en cadena de suministro que comprenden los problemas específicos de cada cliente y diseñan las soluciones. En una estructura de empresa más tradicional, estos serían los gerentes de producto, aquellos que escuchan a los clientes y luego le dicen a los ingenieros de software, con gran detalle, exactamente qué debe implementarse. Nuestra filosofía de desarrollo, y la razón por la cual creamos Envision, nuestro propio lenguaje de programación, es que debería ser más rápido para un Supply Chain Scientist implementar la solución ellos mismos que escribir las especificaciones para que alguien más las implemente.

Para lograr esto sin sacrificar escala o rendimiento, hay tres características que Envision debe ofrecer de forma gratuita1, sin ningún esfuerzo por parte del Supply Chain Scientist.

  • La gestión de memoria debe ser automática. Esto incluye obviamente tener un recolector de basura, pero también significa que Envision debe admitir conjuntos de datos a gran escala de manera transparente. Crear un arreglo de diez gigabytes es lo mínimo: ¡eso ni siquiera son tres mil millones de números! Se espera la capacidad de trabajar con un conjunto de datos más grande que la memoria de una sola máquina. De hecho, Envision admite conjuntos de datos que son más grandes que la memoria de todo el clúster, al derramarse de manera inteligente en unidades de estado sólido NVMe.
  • El paralelismo multinúcleo y multi-máquina debe ser automático. Las operaciones paralelas embarazosamente paralelas deben distribuirse en tantos núcleos como sea posible en el clúster, sin intervención humana. Un script debe sobrevivir al fallo de una sola máquina en el clúster sin tener que comenzar de nuevo. Un script debe poder completarse incluso si el clúster se reduce a una sola máquina. Y, por supuesto, dos ejecuciones de Envision deben poder ejecutarse simultáneamente en el mismo clúster.
  • Esperamos que Envision requiera poco hardware para funcionar. Muchos problemas de rendimiento se pueden resolver gastando millones de dólares en hardware de alta calidad y/o licencias de servidor, pero preferiríamos evitar una situación en la que hacer clic en el botón “Ejecutar” de un script cueste cientos de dólares.

Los lenguajes de programación de propósito general no proporcionan estas características, y aunque generalmente se pueden combinar con marcos que sí lo hacen (Scala + Spark, Python + Dask y otros), esto deja demasiados bordes afilados expuestos al usuario. En este sentido, Envision es más similar a SQL que se ejecuta en BigQuery.

Entorno

Envision no se puede instalar ni ejecutar localmente. En su lugar, todos los usuarios se conectan a la plataforma en línea de Lokad, que proporciona un IDE basado en el navegador para editar y ejecutar scripts. Los datos y los paneles también se almacenan en línea y se acceden a través de una interfaz web, así como a través de SFTP y FTPS.

Cuando un usuario ejecuta un script a través de la interfaz web, se crea una misión que se envía a un clúster de Envision para su ejecución.

Envision se ejecuta en modo por lotes: cada ejecución lee la totalidad de los datos de entrada y produce una salida completa. Esto puede llevar entre 5 segundos para un script muy simple que se ejecuta en pocos datos, hasta 30-40 minutos para un script de gran aumento de datos, e incluso varias horas para algunas tareas de aprendizaje automático.

Otros modos de ejecución, como el procesamiento de transmisión (escuchar nuevos datos de entrada y producir la salida correspondiente sobre la marcha) o el acceso transaccional (leer solo unas pocas líneas de datos, escribir unas pocas líneas de vuelta), no son compatibles: los primitivos del lenguaje y los detalles de implementación de bajo nivel involucrados en la ejecución de estos modos con un rendimiento decente son contrarios a los involucrados en el procesamiento en modo por lotes.

Estructura del clúster

A partir de 2021, todo Envision se ejecuta en .NET 5, alojado en máquinas virtuales Ubuntu 20.04 en la nube de Microsoft Azure. Un clúster está compuesto por entre 2 y 6 instancias Standard_L32s_v2: 32× núcleos AMD EPYC 7551, 256GiB de memoria y 4× unidades de estado sólido NVMe con un espacio de almacenamiento total de 7.68TB. A estas máquinas las llamamos workers.

El clúster mantiene una asociación M-a-N entre los workers y las misiones: un solo worker puede ejecutar varios scripts de Envision de manera concurrente, y si un solo script se asigna a varios workers, estos colaborarán para terminarlo más rápido.

Cada clúster también tiene un planificador, un Standard_B2ms más pequeño con 2× núcleos y 8GiB de memoria. El planificador proporciona los puntos finales de la API para que las aplicaciones externas envíen nuevas misiones y recojan los resultados de las misiones finalizadas. También es responsable de enviar misiones a una o más máquinas en el clúster. Dependiendo de la carga y el grado de paralelismo disponible para cada script en cualquier momento, el planificador puede agregar o eliminar workers de una misión.

Todo el sistema fue diseñado para ser resiliente: una vez que se ha asignado una misión a un worker, incluso si todos los demás workers en el clúster, así como el planificador, se desconectan, el worker sobreviviente aún podrá completar la misión. Como tal, la cooperación entre varios workers y las reasignaciones de misión realizadas por el planificador son optimizaciones de rendimiento, no son necesarias para la finalización de la misión.

Almacenamiento de blobs

Lokad no utiliza bases de datos SQL para los datos de los clientes. Las soluciones alojadas no pueden contener fácilmente los conjuntos de datos de nuestros clientes más grandes (suelen agotarse entre 4TB y 16TB), y ejecutar nuestros propios servidores requeriría un esfuerzo que preferimos invertir en otros lugares.

Por otro lado, Envision se ejecuta en modo por lotes, lo que elimina la necesidad de consultas más complejas que “Leer columna X entre las líneas L y M”: una vez que se ha cargado los datos de entrada, el worker podrá indexarlos y volver a procesarlos según sea necesario.

Debido a esto, utilizamos Azure Blob Storage como nuestro almacenamiento principal. Nos permite almacenar más de un petabyte a menos del 1% del costo de las bases de datos SQL alojadas, y el rendimiento de lectura de consultas se mantiene de manera confiable entre 30MB/s y 60MB/s.

También hemos hecho que nuestros blobs sean inmutables: es posible crear nuevos blobs, pero no cambiar los existentes. Esto asegura que los datos de entrada de un script no puedan cambiarse mientras se está ejecutando el script, y que los resultados de un script no puedan verse hasta que la ejecución se complete y devuelva los identificadores de los nuevos blobs.

Para ser exactos, Lokad ha construido un Almacén de Contenido-Adressable sobre Azure Blob Storage. Es open source, y está disponible como un par de paquetes NuGet Lokad.ContentAddr y Lokad.ContentAddr.Azure. Conocer el hash de los archivos individuales permite a Envision determinar que algunos de sus datos de entrada no han cambiado, por lo que puede reutilizar los valores calculados que se mantienen en caché desde una ejecución anterior.

Implementación

Envision no utiliza ninguna contenedorización (como Docker), porque los beneficios de los contenedores no justifican la complejidad adicional.

En primer lugar, la informática de alto rendimiento requiere hasta la última gota de CPU, RAM y almacenamiento de nuestros workers, por lo que no es posible ejecutar varias aplicaciones en la misma máquina.

En segundo lugar, para empaquetar una aplicación junto con todas sus dependencias, de una manera independiente de la plataforma, hemos encontrado que dotnet publish es suficiente (y, de hecho, más rápido que docker build). Con .NET 5, Microsoft proporciona un soporte excepcional multiplataforma, y es suficiente con copiar literalmente los resultados de una compilación desde una máquina con Windows a un host de Linux.

Por último, evitar crear rápidamente nuevas instancias desde cero es algo que evitamos activamente. Si bien podemos apagar workers para reducir costos, crear nuevos clústeres o agregar más workers a clústeres existentes es una decisión financiera: no facturamos a nuestros clientes en función del uso de recursos, por lo que no tenemos forma de trasladar los costos adicionales.

La próxima semana, profundizaremos en el modelo de ejecución de Envision y en cómo se representa el trabajo a realizar dentro del clúster.

Autopromoción descarada: estamos contratando ingenieros de software. El trabajo remoto es posible.


  1. nada es realmente gratuito, y lo pagamos sacrificando la capacidad de Envision para actuar como un lenguaje de programación de propósito general. Por ejemplo, la paralelización automática significa que nunca admitiremos un control explícito sobre los hilos en Envision; el procesamiento automático de varias máquinas significa que nunca habrá el concepto de una “máquina local” en Envision. ↩︎