Reescrevendo software crítico em Elixir -- Um …...Senior Software Engineer @ Telnyx (Chicago,...

Preview:

Citation preview

@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)

Renan Ranellimilhouse@taming-chaos.com

renan@telnyx.com

Obrigado!

Check us out!

www.telnyx.com

@renanranelli

We're hiring!!

Recommended