Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/commit-ambiguity-as-a-result/decision/decision-retry-unit-is-the-use-case.md
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

4.4 KiB

kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, decisionStatus, decidedOn, source
kind slug title topic project status sourceRevision rootTreeNode decisionStatus decidedOn source
PROJECT_DECISION retry-unit-is-the-use-case 재시도 단위는 statement가 아니라 유스케이스 전체다 commit-ambiguity-as-a-result clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 decision:retry-unit-is-the-use-case ADOPTED 2026-08-11
docs/adr/ADR-JPA-002-full-transaction-retry.md — Status Accepted, Date 2026-08-11, Design §19.2
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/transaction/FullTransactionRetryCoordinator.java
src/application-core/src/main/java/dev/caskeleton/application/transaction/IrreversibleSideEffectContext.java
analysis/05-adapter-outbound-persistence-jpa.md

재시도 단위는 statement가 아니라 유스케이스 전체다

결정문

재시도는 실패한 statement 나 트랜잭션이 아니라 유스케이스 전체를 새 트랜잭션과 새 Persistence Context 에서 다시 실행한다.

판단 이유

statement 수준 재시도는 지금 재시도하려는 실패들에 대해 정확히 틀린 선택이다. 낙관적 충돌은 시도가 계산의 기준으로 삼은 상태가 더 이상 커밋된 상태가 아니라는 뜻이므로, 같은 statement 를 다시 쏘면 이미 움직인 버전에 대해 같은 틀린 답을 계산한다. 도메인 규칙이 다시 읽은 데이터 위에서 다시 돌아야 하고, 그것은 곧 유스케이스 전체다.

Persistence Context 재사용도 같은 이유로 틀리다. 두 번째 시도가 1차 캐시에서 첫 시도의 낡은 엔티티를 읽게 된다.

advice 순서도 결정의 일부다. 재시도 advice 가 Spring 의 트랜잭션 advice 바깥에 놓여야 각 시도가 새 트랜잭션을 시작한다. 순서가 뒤집히면 재시도 루프가 이미 rollback-only 로 표시된 하나의 트랜잭션 안에서 돌고, 두 번째 시도는 아무것도 실행하지 못한 채 즉시 실패한다.

예산은 시도 횟수와 경과 시간 두 상한을 함께 갖는다. 코디네이터가 매 시도마다 경과를 계산해 두 상한을 함께 확인한다.

두 가지 실패는 예산과 무관하게 재시도하지 않는다. 완료를 알 수 없는 실패는 조정으로 가고, 정책이 실패로 분류한 것은 그대로 실패다.

영향

감수하는 것

재시도 가능한 유스케이스는 처음부터 다시 실행해도 안전해야 한다. 커밋 전에 되돌릴 수 없는 외부 효과가 있으면 안 된다.

그 조건이 성립하지 않는 유스케이스는 스스로 선언해야 한다. IrreversibleSideEffectContext 가 그 선언을 받고, 정책은 남은 예산과 무관하게 재시도를 거부한다.

재시도 한 번의 비용이 statement 재시도보다 크다. 유스케이스 전체가 다시 돈다.

얻는 것

낙관적 충돌과 직렬화 실패가 실제로 해소된다. 다시 읽은 데이터 위에서 도메인 규칙이 다시 판단하기 때문이다.

두 프로파일이 하나의 재시도를 나눠 결정하는 상황이 없다. 호출자가 넘긴 프로파일 하나가 적격성과 백오프와 예산을 모두 정한다.

2026-09-01 재검증 재검증 결과다.

결정은 채택되어 있고 코디네이터는 조건부 빈으로 생성된다. 그러나 그 빈을 주입받아 호출하는 프로덕션 코드가 없다.

ADR 의 Enforcement 절은 두 가지를 지목한다. 첫 번째인 FullTransactionRetryCoordinatorTest 는 실재하며 코디네이터 자체를 검증한다. 두 번째인 RetryableJpaTransactionInterceptor 는 이 저장소에 존재하지 않고 구현 계획과 코드 리뷰와 이 ADR 세 문서에만 이름으로 남아 있다.

app-bootstrap 과 persistence-jpa 의 main 에는 Advisor 도 MethodInterceptor 도 Pointcut 도 Aspect 도 없다.

따라서 현재 상태는 이렇다. 코디네이터의 동작은 테스트로 고정되어 있고, 그것이 어떤 유스케이스에 적용되는지는 고정되어 있지 않다. 이 절의 근거는 EVD-336 이다.

근거

  • 번역되지 않은 경합 예외가 재시도 코디네이터의 catch를 통째로 비껴갔다 이 재시도 경로가 실제로는 돌지 않았던 사례다.
  • 모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다 예산과 무관하게 재시도하지 않는 두 실패 중 하나가 이 규칙에서 나온다.