Envision VM (parte 1), Entorno y Arquitectura General
Este artículo es el primero de una serie de cuatro partes sobre el funcionamiento interno de la máquina virtual de Envision: el software que ejecuta los scripts de Envision. Ver parte 2, parte 3 y parte 4. Esta serie no cubre el compilador de Envision (quizás en otra ocasión), así que supongamos que el script ha sido de alguna manera convertido al bytecode que la máquina virtual de Envision toma como entrada.
Una Supply Chain Optimization pipeline cubre una amplia variedad de necesidades de procesamiento de datos: ingesta y aumento de datos, extracción de características, forecast probabilístico, producción de decisiones óptimas bajo restricciones, exportación de datos, analítica y creación de dashboard. La pipeline de cada empresa es diferente, con sus propios insumos, reglas, restricciones y resultados. El volumen de datos es grande: según la experiencia en Lokad, incluso nuestras cuentas más pequeñas deben procesar gigabytes de datos cada día, y nuestras cuentas más grandes alcanzan fácilmente más de un terabyte por día. Dado que la pipeline de optimización generalmente espera las entradas diarias del resto del procesamiento interno de datos de la empresa, solo dispone de unas pocas horas de cómputo para optimizar las decisiones de mañana basadas en los datos de hoy.

No es difícil. Realmente, no lo es. Procesar varios terabytes en pocas horas es un objetivo de rendimiento bien al alcance de un equipo de ingenieros de software calificados.
El desafío de Lokad es construir tales pipelines sin ingenieros de software. Por supuesto, en Lokad trabajan ingenieros de software, pero ellos desarrollan herramientas e infraestructura para toda la empresa, no para clientes individuales. En cambio, contamos con nuestros Supply Chain Scientists – un equipo de expertos en supply chain que entienden los problemas específicos de cada cliente y diseñan las soluciones. En una estructura empresarial más tradicional, estos serían los gerentes de producto, aquellos que escuchan a los clientes y luego le indican a los ingenieros de software, con gran detalle, exactamente lo que se debe implementar. Nuestra filosofía de desarrollo, y la razón para crear 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 por sí mismo, que escribir las especificaciones para que otra persona las implemente.
Para lograr esto sin sacrificar escala ni rendimiento, hay tres características que Envision debe aportar de manera gratuita1, sin ningún esfuerzo por parte del Supply Chain Scientist.
- La gestión de memoria debe ser automática. Esto incluye, obviamente, contar con un recolector de basura, pero también significa que Envision debe soportar conjuntos de datos a gran escala de manera transparente. Crear un arreglo de diez gigabytes es algo básico: ¡eso ni siquiera suma tres mil millones de números! Se espera la capacidad de trabajar con un conjunto de datos mayor que la memoria de una sola máquina. De hecho, Envision soporta conjuntos de datos que superan la memoria de todo el clúster, mediante un ingenioso volcado a discos NVMe.
- El paralelismo multinúcleo y multi-máquina debe ser automático. Las operaciones embarradamente paralelas deben distribuirse a tantos núcleos como sea posible en el clúster, sin intervención humana. Un script debería sobrevivir al fallo de una sola máquina en el clúster sin tener que reiniciarse. Un script debería poder completarse incluso si el clúster se reduce a una sola máquina. Y, por supuesto, dos ejecuciones de Envision deberían poder ejecutarse concurrentemente 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 gama y/o licencias de servidor, pero preferimos evitar una situación en la que hacer clic en el botón “Run” de un script cueste cientos de dólares.
Los lenguajes de programación de propósito general no proporcionan estas características, y aunque usualmente se pueden combinar con frameworks que sí lo hacen (Scala + Spark, Python + Dask, entre otros), esto deja demasiadas aristas expuestas al usuario. En este sentido, Envision es más similar a SQL ejecutándose en BigQuery.
Environment
Envision no puede ser instalado ni ejecutado localmente. En su lugar, todos los usuarios se conectan a la plataforma en línea de Lokad, que proporciona un IDE basado en navegador para editar y ejecutar scripts. Los datos y los dashboards también se almacenan en línea y se acceden a través de una interfaz web, así como mediante SFTP y FTPS.
Cuando un usuario ejecuta un script a través de la interfaz web, se crea una mission que se envía a un clúster de Envision para su ejecución.
Envision se ejecuta en modo batch: cada ejecución lee la totalidad de los datos de entrada y produce una salida completa. Esto puede tomar entre 5 segundos para un script muy simple con pocos datos, hasta 30-40 minutos para un script de gran aumento de datos, e incluso varias horas para algunas tareas de machine learning.
Otras modalidades de ejecución, como el procesamiento en stream (escuchar nuevas entradas y producir la salida correspondiente al instante) o el acceso transaccional (leer solo unas pocas líneas de datos, escribir algunas líneas de vuelta) no están soportadas: las primitivas del lenguaje y los detalles de implementación a bajo nivel involucrados en ejecutar estos modos con un rendimiento decente son opuestos a los del procesamiento en modo batch.
Cluster Structure
A partir de 2021, todo Envision se ejecuta en .NET 5, alojado en máquinas virtuales Ubuntu 20.04 en la cloud 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× discos NVMe que suman un total de 7.68TB de espacio de almacenamiento. A estas máquinas las llamamos workers.
El clúster mantiene una asociación M-a-N entre workers y missions: un solo worker puede ejecutar varios scripts de Envision concurrentemente, y si un mismo script se asigna a varios workers, ellos cooperarán para finalizarlo más rápidamente.
Cada clúster también tiene un scheduler, un Standard_B2ms más pequeño con 2× núcleos y 8GiB de memoria. El scheduler proporciona los endpoints de API para que aplicaciones externas envíen nuevas missions y recopilen los resultados de las missions finalizadas. También es responsable de repartir las missions a una o más máquinas en el clúster. Dependiendo de la carga y del grado de paralelismo disponible para cada script en cada momento, el scheduler puede añadir o eliminar workers de una mission.
Todo el sistema fue diseñado para ser resiliente: una vez que una mission ha sido asignada a un worker, incluso si todos los demás workers en el clúster y el scheduler se desconectan, el worker sobreviviente aún podrá completar la mission. Como tal, la cooperación entre varios workers y las reasignaciones de missions realizadas por el scheduler son optimizaciones de rendimiento y no son necesarias para la finalización de la mission.
Blob Storage
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 alcanzar entre 4TB y 16TB), y ejecutar nuestros propios servidores requeriría un esfuerzo que preferiríamos destinar a otras áreas.
Por otro lado, Envision se ejecuta en modo batch, lo que elimina la necesidad de consultas más complejas que “Leer la columna X entre las líneas L y M”: una vez cargados los datos de entrada, el worker podrá indexarlos y reprocesarlos según sea necesario.
Debido a esto, usamos 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 las consultas de lectura 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 modificar los existentes. Esto garantiza que los insumos de un script no puedan cambiarse mientras el script se ejecuta, y que los resultados no sean visibles hasta que la ejecución se complete y retorne los identificadores de los nuevos blobs.
Para ser exactos, Lokad ha construido una Content-Addressable Store 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 archivos individuales permite a Envision determinar que algunos de sus insumos no han cambiado, de modo que pueda reutilizar los valores calculados guardados en caché de una ejecución anterior.
Deployment
Envision no utiliza ninguna containerización (como Docker), porque los beneficios de los contenedores no justifican la complejidad adicional involucrada.
Primero, la computación de alto rendimiento requiere cada gota de CPU, RAM y almacenamiento de nuestros workers, por lo que no es posible ejecutar varias aplicaciones en la misma máquina.
Segundo, para empaquetar una aplicación junto con todas sus dependencias, de manera independiente a la plataforma, hemos encontrado que dotnet publish es suficiente (y, de hecho, más rápido que docker build). Con .NET 5, Microsoft ofrece un soporte multiplataforma excepcional, y basta con copiar literalmente los resultados de una compilación de una máquina con Windows a un host Linux.
Finalmente, crear nuevas instancias rápidamente desde cero es algo que evitamos activamente. Aunque podemos apagar workers para reducir costos, crear nuevos clústeres o añadir más workers a los 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.
Aviso sin tapujos: estamos contratando software engineers. Se puede trabajar de forma remota.
-
nada es realmente gratis, y lo pagamos sacrificando la capacidad de Envision para funcionar como un lenguaje de programación de propósito general. Por ejemplo, la paralelización automática significa que nunca soportaremos el control explícito sobre los hilos en Envision; el procesamiento automático multi-máquina significa que nunca existirá el concepto de una “máquina local” en Envision. ↩︎