4 points par GN⁺ 2025-12-09 | 1 commentaires | Partager sur WhatsApp
  • Une régression de performance inattendue s'est produite lors de la migration de la base de code de Scala 2.13 vers Scala 3
  • Dans les environnements de test et de déploiement initiaux, toutes les métriques étaient normales, mais quelques heures plus tard le lag Kafka a augmenté
  • Les tests de charge ont confirmé une forte baisse de throughput lors du traitement de messages décomposés
  • L'analyse avec async-profiler a révélé qu'un bug d'évaluation inefficace de chaînes dans la bibliothèque Quicklens était la cause
  • Après la mise à jour de la bibliothèque, les performances ont été rétablies, en soulignant la nécessité de faire attention aux différences de comportement des bibliothèques entre versions de Scala

Processus de migration du service

  • Le service existant a été migré de Scala 2.13 vers Scala 3.7.3
    • Il s'agissait d'un service orienté collecte de données sans macros, avec des performances critiques
    • Après avoir appliqué les dépendances, les options du compilateur, et les changements de types et de syntaxe, la compilation a réussi
  • En environnement de test et lors du déploiement progressif, les logs et métriques sont tous normaux
    • Les indicateurs aux niveaux infrastructure, JVM et application étaient également en bonne santé

Dégradation de performance d'origine inconnue

  • Environ 5 à 6 heures après le déploiement, une hausse du lag Kafka est apparue
    • Une baisse du throughput par instance s'est produite même sans pic de données
    • Après rollback, le throughput s'est rétabli immédiatement, confirmant que le changement de code en était la cause

Analyse des performances et recherche de la cause

  • En test de charge, la régression de performance n'a pas été reproduite au départ
    • Une chute du throughput s'est produite uniquement lorsque les messages étaient décomposés et avec des payloads hétérogènes
  • Les dépendances de bibliothèque (sérialisation, SDK DB, image Docker, bibliothèque de configuration, etc.) ont été testées en les rétablissant une par une, sans aucun changement
  • Après exécution du profiling CPU avec async-profiler :
    • En Scala 3, la part CPU du compilateur JIT et de l'étape de décodage a fortement augmenté
    • Sur le flamegraph, les appels Quicklens représentaient près de la moitié du temps CPU total
    • En Scala 2.13, le même appel ne représentait que 0,5 %

Cause racine

  • Un bug d'évaluation inefficace de chaînes de la bibliothèque Quicklens se produisait sous Scala 3
    • La correction associée est intégrée dans GitHub PR #115
    • Après la mise à jour de la bibliothèque, la différence de performance entre Scala 3 et 2.13 a été résolue

Leçons et recommandations

  • Une dépendance à la métaprogrammation des bibliothèques peut provoquer des écarts de performance entre versions de Scala
  • Même quand une migration se termine correctement, il faut benchmarker les hotspots et les goulots d'étranglement
  • Pour les services où la performance est critique, une validation basée sur des mesures réelles est indispensable au lieu de supposer que ça fonctionne
  • Des vérifications préventives sont nécessaires pour éviter une situation où ce sont les benchmarks qui révèlent les goulots d'étranglement que le code ne montre pas

1 commentaires

 
GN⁺ 2025-12-09
Avis sur Hacker News
  • Je ne suis pas un fan de Scala, mais j’ai été impressionné par le niveau d’analyse et le travail de débogage
    C’est comme ça qu’un blog technique devrait être écrit. Il sera difficile pour l’IA de remplacer un tel niveau de raisonnement
    • En remettant à neuf l’un de nos services, nous avons migré de Scala 2.13 vers Scala 3
      La première question était simple — « pourquoi faudrait-il faire cette mise à niveau ? »
  • Dans Scala 3, le mot-clé inline fonctionne comme une partie du système de macros
    Utiliser inline sur un paramètre indique au compilateur d’inliner l’expression au point d’appel
    Mais si cette expression est volumineuse, cela impose une lourde charge au compilateur JIT
    Dans Scala 2, @inline n’était qu’une simple suggestion, alors qu’en 3 il est appliqué systématiquement
    Donc remplacer simplement @inline par inline est une grosse erreur
    • Cette différence ressemble à ce qui est arrivé autrefois au mot-clé register en C/C++
      Au départ c’était contraignant, mais avec les progrès de l’optimisation, c’est devenu une simple recommandation, puis il a fini par être ignoré
      Le inline de C++ a suivi une évolution comparable
    • Kotlin utilise activement inline presque partout
      Notamment pour supprimer le surcoût des lambdas dans des fonctions comme map
      Il y a eu peu de problèmes de performance, mais combiné au système de macros de Scala, cela peut produire des expressions complexes et poser problème
  • Après la mise à niveau des bibliothèques, les performances de Scala 3 sont devenues presque identiques à celles de Scala 2.13
    J’ai eu une expérience similaire lors du passage de Ruby 2 à 3
    Il ne suffit pas de mettre à jour le langage : il faut actualiser l’ensemble des dépendances pour stabiliser le système
  • Le problème de Scala 3, c’est que personne n’en voulait
    Les problèmes d’inférence de types de Scala 2 n’ont toujours pas été résolus, et à la place seul le langage a changé
    C’est comme fabriquer un produit dont personne ne veut tout en ignorant la demande du marché
    PS : il faut vraiment construire une vraie suite de tests unitaires pour le compilateur
    • Tristement, je suis d’accord. Scala paraissait prometteur vers 2010~15
      Mais la réécriture de Scala 3 n’a pas résolu les problèmes de vitesse de compilation et de tooling, et a complètement fait perdre son élan au projet
      Je me demande vraiment si quelqu’un lancerait un nouveau projet en Scala en 2025
    • J’ai essayé plusieurs fois d’apprendre Scala, mais je suis toujours revenu à Clojure
      Scala donne l’impression d’être un langage conçu par des universitaires, et le fait qu’il soit devenu brièvement populaire dans l’industrie paraît presque étonnant
    • C’est exactement le cœur du problème
      Désormais tous les outils doivent s’aligner sur Scala 3, et même IntelliJ n’offre toujours pas un support parfait
      Il aurait mieux valu améliorer Scala 2 progressivement, mais on a l’impression que l’accent a été mis uniquement sur le succès académique
      Par exemple, il y a encore beaucoup de débats autour de early return, comme dans l’article de tpolecat, alors que Kotlin le prend en charge sans aucun problème
    • Dire qu’« il n’y a pas de tests » est une fausse information
      Le compilateur Scala possède des milliers, voire des dizaines de milliers de tests, et
      le répertoire de tests officiel ainsi que le système de community build valident des millions de lignes de code
    • On dirait que l’article n’a pas été lu correctement. La critique passe complètement à côté du sujet
  • Ce que j’ai trouvé le plus intéressant dans cet article, c’est l’idée qu’il faut disposer par défaut de tests de performance automatisés et d’analyses par flamegraph
    C’est indispensable surtout lors de gros changements comme une mise à niveau de version du langage
    • Les benchmarks demandent un réglage fin, contrairement aux tests ordinaires
      Nous benchmarkons en continu un outil écrit en C++, mais à cause du bruit de l’environnement, il est difficile d’obtenir des résultats cohérents
      Nous réfléchissons à une méthode consistant à exécuter plusieurs fois sur la même machine pour comparer
    • Je me demande quels outils de test de performance sont utilisés sur la JVM de nos jours
  • Le seul vrai problème de Scala 3, c’est la jalousie envers Python
    Le problème, c’est d’avoir créé une deuxième syntaxe puis de l’avoir imposée comme l’avenir
    Cela a aussi ralenti l’écosystème des outils
    • Aujourd’hui, la plupart des outils prennent en charge la nouvelle syntaxe
      On peut aussi convertir automatiquement le style via le compilateur ou scalafmt
    • Fidèle à l’esprit Scala, cela permet désormais plusieurs façons de faire la même chose
      Maintenant, il y a le double : la syntaxe avec accolades et la syntaxe par indentation
    • La comparaison avec Haskell aurait sans doute été plus séduisante
    • En tant qu’ancien fan de Scala, j’ai été déconcerté en voyant les exemples de la nouvelle syntaxe
      La syntaxe match est beaucoup trop verbeuse et donne l’impression d’imiter Python
  • J’ai déjà fait une migration Scala 2.x, et le plus difficile était d’attendre les dépendances
    À l’époque, Scala était sous les projecteurs grâce à Spark, mais il a raté l’occasion d’évoluer en langage commercial majeur
    Aujourd’hui, Spark se fait en Python, et la place des langages JVM modernes est occupée par Kotlin
    Au final, Scala donne l’impression d’être redevenu un langage académique
    • Pourtant, Scala reste largement utilisé dans les grandes entreprises
      Il suffit de regarder le Scala Adoption Tracker
      Les nouvelles fonctionnalités de Scala 3 ont le potentiel de réinventer l’écosystème du langage
      Par exemple : explication de Capture Checking
    • Je ne suis pas sûr que Kotlin ait vraiment pris le contrôle de la JVM
      Java a absorbé une partie de l’attrait de Scala en ajoutant des fonctionnalités fonctionnelles
    • Côté JVM server-side, Kotlin est presque invisible
      D’après mon expérience, la demande du marché reste minime
    • Kotlin est en pratique un langage réservé à Android
      C’est surtout parce que Google a restreint la prise en charge de Java que cela s’est passé ainsi
      Sur l’ensemble du marché JVM, sa part ne représenterait qu’environ 10 %
  • J’ai trouvé étrange de passer à Scala 3 sans mettre à jour les versions des bibliothèques
    Si l’on subit des audits de sécurité (PCI-DSS, etc.), garder les bibliothèques à jour est indispensable
    • Dire que « rester à jour est une bonne habitude » est une formule qui bloque la discussion
      Pour ma part, je préfère plutôt conserver des dépendances anciennes
      Les nouvelles versions apportent de nouveaux bugs, des changements de mainteneurs ou des risques de sécurité
    • Moi aussi j’étais perplexe. L’article disait qu’« on avait mis à jour les dépendances », puis plus loin que « les performances sont devenues identiques après la mise à jour »
      Il semble qu’au début seule une partie ait été mise à jour. Procéder par petites étapes est courant, mais là ils ont simplement manqué de chance
    • Une fois la cause du bug connue, c’était moins impressionnant
      Le problème ne venait pas de Scala 3 lui-même, mais de l’interaction de plusieurs facteurs
    • Plus le changement est important, comme une mise à niveau de version du langage, plus il vaut mieux ne modifier qu’une seule chose à la fois
    • Avec des contraintes de version dans Maven/Gradle/SBT, cela reste gérable indépendamment de la version du langage
      Mais il faut faire attention, car les bibliothèques spécifiques à Scala incluent souvent la version de Scala dans leur propre version
  • Le bug report de SoftwareMill et celui du GitHub de Scala étaient des corrections petites mais précises
    Ils mettaient bien en valeur l’expressivité de Scala et sa sécurité de typage
    Comme dans l’article de Li Haoyi, le langage reste tout à fait séduisant comme alternative à Python
  • La principale leçon, c’est que lors des montées de version majeures d’un langage ou d’un framework, il faut aussi mettre à jour les bibliothèques
    C’est particulièrement important lorsqu’il y a beaucoup de bibliothèques qui abusent de fonctionnalités magiques