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 dados
Problema:
- 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
Docker
Por 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
Cache
Slave
App
Cache
Slave
Cache
Slave
TTL longo
TTL longo
TTL longo
Cache
Master
4a Estratégia de Cache
App
Fonte App
Cache
Slave
App
Cache
Slave
Cache
Slave
TTL longo
TTL longo
TTL longo
Cache
Master
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
Obrigado!!!
Desconto especial ao evento: bit.ly/smtpqcon

Qconsp 2016 escalando uma plataforma de e-mail transacional- aprendizado das trincheiras