1P by GN⁺ | ★ favorite | 댓글 1개
  • Flawless는 장애가 나도 코드가 끝까지 실행되도록 돕는 내구성 있는 계산 실행 엔진
  • 워크플로는 일반 Rust 함수로 작성하지만, 네이티브 코드가 아니라 WebAssembly로 컴파일해 결정적인 환경에서 실행함
  • HTTP 요청이나 난수 생성처럼 외부 세계와 만나는 지점에서만 비결정적 부작용이 생기며, Flawless는 이를 로그로 저장함
  • 실행이 끊기면 저장된 로그를 이용해 같은 상태까지 다시 따라잡고, 이미 수행한 부작용은 반복하지 않음
  • 개발자는 데이터베이스에 모든 상태를 직접 모델링하지 않고 코드와 지역 변수로 지속 상태를 표현할 수 있어, 서버 재시작 후에도 중단 지점부터 이어갈 수 있음

코드로 지속 상태를 표현하는 실행 모델

  • Flawless는 하드웨어나 소프트웨어 장애가 발생해도 코드가 완료될 때까지 실행되도록 하는 내구성 있는 계산 엔진임
  • 복잡한 사용자 경험은 복잡한 UI와 상태를 요구하지만, 모든 상태를 데이터베이스 안에 직접 모델링하는 일은 어려움
  • 사용자가 페이지를 실수로 새로고침해도 진행 상황을 잃지 않으려면 현대 애플리케이션에는 지속적 저장이 필요함
  • Flawless는 지속 상태를 코드와 지역 변수로 모델링하게 해, 애플리케이션의 복잡한 동작을 더 쉽게 표현하는 것을 목표로 함

WebAssembly 기반 실행과 장애 복구

  • 워크플로는 일반 Rust 함수로 작성되며 임의의 로직을 포함할 수 있음
  • 함수는 네이티브 코드 대신 WebAssembly로 컴파일되고, 완전히 결정적인 환경에서 실행됨
  • HTTP 요청 수행이나 난수 생성처럼 외부 세계와 상호작용할 때만 비결정성이 도입됨
  • Flawless는 이런 비결정적 부작용 로그를 저장해 복구에 사용함
    • 워크플로 실행이 중단되면 다시 실행해 같은 상태까지 따라잡음
    • 이미 수행된 부작용은 다시 수행하지 않음
    • 저장해야 하는 데이터 양은 최소화하고, 나머지는 장애 발생 시 필요에 따라 다시 계산함
  • 이 실행 모델은 전체 시스템의 동작을 더 잘 관찰할 수 있게 함
    • 완료된 워크플로나 실행 중인 워크플로의 정확한 실행 경로를 분석할 수 있음
    • 결정적 실행 환경 덕분에 재현하기 어려운 버그를 다루기 쉬워짐
  • 개발자는 상태 저장 부담을 줄이고 비즈니스 로직 작성에 더 집중할 수 있음
  • 유지보수로 서버를 재시작해야 해도 Flawless 엔진을 다시 시작하면 워크플로가 멈춘 지점부터 이어서 실행됨
  • 2024년 12월 9일 기준 Flawless Beta 3가 제공됨

댓글과 토론

Hacker News 의견들
  • 워크플로 버전 관리는 어떻게 처리할지 궁금함. Temporal/Cadence 같은 시스템에서 가장 어려운 문제라고 봄

    • 작성자임. 오래 실행되거나 사실상 영원히 실행되는 워크플로에는 무중단 업그레이드를 허용하는 게 가장 직관적인 해법이라고 생각함
      새 코드로 기존 부수 효과 로그 기록을 정확히 재생할 수 있을 때만 업그레이드가 성공함. 새 코드로 따라잡기를 한 뒤, 따라잡으면 그대로 계속 실행하는 방식임
      새 코드가 기존 기록과 달라지면 업그레이드는 실패하고 이전 코드로 되돌아감. 이 경우 사람이 개입해서 무엇이 잘못됐는지 확인해야 함
      다른 접근도 있지만, 실제로 이해하고 쓰기에는 이 방식이 가장 단순하다고 봄. 개발 중에도 기존 로그를 이용해 코드가 갈라지는지 테스트할 수 있음
    • 이미 살아 있고 진행 중인 워크플로가 있는데, 워크플로를 업데이트해도 깨지지 않게 하려는 문제인지 궁금함
      여러 버전을 동시에 유지하고 싶은 건지, 아니면 진행 중인 것들을 최신 워크플로 정의로 마이그레이션하는 방법이 필요한 건지도 궁금함
    • 맞음. 그 부분은 Conductor가 대신 잘 처리하는 영역임
  • 우리 분야가 이제 건축이나 의학에 점점 가까워지는 느낌임. 이런 기술 덕분에 만지작거리는 단계에서 벗어나 진지한 공학 문화로 들어갈 수 있음
    짧게 묻자면, 이런 시스템에서 DoS 공격의 효과가 영속화되는 건 어떻게 막는지 궁금함

    • “건축과 의학에 가까워진다”는 표현은 기계학습이 만든 듯한 헛소리처럼 읽힘
      건축은 규제 제약 안에서 주관적 의견을 다루는 쪽에 가깝고, 의학은 실험으로 검증되는 경험적 지식임. 둘 다 공학과 닮았다고 보기는 어려움
    • 건축의학도 공학 분야는 아니라서, 그쪽에 가까워지는 게 왜 공학 문화로 가는 길인지 잘 모르겠음
      건축은 예술의 한 형태에 가깝고, 대학의 건축학과도 보통 예술대학에 있음
      의학은 공학처럼 응용과학이긴 하지만 공학 분야 자체는 아님
  • 결정성이 부동소수점 계산까지 확장되는지 궁금함
    멀티플레이어 게임에서는 클라이언트 상태가 부동소수점 계산에서 조금씩 드리프트를 쌓기 때문에 서버 상태와 주기적으로 다시 동기화해야 했고, 역사적으로 꽤 고통스러운 지점이었음

  • 부수 효과의 상태는 어디에 저장되는지 궁금함. 예를 들어 멱등적으로 만들고 싶은 AWS Lambda가 있다고 하면, Lambda는 실행 간에 유지되는 로컬 저장소가 없음
    EBS 볼륨 같은 걸 마운트하지 않는 한 상태가 유지되지 않으니, 상태는 DB에 저장할 수 있다고 보면 되는 건가?

    • 그게 제품이 출시되면 비용을 내고 쓰게 될 부분일 것 같음
  • 핵심 원리와 동작 방식을 보여주는 애니메이션이 마음에 듦. 정말 잘 만들었음

    • 고마움. HTML, CSS, JavaScript만으로 직접 코딩했고, 많은 노력과 애정을 들였음
      코드는 아주 예쁘진 않지만 구현은 직관적이라 확인해보고 싶다면 여기 있음: https://flawless.dev/js/how-does-it-work-animation.js
    • 외부 엔드포인트 호출 시 타임아웃 때문에 훨씬 흔하게 터지는 지점이라, HTTP 실행 단계에서 실패하는 모습도 보고 싶었음
  • 흥미로워 보이지만, 함수를 부수 효과가 있음으로 표시하는 방식이 실수 없이 만들기 쉬울지 궁금함
    예시에서 난수 생성이 부수 효과인 건 flawless가 제공하는 난수 생성기에서 나오기 때문이라고 가정함. 일반 Rust 함수로도 가능했을까?
    개발자가 워크플로를 확인할 수 있는 테스트 하네스 같은 것도 있을 거라고 생각함

    • flawless 작성자임. WebAssembly를 쓰면 기본적으로 꽤 안전하게 만들 수 있음
      WebAssembly는 모듈 안에서 호스트 호출을 명시적으로 선언하도록 요구함. flawless가 제공하지 않는 다른 호스트 호출을 쓰려 하면 모듈을 인스턴스화할 수 없음
      WebAssembly 생태계에서는 여러 표준화 작업도 진행 중임. 예를 들어 Rust rand 크레이트를 쓰고 WebAssembly로 컴파일하면 난수 생성을 위해 WASI 호스트 함수를 사용함
      wasi, wasi-http 등이 표준화되기를 기다리는 동안은 일단 자체 인터페이스를 노출하고 있음
      물론 큰 단점도 있음. 모든 Rust 코드를 WebAssembly로 컴파일할 수는 없음. 그래도 의도치 않은 부수 효과가 절대 생기지 않도록 보장하는 기본 거부 방식이 더 낫다고 봄
    • 난수 생성기와 HTTP 요청이 모두 flawless 네임스페이스 아래 있는 걸 보면, 전체 Rust 생태계에 접근하게 해주는 대신 std::flawless 같은 걸 쓰게 될 것 같음
      하네스가 대부분의 문제를 풀어주겠지만, 기능을 얼마나 많이 매핑할 수 있을지는 궁금함
      지금까지는 Rust를 쓰는 스크립팅 런타임에 더 가까워 보임
  • WASM을 런타임으로 쓰는 Temporal의 Rust 대안처럼 보임. 마음에 듦
    windmill.dev 창업자인데, 우리도 Rust로 만든 내구성 엔진을 다룸. 다만 훨씬 덜 우아함. 워크플로를 Python/TypeScript/Go/Bash의 명확한 단계로 나누고, 미완료 단계에서 재개하려면 마지막 단계부터 다시 시작하며 각 단계 결과를 Postgres DB의 jsonb에 영구 저장함
    사용 사례는 확실히 다르고, flawless는 사이트에서 말한 것처럼 아주 가벼워서 UI 흐름 상태를 모델링하고 작은 서버에서 수백만 개까지 확장하는 데도 쓸 수 있어 보임
    환상적임. 언젠가 Rust가 모든 분산 시스템을 구동하길 바람

  • “다른 언어로 작성된 충분히 복잡한 동시성 프로그램은, 임시방편으로 비공식 명세에 버그 많고 느린 Erlang의 절반짜리 구현을 포함한다” — Virding의 프로그래밍 제1법칙

    • Flawless의 목표는 어떤 워크플로의 중간 상태를 저장하고, 실패 시 중간 지점부터 다시 시작하는 것으로 보임
      이는 Erlang과 꽤 다름. Erlang은 주로 하드웨어 문제가 많은 프로토타입 장비만 있는 상태에서도 소프트웨어를 개발하기 위해 만들어졌음
      내게는 두 접근이 정반대처럼 보임. Flawless는 중간에서 크래시 나는 워크플로를 끝내려고 루프에 갇힐 수 있지만, Erlang은 하드웨어 버그를 밟은 트래픽 50%를 기꺼이 버릴 수 있음
    • 첫 블로그 글에서 Erlang/OTP를 언급함: https://flawless.dev/essays/when-letting-it-crash-is-not-eno...
    • 이건 Erlang과 전혀 같은 문제를 푸는 것 같지 않음
      Erlang은 지속 상태를 사실상 없애는 방식으로 해결함. 거의 모든 상태가 메시지 큐나 외부 DB 같은 곳에 존재함
      Flawless는 파일시스템 저널링과 비슷하지만 완전히 같지는 않은 기법으로 문제를 푸는 듯함. 부수 효과를 수행할 때 기록해두는 방식임
      파일시스템 저널링은 크래시 후 다시 실행하기 위한 것이지만, 여기서는 다시 실행할 필요가 없게 만들기 위한 것임
      어떤 영역에서 얼마나 잘 맞을지는 명확하지 않지만, Erlang이 잘 맞는 영역과의 겹침은 완전하지 않아 보임
    • https://codesync.global/media/erlangrt-a-beam-vm-reimplement...
    • 그렇다면 비공식 명세에 버그 많은 프로그램이 기대를 넘기 위해 필요한 건 빠르기만 하면 되는 것
  • 아주 멋짐. Ambient라는 WASM 게임 런타임에서도 비슷한 문제가 있음. 경쟁하는 프로세스들이 있고 상호작용을 재시도해야 할 수 있어서, 여기서 보여준 접근이 흥미로움
    그런데 Lunatic과의 관계가 궁금함. Lunatic도 계속 작업 중인지, 이건 사이드 프로젝트인지, 아니면 완전히 별개인지?
    https://lunatic.solutions/

    • 좋은 관찰임. 이건 Lunatic의 CEO이자 공동창업자인 Bernard Kolobara가 만든 것임
      https://kolobara.com
  • “임의의 계산을 시작하면 시스템이 완료될 때까지 실행되고 모든 작업이 정확히 한 번 수행된다고 보장한다고 상상해보라”
    이게 어떻게 보장되는지 궁금함. 분산 시스템에서 정확히 한 번 전달은 불가능하지 않나?

    • 최소 한 번 전달 시스템 위에서 멱등성 키를 쓰면 정확히 한 번 처리가 가능함
    • CAP 정리를 말하는 거라면, 약간의 보충 설명이 필요함: https://www.infoq.com/articles/cap-twelve-years-later-how-th...
    • 불가능하지 않음. CAP 정리에서 CP를 선택하면 됨. 즉 가용성을 포기하는 것임
      시스템이 진행할 수 있다면 메시지는 정확히 한 번 전달됨
      존재 증명이 필요하다면 NFSv3가 1980년대에 이미 이걸 동작하게 했음. 최초였는지는 모르겠음
    • 글에는 “상상해보라”라고 되어 있음. 인용한 문구가 어디 있는지 모르겠고, 원문에서도 이 문장을 못 찾겠음