Cet article est le premier d’une série en quatre parties sur le fonctionnement interne de la machine virtuelle Envision : le logiciel qui exécute les scripts Envision. Consultez la partie 2, la partie 3 et la partie 4. Cette série ne couvre pas le compilateur Envision (peut-être une autre fois), nous supposerons donc simplement que le script a été converti en bytecode, qui est l’entrée de la machine virtuelle Envision.

Un pipeline d’optimisation de la Supply Chain couvre un large éventail de besoins de traitement des données : ingestion et augmentation des données, extraction des caractéristiques, prévision probabiliste, prise de décisions optimales sous contraintes, exportation des données, analyse et création de tableaux de bord. Chaque pipeline d’optimisation est différent, avec ses propres entrées, règles, contraintes et sorties. Le volume de données est important : d’après l’expérience de Lokad, même nos plus petits comptes doivent traiter des gigaoctets de données chaque jour, et nos plus grands comptes atteignent plus d’un téraoctet par jour. Comme le pipeline d’optimisation attend généralement les entrées quotidiennes du reste du traitement interne des données de l’entreprise, il ne dispose que de quelques heures de temps de calcul pour optimiser les décisions de demain en fonction des données d’aujourd’hui.

Au-delà des séries temporelles

Ce n’est pas difficile. Vraiment pas. Traiter plusieurs téraoctets en quelques heures est un objectif de performance tout à fait accessible pour une équipe d’ingénieurs logiciels compétents.

Le défi de Lokad est de construire de tels pipelines sans ingénieurs logiciels. Bien sûr, il y a des ingénieurs logiciels qui travaillent chez Lokad, mais ils développent des outils et une infrastructure pour l’ensemble de l’entreprise, pas pour des clients individuels. À la place, nous avons nos Supply Chain Scientists - une équipe d’experts en supply chain qui comprennent les problèmes spécifiques de chaque client et conçoivent les solutions. Dans une structure d’entreprise plus traditionnelle, ce seraient les chefs de produit, ceux qui écoutent les clients et qui disent ensuite aux ingénieurs logiciels, en détail, ce qu’il faut implémenter. Notre philosophie de développement, et la raison de la création d’Envision, notre propre langage de programmation, est que cela devrait être plus rapide pour un Supply Chain Scientist d’implémenter lui-même la solution que de rédiger les spécifications pour que quelqu’un d’autre les implémente.

Afin de réaliser cela sans sacrifier l’échelle ou les performances, il y a trois fonctionnalités qu’Envision doit apporter gratuitement1, sans aucun effort de la part du Supply Chain Scientist.

  • La gestion de la mémoire doit être automatique. Cela inclut évidemment un garbage collector, mais cela signifie également qu’Envision doit prendre en charge de grands ensembles de données de manière transparente. Créer un tableau de dix gigaoctets est la base : ce ne sont même pas trois milliards de nombres ! La capacité de travailler avec un ensemble de données plus grand que la mémoire d’une seule machine est attendue. En fait, Envision prend en charge des ensembles de données plus grands que la mémoire de l’ensemble du cluster, en les écrivant intelligemment sur des disques NVMe.
  • Le parallélisme multi-cœur et multi-machine doit être automatique. Les opérations parallèles embarrassantes doivent être réparties sur autant de cœurs que possible dans le cluster, sans intervention humaine. Un script doit survivre à la panne d’une seule machine dans le cluster sans avoir à recommencer. Un script doit pouvoir s’exécuter même si le cluster est réduit à une seule machine. Et bien sûr, deux exécutions d’Envision doivent pouvoir s’exécuter simultanément sur le même cluster.
  • Nous nous attendons à ce qu’Envision nécessite peu de matériel pour fonctionner. De nombreux problèmes de performance peuvent être résolus en dépensant des millions de dollars pour du matériel de haute qualité et/ou des licences de serveur, mais nous préférons éviter une situation où cliquer sur le bouton “Exécuter” d’un script coûte des centaines de dollars.

Les langages de programmation généralistes ne fournissent pas ces fonctionnalités, et même s’ils peuvent généralement être combinés avec des frameworks qui le font (Scala + Spark, Python + Dask, et autres), cela laisse trop de bords tranchants exposés à l’utilisateur. En ce sens, Envision est plus similaire à SQL s’exécutant sur BigQuery.

Environnement

Envision ne peut pas être installé ou exécuté localement. Au lieu de cela, tous les utilisateurs se connectent à la plateforme en ligne de Lokad, qui fournit un IDE basé sur un navigateur pour éditer et exécuter des scripts. Les données et les tableaux de bord sont également stockés en ligne et accessibles via une interface web, ainsi que via SFTP et FTPS.

Lorsqu’un utilisateur exécute un script via l’interface web, cela crée une mission qui est envoyée à un cluster Envision pour exécution.

Envision s’exécute en mode batch : chaque exécution lit l’intégralité des données d’entrée et produit une sortie complète. Cela peut prendre entre 5 secondes pour un script très simple s’exécutant sur peu de données, à 30-40 minutes pour un script d’augmentation de données volumineuses, voire plusieurs heures pour certaines tâches d’apprentissage automatique.

D’autres modes d’exécution, tels que le traitement en continu (écoute des nouvelles entrées et production de la sortie correspondante en temps réel) ou l’accès transactionnel (lecture de quelques lignes de données, écriture de quelques lignes en retour), ne sont pas pris en charge : les primitives du langage et les détails d’implémentation de bas niveau impliqués dans l’exécution de ces modes avec des performances décentes sont contraires à ceux impliqués dans le traitement en mode batch.

Structure du cluster

À partir de 2021, tout Envision s’exécute sur .NET 5, hébergé sur des machines virtuelles Ubuntu 20.04 dans le cloud Microsoft Azure. Un cluster est composé de 2 à 6 instances Standard_L32s_v2 : 32× AMD EPYC 7551 cores, 256GiB de mémoire et 4× disques NVMe totalisant 7,68 To d’espace de stockage. Nous appelons ces machines des workers.

Le cluster établit une association M-vers-N entre les workers et les missions : un seul worker peut exécuter plusieurs scripts Envision simultanément, et si un seul script est attribué à plusieurs workers, ils coopéreront pour le terminer plus rapidement.

Chaque cluster dispose également d’un scheduler, un plus petit Standard_B2ms avec 2× cores et 8GiB de mémoire. Le scheduler fournit les points d’API pour que les applications externes soumettent de nouvelles missions et collectent les résultats des missions terminées. Il est également responsable de la répartition des missions sur une ou plusieurs machines du cluster. Selon la charge et le degré de parallélisme disponible pour chaque script à un moment donné, le scheduler peut ajouter ou supprimer des workers d’une mission.

Tout le système a été conçu pour être résilient : une fois qu’une mission a été attribuée à un worker, même si tous les autres workers du cluster, ainsi que le scheduler, sont hors ligne, le worker survivant pourra toujours terminer la mission. Ainsi, la coopération entre plusieurs workers et les réaffectations de missions effectuées par le scheduler sont des optimisations de performance, elles ne sont pas nécessaires pour l’achèvement de la mission.

Stockage de Blob

Lokad n’utilise pas de bases de données SQL pour les données des clients. Les solutions hébergées ne peuvent pas facilement contenir les ensembles de données de nos plus grands clients (elles ont tendance à atteindre leur limite entre 4 To et 16 To), et exécuter nos propres serveurs nécessiterait des efforts que nous préférons consacrer ailleurs.

En revanche, Envision s’exécute en mode batch, ce qui élimine la nécessité de requêtes plus complexes que “Lire la colonne X entre les lignes L et M” : une fois que les données d’entrée ont été chargées, le worker pourra les indexer et les retraiter si nécessaire.

Pour cette raison, nous utilisons Azure Blob Storage comme notre stockage principal. Cela nous permet de stocker plus d’un pétaoctet à moins de 1% du coût des bases de données SQL hébergées, et les performances de lecture des requêtes restent de manière fiable entre 30 Mo/s et 60 Mo/s.

Nous avons également rendu nos blobs immuables : il est possible de créer de nouveaux blobs, mais pas de modifier ceux existants. Cela garantit que les entrées d’un script ne peuvent pas être modifiées pendant l’exécution du script, et que les sorties d’un script ne peuvent pas être consultées avant que l’exécution ne soit terminée et que les identifiants des nouveaux blobs ne soient renvoyés.

Pour être précis, Lokad a construit un Content-Addressable Store sur Azure Blob Storage. Il est open source, et disponible sous la forme de deux packages NuGet Lokad.ContentAddr et Lokad.ContentAddr.Azure. Connaître le hachage des fichiers individuels permet à Envision de déterminer que certaines de ses entrées n’ont pas changé, afin de pouvoir réutiliser les valeurs calculées conservées en cache lors d’une exécution précédente.

Déploiement

Envision n’utilise pas de conteneurisation (comme Docker), car les avantages des conteneurs ne justifient pas la complexité supplémentaire impliquée.

Premièrement, le calcul haute performance nécessite chaque dernier pourcentage de CPU, de RAM et d’espace de stockage de nos workers, il n’est donc pas possible d’exécuter plusieurs applications sur la même machine.

Deuxièmement, pour empaqueter une application avec toutes ses dépendances, de manière indépendante de la plateforme, nous avons constaté que dotnet publish était suffisant (et en fait plus rapide que docker build). Avec .NET 5, Microsoft offre une excellente prise en charge multiplateforme, et il suffit de copier littéralement les résultats d’une construction d’une machine Windows vers un hôte Linux.

Enfin, la création rapide de nouvelles instances à partir de zéro est quelque chose que nous évitons activement. Bien que nous puissions arrêter les workers pour réduire les coûts, la création de nouveaux clusters ou l’ajout de plus de workers à des clusters existants est une décision financière : nous ne facturons pas nos clients en fonction de l’utilisation des ressources, nous n’avons donc aucun moyen de leur faire supporter les coûts supplémentaires.

La semaine prochaine, nous plongerons dans le modèle d’exécution d’Envision et dans la façon dont le travail à effectuer est représenté à l’intérieur du cluster.

Petite publicité : nous recrutons des ingénieurs logiciels. Le travail à distance est possible.


  1. rien n’est vraiment gratuit, et nous le payons en sacrifiant la capacité d’Envision à agir en tant que langage de programmation généraliste. Par exemple, la parallélisation automatique signifie que nous ne prendrons jamais en charge le contrôle explicite des threads dans Envision ; le traitement automatique multi-machine signifie qu’il n’y aura jamais le concept de “machine locale” dans Envision. ↩︎