4 pontos por GN⁺ 2025-12-09 | 1 comentários | Compartilhar no WhatsApp
  • Durante a migração da base de código da Scala 2.13 para o Scala 3 houve queda de desempenho inesperada
  • Nos ambientes iniciais de teste e implantação, todos os indicadores estavam normais, mas após algumas horas houve aumento do Kafka lag
  • Em testes de carga, foi constatada uma queda acentuada na taxa de processamento ao tratar mensagens granularizadas
  • A análise com async-profiler revelou que a causa era um bug de avaliação de cadeia ineficiente na biblioteca Quicklens
  • Após atualização da biblioteca, o desempenho se recuperou, destacando a necessidade de atenção para diferenças de comportamento entre versões do Scala

Processo de migração do serviço

  • O serviço existente foi migrado da Scala 2.13 para Scala 3.7.3
    • Era um serviço voltado para coleta de dados, sem uso de macros, e possuía uma composição em que performance era crítica
    • Após aplicar mudanças em dependências, opções do compilador, tipos e sintaxe, a compilação foi concluída com sucesso
  • Em ambientes de teste e implantação gradual, logs e métricas também ficaram normais
    • Métricas de infraestrutura, JVM e aplicação também foram verificadas como saudáveis

Queda de desempenho de causa desconhecida

  • Aproximadamente 5~6 horas após a implantação ocorreu aumento do Kafka lag
    • Diminuição da taxa de processamento por instância mesmo sem pico de dados
    • Após rollback, a taxa de processamento se recuperou imediatamente, confirmando que a alteração de código era a causa

Análise de desempenho e rastreamento da causa

  • Nos testes de carga, a regressão de desempenho não foi reproduzida inicialmente
    • A queda de taxa de processamento ocorreu apenas com mensagens granulares e payloads heterogêneos
  • Foram testados reversões individuais de dependências de biblioteca (serialização, DB SDK, imagem Docker, biblioteca de configuração etc.), sem mudanças
  • O profiling de CPU com async-profiler mostrou que
    • No Scala 3 houve um aumento brusco do uso de CPU nas etapas do compilador JIT e de decodificação
    • No topo do Flamegraph, chamadas ao Quicklens ocupavam metade de todo o tempo de CPU
    • No Scala 2.13, a mesma chamada representa apenas 0,5%

Causa raiz do problema

  • O bug de avaliação de cadeia ineficiente da biblioteca Quicklens ocorria em Scala 3
    • A correção correspondente foi incorporada no GitHub PR #115
    • Após atualizar a biblioteca, houve eliminação da diferença de desempenho entre Scala 3 e 2.13

Lições e recomendações

  • A dependência de metaprogramação de bibliotecas pode causar diferenças de desempenho entre versões de Scala
  • Mesmo com uma migração concluída com sucesso, é preciso benchmarkar hotspots e gargalos
  • Em serviços onde performance é crítica, em vez de assumir que “funciona bem”, a validação baseada em medições reais é obrigatória
  • É necessária uma verificação preventiva para evitar situações em que o benchmark revele gargalos que não aparecem no código

1 comentários

 
GN⁺ 2025-12-09
Opiniões no Hacker News
  • Não sou fã de Scala, mas fiquei impressionado com o processo de análise profunda e depuração do problema
    É assim que um blog técnico deveria ser escrito. IA dificilmente substitui esse nível de raciocínio
    • Ao renovar um dos nossos serviços, fizemos a migração de Scala 2.13 para Scala 3
      A primeira pergunta era simples — "por que exatamente precisaríamos fazer esse upgrade?"
  • No Scala 3, a palavra-chave inline funciona como parte do sistema de macros
    Usar inline em parâmetros instrui o compilador a inserir a expressão no ponto de chamada
    Mas, se isso for grande, impõe uma carga enorme ao compilador JIT
    No Scala 2, @inline era apenas uma sugestão, mas no 3 ela é aplicada obrigatoriamente
    Portanto, simplesmente trocar @inline por inline é um grande erro
    • Essa diferença é parecida com o que aconteceu com a palavra-chave register no antigo C/C++
      No início era obrigatória, mas conforme as otimizações evoluíram virou apenas uma recomendação e acabou sendo ignorada
      O inline do C++ passou por algo semelhante
    • Kotlin usa inline de forma agressiva em quase todo lugar
      Isso é para eliminar o overhead de lambdas em funções como map
      Quase não havia problemas de performance, mas combinado com o sistema de macros do Scala isso pode gerar expressões complexas e causar problemas
  • Depois de atualizar as bibliotecas, o desempenho do Scala 3 ficou quase idêntico ao do Scala 2.13
    Tive experiência parecida no upgrade do Ruby 2→3
    Não basta subir só a linguagem; é preciso atualizar todo o conjunto de dependências para o sistema ficar estável
  • O problema do Scala 3 é que ninguém queria isso
    Os problemas de inferência de tipos do Scala 2 continuam sem solução, e em vez disso só mudaram a linguagem
    Foi como ignorar a demanda do mercado e criar um produto que ninguém queria
    PS: precisam mesmo criar uma suíte de testes unitários de verdade para o compilador
    • Triste, mas concordo. Scala parecia promissora por volta de 2010~15
      Mas a reescrita do Scala 3 não resolveu os problemas de velocidade de compilação e tooling, e o projeto perdeu completamente o embalo
      Fico pensando se alguém começaria um projeto novo em Scala em 2025
    • Tentei aprender Scala várias vezes, mas no fim sempre voltei para Clojure
      Scala parece uma linguagem feita por acadêmicos, e o fato de ter virado moda na indústria por um tempo foi até estranho
    • Acertou em cheio
      Agora todas as ferramentas precisam se adequar ao Scala 3, e nem o IntelliJ ainda dá suporte perfeito
      Teria sido melhor melhorar o Scala 2 de forma gradual, mas parece que focaram só no sucesso acadêmico
      Por exemplo, há muita discussão até sobre early return, como no texto do tpolecat, mas o Kotlin suporta isso sem problema algum
    • Dizer que “não há testes” é desinformação
      O compilador do Scala tem milhares, dezenas de milhares de testes, e
      o diretório oficial de testes e o sistema de community build validam milhões de LOC
    • Parece que você não leu o texto direito. O ponto central passou completamente longe
  • O mais interessante neste texto foi a ideia de que testes automatizados de performance e análise com flamegraph deveriam ser parte básica do processo
    Especialmente em mudanças grandes, como upgrade de versão de linguagem, isso é indispensável
    • Testes de benchmark, ao contrário dos testes normais, exigem configuração detalhada
      Fazemos benchmark contínuo de uma ferramenta escrita em C++, mas é difícil manter consistência por causa do ruído do ambiente
      Estamos pensando em comparar várias execuções na mesma máquina
    • Fico curioso sobre quais ferramentas de teste de performance estão sendo usadas hoje em dia na JVM
  • O único problema do Scala 3 é inveja de Python
    O erro foi criar uma segunda sintaxe e empurrá-la como se fosse o futuro
    Isso também desacelerou o ecossistema de ferramentas
    • Hoje em dia a maioria das ferramentas já suporta a nova sintaxe
      Também dá para converter o estilo automaticamente com o compilador ou o scalafmt
    • Bem ao estilo Scala, agora há várias formas de fazer a mesma coisa
      Agora temos em dobro: sintaxe com chaves e sintaxe por indentação
    • Acho que teria sido mais atraente se a comparação fosse com Haskell
    • Como ex-fã de Scala, fiquei desconcertado ao ver exemplos da nova sintaxe
      A construção match ficou verbosa demais e parece uma imitação de Python
  • Já fiz uma migração de Scala 2.x no passado, e o mais difícil foi esperar pelas dependências
    Na época, Scala ganhou destaque por causa do Spark, mas perdeu a chance de evoluir como linguagem comercial
    Agora o Spark foi para Python, e o espaço de linguagem moderna da JVM ficou com o Kotlin
    No fim, parece que Scala voltou a ser uma linguagem acadêmica
    • Mas Scala ainda é amplamente usado em grandes empresas
      Dá para ver isso no Scala Adoption Tracker
      Os novos recursos do Scala 3 têm potencial para inovar o ecossistema da linguagem de novo
      Ex.: explicação sobre Capture Checking
    • Tenho dúvidas se o Kotlin realmente dominou a JVM
      Com Java adicionando recursos funcionais, ele acabou absorvendo parte do apelo do Scala
    • No lado server-side da JVM, a presença do Kotlin é quase nula
      Pela minha experiência, a demanda de mercado também é mínima
    • Kotlin é, na prática, uma linguagem voltada para Android
      Só ficou assim porque o Google restringiu o suporte a Java
      No mercado total da JVM, sua participação não passa de uns 10%
  • Achei estranho terem migrado para Scala 3 sem atualizar as versões das bibliotecas
    Se você passa por auditorias de segurança (PCI-DSS etc.), manter bibliotecas atualizadas é obrigatório
    • Dizer que “manter tudo atualizado é uma boa prática” é uma forma de encerrar a discussão
      Eu, na verdade, costumo manter dependências antigas
      Versões novas trazem bugs novos, mudanças de mantenedores e riscos de segurança também
    • Também fiquei confuso. No texto diz que “as dependências foram atualizadas”, mas depois fala que “depois da atualização a performance ficou igual”
      Parece que no começo só parte delas foi atualizada. Dividir em etapas menores é comum, mas parece que deram azar
    • Depois de entender a causa do bug, fiquei menos impressionado
      O problema não era o Scala 3 em si, mas a interação entre vários fatores
    • Em mudanças grandes, como upgrade de versão da linguagem, o ideal é mudar uma coisa de cada vez
    • Em Maven/Gradle/SBT, dá para impor restrições de versão e mantê-las separadas da versão da linguagem
      Mas é preciso cuidado com bibliotecas específicas de Scala, porque a versão do Scala vem embutida na versão delas
  • O relatório de bug da SoftwareMill e do GitHub do Scala foi uma correção pequena, mas precisa
    A expressividade do Scala e sua segurança de tipos ficaram evidentes
    Como no texto do Li Haoyi, ele continua bastante atraente até como alternativa ao Python
  • A principal lição é que, em upgrades maiores de linguagem ou framework, as bibliotecas também devem ser atualizadas junto
    Isso é ainda mais importante quando há muitas bibliotecas que abusam de recursos mágicos