--- id: kind: PROJECT_DECISION slug: completion-unknown-is-never-retried title: completion-unknown 은 자동으로도 수동으로도 재시도하지 않는다 topic: commit-ambiguity-as-a-result topicName: 커밋 모호성 — 「모른다」를 결과로 유지하기 project: clean-architecture-backend-template status: 게시 전 studio: "" decisionStatus: ADOPTED decidedOn: 2026-08-11 source: - final/document.md#10-2 - final/document.md#4-1 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 --- # completion-unknown 은 자동으로도 수동으로도 재시도하지 않는다 커밋 결과를 알 수 없는 실패는 조정으로 넘기고 어느 경로로도 재시도하지 않는다. 이 불변식은 정책이 아니라 생성자가 강제한다. ## 근거 - **pg_terminate_backend 가 57P01 로 도착하고 커밋 레코드는 이미 WAL 에 있었다** 이 결정이 다루는 실패가 실제로 어떤 모습인지 보여 주는 사례다. - **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다** 이 결정이 전제하는 상태 어휘다. - **트랜잭션 결과 대수 — 다섯 변형이 각각 답하는 질문** 재시도 금지가 어느 변형에 붙는지를 정의한다. ## 결정문 관측된 단계가 COMMITTING 이고 드라이버가 결과를 말해 주지 못한 실패는 completion-unknown 으로 분류하고, 자동 재시도 정책과 수동 재실행 경로 양쪽에서 제외한다. ## 판단 이유 이 실패는 일시적인 것이 아니라 인식론적이다. 연결이 커밋 도중에 끊기면 서버는 커밋했을 수도 있고 확인 응답만 유실됐을 수도 있는데, 드라이버는 그 둘을 구별하지 못한다. 커밋됐을 수도 있는 쓰기를 재실행하는 것은 이 플랫폼이 할 수 있는 가장 해로운 동작이다. 재시도가 성공하면 중복이 생기고, 그 중복은 원래 실패보다 알아채기 어렵다. 그래서 정책이 아니라 타입이 그 조합을 막는다. JpaFailureContext 의 생성자는 completionUnknown 과 retryable 이 동시에 참인 컨텍스트를 IllegalArgumentException 으로 거부한다. 그 검사는 트랜잭션 결과 대수 기록의 생성자 절에 코드로 있다. 리뷰 규칙으로 두면 그 조합을 만드는 경로마다 같은 검사가 필요하다. 생성자가 거부하므로 그 조합을 가진 값은 프로그램 안에 존재하지 않는다. 방어는 두 겹 더 있다. TransactionCompletionUnknownException 의 생성자는 forceCompletionUnknown 으로 컨텍스트를 다시 만들어 재구성 경로가 검사를 우회하지 못하게 한다. RetryProfile 은 COMPLETION_UNKNOWN 을 화이트리스트에 넣는 것을 생성자에서 거부한다. ## 영향 감수하는 것 : 조정 큐가 생기고 그것을 읽는 운영 절차가 필요하다. 자동으로 해소되지 않는 항목이 쌓인다. 감수하는 것 : 실제로는 롤백된 트랜잭션도 조정으로 넘어간다. 드라이버가 말해 주지 않았으므로 구별할 수 없다. 감수하는 것 : 큐가 무의미해지지 않도록 모호성 판정 조건을 좁게 유지해야 한다. 넓히면 평범한 풀 고갈과 서버 재시작이 큐로 밀려들고, 운영자는 큐를 읽지 않고 비우는 습관을 배운다. 얻는 것 : 중복 쓰기가 자동 경로에서 발생하지 않는다. 얻는 것 : 모르는 것이 상태로 남아 나중에 사람이 판단할 재료가 보존된다.