Escalando uma plataforma de e-mail transacional:
aprendizado das trincheiras
Celso Crivelaro (@celsocrivelaro)Fabio Perrella (@fabioperrella)
SMTP Locaweb
Fabio Perrella- Eng Computação- Dev Ruby, Php, Python, Javascript- Locaweb, time de Email- 1a geração do projeto do SMTP
@fabioperrella
http://fabioperrella.blogspot.com
Celso Crivelaro- Engenheiro e Mestre em Engenharia de Computação- Dev Ruby / Python / Java- Dev do SMTP Locaweb há 1 ano e meio
@celsocrivelaro
http://crivelaro.me
SMTP Locaweb - Produto- Plataforma para envio de e-mails transacionais- Envio de grandes quantidades com velocidade e qualidade- Relatórios “ao vivo” para acompanhar os envios- Envio por SMTP e API HTTP- Webhooks para devoluções de e-mails (bounces)- 2,5 milhões de e-mails por dia- Há + de 2 anos no ar
O que é email transacional?- Disparos granulares- Notificação de eventos- Entrega rápida- Exemplos de uso: CRM, E-commerce, Billing, Websites
Processamento Lento
Processamento Rápido
Arquitetura
Autenticação Tratamento da Mensagem MTA ProvedoresUsuário
Processamento da Mensagem Consolidação Painel
SMTP Locaweb- Arquitetura distribuída- Painel e APIs -> Ruby on Rails- Workers -> Ruby- Postfix milters -> Python- DB -> Postgres- Caches e Filas -> Redis- MTA -> PowerMTA
Desafios- Estabilidade- Escalabilidade- Performance- Relatórios em “tempo real”- Combater fraudes- Manter qualidade da entrega (https://returnpath.
com/downloads/the-ultimate-guide-to-email-deliverability)
Apostas que deram certo!
http://www.lafayetterealestatenews.com/wp-content/uploads/2015/08/Success.jpg
Postgres Schemas- Posgresql >= 9.3- http://www.postgresql.org/docs/9.3/static/ddl-schemas.html
SET search_path TO myschema,public;
Public
Tabelas menores
Tabelas em comum
Migração default
Privado (Conta)
Tabelas maiores
Dados de uma conta
Migração ao criar conta
Postgres Schemas
Prós
Isolamento dados
Tamanho das tabelas
Contras
Complexidade migrações
Relatórios consolidados
Postgres Schemas
Exportação de Relatórios
Postgres Copy - Exportação de dadosProblema:
- Geração de arquivos > 100mil linhas- Código ruby iterando do DB e gerando CSV- Podia levar horas para finalizar geração de arquivo
COPY #{QUERY} TO STDOUT WITH FORMAT CSV
http://www.postgresql.org/docs/9.3/static/app-psql.html#APP-PSQL-META-COMMANDS-COPY
Separação das Apps
Apps por funcionalidade
Milters(python)
WorkersPainel
Core Api
Provisionador
DB
API envio
Open-Click
Auth Policy
Gem “models” para compartilhar código- Modelos, Migrações, Códigos em comum extraídos para gem- Facilidade para usar Activerecord- Atenção na atualização de novas versões da gem- Dica: http://ryanbigg.com/2013/08/bundler-local-paths
Apps por funcionalidade
Milters(python)
WorkersPainel
Core Api
Provisionador
DB
API envio
Open-Click
Auth Policy
Problemas- Ambiente distribuído- Testes de integração eram essenciais!- Configuração do Postfix, Milters, Nginx não é trivial- Dev novo não conseguia levantar ambiente completo- Incompatibilidades Mac OS X / Linux- Versionamento de dependencias (ex: Redis)
Docker com ambiente completo- Docker-compose! (https://docs.docker.com/compose)- Mailcatcher (https://mailcatcher.me)- Fluxo completo do email- Base para configuração de produção
DockerPor onde começar:
https://training.docker.com/self-paced-training
Vale gastar um tempo!
Logs
Archive de logs centralizado- Syslog ajuda!- Log rotate a cada 15min- Archive em storage organizado por servidor/data- Grep em multiplos servidores/datas
- Cuidado: limite de caracteres syslog (aprox 1500 p/ mensagem)
Caches e filas no Redis- Milters dependem de dados das contas (API)- Performance- TTL- Facilidade para debug / “queries” (comparando com memcache)
$ redis-cli monitor | grep “qquer coisa”
- Estruturas de dados: string, hashes, arrays, sets- Operações atômicas / garantia para concorrência
Redis- Sharding com twemproxy (https://github.com/twitter/twemproxy)- Persistência desligada: app deve saber “esquentar” o cache!- Alternativa: Master / Slave (local)- Futuro: Redis Cluster- Atenção à versão do Redis, pode não ser a mesma da sua
máquina (docker nele!)
https://github.com/locaweb/heartcheck
Heartcheck
Redis DB Sidekiq Telnet
ProcessosAPIs Mount Points
DNS
Gráficos!
Painel de monitoração (cockpit)- Ajuda saber quando precisamos aumentar capacidade e onde- Investigação de problemas- Grafana ( http://grafana.org )- New Relic / AppSignal
Painel de monitoração (cockpit)
Deploy contínuo(quase...)
Toggler - Feature Toggle- Deploy contínuo sem impacto- Exibir/esconder feature nova para determinadas contas- Ligar/desligar features sem deploy
De MVP para 2,5 MM
Processamento Lento
Processamento Rápido
Voltando ao diagrama...
Autenticação Tratamento da Mensagem MTA ProvedoresUsuário
Processamento da Mensagem Consolidação
Requisitos não funcionais
Alta Disponibilidade Performance
EscalabilidadeResiliência
Estabilidade
Isole os componentes críticos
Aplicação Painel
Painel Web API Interna - HTTP
Recursos
Pool de Conexões App Server Regras de
Negócio Integrações
Aplicação API InternaAplicação Painel
Painel Web API Interna - HTTP
Recursos
Pool de Conexões
App Server
Regras de Negócio Integrações
Recursos
Pool de Conexões
App Server
Regras de Negócio Integrações
Separação de Máquinas por Aplicação
Mesma máquina
Interdependência de libs
Consumo de Recursos
Versão da Linguagem
Máquina separada
Lib por App
Aplicação não afeta outra
Upgrades podem ser feitos em separado
Poucos Componentes=
Pouca Manutenção
API InternaPainel
Novas Funcionalidades Estabilidade/Performance
Muitas atualizações Poucas atualizações
Framework para Web Framework para API
Quebrou: ninguém morre Quebrou: ponto central de falha
Usar tecnologia que não domina...
Postfix Milter libmilter
pymilter
app
Protoco Milter
- Libs=
+ Alegria
Escalabilidade
Busque o dado de memórias + rápidas e de
lugares pertos
Estratégia de Cache Padrão
App
Cache
Fonte
1
2
3
1a Estratégia de Cache
App
Fonte App Cache
App
TTL curto
2a Estratégia de Cache
App
Fonte App
Cache
App
Cache
Cache
TTL curto
TTL curto
TTL curto
3a Estratégia de Cache
App
Fonte App
CacheSlave
App
CacheSlave
CacheSlave
TTL longo
TTL longo
TTL longo
CacheMaster
4a Estratégia de Cache
App
Fonte App
CacheSlave
App
CacheSlave
CacheSlave
TTL longo
TTL longo
TTL longo
CacheMaster
Cache Manager
Balanceamento de Carga
App
Client App
App
Load Balancer
App
Client
App
App
Load Balancer
App
App
App
Load Balancer
Load Balancer
Client Load Balancer Cache
Client Load Balancer Cache
Use SEMPRE pool de recursos
Problemas com Pool de Recursos
Não explode erros no início
Esperar de liberação de recurso
Timeouts são esporádicos Deadlocks
Timeouts baixos=
Fail Fast
Coisas que devíamos ter feito
Documentação de Arquitetura- Documentação sobre decisões de arquitetura
http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions
- Faltou documentação de decisões
Worker com Ruby puro- Fizemos um grande workers de processamento de mensagens- Em Ruby puro…- Sem recursos de mensageria- Difícil ver errors, retentativa, tamanho da fila….
Não reinvente a roda
Saiba os limites...
Teste de Carga
Teste de Estresse
Teste de Pico
Algumas referências