--- kind: PROJECT_DECISION slug: completion-unknown-is-never-retried title: completion-unknown은 자동으로도 수동으로도 재시도하지 않는다 topic: commit-ambiguity-as-a-result project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: decision:completion-unknown-is-never-retried decisionStatus: ADOPTED decidedOn: 2026-08-11 source: - docs/adr/ADR-JPA-003-completion-unknown.md — Status Accepted, Date 2026-08-11, Design §17 - src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/transaction/CommitFailureClassifier.java - src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/api/transaction/TransactionCompletionEvidence.java - analysis/05-adapter-outbound-persistence-jpa.md --- # completion-unknown은 자동으로도 수동으로도 재시도하지 않는다 ## 결정문 커밋 결과를 알 수 없는 실패는 자동으로도 수동으로도 재시도하지 않고 조정으로 넘긴다. ## 판단 이유 ADR 의 근거는 이 실패가 일시적인 것이 아니라 인식론적이라는 관찰이다. 연결이 커밋 도중에 끊기면 서버는 커밋했을 수도 있고 확인 응답만 유실됐을 수도 있으며, 드라이버는 그 둘을 구별하지 못한다. 커밋됐을 수도 있는 쓰기를 재시도하는 것은 이 플랫폼이 할 수 있는 가장 해로운 일이다. 재시도가 성공하면 중복이 생기고, 그 중복은 원래 실패보다 알아채기 어렵다. 그래서 이 예외는 좁은 조건에서만 만들어진다. 관측된 단계가 COMMITTING 이고, 동시에 SQLSTATE 나 예외 타입이 결과를 말해 주지 못하는 경우다. 그리고 이 불변식은 정책이 아니라 타입 수준에서 강제된다. completionUnknown 이면서 retryable 인 실패 컨텍스트는 생성자가 거부하므로 존재할 수 없다. 정책 버그가 그 조합을 만들 수 있는 경로 자체가 없다. ## 영향 감수하는 것 조정 큐가 생기고 그것을 읽는 운영 절차가 필요하다. 자동으로 해소되지 않는 항목이 쌓인다. 조정 큐가 무의미해지지 않도록 모호성 규칙을 좁게 유지해야 한다. 넓히면 평범한 풀 고갈과 서버 재시작이 큐로 밀려들고, 운영자는 큐를 읽지 않고 비우는 습관을 배운다. 실제로는 롤백된 트랜잭션도 조정으로 넘어간다. 드라이버가 말해 주지 않았으므로 구별할 수 없다. 얻는 것 중복 쓰기가 자동 경로에서 발생하지 않는다. 모르는 것이 상태로 남아서 나중에 사람이 판단할 재료가 보존된다. ## 근거 - **pg_terminate_backend가 57P01로 도착하고 커밋 레코드는 이미 WAL에 있었다** 이 결정이 다루는 실패가 실제로 어떤 모습인지 보여 주는 사례다. - **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다** 이 결정이 전제하는 상태 어휘다. - **위험한 조합은 정책이 아니라 생성자가 거부하게 만든다** 이 결정을 타입 수준에서 강제하는 방법이다. - **커밋 모호성 판정은 넓혀도 좁혀도 해롭다** 이 결정의 조건을 어디에 둘지 정하는 기준이다.