Este documento resume as experiências de três anos usando Scala em produção para um site de monitoramento de notícias. Inicialmente o código foi migrado de Ruby para Scala para melhorar o coletor de feeds e indexação no ElasticSearch. A arquitetura usa Akka para o crawler assíncrono e Scalatra para as APIs. Ao longo dos anos houve atualizações para novas versões do Scala e liabras, e a equipe aprendeu a importância do estilo de código e ferramentas como Scalastyle.
Docker: Conceitos e Primeiros Passos na utilização de Containers - Programand...
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeça
1. Três anos de Scala em Produção:
desafios, aprendizados e dores de
cabeça
Felipe Hummel
Onilton Maciel
2. • Site profissional de monitoramento de notícias
• Coletando diariamente +30K sites
• 140M de notícias
• 5M/mês
• +20 máquinas no EC2
• 2 Devs Backend + 3 Devs PHP/Frontend/Aplicação
• 3 anos com Scala em produção
• 30K linhas de código Scala
3. Como começamos em
Scala?
• 2010/2011
• Scala 2.7 => Scala 2.8
• Início na Spix em Janeiro de
2012
• busk.com
• Código legado em Ruby
• Ninguém viu, ninguém sabia
• Código feito por “consultoria"
• O que fazer?
4. Como começamos em
Scala?
• Movemos a Busca para o
ElasticSearch usando Scala.
• Resolvemos contornar o
código Ruby (nada contra)
• ElasticSearch pega direto do
BD ao invés de conversar com
o Coletor de noticias
• Coletor de Feed RSS deixava
notícias passarem (intervalo
de coleta fixo gerava atrasos)
• Começamos a implementar o
nosso coletor em Scala + Akka
5. Busk => NewsMonitor
• busk.com foi fechado e
pivotamos para o
NewsMonitor (profissional)
• Decisão de reaproveitar
código legado em Ruby ou
criar novo
• Em Scala? Ruby? Python?
6. Busk => NewsMonitor
• busk.com foi fechado e
pivotamos para o
NewsMonitor (profissional)
• Decisão de reaproveitar
código legado em Ruby ou
criar novo
• Em Scala? Ruby? Python?
• Escolhemos PHP
• Por boas razões no
momento
11. Scala: 2.7 ao 2.12
• 2.9 Maio/2011 <— NewsMonitor
• 2.10 - Janeiro/2013
• String interpolation, value classes, scala.concurrent
• Pressão da comunidade para fazer upgrade
• Muitas libs exigindo 2.10 (macros, macros, macros!)
• Começamos a migrar em agosto de 2013. Mas
100% 2.10 só em agosto de 2014.
12. Scala: 2.7 ao 2.12
• 2.11 - Abril/2014
• Poucas novas features (case class > 22)
• Pouca pressão para fazer upgrade
• Maioria das libs cross-compilando 2.10/2.11
• Retro-compatibilidade bem melhor que 2.9 => 2.10
• 2.12 - Janeiro/2016 ???
• Java 8 only.
• Melhor performance de lambdas?
• @interface traits
• Vamos de 2.10 => 2.12?
13. Scala: 2.7 ao 2.12
• Retro-compatibilidade melhorou bastante
• Linguagem vai mais devagar agora
• Libs que param no tempo ainda podem ser problema (cross-compila aí
fazendo favor!)
• Caso Querulous (https://github.com/twitter/querulous)
• Ótimo wrapper do JDBC feito pelo Twitter
• 3 anos sem commits (niver semana passada)
• Oficialmente compilado só pra 2.9
• Vários forks com ports para 2.10
• Caso Goose (https://github.com/GravityLabs/goose)
• Forks com PRs e compilação pra 2.10
15. SBT
• Simple Build Tool?
• Sintaxe um pouco esotérica
• Nunca tivemos muitos problemas
• Usamos apenas build.sbt
• Um projeto por repositório. Sem multi-projects
• Dica: sempre especificar versão do SBT no project/
build.properties
• Sempre: sbt ~compile OU sbt ~test OU sbt “~test-only …”
16. Compilador
• Compilação é lenta
• Compilação incremental (sbt ~compile) resolve na maior
parte do tempo
• Mudar algo importante do projeto gera uma compilação lenta
• Crawler com 9.1K linhas
• sbt clean compile => 1min 27segs
• Projetos maiores devem sofrer mais
17. Estilo de Scala
• Muitas formas de escrever o mesmo código
• Um mais cool, outro mais funcional, outro mais
imperativo
• Qual usar?
22. Estilo de Scala
• Empolgação é nossa inimiga
• Typeclasses são legais mas não precisamos criar uma implementação
própria pra serialização, pra JSON, pra concorrência, pra….
• Implicits são legais mas toda função ter implicit é NÃO
• UrlFetcher no Crawler funcionou muito bem
• Macros são legais. Nunca chegamos a criar novos
• Implicit conversions são legais mas podem gerar código aparentemente
mágico
• Se usar, é bom limitar o escopo onde é utilizado
• Densidade de código
23. Dicas
• Cuidado com default parameter
• Dois parâmetros do mesmo tipo com default
• Adicionar parâmetro com default e esquecer de
mudar os lugares que precisavam passar
• null não kct! Option sempre
• Toda equipe tentar seguir o mesmo padrão
• Code review ajuda a manter estilo
24. Estilo de Scala
• ScalaStyle (https://github.com/scalastyle/scalastyle)
• Scapegoat (https://github.com/sksamuel/scalac-scapegoat-
plugin)
• Wart remover (https://github.com/typelevel/wartremover)
• Linter (https://github.com/HairyFotr/linter)
• CPD (https://github.com/sbt/cpd4sbt)
• Abide (https://github.com/scala/scala-abide)
• Caminhando pra ser “O" oficial agora
25. Estilo de Scala
• scalacOptions += “-deprecation"
• scalacOptions += “-unchecked” (unchecked type-args)
• scalacOptions += “-feature” (reclama de features
avançadas)
• scalacOptions += “-Xlint" (faz checagens de boas
práticas)
• Tem gente que usa: scalacOptions += "-Xfatal-
warnings"
26. Akka
• Framework para concorrência baseada (principalmente)
em Actors
• Actors são objetos "especiais"
• Você não chama um método de um Actor de fora dele
• Você manda uma mensagem (objetos imutáveis) para
ele e em algum momento no futuro ele vai processar
• Qualquer código dentro de um Actor é garantido que só
vai rodar em condições thread-safe
28. Akka e NewsMonitor
• Nossos Crawlers são 100% Akka
• Começamos no Akka 2.0
• 2.0 tinha coisas pouco desenvolvidas
• Como usar? Melhores práticas? Boas arquitetura?
• Remoting. Actors em máquinas diferentes
• Meu conhecimento de Akka
• Hoje: Akka 2.3
• Muito mais maduro
• Akka Cluster
29. Akka :)
• Simplifica muito o modelo mental para trabalhar com concorrência
• Cria "bolhas" onde você pode ser mutável à vontade (com moderação)
• Muito bom quando você tem Actors de vida longa e com estado
mutável
• Não precisa lidar com criação/manutenção de filas (na maior parte do
tempo)
• Tunar threadpools (dispatchers) não é necessário no início e é bem fácil
(via config)
• O código do Actor não contém (quase) nada de lógica lidando com
concorrência, threads ou threadpool
30. Akka :(
• Toma conta do código (framework)
• A relação entre actors não é tão óbvia no código quanto a relação entre objetos
num código tradicional (perca de tipagem)
• Você pode acabar tunando ExecutionContexts e ThreadPool de qualquer forma.
E não é trivial (independente do Akka).
• Mais "difícil" de testar
• Dá pra isolar algumas coisas e criar determinismo
• No final das contas tudo é concorrente
• Debugar é bem diferente
• Rastrear de onde uma coisa veio, pra onde vai…
31. Akka Dicas
• Não criar Actor pra executar código trivial achando que vai ser mais eficiente
• Actors tem overhead
• É comum precisar de pelo menos um Dispatcher específico pra IO
• Bulkhead: separar Dispatchers por uso para evitar starvation e garantir responsividade
• Evita que o problema de uma área atrapalhe outras
• eventually {} nos testes
• O isolamento de actors não é desculpa pra usar coisas mutáveis por toda parte
• vars não são proibidas dentro de um Actor mas é possível evitar
• Toda estrutura de dados deve ser "owned" por um Actor
• Pode ser mutável, contanto que não vaze para fora do Actor
32. Quando Usar Akka
• Quando o problema é inerentemente concorrente/paralelo
• Crawler é um bom exemplo
• Actors de vida longa e com estado mutável
• Não tão necessário se tudo o que precisa é rodar uns jobs em background.
• Dá pra ir longe com: ExecutionContexts + Future
• Mais simples de entender (e mais familiar para boa parte dos devs)
• Casos mais simples e tradicionais de "Processamento de Request" (APIs/
RPCs em geral) podem ser resolvidos com Futures
• Twitter
33. Outras libs
• Scalatra: usamos em todas APIs
• Similar ao Sinatra (Ruby)
• Mais simples quase impossível
• Funciona muito bem para APIs
• ScalaTest
• Metrics (https://github.com/dropwizard/metrics)
• HTTP
• Dispatch (http://dispatch.databinder.net/). DSL curiosa
• WS (acoplado ao Play)
34. Conclusões
• Programação funcional é o futuro
• Options >>> null
• Case classes: parece pouco mas mudam a forma de programar
• Muito fácil se aproveitar das bibliotecas já existentes em Java
• IDE: Sublime/Eclipse/IntelliJ
• Como somos uma equipe minúscula (2), não encontramos vários problemas
• Escolhas se estivéssemos começando hoje
• Scala + SBT de certeza
• Scalatra para APIs simples. Play para uma aplicação completa (NewsMonitor)
• Querulous nunca mais. Slick? ScalikeJDBC?
• Akka no Crawler de certeza. Com Akka Cluster talvez não usaria fila externa