3 points par GN⁺ 2023-12-08 | 1 commentaires | Partager sur WhatsApp
  • FLAME vise une montée en charge élastique fine en encapsulant certaines parties du code applicatif dans des fonctions exécutées sur des copies temporaires de l’application, sans déplacer le code vers un runtime séparé
  • L’approche FaaS classique peut multiplier HTTP, S3, SQS, API Gateway, encodeurs/décodeurs et orchestration de workflow, même pour des tâches comme la génération de miniatures vidéo
  • La bibliothèque flame d’Elixir délègue l’exécution d’une fonction à un runner distant via FLAME.call, et FLAME.FlyBackend de Fly.io démarre une nouvelle Fly Machine avec la même image Docker, qui se connecte au nœud parent en environ 3 secondes
  • En développement et en test, LocalBackend exécute le code dans le même runtime ; en production, min, max, max_concurrency et idle_shutdown_after permettent d’ajuster le scale-to-zero et le maintien bref d’un état hot
  • Plutôt que de supprimer les files de tâches, FLAME sépare les garanties de durabilité de l’exécution élastique : la file gère dispatch, commit et retry, tandis que les tâches CPU-intensives sont traitées dans un appel FLAME

Le problème visé par le modèle FLAME

  • L’auto-scaling élastique réduit la charge d’administration des serveurs et promet un coût à l’usage, mais adopter du FaaS peut aussi faire exploser la complexité avec des files séparées, du stockage, du glue code et davantage de complexité en développement, test et CI
  • FLAME signifie Fleeting Lambda Application for Modular Execution : l’idée est de traiter toute l’application comme une lambda, en n’exécutant que certaines tâches modulaires sur une infrastructure éphémère
  • Les objectifs se résument en trois points
    • réduire la gestion des serveurs avec les flux de déploiement existants comme fly deploy, git push heroku ou kubectl
    • permettre une montée en charge granulaire à la demande sur certaines parties seulement du code applicatif
    • éviter de réécrire l’application ou de déplacer une partie du code vers un runtime propriétaire

Déporter des fonctions existantes avec FLAME.call

  • L’exemple présenté est une fonction generate_thumbnails dans une application Elixir, qui transforme une vidéo téléversée en miniatures avec ffmpeg
  • La fonction existante crée un répertoire temporaire, exécute ffmpeg, stocke les miniatures produites dans un stockage durable, puis enregistre leurs URL dans la base avec Repo.insert_all
  • Comme le transcodage vidéo CPU-intensif peut bloquer tout le service en production, le corps de la fonction est simplement encapsulé dans FLAME.call(MyApp.FFMpegRunner, fn -> ... end) au lieu d’être déplacé vers un FaaS ou un microservice entier
  • FLAME.call prend un nom de pool de runners et une fonction, puis cherche ou démarre une nouvelle copie complète de l’application pour n’exécuter que cette fonction
    • les variables capturées par la fermeture, comme la structure %Video{} et interval, sont transmises automatiquement
    • après démarrage, le runner FLAME se connecte au nœud parent, reçoit la fonction à exécuter, puis renvoie le résultat à l’appelant
    • selon la configuration, le runner peut attendre d’autres tâches puis s’arrêter à l’état idle, ou se terminer immédiatement
  • Comme l’application complète tourne, y compris la connexion à la base, on peut continuer à utiliser Repo.insert_all tel quel

Complexité du FaaS et différence avec FLAME

  • Le FaaS fournit des briques pour résoudre le problème, tandis que FLAME cherche plutôt à réduire la couche de communication séparée elle-même
  • Dans l’exemple des miniatures vidéo, même en partant d’une simple AWS Lambda Function URL, la complexité apparaît vite
    • pour retransmettre les miniatures à l’application via HTTP, il faut écrire un encodeur et un décodeur personnalisés de chaque côté
    • si le transcodage vidéo ou le téléversement dépasse 15 minutes, le hard timeout de Lambda impose de découper la vidéo en morceaux et d’utiliser encore plus de fonctions Lambda
    • il faut alors ajouter de l’orchestration de workflow et des services comme SQS et S3
  • Une implémentation fondée sur le FaaS crée généralement les postes de coût suivants
    • déclenchement de Lambda via un endpoint HTTP, S3 ou API Gateway
    • écriture d’une Lambda dédiée au transcodage vidéo
    • stockage du résultat des miniatures dans SQS
    • écriture d’un consumer SQS côté application
    • mise en place du stockage en base et d’un moyen de renvoyer les événements aux abonnés actifs connectés à d’autres instances
  • FLAME permet de réutiliser le code interne de l’application, la base existante, PubSub et les fonctions de la plateforme, ce qui réduit les services séparés et les stockages intermédiaires destinés à récupérer les résultats

Backend Fly.io et exécution locale

  • La bibliothèque flame pour Elixir est une implémentation du modèle FLAME et fournit par défaut LocalBackend et FlyBackend
  • FLAME.FlyBackend démarre une nouvelle copie de l’application sur une Machine de l’infrastructure Fly.io et peut la connecter au nœud parent en environ 3 secondes pour recevoir des tâches
  • Comme Fly.io exécute les applications sous forme d’images Docker packagées, l’API Fly reçoit une demande de démarrage d’une nouvelle Machine avec la même image que l’application courante
  • Sur l’infrastructure Fly.io, les runners FLAME peuvent être lancés dans la même région que le parent, ce qui réduit la latence entre parent et runner
  • FLAME.FlyBackend fait moins de 200 LOC, documentation comprise, et sa seule dépendance externe est le client HTTP req
  • En développement et en test, LocalBackend exécute le code concerné dans le runtime existant du laptop ou du serveur CI

Transfert de fichiers et simplification du développement et des tests

  • FLAME permet de réutiliser la logique métier, la configuration base de données, PubSub et les fonctions de la plateforme sans écrire de code en dehors de l’application
  • En Elixir, les capacités distribuées de la VM Erlang permettent d’envoyer un flux de fichier du nœud parent vers l’application FLAME distante
    • le nœud parent ouvre un flux de fichier sur le chemin de la vidéo
    • l’enfant FLAME ouvre un flux de fichier temporaire et y copie le flux du parent
    • ffmpeg utilise ensuite ce fichier temporaire sur le runner distant comme entrée pour générer les miniatures
  • Cette approche permet de transférer les fichiers vers le serveur FLAME sans configurer séparément S3 ou une interface HTTP
  • Elle réduit les déploiements de services séparés, la gestion d’endpoints, la récupération de résultats via S3/SQS et la configuration des dépendances en développement, test et CI

FLAME en dehors d’Elixir

  • Elixir se prête bien au modèle FLAME grâce à sa supervision de processus et sa messagerie distribuée, mais d’autres langages disposant de primitives de concurrence raisonnables peuvent aussi adopter ce modèle
  • Un proof of concept en JavaScript exécute une fonction applicative sur une autre machine Fly Machine : fly-run-this-function-on-another-machine
  • Dans ce modèle d’appel FLAME côté JavaScript, la partie d’exécution du module est déplacée dans un nouveau fichier puis exécutée dans un pool de runners
  • Si les arguments sont sérialisables en JSON, le flux global ressemble à l’exemple Elixir : le code applicatif s’exécute sur une instance éphémère
  • Une bibliothèque FLAME complète devrait gérer les points suivants
    • la logique de scale-up et scale-down d’un pool élastique
    • la gestion du pool en tenant compte des démarrages hot et cold
    • la supervision des runners distants pour éviter les ressources orphaned
    • le maintien des déploiements à jour

Relation avec les files de tâches en arrière-plan

  • FLAME peut fonctionner à l’intérieur d’un worker de tâches en arrière-plan, mais son rôle recoupe partiellement celui d’une file de tâches
  • Une file de tâches sert généralement quand il faut une garantie de durabilité, et elle peut être ajustée pour traiter davantage de tâches selon la charge
  • Les tâches durables et l’exécution élastique sont deux préoccupations distinctes
    • si la file n’est utilisée que pour déporter l’exécution, il faut ajouter du glue code pour pousser les données dans la tâche et renvoyer le résultat à l’appelant ou à l’appareil de l’utilisateur
    • si la génération de miniatures après un téléversement vidéo doit être garantie, la file peut prendre en charge dispatch, commit et retry
    • le transcodage lui-même peut alors être exécuté via un appel FLAME à l’intérieur de la tâche, ce qui sépare la durabilité de l’exécution scalable
  • Pour des traitements qui n’ont pas besoin de durabilité, comme un aperçu vidéo avant enregistrement ou l’exécution d’un modèle ML après que l’utilisateur a déjà quitté l’application, écrire la tâche dans un stockage durable peut ne pas être adapté

Pools de runners pour la montée en charge élastique

  • L’implémentation Elixir de FLAME définit un pool élastique de runners qui prend en charge à la fois le scale-to-zero et les limites de concurrence
  • Dans l’exemple de configuration, FLAME.Pool est ajouté au start/2 de l’application
    • min: 0 autorise le scale-to-zero
    • max: 10 permet de démarrer jusqu’à 10 runners
    • max_concurrency: 5 autorise 5 tâches ffmpeg par runner
    • idle_shutdown_after: 30_000 arrête le runner s’il n’y a aucun appel pendant 30 secondes
  • Le serveur web Phoenix démarre de façon conditionnelle selon la présence ou non d’un parent FLAME
    • il n’est pas nécessaire de lancer le serveur web sur un runner FLAME qui ne traite pas de trafic HTTP
    • en revanche, MyApp.Repo et la base doivent rester disponibles car ils sont utilisés dans le runner FLAME
  • Avec min: 1, on peut maintenir au moins un runner ffmpeg à chaud dès le démarrage de l’application

Placement de processus avec état

  • Dans une application Elixir, les parties avec état reposent sur des processus légers disposant d’une mailbox de messages
  • FLAME.call et FLAME.cast conviennent à du code relativement stateless, tandis que FLAME.place_child permet de démarrer une spécification de processus existante sur un runner FLAME plutôt qu’en local
  • FLAME.place_child peut être utilisé partout où l’on emploie des interfaces comme Task.Supervisor.start_child ou DynamicSupervisor.start_child
  • Dans l’exemple de génération de miniatures pendant un téléversement LiveView, les chunks uploadés sont envoyés à un processus ThumbnailGenerator, qui communique ensuite avec ffmpeg
    • lorsqu’il trouve un délimiteur PNG dans la sortie standard de ffmpeg, il envoie un message image au processus LiveView
    • LiveView reçoit ce message dans handle_info et ajoute la nouvelle image à l’interface
  • En remplaçant DynamicSupervisor.start_child(@sup, spec) par FLAME.place_child(Thumbs.FFMpegRunner, spec), le processus ThumbnailGenerator s’exécute sur un runner FLAME
  • Comme les processus peuvent échanger des messages quelle que soit leur localisation, si le processus se termine à la fin de l’upload ou à la fermeture de l’onglet du navigateur, le serveur FLAME détecte l’arrêt et s’éteint à l’état idle s’il n’a plus d’autre travail

Supervision distante et gestion des pannes

  • Une infrastructure éphémère a besoin de mécanismes de sécurité pour éviter les ressources orphaned
  • Lorsqu’un parent lance un runner, celui-ci doit pouvoir s’arrêter lui-même à l’état idle quand il n’a plus de travail, et déclencher un arrêt de secours s’il ne peut plus contacter le nœud parent
  • Quand un nouveau déploiement remplace le parent, les runners doivent aussi s’arrêter afin de garantir que le cluster exécute bien le même code partout
  • Un appelant actif qui attend le résultat d’un runner doit prendre en compte le fait que ce runner peut s’arrêter pour n’importe quelle raison
  • Les primitives fournies par la VM Erlang simplifient cette implémentation
    • supervision et monitoring de processus locaux et distants
    • node monitoring pour détecter l’arrivée et la disparition de nœuds
    • flux de shutdown contrôlant l’ordre de démarrage et d’arrêt des applications afin de laisser aux runners actifs le temps de terminer leur travail lors d’un nouveau déploiement
  • Les détails internes de l’implémentation seront abordés dans un autre article ; en attendant, on peut consulter le code source de flame

État actuel et prochaines étapes

  • La bibliothèque FLAME pour Elixir en est encore à ses débuts, mais elle peut déjà être testée
  • Des techniques plus avancées de croissance de pool et une analyse approfondie de l’implémentation Elixir sont prévues par la suite
  • Il est également possible de discuter d’implémentations du modèle FLAME dans d’autres langages

1 commentaires

 
GN⁺ 2023-12-08
Avis sur Hacker News
  • Après avoir subi, ces 4 dernières années, la douleur et la complexité d’une appli composée de plus de 100 fonctions Lambda, je pense que cet article met précisément le doigt sur les défauts des architectures serverless FaaS
    Au début, ces défauts ne sont pas évidents. Au contraire, quand l’usage est faible, les avantages sont nets : c’est presque gratuit et il n’y a quasiment pas de maintenance
    Ce n’est que plus tard, quand les workflows Lambda s’entremêlent et deviennent de plus en plus rigides à cause des interdépendances, qu’on regrette de ne pas être parti sur un monolithe et de ne pas avoir payé quelques centaines de dollars de plus pour l’administrer soi-même. De nos jours, avec des services comme fly.io, cela pourrait même coûter moins cher
    Je me demande ce que cela donnerait si l’on n’utilise pas Elixir

    • J’ai vu revenir sans cesse ce schéma : des développeurs motivés peuvent faire tenir à peu près n’importe quel processus ou architecture pendant environ 18 mois, et cela ressemble désormais presque à une généralisation, voire à une loi
      Au moment où la situation se dégrade, c’est souvent aussi le moment de chercher un nouveau poste. Surtout si, environ un an après son arrivée, on regrette déjà le processus que l’on a introduit. Je l’ai vu plusieurs fois avec de mauvais managers, des « tests unitaires » bancals, Scrum, etc.
      Cela dit, je ne sais pas si les gens identifient clairement la cause de leur malaise au travail, ou s’ils le ressentent simplement comme « il est temps de partir ». Quand j’ai essayé de nommer ce qui n’allait pas, j’ai rencontré beaucoup de résistance, et depuis que j’ai lu Good to Great, cela me coûte beaucoup moins émotionnellement. Personne n’a envie de dire : « ah, c’est la conséquence de mes actes »
      Les personnes qui avaient effectivement créé le système à la Rube Goldberg que je maintiens sont parties les premières. Le capitaine de ce navire a demandé à un collègue ce qu’il penserait d’ouvrir notre moteur en open source, et celui-ci lui a répondu que personne ne voudrait utiliser un système qui réinventait une roue déjà existante et meilleure. Cette personne est partie volontairement dans les 3 à 4 mois
    • Nous construisons quelque chose qui devrait permettre de résoudre ce problème dans n’importe quel langage. Pour l’instant, il existe un SDK TypeScript, et Java est aussi en cours de développement
      Si l’on peut écrire du code orienté services classique où des Lambda peuvent s’appeler entre elles, il n’est pas nécessaire de découper l’application en centaines de petits morceaux à exécution courte. Mais si l’on doit payer aussi pour le temps passé à attendre, c’est impossible. Si Lambda pouvait suspendre l’exécution pendant les attentes d’E/S, le problème serait résolu. C’est pourquoi je pense que l’exécution durable pourrait être la réponse
      Ces dernières semaines, j’écrivais un article qui montre cela : https://restate.dev/blog/suspendable-functions-make-lambda-t...
    • Une section de l’article traite aussi de FLAME en dehors d’Elixir. En résumé, c’est un pattern généralement applicable aux langages qui disposent d’un modèle de concurrence raisonnable
      Il sera difficile d’obtenir toute l’ergonomie qu’Elixir fournit gratuitement, comme les fonctions avec sérialisation des variables capturées, mais dans des langages comme JavaScript, on devrait pouvoir atteindre environ 90 % du résultat en déplaçant le corps d’exécution du module dans un nouveau fichier plutôt qu’en l’enveloppant dans une closure
      La personne qui implémente une bibliothèque FLAME devra aussi écrire les parties pooling, monitoring et communication distante. Elixir fournit beaucoup de choses gratuitement côté messagerie distribuée et monitoring. Les fonctionnalités liées au placement des processus sont, en pratique, assez spécifiques à Elixir
    • Même avec seulement 12 Lambda, c’était difficile à supporter. L’appli d’origine avait été créée par quelqu’un qui ne s’était pas vraiment soucié de la maintenance ni du déploiement, et le code était copié-collé un peu partout
      Nous avons fini par migrer vers un gros monolithe Lambda où une seule Lambda gère plusieurs endpoints
    • Si l’on ne se préoccupe pas trop des cold starts, ou si l’on peut les gérer, on peut aussi faire un monolithe sur Lambda
      Autrement dit, on peut partir sur un monolithe tout en minimisant les coûts AWS. Ce n’est pas un choix exclusif entre les deux
      En ce moment, j’utilise asp.net, et même une appli assez grosse déployée en ready-to-run, avec un modèle EF optimisé, démarre relativement vite
  • Je suis l’auteur. Je suis ravi de pouvoir enfin le publier et je répondrai volontiers aux questions. J’espère que plusieurs personnes seront suffisamment inspirées pour implémenter le pattern FLAME en JavaScript, Go ou dans d’autres langages

    • Ça a l’air bien. J’aimerais que Microsoft y prête attention. Azure Functions est trop complexe à configurer côté sécurité et déploiement, et repose sur des hypothèses étranges concernant le code que l’on veut exécuter
    • L’implémentation la plus naturelle ressemblerait sans doute à quelque chose comme vert.x sur la JVM
      Il existe déjà, au-dessus d’un bus d’événements, des mécanismes de scaling réactif et d’exécution asynchrone via Future et coroutines, ainsi que la sérialisation et la distribution des données à l’échelle d’un cluster. Comme il existe aussi des clients de bus d’événements pour plusieurs langages populaires, on pourrait construire des applications en mélangeant plusieurs langages
    • Il vaudrait mieux réduire un peu l’emphase
      Quand on présente le problème comme « …un sort pire que la mort » et la solution comme tellement simple et indolore que ne pas abandonner les autres approches ferait passer pour idiot, ce genre d’article risque facilement d’être classé comme un pur argumentaire commercial. C’est la méthode des vendeurs de remèdes miracles
      Le début de l’article identifie bien le problème lui-même ; une comparaison objective beaucoup moins émotionnelle aurait sans doute été moins rebutante pour les personnes curieuses de meilleures approches
      Le fond de l’article était intéressant, mais la manière de le présenter m’a refroidi. J’espère que ce sera pris comme un retour constructif
    • L’article et la vidéo sont bons, et le concept est très intéressant. J’attends avec impatience une implémentation JavaScript, même si cela ne semble pas facile
      Et je me suis senti très légèrement coupable d’avoir réservé ffmpeg.fly.dev
    • Je me demande en quoi cela diffère fondamentalement de la manière dont Sidekiq instancie une base de code Rails pour exécuter des tâches en arrière-plan
  • Le passage « imaginez qu’il suffise d’envelopper n’importe quelle partie du code existant de votre appli dans une fonction pour qu’elle scale automatiquement, et que ce bloc de code s’exécute dans une copie temporaire de l’appli » est intéressant
    On dirait ce que fait fork, mais conçu pour le serverless. Beau travail

  • Il y a quelques années, j’ai utilisé un service qui faisait pratiquement ça. PiCloud a malheureusement été absorbé par Dropbox, mais avant cela, il avait exactement ce modèle consistant à répartir les tâches de façon transparente vers des workers. Le code était empaqueté puis exécuté sur les workers.
    Un exemple se trouve ici. On voit que c’est exactement le même modèle : https://github.com/picloud/basic-examples/blob/master/exampl...
    Je n’ai jamais utilisé Elixir, mais j’ai utilisé Erlang il y a des décennies, et BEAM ne semble pas avoir fondamentalement beaucoup changé. Comme cela fait partie du cœur de sa conception, ça paraît bien mieux adapté à ce genre de tâches. Cela dit, ce n’est pas non plus un déjeuner totalement gratuit : pendant l’attente, il y a bien un risque que le processus principal meure, non ?

  • Je suis d’accord avec l’argument général. Chez https://www.windmill.dev, nous avons adopté une autre approche : nous considérons l’unité d’abstraction au niveau du code source, et non au niveau du conteneur.
    Nous analysons la fonction main et les imports pour extraire les arguments et les dépendances, puis nous exécutons le code tel quel dans le runtime souhaité (TypeScript, Python, Go, Bash). Le point clé consiste à gérer le cache efficacement afin que les workers restent toujours chauds, quels que soient les imports.
    Cette approche n’est pas aussi intégrée à la codebase que FLAME, mais elle vise des utilisateurs différents. Nos utilisateurs créent de zéro des workflows complexes, des tâches cron, ou des scripts ponctuels accompagnés d’une UI générée automatiquement.
    Dans FLAME, il semble que tout le contexte soit pris en snapshot puis restauré sur la VM cible. Une autre approche consiste à introduire une syntaxe permettant de préciser quel contexte est nécessaire ou non, afin de ne charger que le minimum. C’est quelque chose que nous explorons actuellement pour mieux intégrer Windmill aux codebases existantes et ne pas dépendre d’appels HTTP.

    • Ce qui se passe réellement dans FLAME n’est pas exactement cela. FLAME se contente d’appeler une fonction sur un nœud distant via les fonctionnalités de clustering intégrées de BEAM.
      Dans ce processus, l’envoi du seul contexte nécessaire est géré implicitement. L’article l’explique aussi ainsi : « FLAME.call prend le nom d’un pool de runners et une fonction. Il trouve ensuite, ou démarre, une nouvelle copie de toute l’application, puis y exécute la fonction. Les variables capturées par la fonction sous forme de closure, par exemple la structure %Video{} et interval, sont automatiquement transmises avec elle. »
    • Il y a un w en trop. Pour ceux qui cherchent, l’URL est ici : https://www.windmill.dev/
      J’aime l’objectif du projet. J’espère vraiment que Windmill deviendra une meilleure alternative open source à Retool/Airtable.
  • J’aime le fait que « dans FLAME, les runners de développement et de test s’exécutent simplement sur le backend local ». Du serverless avec une expérience de développement locale correcte, c’est appréciable.

    • Oui. L’une des raisons pour lesquelles je n’aime pas le serverless, c’est que l’expérience de développement locale est bien moins bonne que lorsqu’on exécute un monolithe.
  • Si « une nouvelle copie de toute l’application est ensuite trouvée ou démarrée, et la fonction s’y exécute », est-ce que chaque Flame.call redémarre tout le processus de l’app et y copie le contexte d’exécution ?
    C’est une solution très simple du point de vue de la scalabilité, mais elle doit aussi avoir des inconvénients.
    Si le temps de démarrage de l’app augmente de 10 ms, cela ajoute 10 ms à chaque point Flame.call de l’application, et j’imagine que c’est pareil pour la mémoire.
    Il faut probablement tenir compte de ces préoccupations quand on utilise ce système.

    • La section FLAME.Pool plus loin dans l’article traite ce point. Les runners sont mis en pool et restent chauds pendant une durée configurable, puis redescendent en veille lorsqu’ils sont inactifs.
      Sous charge, le pool est déjà chaud, donc on paie à peine le coût du cold start. Nous ajoutons aussi ensuite à la bibliothèque Elixir des techniques plus sophistiquées de croissance du pool, afin d’éviter de tomber sur un runner saturé et de devoir en démarrer un nouveau à froid.
      Sur un runner chaud, la seule surcharge est la latence entre le parent et l’enfant. Comme ils doivent être dans le même datacenter, cela devrait être de l’ordre de 1 ms ou moins.
  • Excellent. Ça ressemble à une version très légère, propre à Elixir, de ce que nous avons construit chez https://www.inngest.com/.
    Les deux approches se ressemblent en ce qu’elles enveloppent du code existant pour le rendre utilisable dans des fonctions serverless et, en substance, appelable via du RPC distant.
    Ce genre de code s’exécute souvent comme une suite d’étapes impératives. Chaque étape peut être exécutée en série ou en parallèle dans une Lambda supplémentaire. Mais entre les étapes, il existe un état implicite capturé dans des variables. C’est ce qui transforme la fonction en workflow. Dans le modèle d’Inngest, cet état est capturé puis réinjecté dans la fonction pour la rendre durable.
    Du point de vue de la durabilité, ce type de processus doit s’appuyer sur une queue. L’intérêt de ce modèle est qu’une queue est peu coûteuse. Si l’on rend une queue aussi peu coûteuse qu’une ligne de code, tout devient plus simple. N’importe quel développeur peut écrire du code fiable sans se soucier de l’infrastructure.
    Le monitoring et l’observabilité sont également importants. Les dead letter queues sont vraiment horribles, et il faut pouvoir gérer et relancer les fonctions ou étapes qui échouent.
    Il existe aussi des différences entre FLAME et Inngest. Inngest est basé sur des queues, piloté par les événements, et peut être servi en HTTP depuis n’importe quel langage. Comme Inngest stocke l’état à l’extérieur, on peut écrire un workflow en Elixir, puis le réécrire en TypeScript et le redéployer, tandis qu’une fonction en cours d’exécution peut être migrée en temps réel entre langages backend, un peu comme avec CRIU.
    Une approche event-driven permet aussi de contrôler les flux. Debounce, batching, throttling, fan-out : tout cela peut être géré depuis n’importe quel runtime ou langage. Par exemple, une application Elixir sur Fly peut envoyer un événement pour déclencher l’exécution d’une fonction en TypeScript + Lambda.
    J’ai hâte de voir où va FLAME. Je pense que certains objectifs se recoupent.

    • Inngest a l’air d’être un service intéressant. L’article aborde aussi les gestionnaires de tâches, la durabilité et les retries.
      En Elixir, quand on a besoin de durabilité, de retries et de workflows, on utilise généralement Oban, et c’est ce que l’on continuera à faire ici. Une tâche Oban appellera FLAME pour gérer l’exécution élastique.
  • C’est l’une des raisons pour lesquelles je déteste vraiment la casse des titres à l’américaine imposée par HN. Serverless donne l’impression qu’il s’agit de repenser l’entreprise serverless.com avec un S majuscule, et non de repenser le principe du serverless avec un s minuscule
    Cela dit, j’aimerais que quelqu’un repense effectivement le serverless

    • Il me semble que la casse n’est pas imposée par HN, mais choisie par la personne qui publie l’article
      Je ne sais pas si tu as regardé https://sst.dev/, mais ils semblent avoir fait exactement cela. Par exemple, avec Live Lambda Development, il devient vraiment facile de faire du développement local, car la boucle de feedback est fortement réduite sans avoir à envoyer le code dans le cloud et attendre le déploiement
  • L’idée est plutôt chouette, et l’API est excellente
    Concernant le passage « les tâches bound CPU comme le transcodage vidéo peuvent rapidement mettre tout un service de production à l’arrêt », est-ce qu’il ne suffirait pas simplement de faire de l’autoscaling basé sur le CPU ?

    • J’ai essayé d’aborder cette idée au début de l’article. Le problème de cette approche, c’est qu’elle scale au mauvais niveau de fonctionnement
      Pour traiter une tâche ponctuelle qui chauffe, on finit par scaler toute l’application, serveur web compris. Ce que l’on veut, et la raison pour laquelle on se tourne vers le FaaS, c’est une scalabilité élastique fine. L’idée ici est de permettre ce type de scaling fin sur le code applicatif existant, plutôt que d’appuyer frénétiquement sur le bouton de scaling du serveur web ou des workers en espérant que tout se passe bien
    • Oui et non. Le reste de la charge de travail peut ne pas nécessiter beaucoup de CPU. Il se peut qu’un ou deux workloads seulement aient besoin de ce niveau de performance, et qu’on veuille éviter que cette tâche soit évincée par d’autres
      Ou bien il peut aussi falloir un GPU
      Le service principal peut très bien se contenter d’un ou deux serveurs, tout en devant scaler à la demande vers des dizaines, des centaines ou des milliers de machines pour une tâche qui ne se produit qu’environ une fois par jour