A essência de Erlang não são processos leves e passagem de mensagens (2023)
(stevana.github.io)- O ponto central que permitiu ao Erlang criar sistemas distribuídos confiáveis não está nos processos leves em si, mas nos behaviours, que generalizam padrões recorrentes de concorrência e tratamento de falhas
- Os behaviours fornecem pontos de implementação como uma interface, mas ao mesmo tempo escondem o tratamento de concorrência dentro de componentes comuns, como
gen_server, fazendo o código da aplicação se aproximar de uma lógica sequencial - A tese de Joe Armstrong trata de um caso em que sistemas grandes como o AXD301 foram compostos usando apenas pequenos blocos como
gen_server,gen_event,gen_fsm,supervisor,applicationerelease - O
supervisorreinicia processos que falharam com estratégias comoone_for_oneeone_for_all, e a filosofia “Let it crash!” transfere a responsabilidade de recuperação para a árvore de supervisors - Behaviours em forma de máquina de estados facilitam testes por simulação e verificação formal, permitindo que desenvolvedores foquem na semântica do problema em vez da complexidade da concorrência e do simulador
O problema que Erlang tentou resolver
- Erlang começou como uma ferramenta para criar sistemas distribuídos confiáveis
- No início, era uma biblioteca Prolog para criar sistemas distribuídos confiáveis; depois passou por um dialeto de Prolog até se tornar uma linguagem independente
- Foi usado na programação das centrais telefônicas da Ericsson e, nas décadas de 80 e 90, lidou com tráfego na escala de centenas de milhões de pessoas e SLAs rigorosos
- A Ericsson proibiu o uso de Erlang em 1998, e a equipe de desenvolvimento argumentou que, se fosse para proibir, seria melhor liberar como open source; a Ericsson acabou fazendo isso
- Joe Armstrong foi uma das figuras centrais no design e na implementação de Erlang e, em 2002, começou sua tese de doutorado no SICS, concluindo em 2003 Making reliable distributed systems in the presence of software errors
- Essa tese foca menos em matemática ou teoria e mais nas ideias por trás do Erlang e na experiência de construir sistemas distribuídos confiáveis
- A grande ideia do Erlang não é processos leves e passagem de mensagens, mas os componentes genéricos chamados de behaviours no Erlang
behaviours: união entre interface e infraestrutura
- Os behaviours do Erlang são conjuntos de assinaturas de tipos que podem ter várias implementações, como interfaces em Java ou Go
- Se o programador fornece a implementação dessa interface, pode usar funções genéricas escritas para essa interface
- A diferença principal é que behaviours vão além de uma interface simples e fornecem também código de infraestrutura
- O programador da aplicação escreve a semântica do problema, isto é, a lógica de negócio
- Código de infraestrutura, como concorrência, é fornecido automaticamente pelo behaviour
- Behaviours incorporam boas práticas escritas por especialistas experientes
- Se o sistema inteiro reutiliza um pequeno conjunto de behaviours, melhorias na implementação desses behaviours podem melhorar o sistema sem mudanças no código
- O uso de behaviours impõe uma estrutura que facilita testes e verificação formal
gen_server: concorrência escondida atrás de código sequencial
- O exemplo de
gen_serveré um armazenamento chave-valor:storesalva um par chave-valor elookupbusca o valor de uma chave - O callback principal,
handle_call, atualiza o estadoDictem requisiçõesstoree consulta a chave no estado em requisiçõeslookup - Ao fornecer essa implementação ao
gen_server, cria-se um servidor capaz de lidar com requisições simultâneas destoreelookup - O ponto importante é que
handle_callem si é totalmente sequencial- A concorrência fica escondida dentro do componente genérico
gen_server - O código da aplicação fica mais próximo de uma forma que recebe estado e entrada e produz novo estado e saída
- A concorrência fica escondida dentro do componente genérico
- A tese de Joe Armstrong estende essa abordagem para
gen_server,gen_event,gen_fsm,supervisor,applicationerelease
O caso AXD301 e a escala dos behaviours
- Joe Armstrong usa a central telefônica AXD301 da Ericsson como estudo de caso
- O projeto AXD301 incluía os seguintes componentes
- 122 instâncias de
gen_server - 36 instâncias de
gen_event - 10 instâncias de
gen_fsm - 20 supervisors
- 6 applications
- Tudo empacotado em um único release
- 122 instâncias de
- O AXD301 é um sistema com mais de 1 milhão de linhas de código Erlang
- O fato de um sistema desse porte ter sido composto a partir de um pequeno conjunto de behaviours é uma evidência central da estrutura do Erlang
O papel de outros behaviours
gen_eventé um gerenciador de eventos genérico- Ele permite registrar handlers de eventos, e o gerenciador executa esses handlers quando recebe mensagens relevantes
- Joe Armstrong cita usos como logging de erros
- Um logger de exemplo pode registrar e reportar as cinco mensagens de erro mais recentes
gen_fsm, depois renomeado paragen_statem, é um behaviour de máquina de estados- Ele é mais adequado para implementação de protocolos
- Protocolos frequentemente são especificados como máquinas de estados
- Considera-se que qualquer
gen_serverpode ser implementado comogen_statem, e vice-versa
applicationé composto pela árvore de supervisors e pelos demais elementos necessários para entregar a aplicaçãoreleaseempacota uma ou mais applications- Também inclui código para lidar com upgrades
- Se um upgrade falhar, deve ser possível fazer rollback para o estado estável anterior
supervisor e “Let it crash!”
supervisoré um processo que verifica se outros processos estão funcionando corretamente- Se um processo monitorado falha, o supervisor pode reiniciá-lo conforme uma estratégia predefinida
- A estratégia de exemplo
{one_for_one, 5, 1000}significa o seguinte- Se
packet_assembler,kvousimple_loggerfalhar, apenas o processo que falhou é reiniciado - Se for necessário reiniciar mais de 5 vezes em 1000 segundos, o próprio supervisor falha
- Se
permanent, 500, workersignifica que esse processo é um worker que deve estar sempre vivo, e que o supervisor concede 500 ms para encerramento normal ao tentar reiniciá-lo- Na estratégia
one_for_all, se um processo falha, todos os processos filhos são reiniciados - Um supervisor pode monitorar outros supervisors e não precisa necessariamente rodar no mesmo computador
- Essa estrutura opera no nível de threads/processos leves, não no nível de contêineres Docker, então não dá para tratá-la simplesmente como “só Kubernetes”
- “Let it crash!” assume que um processo que falha será reiniciado
- O programa expressa apenas o caminho normal
- Se surgir um problema no caminho normal, ele não tenta se recuperar diretamente e sim dá crash
- Outro programa acima na árvore de supervisors assume o tratamento
- Não houve coleta sistemática de evidência da estabilidade de longo prazo do AXD301, mas a tese de Joe Armstrong registra que um grande cliente operava um sistema de 11 nós com 99,9999999% de confiabilidade, segundo um número mostrado em PowerPoint
- Não foi documentado como esse número foi obtido
- Outros números de relatórios de downtime também vêm acompanhados da observação de que o método de cálculo não está claro
Se fosse implementar behaviours em outras linguagens
- Apenas processos leves e passagem de mensagens não bastam para tornar Erlang adequado para sistemas confiáveis
- Uma explicação mais honesta é a estrutura fornecida pelos behaviours e como essa estrutura leva a software confiável
- A assinatura de interface de
gen_serverpode ser vista na formaInput -> State -> (State, Output)- Ou seja, recebe uma entrada e o estado atual e produz um novo estado e uma saída
- Uma forma de transformar essa assinatura sequencial em tratamento de requisições concorrentes seria a seguinte
- Um servidor HTTP converte a requisição em
Inpute a coloca em uma fila - Um event loop remove a entrada da fila e a entrega à implementação sequencial
- A saída é escrita de volta como resposta ao cliente
- Para suportar vários
gen_server, cada servidor pode receber um nome, e a requisição pode incluir nome e entrada juntos
- Um servidor HTTP converte a requisição em
gen_eventpode ser implementado permitindo registrar callbacks para certos tipos de evento em uma filasupervisorpode ser simplificado envolvendo a chamada da funçãogen_servercom um tratador de exceções e notificando o supervisor quando ocorrer uma exceção- Se o supervisor não estiver rodando na mesma máquina, fica mais complexo
applicationereleasesão importantes porque configuração, deploy e upgrade são problemas difíceis, mas não são tratados em profundidade aqui
behaviours e testabilidade
- A estrutura dos behaviours do Erlang leva a testes por simulação e verificação formal
- Um interesse mais recente está nos testes por simulação de sistemas distribuídos ao estilo FoundationDB
- Em testes por simulação, o sistema roda em um mundo simulado, e a simulação controla totalmente o momento de envio das mensagens de rede
- O FoundationDB criou, para testes por simulação, um dialeto de C++ com actors ou uma linguagem de programação própria
- Considera-se que essa abordagem conseguiu ir bastante longe usando apenas o seguinte tipo de máquina de estados
Input -> State -> (State, [Output])[Output]é uma sequência de saídas
- O simulador gerencia mensagens com uma fila de prioridade ordenada por horário de chegada
- Remove uma mensagem da fila
- Avança o relógio para o horário de chegada dessa mensagem
- Entrega a mensagem à máquina de estados receptora
- Gera novos horários de chegada para as mensagens de saída e as reinsere na fila
- Se tudo for determinístico e os horários de chegada forem gerados a partir de uma seed, é possível explorar vários interleavings e obter falhas reproduzíveis
- Essa abordagem é mais rápida que Jepsen porque as mensagens são processadas na memória e o relógio avança diretamente para o horário de chegada, sem esperar por timeouts
- Essa forma de máquina de estados é chamada de “network normal form” e leva à hipótese de que todo programa que recebe e envia pela rede poderia ser refatorado para esse formato
- O fato de
gen_serveregen_statemterem essencialmente a mesma estrutura de tipos pode ser visto como um sinal de que essa estrutura não é arbitrária
Verificação formal e estrutura de máquina de estados
- Joe Armstrong já disse em uma palestra que é difícil implementar corretamente eleição de líder distribuída
- Com um simulador, esse problema pode ficar muito mais simples
- Assim como um túnel de vento ajuda a fabricar aviões, seria possível testar condições extremas, como redes instáveis ou perda de energia, antes de ir para produção
- Esse simulador pode ser genérico ou parametrizado para behaviours
- O desenvolvedor não precisa escrever o simulador separadamente
- A complexidade fica escondida atrás do behaviour, assim como o código de concorrência em
gen_server
- Sobre os testes do FoundationDB, Kyle “aphyr” Kingsbury publicou um tweet sugerindo que os testes do FoundationDB parecem muito mais rigorosos que seus próprios testes com Jepsen
- A verificação formal também fica mais fácil quando o programa é escrito como máquina de estados
- O model checking em TLA+ de Lamport assume que a especificação é uma máquina de estados
- Kleppmann mostrou como lidar com o problema de explosão de estados com indução estrutural usando a estrutura de máquina de estados
- A estrutura obtida com os behaviours do Erlang pode ser reutilizada para tornar mais fáceis problemas que Joe Armstrong considerava difíceis
Trabalhos relacionados e materiais de referência
- Trabalhos relacionados que trazem ideias do Erlang incluem
- Trabalho de Martin Thompson com LMAX Disruptor e aeron para criar event loops rápidos onde behaviours possam rodar
- Trabalho que adiciona async I/O ao tipo de máquina de estados
- Trabalho que implementa supervisors com mais detalhes
- Troca a quente de código de máquinas de estados
- Como referências, são indicados a tese de doutorado de Joe Armstrong, os OTP design principles e a documentação de
gen_server,gen_event,gen_statem,supervisor,applicatione release - Sobre a relação entre o modelo de actors e Erlang, resume-se que os desenvolvedores do Erlang criaram a linguagem sem conhecer o actor model, e que um artigo de Carl Hewitt documenta as diferenças entre processos de Erlang e o actor model
- O Akka tem “actors” e árvores de supervisors, mas aparentemente não possui equivalentes para os outros behaviours do Erlang, e o conceito de “behavior” do Akka também é diferente dos behaviours do Erlang
1 comentários
Comentários no Hacker News
O impressionante em Erlang e na BEAM é a profundidade dos recursos. No post original, Behaviour/Interface foi a principal impressão, mas para mim o ponto maior é que, ao criar sistemas complexos, dá para fazer com muito menos recursos de desenvolvimento do que em outras linguagens
O próprio OTP já traz muita coisa. Já trabalhei para compilar Elixir para rodar em dispositivos iOS e, além do processo de release do Erlang, também foi possível usar a biblioteca ei para compilar um Node em C e se comunicar com outros nós Erlang — Erlang, Elixir, Gleam etc. — como em uma rede distribuída comum
Também foi possível chamar funções de uma aplicação Elixir a partir de C por meio da biblioteca rpc do Erlang. Há overhead de codificação/decodificação, e FFI seria mais rápido, mas cabia tranquilamente no orçamento de latência, e consegui colocar de pé em poucos dias um recurso do qual eu nunca tinha ouvido falar antes
O ponto maior é que Erlang já resolveu, décadas atrás, muitos problemas com os quais stacks modernas ainda brigam, tanto em escala quanto em custo de implementação. O HN tem um certo carinho clickbait por Erlang/Elixir, mas isso não se traduziu em adoção real, e há empresas queimando dinheiro para implementar coisas que, no stack Erlang, você ganha de graça por padrão
O projeto em que trabalhei era um pipeline de dados de backend, e nem processava tantos dados assim, mas isolar exatamente um bug central foi inacreditavelmente difícil
Nesse processo, fui conhecendo várias características do Node.js e, comparando com Elixir/Erlang/OTP, cheguei à conclusão de que Node.js é menos confiável por design
Também trabalhei bastante com Ruby e mexi com Python, mas muitas plataformas de linguagem da geração atual têm dificuldade para construir sistemas distribuídos confiáveis. A BEAM VM e a plataforma OTP já resolveram essa parte
Por exemplo, o GenServer, que faz o papel de worker na maioria dos sistemas BEAM, no fim é uma estrutura que chama várias funções com parâmetros simples
Portanto, é possível testar chamando essas funções diretamente, passando os parâmetros manualmente e validando apenas a saída. Não é preciso configurar um sistema de testes complexo para lidar com código assíncrono nem travar esperando a conclusão do trabalho nos testes
É algo que juniores frequentemente deixam passar, mas, quando percebem, é bastante libertador
Já vi algumas pessoas, principalmente gestores, dizerem que escreveriam um livro com base na nossa experiência. Sempre foi frustrante ver que cada um enxergava partes diferentes como o fator central do sucesso do nosso trabalho. O que eu sentia como essencial eles muitas vezes reduziam a algo apenas desejável
Aqui também, alguém diz que processos leves e passagem de mensagens não são o molho secreto, mas deixa passar que a visão de Erlang como processos sequenciais comunicantes (CSP) não pode ser separada dessas características. Ainda assim, menciona repetidamente CSP como parte do molho secreto
Por exemplo, que o programador de aplicação escreve código sequencial e a concorrência fica escondida dentro de behaviours; que a lógica de negócio é sequencial, facilitando a entrada de novos membros na equipe; ou que supervisors e a filosofia “let it crash” criam sistemas confiáveis
Behaviours são interessantes, eram comuns nos anos 80 e resolvem um problema que alguns ainda atacavam nos anos 2000, mas em Erlang são tanto fim quanto meio. São a forma como essas outras características foram implementadas, mas não sei se necessariamente precisavam ser assim para Erlang continuar parecendo Erlang
CSP é a vertente que, passando por occam e outras linguagens, inspirou os canais de Go. A sincronização em canais sem buffer é a diferença mais evidente, e há outras, como pattern matching em caixas de correio no modelo de atores
O debate entre CSP e modelo de atores parece parecido por fora, mas as implicações reais são bem diferentes, então é bastante interessante
Acho que o modelo de atores faz mais sentido, mas isso pode variar bastante de pessoa para pessoa
Entrei querendo saber mais sobre por que a Ericsson parou de usar Erlang e por que Joe foi demitido
Em resumo, a Ericsson migrou novos projetos para Java, e isso parece ter marginalizado Erlang. Depois, Joe e colegas fundaram a Bluetail em 1998, que foi adquirida pela Nortel
A Nortel era uma gigante de telecomunicações que respondia por cerca de um terço do valor da Bolsa de Toronto. Em 2000, suas ações chegaram a US$ 125 por ação, mas em 2002 caíram para menos de US$ 1. Foi parte do colapso das ponto-com e, combinado com a queda brusca nos gastos com telecomunicações, atingiu a Nortel de forma particularmente dura
Parece mais seguro ver a demissão de Joe como uma situação em que “o departamento dele foi um dos primeiros a ser inundado em um navio afundando”. A Nortel demitiu 60 mil pessoas, ou seja, mais de dois terços de toda a força de trabalho. Essa demissão não era sinal de que Joe não estava fazendo seu trabalho, nem de ineficiência da unidade de negócios; foi uma medida desesperada e em massa
“fired” carrega um juízo de valor forte e sugere demissão por justa causa, por alguma falha. Mesmo que isso tenha acontecido, o autor do post original não teria como saber, e nem caberia a ele saber
Comecei a olhar para Erlang de novo por causa dos processos leves e da passagem de mensagens, e até agora os behaviours eram algo secundário
O projeto é trazer a programação visual baseada em fluxo (FBP) para Erlang. A FBP parece ter sido feita para Erlang, então foi surpreendente não encontrar uma implementação já existente
A ferramenta que eu mais usava para FBP era o Node-RED, então a ideia básica é acoplar o frontend do Node-RED a um backend Erlang e transformar todos os nós em processos. O frontend do Node-RED é bom para modelar a passagem de mensagens entre nós, o que permite um mapeamento 1:1 muito simples para processos e mensagens do Erlang
Implementei parte das funcionalidades básicas e comecei a criar fluxos como testes unitários para ir acumulando recursos aos poucos. Seria bom se ficasse 100% compatível com o Node-RED, que tem backend em Node.js. Mais detalhes estão no repositório do GitHub → https://github.com/gorenje/erlang-red
No geral, Erlang se encaixa surpreendentemente bem nisso, e me espanta que ninguém tenha feito algo parecido. Será que já existia?
[1] = https://jpaulm.github.io/fbp/index.html
Para mim, a força de Erlang/Elixir não está tanto na implementação do modelo de atores, no matching vindo do Prolog, na imutabilidade ou nos behaviours em si, mas na vontade do Joe de mostrar que é possível fazer mais com menos
É um sistema computacional bem projetado e comprovado, com uma consistência rara de ver não só em outras linguagens, mas também no mundo “web”. Não é perfeito, mas é bem impressionante
Infelizmente, acho que no mundo do software há pouco reconhecimento e pouca adoção do que a simplicidade torna possível. A complexidade faz as pessoas virarem especialistas, permite que gestores justifiquem equipes grandes e muitas reuniões, e mantém os especialistas como especialistas
Erlang foi desenvolvido numa época em que empresas tentavam implementar soluções de software com menos gente e desempenho limitado. Nas décadas seguintes, com tanto dinheiro sendo despejado nessa área, o valor de “menos significa mais de uma forma boa para todos” acabou parecendo menos atraente
O conceito mais interessante em Erlang/BEAM é que a recuperação parcial vem embutida desde o começo
Ao encontrar um estado inesperado, em vez de matar todo o processo ou continuar e aceitar a corrupção, ele volta para um estado conhecido e correto no nível mais granular possível
Essa ideia foi pesquisada há muito tempo com o nome de “microreboots” e também tinha relação com “crash-only software”, mas só Erlang/BEAM a tornou um conceito de primeira classe do sistema operacional
Erlang oferece um bom modelo para lidar com esses problemas, mas isso não significa que você possa desligar completamente o cérebro. Se entrar achando que é só deixar reiniciar e tudo ficará bem, pode se queimar
Erlang, OTP e BEAM oferecem mais do que behaviours. A VM é quase um kernel virtual, com supervisors, processos isolados e um modo distribuído que trata várias máquinas físicas/virtuais como um único pool de recursos
O OTP também oferece vários modos úteis, como o banco de dados Mnesia, contadores atômicos para cache e tabelas ETS. O runtime também suporta recarregamento a quente de bytecode, permitindo aplicar patches sem interromper o sistema. A sintaxe não é muito amigável para leitores de tela, mas é legível
A única plataforma que me vem à mente como parecida com a BEAM na capacidade de tratar recursos de várias máquinas como um único pool é o Apache Mesos[1]
Há cerca de um ano, minha empresa pessoal de consultoria adotou Erlang como linguagem de backend. Desde então, comecei a explorar o interior da BEAM, por exemplo trocando uma stack baseada em TCP por QUIC ou integrando patches em Rust
É uma escolha realmente excelente para sistemas leves e de alta vazão que não falham a menos que haja kernel panic ou perda de energia. Atualmente estamos criando software muito ocupado e altamente concorrente, como rastreadores de produção de filmes/jogos e gerenciadores de pipeline, e também preparando P&D para um serviço privado de gestão hospitalar
[1]: https://mesos.apache.org/
É bem provável que tenhamos de manter internamente bindings ou implementações para coisas que não são mantidas no lado do Elixir
Além disso, há muito açúcar sintático, e os usuários demonstram uma obsessão peculiar por abstrair coisas em interfaces de DSL
Há um motivo por trás da pergunta: “por que designers de linguagens e bibliotecas só copiam a ideia de processos leves e passagem de mensagens, mas não roubam a estrutura por trás dos behaviours do Erlang?”
As assinaturas de função dos behaviours do Erlang estão fortemente ligadas a outros recursos do Erlang, especialmente ao seu modo peculiar de usar imutabilidade. É por isso que o servidor precisa de uma chamada
initseparada, e a gestão de estado também precisa ter uma forma muito clara para funcionar do mesmo jeitoMas, em outras linguagens, para alcançar o mesmo objetivo, quase sempre há muitos casos em que não se deve simplesmente copiar o que o Erlang faz. Se alguém diz que “portou o gen_server para outra linguagem” e mostra exatamente a mesma interface do Erlang, vejo isso como um porte que não entende profundamente o que o Erlang faz
Quando levei a ideia de supervisor tree para Go[1], fiz de um jeito idiomático para Go. Em Go moderno, a interface correta para algo “supervisionável” não é uma assinatura como a do Erlang, mas apenas isto
type Service interface {
Serve(context.Context)
}
Em Go, isso é tudo, e é só isso que se deve usar. Em outras linguagens pode ser diferente. Go tem canais, então não precisa de “handle_event/2”, e deve usá-los. Não porque canais sejam melhores ou piores, mas porque a linguagem funciona assim
Em outras linguagens, pode-se usar outra coisa; em outras infraestruturas, em vez de “chamar handle_event/2”, também se pode enviar para Kafka ou para um barramento de eventos em nuvem. O ponto central é criar um sistema orientado a eventos, não copiar a implementação exata do Erlang
Na comunidade Erlang existe um problema de confiança excessiva de que há algo tremendamente especial no modo exato como o Erlang faz as coisas, e que fazer diferente é automaticamente errado e não confiável. Talvez fosse assim em 2005, mas não em 2025
Houve uma época em que Erlang era praticamente a única resposta que fazia sentido, mas o problema de 2025 é navegar por uma enxurrada de respostas. Recomendo fortemente aprender com Erlang para criar software confiável, mas sou fortemente contra portar cegamente para outras linguagens o modo exato como Erlang atingiu isso. Em quase todos os outros contextos de linguagem, essa é a resposta errada. Mesmo outras linguagens imutáveis têm estruturas suficientemente diferentes para que não dê para copiar literalmente
[1]: https://jerf.org/iri/post/2930/
Como alguém que usa Elixir profissionalmente, essa é para mim a pergunta mais interessante sobre Erlang
Vendo o grande sucesso de k8s, Kafka e dos produtos de sistemas distribuídos da AWS, fica claro que há uma demanda enorme por ferramentas que ajudem a projetar sistemas concorrentes confiáveis. Então por que Erlang/Elixir não capturou essa fatia?
Discuto isso com frequência com amigos, mas não sei a resposta
Imagino que Erlang tenha abstraído o tratamento de mensagens de rede. Além disso, já vi umas 3 ou 4 variações do padrão que você propôs para coisas como servidores de longa duração
Concordo totalmente que, ao portar, é preciso usar componentes idiomáticos. Mas uma linguagem pode ter mecanismos ocultos que fazem perder, no processo de porte, a essência valiosa. Dá para chamar isso de uma espécie de antirrelativismo de linguagens de programação
“Canais? É só envolver com X” também pode ser muito mais prejudicial à interoperabilidade do que parece. Por exemplo, olhando para
http.Handlerem Go, ele é simples, mas o fato de estar na biblioteca padrão tem grandes implicações práticas. Sem coordenação prévia, surge um ecossistema de middlewares em geral compatíveis entre siio.Readere seus parentes são parecidos. Dá para dizer que essas interfaces extremamente simples são mais valiosas do que as implementaçõesÉ especulação, mas, se Erlang acertou muitas interfaces para sistemas distribuídos confiáveis, talvez isso tenha sido justamente o fator que tornou o todo possível
Discordo. Interfaces são um conceito trivial que pode ser anexado a qualquer linguagem. Mesmo em linguagens sem sintaxe oficial de interfaces, dá para imitá-las no espaço do programa
A BEAM teve sucesso porque consegue rodar 1 milhão de processos em um único nó, expressar facilmente máquinas de estado distribuídas complexas e reiniciar partes do sistema sem interrupção. Há muitos outros motivos também
Realmente não acho que behaviour/interface seja a peça mais central
Heaps de processos isolados podem ser obtidos usando processos do sistema operacional; supervisor trees podem ser feitas com Kubernetes; passagem de mensagens pode ser criada em Java com duas threads e uma fila compartilhada; hot code loading também é possível em Java; e, para baixa latência, talvez dê até para superar Erlang ajustando o LMAX Disruptor
Mas o ponto central é reunir tudo isso em uma única plataforma ou biblioteca. Processos do sistema operacional são pesados, e não é fácil rodar 2 milhões deles em um servidor. Se você usa green threads ou promises, perde o heap isolado
Kubernetes também pode ser usado até certo ponto, mas não lida bem com supervisor trees aninhadas. Talvez seja possível, mas além do código surgem pods, controllers, volumes e todo tipo de coisa
É possível fazer passagem de mensagens com bibliotecas de atores em várias linguagens, mas elas não integram de forma transparente coisas como fazer pattern matching no
receiveou enviar para outra thread em outro nóHot code loading também é possível, mas o problema é como lidar com estruturas de dados e estado em tempo de execução. Erlang foi construído em torno disso, e, como em
gen_servero estado é imutável e explícito, ele tem callbacks para atualizar não só o código, mas o próprio estadoNão diria que a BEAM elimina esse problema, mas parece reduzir a inclinação da curva. Idiomas e recursos autoconsistentes, incluindo deploy, recuperação automática e balanceamento de carga, reduzem o atrito entre módulos
Ela permite criar um sistema em que 12 engenheiros conseguem gerenciar facilmente 30 endpoints, e cuja área de superfície ainda pode seguir uma lei de potência
Não concordo com o conteúdo deste texto. Behaviours são possíveis por causa da arquitetura de base do sistema
Behaviour não é uma interface; está mais perto de um objeto abstrato em linguagens como Java. Ele implementa uma funcionalidade básica e autocontida por trás de uma interface de colaboração, mas não consegue fazer muita coisa sem a infraestrutura subjacente que garante que cada processo seja completamente separado dos outros, que todos os processos possam ser encerrados com segurança sem vazamentos de memória ou recursos, e que não seja possível compartilhar ponteiros perigosos entre dois processos
O que Joe mostrou no artigo foi como construir um sistema confiável, e até certo ponto um sistema distribuído confiável, a partir de um determinado conjunto de blocos de Lego
Para implementar isso corretamente, é preciso a VM do Erlang, e não dá para implementar completamente em outras VMs. Sem a tubulação de baixo nível, as árvores de supervisão vazam. Em Java, não dá para matar uma thread que está segurando recursos e esperar que tudo sempre dê certo, e também faltam formas de monitorar processos diferentes