Três anos de Scala em Produção: desafios, aprendizados e dores de cabeça

Post on 11-Aug-2015

52 views 0 download

Transcript of Três anos de Scala em Produção: desafios, aprendizados e dores de cabeça

Três anos de Scala em Produção: desafios, aprendizados e dores de

cabeçaFelipe Hummel Onilton Maciel

• 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

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?

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

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?

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

CrawlerSlaveCrawlerSlave

Elastic SearchElastic Search

MySQL

Arquitetura

FeedCrawler

SiteCrawler

CrawlerSlave

Stark (processamento) Indexer

MySQL Redis SearchAPI

Elastic Search

Arquitetura

Stark (processamento) Indexer

SearchAPI

autocomplete

SocialCrawler

CrawlerSlaveCrawlerSlave

FeedCrawler

SiteCrawler

CrawlerSlave

MySQLMySQL Redis

Elastic SearchElastic SearchElastic Search

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.

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?

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

Deploy e Ops• Simples (mas automatizado)

• SSH

• git pull

• sbt compile (sbt start-script)

• sudo restart crawler

• Deploys elásticos

• Gerar AIM/docker/algo ou gerar fat-jar (sbt-assembly)

• Hoje, geramos AIMs mas queremos mudar pra fat-jar.

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 …”

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

Estilo de Scala• Muitas formas de escrever o mesmo código

• Um mais cool, outro mais funcional, outro mais imperativo

• Qual usar?

Estilo de Scala

Estilo de Scala<— Usamos<— Usamos

<— Usamos<— Usamos

<— Usamos

Estilo de Scala

Estilo de Scala

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

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

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

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"

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

Akka

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

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

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…

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

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

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)

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

Dúvidas?

felipe.hummel@newsmonitor.com.br