Flawless - Motor de execução durável para Rust
(flawless.dev)- Flawless é um motor de execução de computação durável que ajuda o código a continuar executando até o fim mesmo quando ocorrem falhas
- Os workflows são escritos como funções Rust comuns, mas são compilados para WebAssembly em vez de código nativo e executados em um ambiente determinístico
- Efeitos colaterais não determinísticos surgem apenas nos pontos em que há contato com o mundo externo, como requisições HTTP ou geração de números aleatórios, e o Flawless os armazena em logs
- Se a execução for interrompida, ele usa os logs salvos para recuperar até o mesmo estado, sem repetir efeitos colaterais já executados
- Os desenvolvedores podem representar o estado persistente com código e variáveis locais sem modelar todo o estado diretamente no banco de dados, permitindo retomar a partir do ponto de interrupção mesmo após reiniciar o servidor
Modelo de execução que representa estado persistente com código
- O Flawless é um motor de computação durável que executa o código até sua conclusão mesmo quando ocorrem falhas de hardware ou software
- Experiências de usuário complexas exigem UIs e estados complexos, mas modelar todo o estado diretamente dentro do banco de dados é difícil
- Para que o usuário não perca o progresso mesmo ao recarregar a página por engano, aplicações modernas precisam de armazenamento persistente
- O Flawless busca permitir que o estado persistente seja modelado com código e variáveis locais, facilitando a expressão de comportamentos complexos da aplicação
Execução baseada em WebAssembly e recuperação de falhas
- Os workflows são escritos como funções Rust comuns e podem incluir lógica arbitrária
- As funções são compiladas para WebAssembly em vez de código nativo e executadas em um ambiente totalmente determinístico
- A não determinismo só é introduzido ao interagir com o mundo externo, como ao fazer requisições HTTP ou gerar números aleatórios
- O Flawless armazena esses logs de efeitos colaterais não determinísticos para uso na recuperação
- Se a execução de um workflow for interrompida, ele o executa novamente até alcançar o mesmo estado
- Efeitos colaterais já executados não são executados novamente
- A quantidade de dados que precisa ser armazenada é minimizada, e o restante é recalculado sob demanda quando ocorre uma falha
- Esse modelo de execução permite observar melhor o comportamento de todo o sistema
- É possível analisar o caminho exato de execução de workflows concluídos ou em andamento
- Graças ao ambiente de execução determinístico, fica mais fácil lidar com bugs difíceis de reproduzir
- Os desenvolvedores podem reduzir a carga de persistência de estado e se concentrar mais na escrita da lógica de negócio
- Mesmo que seja necessário reiniciar o servidor para manutenção, ao reiniciar o motor Flawless o workflow continua a partir do ponto em que parou
- Em 9 de dezembro de 2024, está disponível o Flawless Beta 3
1 comentários
Comentários no Hacker News
Fico curioso sobre como lidarão com versionamento de workflows. Vejo isso como o problema mais difícil em sistemas como Temporal/Cadence
O upgrade só tem sucesso quando o novo código consegue reproduzir exatamente o registro anterior de efeitos colaterais. Depois que o novo código faz o catch-up, ele simplesmente continua executando dali em diante
Se o novo código divergir do histórico anterior, o upgrade falha e volta para o código antigo. Nesse caso, uma pessoa precisa intervir para verificar o que deu errado
Existem outras abordagens, mas acho que essa é a mais simples de entender e usar na prática. Durante o desenvolvimento, também dá para usar logs existentes para testar se o código diverge
Também fico curioso se a ideia é manter várias versões ao mesmo tempo ou se é preciso um jeito de migrar os que estão em andamento para a definição de workflow mais recente
Sinto que nossa área está ficando cada vez mais próxima da arquitetura ou da medicina. Com tecnologias assim, podemos sair da fase de ficar mexendo e entrar em uma cultura de engenharia séria
Perguntando de forma breve: como vocês impedem que o efeito de ataques DoS seja persistido nesse tipo de sistema?
Arquitetura tem mais a ver com lidar com opiniões subjetivas dentro de restrições regulatórias, e medicina é conhecimento empírico validado por experimentos. É difícil dizer que qualquer uma das duas se parece com engenharia
Arquitetura é mais próxima de uma forma de arte, e os cursos de arquitetura nas universidades normalmente ficam em faculdades de artes
Medicina, assim como engenharia, é uma ciência aplicada, mas não é em si uma área de engenharia
Fico curioso se esse determinismo se estende a cálculos de ponto flutuante
Em jogos multiplayer, o estado dos clientes acumulava pequenos desvios em cálculos de ponto flutuante em relação ao estado do servidor, então era preciso ressincronizar periodicamente; historicamente, isso foi um ponto bem doloroso
Veja https://webassembly.org/docs/faq/#why-is-there-no-fast-math-... e https://github.com/WebAssembly/design/blob/main/Nondetermini...
https://asawicki.info/news_1741_myths_about_floating-point_n...
Antigamente, até os resultados em CPU podiam variar dependendo do caminho de execução. Veja o tuíte no artigo
f64também poderia ser uma opção, ou se isso não é realista em termos de complexidade e tamanhoOnde o estado dos efeitos colaterais é armazenado? Por exemplo, se eu tiver uma AWS Lambda que quero tornar idempotente, a Lambda não tem armazenamento local que persista entre execuções
A menos que se monte algo como um volume EBS, o estado não persiste; então dá para assumir que o estado pode ser armazenado em um DB?
Gostei da animação que mostra o princípio central e o modo de funcionamento. Ficou muito bem feita
O código não é exatamente bonito, mas a implementação é direta; se quiser conferir, está aqui: https://flawless.dev/js/how-does-it-work-animation.js
Parece interessante, mas fico curioso se será fácil marcar funções como tendo efeitos colaterais sem cometer erros
No exemplo, presumo que a geração de números aleatórios seja um efeito colateral porque vem do gerador de aleatórios fornecido pelo flawless. Seria possível com uma função Rust comum também?
Imagino que também exista algum tipo de harness de testes para o desenvolvedor verificar o workflow
O WebAssembly exige que chamadas ao host sejam declaradas explicitamente dentro do módulo. Se você tentar usar outra chamada ao host que não seja fornecida pelo flawless, o módulo não poderá ser instanciado
No ecossistema WebAssembly, também há vários trabalhos de padronização em andamento. Por exemplo, se você usa o crate Rust
rande compila para WebAssembly, ele usa funções de host WASI para gerar números aleatóriosEnquanto esperamos pela padronização de
wasi,wasi-httpetc., por enquanto estamos expondo nossas próprias interfacesClaro que há uma grande desvantagem: nem todo código Rust pode ser compilado para WebAssembly. Ainda assim, acho melhor a abordagem de negação por padrão, que garante que efeitos colaterais não intencionais nunca ocorram
flawless, parece que, em vez de dar acesso a todo o ecossistema Rust, a ideia será usar algo comostd::flawlessO harness deve resolver a maioria dos problemas, mas fico curioso sobre quantas funcionalidades será possível mapear
Até agora, parece mais um runtime de scripting que usa Rust
Parece uma alternativa em Rust ao Temporal que usa WASM como runtime. Gostei
Sou fundador da windmill.dev, e nós também lidamos com um motor durável feito em Rust. Só que é bem menos elegante. Dividimos os workflows em etapas claras em Python/TypeScript/Go/Bash e, para retomar a partir de uma etapa inacabada, reiniciamos desde a última etapa e persistimos permanentemente o resultado de cada etapa em
jsonbno banco PostgresOs casos de uso são claramente diferentes, e o flawless parece ser bem leve, como o site diz, a ponto de poder ser usado para modelar o estado de fluxos de UI e escalar para milhões deles em servidores pequenos
Fantástico. Espero que um dia Rust mova todos os sistemas distribuídos
“Todo programa concorrente suficientemente complexo escrito em outra linguagem contém uma implementação ad hoc, informalmente especificada, cheia de bugs e lenta de metade do Erlang” — Primeira Lei de Programação de Virding
Isso é bem diferente de Erlang. Erlang foi criado principalmente para desenvolver software mesmo quando só havia equipamentos protótipo com muitos problemas de hardware
Para mim, as duas abordagens parecem opostas. O Flawless pode ficar preso em um loop tentando concluir um workflow que trava no meio, enquanto Erlang pode descartar tranquilamente os 50% do tráfego que acionaram um bug de hardware
Erlang resolve isso praticamente eliminando estado persistente. Quase todo o estado fica em lugares como filas de mensagens ou bancos de dados externos
O Flawless parece resolver o problema com uma técnica parecida com journaling de sistema de arquivos, mas não exatamente igual. A ideia é registrar quando efeitos colaterais são executados
O journaling de sistema de arquivos serve para reexecutar depois de um crash, mas aqui é para tornar desnecessário reexecutar
Não está claro em quais áreas isso se encaixa melhor, mas a sobreposição com as áreas em que Erlang se encaixa bem não parece ser total
Muito legal. Em Ambient, um runtime de jogos em WASM, temos um problema parecido. Há processos concorrentes e as interações podem precisar ser repetidas, então a abordagem mostrada aqui é interessante
Mas fico curioso sobre a relação com o Lunatic. O Lunatic ainda está em desenvolvimento, isto é um projeto paralelo, ou são coisas totalmente separadas?
https://lunatic.solutions/
https://kolobara.com
“Imagine que você inicia um cálculo arbitrário e o sistema garante que ele será executado até a conclusão e que todas as operações serão realizadas exatamente uma vez”
Fico curioso para saber como isso é garantido. Em sistemas distribuídos, a entrega exatamente uma vez não é impossível?
Se o sistema puder avançar, a mensagem será entregue exatamente uma vez
Se for preciso uma prova de existência, o NFSv3 já fez isso funcionar nos anos 1980. Não sei se foi o primeiro