reescrevendo software crítico em elixir -- um …...senior software engineer @ telnyx (chicago,...
TRANSCRIPT
@renanranelli
Reescrevendo software crítico em Elixir -- Um estudo de caso
2019-05-08
Renan Ranelli
@renanranelli
Senior Software Engineer @ Telnyx (Chicago, IL)
São Paulo @ Brasil
Elixir desde 2015
Organizador do ElugSP
Antes de Elixir, varios Ruby, Python, C#, etc
(Há vagas !)
Reescrevendo software crítico em Elixir
disclaimer
Contexto e Motivações
Back-to-back User-Agent
Back-to-back User-Agent
“The Dialplan Service”
- (almost) Stateless
- Super sensível à latência
- Baixo throughput (bem baixo)
Propriedades do Dialplan service
- A antiga implementação do serviço era escrita em python.(De agora em diante chamada de “dialplan python”)
- Todos os devs python acabaram saindo do time de telefonia.
- Python é notoriamente mais difícil de escalar vertical & horizontalmente do que Elixir
“The Python situation”
A decisão:Vamos re-escrever esse
serviço em Elixir!
Mas pera lá… Elixir?
Linguagem vs Plataforma
Definições:
1. Erlang é uma linguagem2. Erlang/OTP é plataforma3. Elixir é uma linguagem
que roda na plataforma Erlang/OTP
Erlang/OTP
- "Erlang é uma linguagem funcional"!
- Erlang "Resolve" concorrência!
- É o "segredo" por trás do Whatsapp
- Metade da telefonia do mundo é feita em Erlang (citation needed)
- "Nine-nines availability"
Coisas que ouvimos falar:
The Erlang/OTP Runtime
- "Soft real time"- Preemptive scheduling (responsivo, justo & baixa variância da latência)
- Verticalmente Escalável- Distribuição e Concorrência são cidadãos de 1a classe- Garantias de execução fornecidas pelo runtime
(a base para tolerância a falha)
- "Alta performance"
Características da VM do Erlang
Tá, mas e Elixir com isso?
- Elixir compila para o bytecode Erlang- Criada por um BR! José Valim <3- A semântica é absolutamente a mesma entre Elixir e
Erlang. (mais parecido com TypeScript/JS do que Clojure/Java, Elm/JS, etc)
- Para a plataforma, código Elixir e código Erlang são *indistinguíveis*
Elixir é só uma pequena gota
"When designing the Erlang language and the Erlang VM, Joe,Mike and Robert did not aim to implement a functional programming language, they wanted a runtime where they could build distributed, fault-tolerant applications. It just happened that the foundation for writing such systems share many of the functional programming principles. And it reflects in both Erlang and Elixir.
Therefore, the discussion becomes much more interesting when you ask about their end-goals and how functional programming helped them achieve them. The further we explore those goals,we realize how they tie in with immutability and the control of shared state, for example: ..."
-- Valim, José
Elixir Beyond Functional Programming
TOLERÂNCIA A FALHAS
Fault-tolerance requires hardware redundancy
Hardware redundancy requires distribution
Distribution on a single node is concurrency
Mas… Se Erlang é tão topper, pra quê Elixir?
- A sintaxe e ecossistema Erlang são *alienigenas* para os desenvolvedores dessa geração.
- Existem inúmeras barreiras para adoção de Erlang: falta ferramental, docs difíceis de ler, zero polimorfismo de dados, etc.
- Elixir traz o "21st century" para a Erlang-landia, através de metaprogramming, ferramental coeso e bem desenhado, documentação *excelente* e uma comunidade vibrante e acolhedora
Raison d'être do Elixir:
Erlang -> Hard SkillsElixir -> Soft Skills
Exemplo: Preemptive Scheduling
Suponha que temos 6 tarefas:
T1 T2 T3 T4
T5
Com *cooperative scheduling*:
T1 T2 T3 T4 T5
T1 T2 T3T4 T5
T1 T2 T3 T4T5
1)
Or 2)
Or 3)
Sem *preemptive scheduling*:
T1 T2 T3 T4 T51)
Or 2)
E por ai vai...
T4 T5
T1 T2T3T4 T5 T4 T5T5
Mais sobre isso: https://www.youtube.com/watch?v=a7s25To6oII
Tá… e como re-escrevemos?
- Buscar "buy-in" stakeholders de negócio- Reservar recursos para o projeto- Codar & deploy-ar continuamente (shadow deploys)- Verificar a paridade de features
(Tudo isso enquanto o tráfego de *PRODUÇÃO* cresce dia após dia!)
Nossa jornada
- Tivemos de convencer vários stakeholders de negócio que seria necessário "desacelerar" o desenvolvimento de novas features para pagar "débito técnico"
- É fundamental frasear os benefícios em *termos de negócio*
Buscar "buy-in" stakeholders de negócio
- Conseguimos aprovar um desenvolvedor dedicado full-time para esse projeto. Nenhuma outra responsabilidade. (#sqn)
- Re-escrever é um trabalho imenso em que você busca um "alvo em movimento". Se você não conseguir focar, você vai fracassar
Reservar recursos
Codar & deploy-ar continuamente (shadow deploys)
Codar & deploy-ar continuamente
Proxy Python Dialplan Graylog
XML Request
XML Response
Codar & deploy-ar continuamente
Proxy Python Dialplan Graylog
XML Request
Elixir Dialplan
Ignore response!
XML Response
XML Response
Codar & deploy-ar continuamente
Proxy Python Dialplan Graylog
XML Request
Elixir Dialplan
Ignore response!
XML Response
XML Response “Operational database”
Verificar paridade de Features
SELECT p.xml_dialplan, e.xml_dialplan, p.request FROM python_dialplan_logs as p INNER JOIN elixir_dialplan_logs as e ON p.call_id == e.call_id WHERE e.xml_dialplan != p.xml_dialplan
Verificar paridade de Features
Logo no começo, percebemos:
- A maioria das diferenças eram causadas por falhas em serviços "downstream".
- Quando não era esse o caso, "gravar" a resposta das dependências era suficiente para reproduzir o cenário de erro.
Verificar paridade de Features
Logo no começo, percebemos:
- A maioria das diferenças eram causadas por falhas em serviços "downstream".
- Quando não era esse o caso, "gravar" a resposta das dependências era suficiente para reproduzir o cenário de erro.
Verificar paridade de Features
(Tipo o VCR do Ruby)
Um efeito colateral top: Regressão
E continuamos fazendo isso...
E continuamos fazendo isso... … até que um dia acabaram as diffs!
O familiar ciclo “TDD”
Nosso ciclo “tipo-TDD”Capture um request com erro Corrige & cria cenário
de regressão
Reescreve
Resultados
Praticamente *zero* incidentes em produção após o cutover
Um runtime muito superior. Code-base 100% Elixir.
Melhorias *enormes* em observabilidade
Features shipando mais rápido
Paralelização ridiculamente mais fácil & barata. (less latency -> happier users)
Agora… A parte menos gloriosa
Expectativas de negócio mal-gerenciadas
Amostragem burra das discrepâncias
O recurso "dedicado" acabou não ficando tão dedicado assim
O serviço python *continuou sendo modificado* enquanto a re-escrita acontecia!
98% pronto por … muito tempo
Tentando refatorar código bizarro no momento errado do ciclo "tipo-TDD"
Wrapping up
Valeu a pena?
Vá para produção o mais cedo e o mais frequentemente possível!
Monitoração e observabilidade valem a pena. Você nunca deve ter
medo de *testar em produção*!
Refatorar >>> Reescrever(sempre que possível)
Re-escrever é difícil e perigoso.
Porém, possível.
Agora, uma reflexão final
O que garante que a re-escrita em Elixir não vai degringolar da mesma forma que o python
degringolou?
Elixir economiza um pouquinho de tempo em *várias* situações
Elixir economiza um pouquinho de tempo em *várias* situações
… portanto, sobra *mais tempo* pra fazer o que é *importante*!
“Fully utilize *all* your resources”
“Fully utilize *all* your resources”(incluindo humanos)