- 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
Opiniões no Hacker News
É assim que um blog técnico deveria ser escrito. IA dificilmente substitui esse nível de raciocínio
A primeira pergunta era simples — "por que exatamente precisaríamos fazer esse upgrade?"
inlinefunciona como parte do sistema de macrosUsar
inlineem parâmetros instrui o compilador a inserir a expressão no ponto de chamadaMas, se isso for grande, impõe uma carga enorme ao compilador JIT
No Scala 2,
@inlineera apenas uma sugestão, mas no 3 ela é aplicada obrigatoriamentePortanto, simplesmente trocar
@inlineporinlineé um grande erroregisterno 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
inlinedo C++ passou por algo semelhanteinlinede forma agressiva em quase todo lugarIsso é para eliminar o overhead de lambdas em funções como
mapQuase não havia problemas de performance, mas combinado com o sistema de macros do Scala isso pode gerar expressões complexas e causar problemas
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
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
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
Scala parece uma linguagem feita por acadêmicos, e o fato de ter virado moda na indústria por um tempo foi até estranho
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 algumO 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
Especialmente em mudanças grandes, como upgrade de versão de linguagem, isso é indispensável
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
O erro foi criar uma segunda sintaxe e empurrá-la como se fosse o futuro
Isso também desacelerou o ecossistema de ferramentas
Também dá para converter o estilo automaticamente com o compilador ou o scalafmt
Agora temos em dobro: sintaxe com chaves e sintaxe por indentação
A construção
matchficou verbosa demais e parece uma imitação de PythonNa é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
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
Com Java adicionando recursos funcionais, ele acabou absorvendo parte do apelo do Scala
Pela minha experiência, a demanda de mercado também é mínima
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%
Se você passa por auditorias de segurança (PCI-DSS etc.), manter bibliotecas atualizadas é obrigatório
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
Parece que no começo só parte delas foi atualizada. Dividir em etapas menores é comum, mas parece que deram azar
O problema não era o Scala 3 em si, mas a interação entre vários fatores
Mas é preciso cuidado com bibliotecas específicas de Scala, porque a versão do Scala vem embutida na versão delas
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
Isso é ainda mais importante quando há muitas bibliotecas que abusam de recursos mágicos