Flawless - motor de ejecución durable para Rust
(flawless.dev)- Flawless es un motor de ejecución de cómputo durable que ayuda a que el código se ejecute hasta el final incluso cuando ocurren fallas
- Los flujos de trabajo se escriben como funciones normales de Rust, pero en lugar de ejecutarse como código nativo, se compilan a WebAssembly y se ejecutan en un entorno determinista
- Los efectos secundarios no deterministas solo aparecen en los puntos donde el sistema interactúa con el mundo exterior, como solicitudes HTTP o generación de números aleatorios, y Flawless los guarda en un registro
- Si la ejecución se interrumpe, usa el registro guardado para volver a alcanzar el mismo estado, sin repetir los efectos secundarios ya ejecutados
- Los desarrolladores pueden expresar el estado persistente con código y variables locales, sin modelar manualmente todo el estado en una base de datos, por lo que pueden continuar desde el punto de interrupción incluso después de reiniciar el servidor
Modelo de ejecución para expresar estado persistente con código
- Flawless es un motor de cómputo durable que permite que el código siga ejecutándose hasta completarse incluso si ocurren fallas de hardware o software
- Las experiencias de usuario complejas requieren UI y estados complejos, pero modelar directamente todo el estado dentro de la base de datos es difícil
- Para que los usuarios no pierdan el progreso aunque recarguen la página por error, las aplicaciones modernas necesitan almacenamiento persistente
- Flawless busca facilitar la expresión de comportamientos complejos de la aplicación permitiendo modelar el estado persistente con código y variables locales
Ejecución basada en WebAssembly y recuperación ante fallas
- Los flujos de trabajo se escriben como funciones normales de Rust y pueden incluir lógica arbitraria
- Las funciones se compilan a WebAssembly en lugar de código nativo y se ejecutan en un entorno completamente determinista
- La no determinación solo se introduce al interactuar con el mundo exterior, como al realizar solicitudes HTTP o generar números aleatorios
- Flawless guarda este registro de efectos secundarios no deterministas y lo usa para la recuperación
- Si la ejecución del flujo de trabajo se interrumpe, lo vuelve a ejecutar hasta alcanzar de nuevo el mismo estado
- No vuelve a ejecutar los efectos secundarios que ya se realizaron
- Minimiza la cantidad de datos que deben almacenarse y vuelve a calcular el resto según sea necesario cuando ocurre una falla
- Este modelo de ejecución permite observar mejor el comportamiento de todo el sistema
- Se puede analizar la ruta exacta de ejecución de flujos de trabajo completados o en curso
- Gracias al entorno de ejecución determinista, es más fácil manejar errores difíciles de reproducir
- Los desarrolladores pueden reducir la carga del almacenamiento de estado y concentrarse más en escribir la lógica de negocio
- Incluso si es necesario reiniciar el servidor por mantenimiento, al reiniciar el motor Flawless el flujo de trabajo continúa desde el punto donde se detuvo
- Al 9 de diciembre de 2024, está disponible Flawless Beta 3
1 comentarios
Opiniones en Hacker News
Me pregunto cómo manejarían el versionado de workflows. Creo que es uno de los problemas más difíciles en sistemas como Temporal/Cadence.
La actualización solo tiene éxito cuando el código nuevo puede reproducir exactamente el registro existente de efectos secundarios. La idea es ponerse al día con el código nuevo y, una vez alcanzado, seguir ejecutando con ese mismo código.
Si el código nuevo diverge del historial existente, la actualización falla y se vuelve al código anterior. En ese caso, una persona tiene que intervenir para revisar qué salió mal.
Hay otros enfoques, pero en la práctica me parece que este es el más simple de entender y usar. Durante el desarrollo también se puede usar el registro existente para probar si el código se bifurca.
También me pregunto si quieren mantener varias versiones al mismo tiempo, o si necesitan una forma de migrar los que están en curso a la definición de workflow más reciente.
Siento que nuestro campo se está acercando cada vez más a la arquitectura o la medicina. Gracias a tecnologías como esta podemos dejar atrás la etapa de estar jugando y entrar en una cultura de ingeniería seria.
En pocas palabras, me pregunto cómo se evita que en un sistema así se persistan los efectos de un ataque DoS.
La arquitectura tiene más que ver con lidiar con opiniones subjetivas dentro de restricciones regulatorias, y la medicina es conocimiento empírico validado mediante experimentos. No es fácil decir que ninguna de las dos se parezca a la ingeniería.
La arquitectura se parece más a una forma de arte, y en las universidades el departamento de arquitectura normalmente está dentro de una facultad de artes.
La medicina, como la ingeniería, es una ciencia aplicada, pero no es una disciplina de ingeniería en sí.
Me pregunto si este determinismo se extiende también a los cálculos de punto flotante.
En juegos multijugador, como el estado del cliente va acumulando pequeñas desviaciones en los cálculos de punto flotante, había que resincronizarlo periódicamente con el estado del servidor, y históricamente fue un punto bastante doloroso.
Ver https://webassembly.org/docs/faq/#why-is-there-no-fast-math-... y https://github.com/WebAssembly/design/blob/main/Nondetermini...
https://asawicki.info/news_1741_myths_about_floating-point_n...
Antes, los resultados en CPU también podían variar según la ruta de ejecución. Ver el tuit dentro del artículo.
f64también podría ser una opción, o si no es realista por complejidad y tamaño.Me pregunto dónde se almacena el estado de los efectos secundarios. Por ejemplo, si tengo una AWS Lambda que quiero hacer idempotente, Lambda no tiene almacenamiento local que persista entre ejecuciones.
Como el estado no se conserva a menos que montes algo como un volumen EBS, ¿podemos asumir que el estado se guarda en una DB?
Me gustó la animación que muestra el principio central y cómo funciona. Está realmente bien hecha.
El código no es muy bonito, pero la implementación es directa; si quieres revisarla, está aquí: https://flawless.dev/js/how-does-it-work-animation.js
Se ve interesante, pero me pregunto si será fácil marcar una función como con efectos secundarios sin cometer errores.
Supongo que, en el ejemplo, la generación de números aleatorios es un efecto secundario porque proviene del generador de números aleatorios que proporciona flawless. ¿Habría sido posible con una función normal de Rust?
Imagino que también habrá algún test harness para que los desarrolladores puedan verificar el workflow.
WebAssembly exige que dentro del módulo se declaren explícitamente las llamadas al host. Si intentas usar otra llamada al host que flawless no proporciona, no se puede instanciar el módulo.
En el ecosistema WebAssembly también hay varios trabajos de estandarización en curso. Por ejemplo, si usas el crate
randde Rust y compilas a WebAssembly, utiliza una función de host de WASI para generar números aleatorios.Mientras esperamos a que
wasi,wasi-http, etc. se estandaricen, por ahora exponemos nuestras propias interfaces.Por supuesto, también hay una gran desventaja: no todo el código Rust se puede compilar a WebAssembly. Aun así, creo que es mejor un enfoque de denegación por defecto que garantice que no aparezcan efectos secundarios no intencionales.
flawless, parece que en vez de dar acceso a todo el ecosistema Rust terminarán haciendo que se use algo comostd::flawless.El harness resolverá la mayoría de los problemas, pero me pregunto cuántas funcionalidades podrán mapear.
Hasta ahora parece más un runtime de scripting que usa Rust.
Parece una alternativa en Rust a Temporal que usa WASM como runtime. Me gusta
Soy el fundador de windmill.dev, y nosotros también manejamos un motor durable hecho en Rust. Aunque es mucho menos elegante. Dividimos los workflows en pasos claros en Python/TypeScript/Go/Bash y, para reanudar desde un paso incompleto, volvemos a empezar desde el último paso y almacenamos de forma persistente el resultado de cada paso en
jsonben una base de datos PostgresLos casos de uso claramente son distintos, y flawless parece ser muy ligero, como dice en el sitio, así que también podría servir para modelar el estado de flujos de UI y escalar hasta millones en un servidor pequeño
Fantástico. Espero que algún día Rust impulse todos los sistemas distribuidos
“Todo programa concurrente suficientemente complejo escrito en otro lenguaje contiene una implementación improvisada, especificada informalmente, llena de bugs y lenta de la mitad de Erlang” — Primera ley de programación de Virding
Eso es bastante distinto de Erlang. Erlang se creó principalmente para poder desarrollar software aun cuando solo se contaba con equipos prototipo con muchos problemas de hardware
Para mí, los dos enfoques parecen opuestos. Flawless puede quedar atrapado en un bucle intentando terminar un workflow que crashea a la mitad, mientras que Erlang puede estar perfectamente dispuesto a descartar el 50% del tráfico que se topó con un bug de hardware
Erlang lo resuelve prácticamente eliminando el estado persistente. Casi todo el estado existe en lugares como colas de mensajes o bases de datos externas
Flawless parece resolver el problema con una técnica parecida al journaling de sistemas de archivos, aunque no exactamente igual. Consiste en registrar cuando se realizan efectos secundarios
El journaling de sistemas de archivos sirve para volver a ejecutar después de un crash, pero aquí es para hacer que no sea necesario volver a ejecutar
No está claro en qué áreas encajará bien, pero el solapamiento con las áreas donde Erlang encaja bien no parece ser completo
Muy genial. En Ambient, un runtime de juegos WASM, tenemos un problema similar. Hay procesos que compiten y puede que tengamos que reintentar interacciones, así que el enfoque mostrado aquí es interesante
Pero me da curiosidad su relación con Lunatic. ¿Lunatic sigue en desarrollo, esto es un proyecto paralelo o es algo completamente separado?
https://lunatic.solutions/
https://kolobara.com
“Imagina que puedes iniciar un cálculo arbitrario y el sistema garantiza que se ejecutará hasta completarse y que todas las operaciones se realizarán exactamente una vez”
Me pregunto cómo se garantiza eso. ¿No es imposible la entrega exactamente una vez en sistemas distribuidos?
Si el sistema puede avanzar, el mensaje se entrega exactamente una vez
Si necesitas una prueba de existencia, NFSv3 ya lo hizo funcionar en los años 80. No sé si fue el primero