Jepsen: Orkes Conductor 5.3.2
Jepsen: Orkes Conductor 5.3.2 장애 내성 분석
Jepsen이 Orkes Conductor 5.3.2와 후속 버전을 장애 주입 테스트한 결과, 워크플로가 영구히 방치되거나 사라지고 실행되지 않은 채 완료 처리되는 안전성 문제가 확인됐습니다. Orkes는 주요 문제를 5.5.0과 5.5.6에서 수정했지만, 중복 태스크 실행과 클라이언트 오류 처리, 저장소의 내구성은 별도로 고려해야 합니다.
- 주제
에디터 노트
가장 설득력 있는 대목은 오케스트레이터의 상태 기록만으로 작업의 실제 실행과 외부 효과까지 보장하지 못한다는 점입니다. 같은 태스크가 동시에 실행될 수 있고, 실패 뒤 워크플로가 사라지거나 잘못 완료 처리된 사례는 작업의 멱등성뿐 아니라 재시도와 저장 단계의 원자성도 운영 설계에 포함해야 함을 보여줍니다. 5.5.6은 보고서에서 확인한 중대한 안전성 문제 세 가지를 수정했지만, null ID와 성공한 요청을 연결 실패로 알리는 문제는 남아 있습니다. 특히 후자는 자동 재시도가 중복 실행을 만들 수 있어 클라이언트의 오류 처리와 직접 연결됩니다. 댓글이 짚은 것처럼 클러스터 구성만으로 내구성이 보장되지는 않습니다. 실제 보장 범위는 PostgreSQL과 Redis가 확인된 쓰기를 장애 상황에서도 보존하는지에 달려 있습니다. 다만 이번 시험은 특정 저장소 조합과 단일 작업 워크플로에 한정됐으므로, 다른 구성이나 복잡한 워크플로의 동작까지 같은 결과로 일반화할 근거는 없습니다.
AI 요약
Jepsen은 워크플로 오케스트레이터 Orkes Conductor의 5.3.2, 5.4.0, 5.5.0 릴리스 후보를 대상으로 장애 내성을 시험했습니다. 테스트 클러스터는 PostgreSQL 데이터베이스와 Redis 큐를 사용했고, 서버와 워커를 중단하거나 네트워크를 분리했습니다. 각 워크플로에는 작업 하나를 넣고, 작업 실행과 입출력을 기록해 최종 상태와 대조했습니다.
태스크가 중복 실행될 수 있습니다
정상 동작 중에도 같은 워크플로의 태스크를 여러 워커가 동시에 실행하는 사례가 나왔습니다. 테스트 설정에서는 기본 잠금 임대 시간 1분일 때 약 6시간마다 관찰됐고, 임대 시간을 1초로 줄이면 몇 분마다 발생했습니다. 잠금은 빈도를 낮추지만 중복 실행을 완전히 막지 못합니다. 작업이 외부 시스템에 부수 효과를 남기면 그 효과도 중복될 수 있으므로, Jepsen은 태스크를 멱등적으로 만들고 다른 태스크와 실행 순서가 바뀌어도 결과가 일관되도록 설계하라고 설명합니다. 재예약을 담당하는 sweeper의 경쟁 조건은 중복 전달을 더 자주 일으켰으며 Orkes는 5.5.6에서 수정했다고 보고했습니다.
실패 뒤 멈추거나 사라지는 워크플로
PostgreSQL과 Redis를 함께 쓴 5.3.2에서는 장애 뒤 작업이 실행되지 않은 채 멈추는 현상이 나타났습니다. 작업이 특정 워커에 배정된 것으로 기록됐지만 워커의 실행 함수는 호출되지 않았습니다. 기본 응답 시간 제한은 1시간이며, 시간이 지나면 재예약됐습니다. Orkes는 이 동작을 설계된 동작으로 분류했습니다.
더 심각한 문제는 워크플로가 영구히 방치되거나 사라지는 경우였습니다. 5.3.2, 5.4.0, 5.5.0 릴리스 후보에서는 서버나 PostgreSQL 장애 뒤 실행 기록은 남았지만 decider 큐에서 빠져 작업이 영원히 시작되지 않는 사례가 있었습니다. sweeper가 데이터베이스가 아니라 큐만 확인해 복구하지 못했습니다. Orkes는 5.5.0에 방치된 실행을 큐에 다시 넣는 백그라운드 작업을 추가했습니다. 또 데이터베이스 기록 후 큐 등록에 실패했을 때 실행 기록을 삭제해, 이미 조회되던 워크플로가 이후 사라지는 문제도 있었습니다. 이 삭제 동작은 5.5.0에서 제거됐습니다.
5.5.0 릴리스 후보에서는 작업이 한 번도 실행되지 않았는데 워크플로 상태가 COMPLETED로 표시된 사례도 확인됐습니다. 재시도 작업 기록을 저장하는 중 데이터베이스 쓰기가 실패했지만 오류를 무시했고, 이후 sweeper가 작업이 끝났다고 잘못 판단했습니다. Orkes는 5.5.6에서 재시도 기록 저장 실패를 무시하지 않도록 수정했습니다.
시작 요청 오류와 클라이언트 동작
테스트에서는 Redis 장애 때 Java 클라이언트의 startWorkflow()가 null을 반환하는 문제와, 정상 클러스터에서 PostgreSQL 파티션 정보가 일시적으로 비어 SQL 구문 오류가 나는 문제도 관찰됐습니다. 5.5.6은 파티션 초기화 문제를 수정했고, null 반환 문제는 조사 중이며 Java 클라이언트 6.0.2에 완화책이 들어갔습니다.
서버가 종료되는 동안 startWorkflow()가 연결 실패를 반환했지만, 요청한 워크플로는 이미 생성돼 실행된 사례도 있었습니다. 테스트에서는 실패한 호출 25,920건 가운데 1,752건이 실제로 성공했습니다. 클라이언트가 같은 요청을 재시도하면 워크플로가 중복 생성될 수 있습니다. Orkes는 Java 클라이언트가 멱등성이 없는 요청을 자동 재시도하지 않도록 바꿨습니다. Jepsen 테스트에서는 5.5.0 릴리스 후보의 드문 중복 키 오류도 확인했으며, 이 역시 5.5.6 수정 항목에 포함됐습니다.
결과와 범위
Jepsen은 5.5.6으로 방치된 실행, 사라진 실행, 실행되지 않은 채 완료 처리되는 문제 등 세 가지 중대한 안전성 결함이 수정됐다고 보고하며 업그레이드를 권고합니다. 테스트 당시 null 워크플로 ID와 실제 성공한 요청이 연결 실패로 보이는 문제는 미해결 상태였습니다. 다른 측면에서는 작업 입출력이 일관되고, 작업이 여러 번 실행돼도 Conductor 안에서 보이는 성공 결과가 달라지지 않았습니다. Jepsen은 이번 테스트가 결함의 존재를 보일 수는 있어도 결함이 없음을 증명하지는 못한다고 명시합니다.
또한 Conductor의 안전성은 데이터베이스와 큐가 확인 응답한 쓰기를 보존하는지에 달렸습니다. Redis 클러스터와 관리형 저장소도 네트워크 분할이나 설정에 따라 확인된 쓰기를 잃을 수 있고, PostgreSQL 복제 구성도 예외가 아닙니다. 이번 시험은 단일 작업 워크플로와 PostgreSQL, Redis 조합에 집중했으며 다단계 워크플로, 내장 태스크, 다른 저장소 구성은 범위에 포함하지 않았습니다.
Lobsters 반응
- @iv — 데이터베이스 안전성 부분에서 단일 노드는 고가용성이 아니지만 클러스터도 공짜 해결책은 아니라고 분명히 한 점이 좋았습니다. 무엇이든 실패할 수 있습니다. 태스크를 Agda로 작성하고 교환법칙과 멱등성을 증명하세요!
원문: Jepsen / 번역·요약: Trawling