The AI demo failed, but the database remembered half of it

개요

Formbricks의 AI 예제 응답 생성 기능에서 발생한 오류는 데이터베이스에 부분적으로만 데이터가 기록되는 문제를 야기했으나, 이후 수정 작업을 통해 완전한 커밋 또는 롤백을 보장하는 방향으로 개선되었습니다.

주요 내용

* 문제점 발견: Formbricks의 AI 예제 응답 생성 과정에서 개별 작업이 커밋될 수 있어, 특정 단계에서 오류가 발생해도 이전 단계의 데이터는 데이터베이스에 남는 문제가 확인되었습니다. 이로 인해 오류 발생 시 복구가 불가능해지고, 사용자 경험에 부정적인 영향을 미쳤습니다.
* 원인 분석: Formbricks는 예제 응답 생성을 위해 여러 데이터베이스 쓰기 작업을 수행하며, 각 작업은 독립적으로 커밋될 수 있었습니다. 오류 발생 시 완전한 원자성(atomicity)이 보장되지 않아 부분적인 데이터 변경이 발생했습니다.
* 첫 번째 해결 시도 (Prisma 트랜잭션): 초기 해결 시도로 전체 작업을 단일 Prisma 트랜잭션으로 묶었으나, 트랜잭션이 데이터베이스 연결을 독점하여 다른 서비스의 읽기 작업이 차단되는 교착 상태(deadlock)가 발생했습니다.
* 개선된 트랜잭션 관리: 안정적인 컨텍스트 로딩을 위해 트랜잭션 클라이언트를 사용하고, 뮤터블한 데이터(할당량 등)는 해당 클라이언트를 통해 관리하도록 변경했습니다. 이는 트랜잭션 경계가 실제로 유효하려면 모든 데이터베이스 작업이 동일한 트랜잭션 클라이언트를 사용해야 함을 보여줍니다.
* 외부 모델 호출 분리 및 상태 재검증: 외부 LLM 호출은 데이터베이스 트랜잭션 외부로 분리되었습니다. 이후 데이터베이스 트랜잭션 내에서 설문(Survey) 잠금을 획득하고, 생성된 데이터베이스 상태(설문 존재 여부, 소유권, 응답 수 등)를 재검증하여 데이터 일관성을 확보합니다.
* 올바른 잠금 메커니즘 선택 (FOR NO KEY UPDATE): 경쟁적인 생성 작업과 실제 응답 삽입 간의 충돌을 방지하기 위해 SELECT ... FOR NO KEY UPDATE를 사용하여 설문 잠금을 획득합니다. 이는 실제 응답 삽입을 방해하지 않으면서도 예제 생성 작업을 직렬화합니다.
* 할당량(Quota) 평가 강화: 정상적인 응답 처리 시에는 할당량 오류를 일부 허용했지만, 원자적인 예제 응답 생성을 위해서는 할당량 링크 생성 오류를 엄격하게 처리하여 트랜잭션 실패를 유발하도록 변경했습니다.
* 다양한 실패 시나리오 테스트: 예제 생성 중 오류, 할당량 오류, 트랜잭션 만료, 동시 생성 경쟁, 실제 응답 삽입, 설문 아카이브 등 다양한 시나리오에서 데이터의 완전한 커밋 또는 롤백을 보장하는지 테스트했습니다.
* 기여 및 현황: 발견된 버그는 upstream에 보고되었고, 수정된 Pull Request가 제출되었으나 아직 머지되지 않은 상태입니다.

시사점

이 사례는 복잡한 데이터베이스 작업에서 부분 커밋으로 인한 문제를 해결하기 위해 엄격한 트랜잭션 관리, 외부 서비스 호출 시 상태 재검증, 그리고 작업의 본질에 맞는 잠금 메커니즘 선택이 중요함을 보여줍니다. 또한, 실제 작동 환경에서 발생할 수 있는 다양한 오류 시나리오를 철저히 테스트하고, 각 경로가 생존할 수 있는 실패의 종류를 명확히 정의하는 것이 신뢰성 높은 소프트웨어 개발의 핵심임을 강조합니다.

원문 읽기 →
원문을 불러오는 중...

댓글

GitHub Discussions