--- kind: CASE slug: commit-ambiguity-is-not-only-sqlstate-08 title: pg_terminate_backend가 57P01로 도착하고 커밋 레코드는 이미 WAL에 있었다 topic: commit-ambiguity-as-a-result project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:commit-ambiguity-is-not-only-sqlstate-08 evidenceCapturedOn: 2026-09-01 body: case-commit-ambiguity-is-not-only-sqlstate-08.body.md assets: - key: commit-ambiguity-is-not-only-sqlstate-08 file: ../../../final/evidence/rendered/commit-ambiguity-is-not-only-sqlstate-08.svg evidence: - ../../../final/evidence/raw/commit-ambiguity-is-not-only-sqlstate-08.txt source: - 원본 분석 절은 final/document.md#4-1 · analysis/05 §3.5 이다. --- # pg_terminate_backend가 57P01로 도착하고 커밋 레코드는 이미 WAL에 있었다 커밋 모호성을 SQLSTATE class 08로만 정의했던 규칙이, 서버가 자기 종료를 알리는 57P01 앞에서 성립하지 않았다. 그 시점에 커밋 레코드는 이미 WAL에 있을 수 있다. ## 관계 - **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다** 이 사례가 그 세 번째 결과를 필요로 하는 이유다. - **커밋 모호성 판정은 넓혀도 좁혀도 해롭다** 이 사례가 규칙을 넓힌 쪽이고, 그 규칙의 반대편 비용을 함께 다룬다. - **completion-unknown은 자동으로도 수동으로도 재시도하지 않는다** 이 분류가 만들어 내는 예외를 어떻게 다룰지 정한 결정이다. - **커밋 모호성 계약 레인이 이 리비전에서 통과하는지 실행으로 확인되지 않았다** 이 사례의 회귀 방지 레인에 대한 미해결 질문이다. ## 문제 설계는 커밋 모호성을 SQLSTATE class 08, 즉 연결 예외로 정의했다. 그 정의는 클라이언트가 자기 연결에서 무슨 일이 일어났는지를 기준으로 삼는다. 문제는 서버가 스스로 종료를 알리는 경우다. in-flight 커밋 중인 백엔드를 pg_terminate_backend 로 끊으면 클라이언트는 57P01 을 받는다. 이것은 class 08 이 아니다. 클라이언트의 연결 시도에 대한 이야기가 아니라 서버가 자기 종료를 알린 것이기 때문이다. 그러나 커밋 입장에서 결과는 같고 오히려 더 나쁘다. 57P01 이 도착한 시점에 커밋 레코드가 이미 WAL 에 있을 수 있다. class 08 만 모호성으로 보는 규칙은 이 실패를 평범한 실패로 분류하고, 평범한 실패는 재시도된다. 커밋됐을 수도 있는 쓰기를 다시 실행하는 경로가 여기서 열린다. ## 결론 규칙이 57P01 과 57P02 와 57P03 까지 넓어졌다. 현재 CommitFailureClassifier 는 네 가지를 모호성으로 본다. 40003 : statement completion unknown 08 으로 시작하는 상태 : connection exception 57P01 57P02 57P03 : 서버가 자기 종료를 알린 상태 transport 수준 단절 세 상태를 함께 넣은 이유는 코드 주석에 남아 있다. 57P01 은 종료된 백엔드나 fast shutdown 이나 failover 가 보고하는 것이고, 그것이 도착할 때 커밋 레코드가 이미 WAL 에 있을 수 있다. 57P02 는 crash shutdown, 57P03 은 지금 접속할 수 없음이며, 서버가 in-flight 커밋을 어떻게 했든 클라이언트가 그것을 알지 못했다는 점에서 같다. 넓어진 규칙이 다시 좁아지지 못하도록 CommitAmbiguityContractTest 가 SQLSTATE 를 직접 assert 한다. 이 규칙에는 반대 방향의 비용도 함께 기록되어 있다. 커밋 단계의 모든 연결 오류를 모호성으로 표시하면 평범한 풀 고갈과 서버 재시작이 조정 큐로 밀려들고, 운영자는 그 큐를 읽지 않고 비우는 습관을 배운다. 그리고 정작 중요한 항목 하나가 나머지와 함께 지워진다. ## 검증 환경 OpenJDK : 21.0.12 Gradle : 9.0.0 Spring Boot : 4.0.8 데이터베이스 : PostgreSQL 관측 출처 : 저장소가 기록한 컨테이너 레인. 이 분석에서 재실행하지 않았다 ## 재현 조건 원래 관측은 컨테이너 레인에서 나왔다. 절차는 다음과 같다. 1. 커밋이 진행 중인 트랜잭션을 만든다. 2. 그 백엔드를 pg_terminate_backend 로 끊는다. 3. 클라이언트가 받는 SQLSTATE 와 그 시점의 WAL 상태를 확인한다. 이번 사이클에서 확인한 것은 규칙의 현재 코드 형태와 그 근거 문장이다. ## 본문 설계가 "커밋 모호성은 SQLSTATE class 08뿐"이라고 적었다. ## 컨테이너 레인이 보인 것 in-flight 커밋 중인 백엔드를 `pg_terminate_backend`로 끊었을 때 `57P01`(admin_shutdown)이 도착하며, 그 시점에 커밋 레코드가 이미 WAL에 있을 수 있다. ## CommitAmbiguityContractTest 참조 위치 :::evidence key="commit-ambiguity-is-not-only-sqlstate-08" alt="코드베이스에서 CommitAmbiguityContractTest 를 검색한 출력 1줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="CommitAmbiguityContractTest 코드베이스 검색 — 1줄 · exit 0" zoom="true" ::: ## 규칙이 넓어지고 다시 좁아지지 못하게 고정됐다 `57P01/57P02/57P03`까지 넓어졌고 `CommitAmbiguityContractTest`가 SQLSTATE를 직접 assert한다. ## 확인하지 못한 것 jpaPlatformFailureTest 를 이 리비전에서 실행하지 않았다. 57P01 관측이 PostgreSQL 16 과 17 과 18 전부에서 재현되는지도 확인하지 않았다. 관련 미해결 질문에 그 조건을 적어 두었다.