46 KiB
title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
| title | source_type | status | branch | parent_branch | related_projects | governing_docs | tags | created | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | contract_packet_sha256 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-transaction-concurrency-contract | branch-note | raw | feature-transaction-concurrency-contract |
|
|
|
2026-05-21 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-012 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-012 |
|
1 | f8840ebc8c3775ace9287ef6e88d907803b2e8d13db1a9a4c1da841d1f3abc29 |
branch: feature-transaction-concurrency-contract
Layer:
raw/branch-notes/— transaction boundary와 concurrency 실패 계약을 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§14 Transaction/Concurrency) 의 결정/근거/금지 사항을 정제한다.
범위 정합 (2026-06-09 ground-truth 대조): TransactionPort abstraction 자체(
inWrite/inRead/inNew, callback signature,@Transactional금지 ArchUnit rule,inNewpool sizing)는 raw/branch-notes/feature-application-port-usecase-contract (D3/D11/D12) 가 SSOT 이며 이미 구현·검증 완료(Phase C2). 본 branch 는 그 위에 얹는 isolation 정책(D3) · lock-failure 분류 정책(D5) · idempotency 요구 정책(D6) · outbox trigger 정책(D7) 의 소비자/정책 계층이다. D1/D2/D4 는 소비자 관점 재진술이며 원본 계약은 app-port branch 소유 (§Audit & Findings 참조).
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: transaction·concurrency failure fixture가 통과한다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1 |
application은 Spring transaction annotation 대신 TransactionPort 또는 TransactionalUseCaseRunner를 사용한다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1 |
database는 PostgreSQL 16 단일 stack이다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
목표
운영 장애는 단순 DB unavailable보다 transaction boundary, lock, deadlock, duplicate command, retry 중복 write에서 자주 발생합니다. CA skeleton은 application use case 기준의 transaction/concurrency 규칙을 가져야 합니다.
- 이슈:
- PR:
범위
포함 범위
- application use case transaction boundary.
- read-only transaction 기준.
- optimistic/pessimistic lock 실패 분류.
- deadlock/lock timeout 분류.
- duplicate command와 idempotent command 처리 기준.
- retry 중복 write 방지 기준.
- outbox pattern 도입 기준.
제외 범위
- business transaction 상세 설계.
- distributed transaction 구현.
- event sourcing 기본 탑재.
근거 (필수, 최소 1개+)
본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 참조.
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/postgres-transaction-isolation-official | D3 — PostgreSQL READ COMMITTED 기본값 + statement/transaction-level snapshot 시맨틱 (#PG-ISO-C1~#PG-ISO-C6) |
| raw/official-docs/mysql-innodb-transaction-isolation-official | D3 — MySQL InnoDB 기본값 = REPEATABLE READ (Postgres 와 상이) + consistent/locking read 시맨틱 (#MYSQL-ISO-C1~#MYSQL-ISO-C6) |
| raw/official-docs/spring-tx-management-reference | D1 자체-호출 함정(#SPRING-TX-MGR-C5) + D4 propagation REQUIRED default(#SPRING-TX-MGR-C3) + isolation/readOnly/timeout 적용 범위(#SPRING-TX-MGR-C6) |
| raw/official-docs/spring-tx-propagation-required-new-nested-official | D4 — REQUIRED/REQUIRES_NEW/NESTED propagation 정확한 시맨틱 |
| raw/company-tech-blogs/transaction-port-clean-ddd-spring-medium | D1 — UNIL의 동일 진화 경로 (2024-05, company-case-study) |
| raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme | D1 — TransactionPort 참고 구현 (company-case-study) |
| raw/official-docs/at-transactional-spring-official | D1 — @Transactional 직접 부착 대안 + proxy self-invocation 함정 |
| raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement | D1 — Hexagonal 표준 다수파 (@Transactional 직접 부착, company-case-study) |
| raw/official-docs/transaction-template-spring-official | D2 — programmatic TransactionTemplate 권장 패턴 |
| raw/official-docs/functional-tx-arrow-kt-resource-docs | D1 대안 — Functional Resource monad (Arrow Kt) |
| raw/company-tech-blogs/custom-transaction-interceptor-catnipcoder | D1 대안 — Custom TransactionInterceptor (AOP) |
| raw/company-tech-blogs/woowahan-hexagonal-multimodule | 보완 — multi-module 분리 |
근거 자료가 raw에 아직 없다면 먼저 raw-source-template 또는 lecture-note-template 으로 raw에 등록한 뒤 여기서 링크.
외부 근거 / 대안 조사 (2026-05-22 — Topic 2; isolation 보강 2026-06-09)
본 branch의 transaction boundary + isolation + propagation 결정에 대한 외부 source 조사. 5종 대안 비교는 (예정) wiki/concepts/transaction-boundary-abstraction.md 참조.
- 채택 결정 (TransactionPort abstraction):
- raw/company-tech-blogs/transaction-port-clean-ddd-spring-medium — UNIL의 동일 진화 경로 (2024-05)
- raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme — TransactionPort 참고 구현
- 검토한 대안:
- 대안 1: @Transactional direct — raw/official-docs/at-transactional-spring-official, raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement (Hexagonal 표준 다수파)
- 대안 2: TransactionTemplate programmatic — raw/official-docs/transaction-template-spring-official
- 대안 3: Functional Resource monad — raw/official-docs/functional-tx-arrow-kt-resource-docs (Arrow Kt)
- 대안 4: Custom TransactionInterceptor (AOP) — raw/company-tech-blogs/custom-transaction-interceptor-catnipcoder
- 대안 5: TransactionalUseCaseRunner (별도 runner abstraction) — 검토 후 미채택. ca-tmpl 은 단일
TransactionPortabstraction 만 채택했고, 코드에TransactionalUseCaseRunner는 존재하지 않음 (governing doctransaction-boundary-abstractionL79 + ca-tmplsrc/grep 으로 확인). D1 본문의TransactionalUseCaseRunner표현은 stale → §Audit & FindingsDRIFT-1.
- 보완 (대체 X): raw/company-tech-blogs/woowahan-hexagonal-multimodule — multi-module 분리
- isolation 보강 (2026-06-09): D3 의 isolation default 근거가 cited raw 8종에 없어 vendor 공식 doc 2종 신규 수집 → raw/official-docs/postgres-transaction-isolation-official (
#PG-ISO-C1: Postgres 기본 = READ COMMITTED) + raw/official-docs/mysql-innodb-transaction-isolation-official (#MYSQL-ISO-C1: MySQL InnoDB 기본 = REPEATABLE READ). 두 vendor 의 기본 isolation 이 다르다는 사실 이 "묵시적 vendor default 사용 forbidden, 명시 pin 강제" 정책의 핵심 근거. - 비교 핵심: ca-tmpl 결정은 "Spring 의존 숨김" 소수파. 다수파는
@Transactional직접 부착(testability 낮지만 boilerplate 최저). Functional monad는 testability 최고지만 팀 학습 비용 큼. concurrency 관점에서 isolation default(READ_COMMITTED), propagation REQUIRED 1택은 5종 abstraction 대안 어디서도 직접 비교 source 부재 — Spring 공식 기본값 + vendor 공식 isolation 시맨틱을 따른 결정.
TODO
TODO drained — 결정은 아래 표/결정 사항 참조.
진행 중 메모
- transaction policy는 repository capability와 연결되어야 합니다.
결정 사항 (decisions)
- 2026-05-21: transaction boundary는 application use case 기준으로 검토.
- 2026-05-22: transaction abstraction의 SSOT는
feature-application-port-usecase-contract이며, 이 branch는 lock/isolation/retry/idempotency 분류를 소비자 관점에서 정의. - 2026-05-22: application package의 Spring
@Transactional직접 import는 금지. transaction 실행은TransactionPort또는TransactionalUseCaseRunner구현체를 통해 수행. - 2026-05-22: isolation level default =
READ_COMMITTED(PostgreSQL/MySQL 양쪽 동일 의미). write-heavy use case는 명시 선언으로 REPEATABLE_READ/SERIALIZABLE 허용. 묵시적 vendor default 사용은 forbidden. - 2026-05-22: propagation default = REQUIRED 1택. REQUIRES_NEW는 outbox/audit row 분리 케이스에 한해 명시 선언 시만 허용. NESTED/NEVER 등 묵시 사용은 forbidden.
- 2026-06-09 (정합 보강):
TransactionalUseCaseRunner는 미채택 대안 — 코드 미존재(§AuditDRIFT-1). isolation "PostgreSQL/MySQL 양쪽 동일 의미" 는 부정확 — 두 DB 기본값이 다름(Postgres=READ COMMITTED, MySQL InnoDB=REPEATABLE READ)이라서 명시 pin 이 필요하다는 것이 정확한 근거(§AuditDRIFT-2).
결정-근거 매핑
각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. company-tech-blog 는
company-case-study로만 라벨 (official best practice 단정 금지).선택 조건열: "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면 N/A.
| Decision ID | Decision (요약) | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | transaction boundary 는 application use case 기준. application package 의 Spring @Transactional 직접 import 금지 — TransactionPort / TransactionalUseCaseRunner 구현체로만 실행 |
N/A (모든 application use case 항상) | raw/official-docs/at-transactional-spring-official.md#AT-TX-C1 (concrete class 부착 권장), #AT-TX-C2 (interface annotation AspectJ silently ignored), #AT-TX-C5 (proxy self-invocation 함정), raw/official-docs/transaction-template-spring-official.md#TX-TMPL-C1, #TX-TMPL-C2 (programmatic callback 권장), raw/official-docs/spring-tx-management-reference.md#SPRING-TX-MGR-C2 (PlatformTransactionManager 는 SPI — application code 에서 직접 사용 + mock/stub 가능), #SPRING-TX-MGR-C5 (proxy mode default 에서 self-invocation 은 @Transactional 우회 — UseCase 외부 호출 강제 근거), raw/company-tech-blogs/transaction-port-clean-ddd-spring-medium.md, raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme.md (company-case-study — UNIL 동일 진화 경로 + TransactionPort 참고 구현) |
official-vendor-doc (AT-TX-C1/C2/C5, TX-TMPL-C1/C2, SPRING-TX-MGR-C2/C5) + company-case-study (UNIL / Vassilis Soum) |
OWNERSHIP: TransactionPort + @Transactional 금지 ArchUnit rule 은 raw/branch-notes/feature-application-port-usecase-contract D3 가 SSOT 이며 이미 구현·검증 완료 (code: application-core/.../transaction/TransactionPort.java, app-bootstrap/.../CleanArchitectureTest.java L167-175 — 주석에 "raw/branch-notes/feature-application-port-usecase-contract D3"). 본 row 는 소비자 재진술. TransactionalUseCaseRunner 는 코드 미존재(§Audit DRIFT-1). Spring 공식은 @Transactional 함정만 명시 — clean/hexagonal 양립성 평가는 cited raw 범위 밖. TransactionPort 채택은 소수파. SPRING-TX-MGR-C5 는 AspectJ mode 동일 우회 의미 아님 |
| D2 | TransactionPort adapter 는 내부적으로 TransactionTemplate.execute(...) 사용 (programmatic 권장 패턴) |
N/A (adapter 구현 항상) | raw/official-docs/transaction-template-spring-official.md#TX-TMPL-C1 (callback 접근법으로 boilerplate 제거), #TX-TMPL-C2 (Spring 팀 권장: imperative=TransactionTemplate, reactive=TransactionalOperator), #TX-TMPL-C3 (TransactionCallback + execute() 패턴), #TX-TMPL-C4 (setRollbackOnly() 명시적 rollback) |
official-vendor-doc |
OWNERSHIP: SpringTransactionPort (adapter-persistence) 가 모드별 TransactionTemplate 3개를 미리 빌드 — 코드 확인(actually-implemented), app-port branch 소유. 본 row 는 소비자 재진술. adapter 내부 self-invocation 함정(D1 #AT-TX-C5) 이 TransactionTemplate 경로에서 어떻게 처리되는지 별도 검증 필요 |
| D3 | isolation level default = READ_COMMITTED (명시 pin). write-heavy use case 는 명시 선언으로 REPEATABLE_READ/SERIALIZABLE 허용. 묵시적 vendor default 사용 forbidden |
write-heavy / read-consistency 필요 use case → 명시 REPEATABLE_READ/SERIALIZABLE; 그 외 모든 use case → READ_COMMITTED default. READ_UNCOMMITTED → forbidden | raw/official-docs/postgres-transaction-isolation-official.md#PG-ISO-C1 (Postgres 기본 = READ COMMITTED), #PG-ISO-C2 (statement-level snapshot), #PG-ISO-C3 (REPEATABLE READ = tx-level snapshot), #PG-ISO-C4 (serialize 실패 에러), #PG-ISO-C5 (SERIALIZABLE = SSI), #PG-ISO-C6 (내부 3 레벨, READ UNCOMMITTED=READ COMMITTED); raw/official-docs/mysql-innodb-transaction-isolation-official.md#MYSQL-ISO-C1 (InnoDB 기본 = REPEATABLE READ), #MYSQL-ISO-C4 (READ COMMITTED = fresh snapshot per read), #MYSQL-ISO-C2/C3 (REPEATABLE READ snapshot + gap lock) |
official-vendor-doc (PostgreSQL + MySQL 공식) |
두 vendor 기본값이 다름(Postgres READ COMMITTED vs MySQL InnoDB REPEATABLE READ)이 명시 pin 필요성의 근거. ca-tmpl Isolation enum 은 현재 READ_COMMITTED 단일값만 노출(code 확인) — REPEATABLE_READ/SERIALIZABLE 노출 + per-use-case 선택 메커니즘은 본 branch 미구현(planned). READ_COMMITTED 의 non-repeatable read/phantom 허용 trade-off 는 read-then-write use case 에서 lost-update 위험 (§구현 가이드 1) |
| D4 | propagation default = REQUIRED 1택. REQUIRES_NEW 는 outbox/audit row 분리 명시 선언 시만. NESTED/NEVER 묵시 사용 forbidden | 일반 use case → REQUIRED; outbox/audit row 분리 필요 → 명시 REQUIRES_NEW (inNew); NESTED/NEVER → forbidden |
raw/official-docs/spring-tx-management-reference.md#SPRING-TX-MGR-C3 (@Transactional default propagation = PROPAGATION_REQUIRED verbatim), #SPRING-TX-MGR-C6 (isolation/readOnly/timeout 은 REQUIRED/REQUIRES_NEW 한정), raw/official-docs/spring-tx-propagation-required-new-nested-official.md (REQUIRED/REQUIRES_NEW/NESTED 정확한 시맨틱) |
official-vendor-doc (Spring Framework Reference verbatim) |
OWNERSHIP: code 확인 — SpringTransactionPort inWrite/inRead=REQUIRED, inNew=REQUIRES_NEW (actually-implemented); inNew pool-sizing 공식은 app-port D12 소유. NESTED/NEVER 금지 자체는 ca-tmpl 내부 결정 — Spring 공식 prescribe 아님 |
| D5 | optimistic lock conflict 409 vs deadlock/timeout retryable by policy. all locks generic 500 금지 | optimistic(@Version) 충돌 → 409 client non-retryable; deadlock(40P01)/serialization(40001) → retryable by policy; pessimistic lock → 명시 시만 | raw/official-docs/postgres-transaction-isolation-official.md#PG-ISO-C4 (REPEATABLE READ serialize 실패 → 재시도), raw/official-docs/transaction-template-spring-official.md#TX-TMPL-C4 (명시적 rollback) + 위임: raw/branch-notes/feature-persistence-failure-baseline D6 (raw/official-docs/persistence-spring-dataaccessexception-hierarchy.md#SDA-EX-C5 optimistic locking failure 예시; SQLState 40001→DB_SERIALIZATION_FAILURE, 40P01→DB_DEADLOCK, 둘 다 category CONFLICT·retryable, 23505→DB_UNIQUE_VIOLATION) |
official-vendor-doc (transaction boundary) + cross-branch-delegation (persistence-failure-baseline D6 — exception→error-code 매핑 SSOT) |
본 branch 는 정책(409 vs retryable) 만 소유 — exception→error-code 매핑은 persistence baseline 소유. error-codes.yaml 에 optimistic-lock 전용 code 부재(DB_SERIALIZATION_FAILURE/DB_DEADLOCK/DB_UNIQUE_VIOLATION 만) → optimistic @Version 충돌의 정확한 code 매핑은 registry gap(§구현 가이드 2). code 확인: @Version on WorkLogEntity (actually-implemented); pessimistic lock / lock-timeout 코드 NOT FOUND |
| D6 | duplicate command → idempotency branch key scope. retryable write without idempotency forbidden | 동일 idempotency key 재도착 → dedupe(sibling 소유); key 없는 mutating command 의 retryable write → forbidden(본 branch 정책) | 위임: raw/branch-notes/feature-rate-limit-idempotency-contract D2 (key scope = (authenticatedPrincipal, idempotencyKey, useCaseName) triple + tenant), D3 (dedup 저장), D6 (TTL 24h), D7 (in-flight → 409 IDEMPOTENT_IN_FLIGHT), D8 (fingerprint mismatch → 422 IDEMPOTENT_REQUEST_MISMATCH). error-codes.yaml: IDEMPOTENT_IN_FLIGHT(409)·IDEMPOTENT_REQUEST_MISMATCH(422) owner_layer application |
cross-branch-delegation (rate-limit-idempotency 가 key scope/TTL/dedup/in-flight/mismatch 메커니즘 SSOT) |
본 branch 는 "non-idempotent retryable write 금지" 정책만 소유 — idempotency 메커니즘은 sibling SSOT. code: @UseCaseCapability(idempotency=IDEMPOTENT|KEYED|NOT_IDEMPOTENT) enum 존재, KEYED 는 rate-limit merge 전까지 ArchUnit 으로 freeze. 어떤 use case 가 idempotency 선언을 요구하는지는 도메인 결정(§구현 가이드 3) |
| D7 | outbox required for atomic external publish. DB commit then lossy publish 금지 | external publish 필요 use case → outbox; internal-only domain event → outbox 불필요 | 위임: raw/branch-notes/feature-domain-event-outbox-contract D2 (transaction+publish atomicity = outbox default), D4 (SKIP LOCKED leadership), D9 (publisher claim tx = READ_COMMITTED + FOR UPDATE SKIP LOCKED) | internal-cross-reference (outbox 메커니즘 SSOT = domain-event-outbox-contract) |
outbox 메커니즘 (SKIP LOCKED polling vs CDC) 의 근거는 raw/branch-notes/feature-domain-event-outbox-contract Decision Evidence Map 참조. D9 의 claim tx isolation(READ_COMMITTED) 이 본 branch D3 default 와 일치 — cross-vendor 일관성 확인 완료 |
Work Item Contract
각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 기준 작성으로 남아 있으면 이 branch는 완료로 보지 않습니다.
| field | required | rule |
|---|---|---|
| Decision | yes | 구현자가 선택해야 하는 기본값 |
| Allowed | yes | 허용되는 예외와 조건 |
| Forbidden | yes | 절대 금지되는 구현/문서 상태 |
| Required registry update | conditional | error/env/header/log/metric/capability 변경 시 필수 |
| Required contract test | yes | 계약 위반 시 실패해야 하는 테스트 |
| Failure condition | yes | review/build에서 실패로 판정할 상태 |
| Canonical extraction target | yes | wiki/projects 승급 위치 |
판정 기준
| 구분 | 기준 |
|---|---|
| Decision | transaction execution은 application port abstraction으로 통과 |
| Allowed | read-only query는 readOnly mode만 선언 가능. infra implementation은 Spring transaction 사용 가능 |
| Forbidden | application use case의 direct @Transactional, hidden write transaction, idempotency 없는 retryable write |
| Required fields | transaction mode, isolation exception 여부, retryable 여부, idempotency key scope |
| Failure condition | transaction/capability/idempotency 선언 없이 write repository 접근이 가능하면 실패 |
Decisionized Work Items
| item | Decision | Allowed | Forbidden | Required test |
|---|---|---|---|---|
| boundary | application use case via TransactionPort | infra adapter uses Spring tx | direct application @Transactional |
forbidden import test |
| read-only | query mode readOnly |
no transaction for pure in-memory query | write in read-only use case | read-only test |
| lock failures | optimistic conflict vs retryable deadlock/timeout | explicit pessimistic lock | all locks generic 500 | lock mapping test |
| duplicate command | idempotency branch key scope | non-idempotent command explicit conflict | retryable write without idempotency | duplicate write test |
| outbox | required for atomic external publish | internal-only domain event no outbox | DB commit then lossy publish | outbox atomicity test |
| isolation | READ_COMMITTED default | explicit REPEATABLE_READ/SERIALIZABLE for write-heavy | vendor default 묵시 사용 | isolation contract test |
| @Transactional propagation | REQUIRED | 명시된 REQUIRES_NEW (outbox/audit row 분리) | NESTED/NEVER 묵시 사용 | propagation contract test |
구현 가이드
결정 이 "무엇" 이라면 본 §는 "어디에 어떻게" 의 사전 명세. 본 branch 의 고유 소유 결정(D3·D5·D6·D7) 만 in-scope. boundary/template/propagation 메커니즘(D1·D2·D4)은
feature-application-port-usecase-contract가 SSOT 이므로 §구현 가이드에 명세하지 않고 §엣지·의존 + §Audit 에 위임 기록만 남긴다 (R3 OUT_OF_BRANCH_SCOPE).code anchor 는 2026-06-09 ca-tmpl ground-truth grep 으로 확인.
actually-implemented는src/에서 확인된 것, 그 외는planned.
1. Isolation level 선택 메커니즘 (D3 — 본 branch 핵심 소유)
Trace: D3 ←
#PG-ISO-C1C6,#MYSQL-ISO-C1C4. 현재 code:application-core/.../transaction/Isolation.java=READ_COMMITTED단일값(actually-implemented);adapter-persistence/.../transaction/SpringTransactionPort.javaL76 = 3 template 모두ISOLATION_READ_COMMITTEDpin (actually-implemented).
- UNSUPPORTED_IMPL_DECISION: REPEATABLE_READ/SERIALIZABLE 을 어떻게 노출 할지(①
Isolationenum 확장 +TransactionPort.inWrite에 isolation 파라미터 추가, ②@UseCaseCapability(isolation=...)속성 추가, ③ 새 TransactionPort 오버로드) — vendor doc 은 어떤 레벨이 존재/무엇을 보장 하는지만 근거. ca-tmpl 노출 API 모양은 근거 없음. trade-off: capability 속성 = ArchUnit 정적 강제 가능하나 use-case 단위 coarse; 메서드 파라미터 = fine-grained 하나 런타임. 권고 기본값: ② capability 속성 (기존transactionMode와 동일한 정적 강제 경로 재사용).- 변경 파일 후보 (착수 시 헤매지 않도록):
application-core/.../transaction/Isolation.java(enum 확장 — 현재READ_COMMITTED단일 상수),adapter-persistence/.../transaction/SpringTransactionPort.java(현재 3개TransactionTemplate이ISOLATION_READ_COMMITTED고정 pin → isolation 별 라우팅 필요),application-core/.../capability/UseCaseCapability.java(② 채택 시 속성 추가) + 대응 ArchUnit rule. 이 abstraction 은 app-port branch 가 SSOT 이므로 REPEATABLE_READ/SERIALIZABLE 실제 노출은feature-application-port-usecase-contract와 공동 PR 필요 — 그 전까지 호출 경로는planned.
| level | 언제 | Postgres 시맨틱 (claim) | MySQL InnoDB 시맨틱 (claim) | ca-tmpl 상태 |
|---|---|---|---|---|
| READ_COMMITTED | default (모든 use case) | statement 시작 시점 snapshot (#PG-ISO-C2) |
매 consistent read 마다 fresh snapshot (#MYSQL-ISO-C4) |
actually-implemented (enum + pin) |
| REPEATABLE_READ | write-heavy / read 일관성 필요, 명시 | tx 시작 snapshot 고정; write 충돌 시 serialize 에러 (#PG-ISO-C3,#PG-ISO-C4) |
tx 첫 read snapshot 재사용; locking read 시 gap/next-key lock (#MYSQL-ISO-C2,#MYSQL-ISO-C3) |
actually-implemented (enum 노출, 2026-06-09); call-path 라우팅은 planned (app-port 공동 PR) |
| SERIALIZABLE | 최강 격리, 명시 | SSI — anomaly 시 serialization failure (#PG-ISO-C5) |
autocommit=0 시 plain SELECT→FOR SHARE 묵시 변환 (#MYSQL-ISO-C6) |
actually-implemented (enum 노출, 2026-06-09); call-path 라우팅은 planned |
| READ_UNCOMMITTED | forbidden | 내부적으로 READ COMMITTED 로 매핑 (#PG-ISO-C6) |
(해당) | forbidden (enum 제외) |
- 핵심 근거: Postgres 기본 = READ COMMITTED(
#PG-ISO-C1), MySQL InnoDB 기본 = REPEATABLE READ(#MYSQL-ISO-C1) → 기본값이 vendor 마다 다름 → 묵시 vendor default 위임 시 동일 코드가 DB 따라 다른 격리 → 명시 pin 강제. 이것이 D3 forbidden 정책의 근거.
2. Lock-failure 분류 정책 (D5 — persistence baseline 소비)
Trace: D5 ← raw/branch-notes/feature-persistence-failure-baseline D6 (
#SDA-EX-C5) +#PG-ISO-C4. 본 branch 는 분류 정책 만 소유; exception→error-code 매핑 은 persistence baseline 소유.
- UNSUPPORTED_IMPL_DECISION: optimistic
@Version충돌의 정확한 error code — error-codes.yaml 에 optimistic 전용 code 부재(DB_SERIALIZATION_FAILURE/DB_DEADLOCK/DB_UNIQUE_VIOLATION만, owner=persistence-baseline). 신규OPTIMISTIC_LOCK_CONFLICTcode 추가 vs 기존 generic CONFLICT 재사용 — registry 결정이며 owner_branch=persistence-baseline 이므로 본 branch 는 정책 요구만, code 신설은 persistence baseline 으로 이관(R3).
| 실패 유형 | 정책 (본 branch 소유) | error-code 매핑 (persistence baseline 소유) | code 확인 |
|---|---|---|---|
optimistic lock (@Version) |
409, client non-retryable | (registry gap — 신규 제안 필요) | @Version on WorkLogEntity = actually-implemented. ⚠️ JPA @Version flush 시 OptimisticLockingFailureException 변환 경로는 persistence-baseline D6 #SDA-EX-C7(sql-error-codes.xml 매핑) needs-confirmation 해소 전까지 planned — integration test 로만 검증 가능 |
| deadlock | retryable by policy | 40P01→DB_DEADLOCK (CONFLICT, 409, retryable) |
error-codes.yaml = actually-implemented |
| serialization failure | retryable by policy | 40001→DB_SERIALIZATION_FAILURE (CONFLICT, retryable) |
error-codes.yaml = actually-implemented |
| unique violation | 충돌 (non-retryable) | 23505→DB_UNIQUE_VIOLATION (CONFLICT, non-retryable) |
error-codes.yaml = actually-implemented |
| pessimistic lock / lock-timeout | 명시 선언 시만 | (코드/registry 부재) | planned (NOT FOUND in src/) |
| forbidden | 모든 lock 실패를 generic 500 으로 뭉갬 | — | — |
3. Idempotency 요구 정책 (D6 — rate-limit-idempotency 소비)
Trace: D6 ← raw/branch-notes/feature-rate-limit-idempotency-contract D2/D3/D6/D7/D8. 본 branch 는 "non-idempotent retryable write 금지" 정책만 소유; key scope/TTL/dedup/in-flight/mismatch 메커니즘은 rate-limit branch SSOT.
- UNSUPPORTED_IMPL_DECISION: 어떤 use case 가 idempotency 선언을 요구하는지 — 도메인 결정이며 ca-tmpl skeleton 이 prescribe 불가. 신규 use case 작성 시
@UseCaseCapability(idempotency=...)선언을 ArchUnit 으로 강제하되 값 선택은 도메인 작성자. trade-off: 전 use case 강제 선언 = 누락 방지하나 NOT_IDEMPOTENT 보일러플레이트; 옵트인 = 가볍지만 누락 위험. 권고: 전 use case 선언 강제(기존inbound_port_implementations_declare_capabilityrule 과 일치).
- code 확인:
@UseCaseCapability(idempotency = IDEMPOTENT | KEYED | NOT_IDEMPOTENT)enum =actually-implemented.KEYED는 rate-limit merge 전까지 ArchUnitinbound_port_implementations_do_not_declare_keyed_idempotency로 freeze (planned/의도적 차단). - 본 branch 책임: "retryable 로 분류된 write use case 가 idempotency 선언 없이 재시도 경로에 노출되면 실패" 계약 test (아래 §테스트 계약).
4. Outbox trigger 정책 (D7 — domain-event-outbox 소비)
Trace: D7 ← raw/branch-notes/feature-domain-event-outbox-contract D2/D9. 본 branch 는 "external publish 는 outbox 경유, DB commit 후 lossy publish 금지" trigger 정책만 소유; outbox 메커니즘(SKIP LOCKED/CDC)은 outbox branch SSOT.
- outbox publisher claim transaction 이 READ_COMMITTED(outbox D9) 를 쓰므로 본 branch D3 default 와 일치 — isolation 일관성 확인됨.
planned— outbox 메커니즘 미구현(feature-domain-event-outbox-contractstatus=raw).
엣지·실패·의존
R4(깊이 게이트) 캡처용. 정상 경로 외 구현 중 부딪힐 실패/엣지/다른 계약 의존.
- 실패·엣지 경로:
- READ_COMMITTED 하 read-then-write use case → non-repeatable read/phantom 으로 lost update 위험(
#PG-ISO-C2,#MYSQL-ISO-C4). 기대 동작: 명시 REPEATABLE_READ 선언 또는SELECT ... FOR UPDATE(pessimistic) 로 보호. skeleton 은 위험만 문서화, 도메인 use case 가 선택. - REPEATABLE_READ/SERIALIZABLE 선택 시 serialization failure(Postgres "could not serialize access",
#PG-ISO-C4/#PG-ISO-C5) → retryable. 기대 동작: 호출측 retry 정책 필요(현재 미구현planned). - MySQL REPEATABLE_READ locking read 의 gap/next-key lock(
#MYSQL-ISO-C3) → deadlock 빈도 증가. 기대 동작: D5 deadlock 분류(retryable) 로 흡수. inNew(REQUIRES_NEW) 를 loop 내 호출 → connection pool 고갈(app-port D12 anti-pattern). 기대 동작: ArchUnit/리뷰로 차단(app-port 소유).- optimistic
@Version충돌이 generic 500 으로 뭉개짐 → D5 위반, 계약 test 실패. - REPEATABLE_READ/SERIALIZABLE serialization failure 재시도 ↔ D6 idempotency 충돌: serialization failure(
#PG-ISO-C4) 의 retry 가 idempotency key 없는 mutating command 에서 발화하면 D6 "non-idempotent retryable write forbidden" 에 해당. 기대 동작: KEYED idempotency 선언된 use case 에 한해 재시도 허용 —NOT_IDEMPOTENTuse case 의 REPEATABLE_READ/SERIALIZABLE 선언 + 자동 retry 는 사실상 forbidden.
- READ_COMMITTED 하 read-then-write use case → non-repeatable read/phantom 으로 lost update 위험(
- 다른 계약 의존:
- raw/branch-notes/feature-application-port-usecase-contract 의
D3(TransactionPort abstraction)·D11(callback signature)·D12(inNewREQUIRES_NEW + pool sizing) 에 의존 — 본 branch 는 그 위에 isolation 정책만 추가. 그 계약이 바뀌면 본 branch D3/D4 영향. - raw/branch-notes/feature-persistence-failure-baseline 의
D6(40001/40P01→error-code, optimistic#SDA-EX-C5) 에 의존 — D5 가 exception→code 매핑 consume. 매핑이 바뀌면 D5 분류 표 영향. - raw/branch-notes/feature-rate-limit-idempotency-contract 의
D2(key scope)·D7(in-flight 409)·D8(mismatch 422) 에 의존 — D6 가 idempotency 메커니즘 consume. - raw/branch-notes/feature-domain-event-outbox-contract 의
D2/D9(outbox + claim tx READ_COMMITTED) 에 의존 — D7 가 outbox trigger consume. - raw/branch-notes/feature-repository-access-permission-contract 의
@UseCaseCapability(transactionMode/repositoryAccess)에 의존 — read-only(readOnly) + write repository 정책 consume.
- raw/branch-notes/feature-application-port-usecase-contract 의
테스트 계약
- write use case가 transaction 없이 repository write를 수행하면 실패.
- application use case가 Spring transaction annotation을 직접 import하면 실패.
- read-only use case가 write repository를 사용하면 실패.
- optimistic lock 실패가 internal error로 뭉개지면 실패.
- idempotent command 재시도 시 중복 row/write가 발생하면 실패.
- TransactionPort 사용 use case에서 isolation을 명시하지 않은 채 vendor default에 위임하면 실패.
- application use case의 @Transactional propagation이 NESTED 또는 NEVER로 명시되면 실패.
검증해야 할 주장
공식 문서 인용이 결정의 근거이지만, ca-tmpl 자체 구현에서의 동작은 별도 검증이 필요한 주장.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
TransactionPort adapter 가 Spring bean 외부에서 호출되어 self-invocation 함정 (#AT-TX-C5) 회피 |
#AT-TX-C5 는 proxy mode 의 self-invocation 함정만 명시 — adapter call path 가 실제로 외부 호출인지 별도 보장 필요 |
adapter bean 호출 경로 trace + Spring AOP proxy 적용 여부 단언 integration test | planned |
application 패키지가 org.springframework.transaction.annotation.Transactional 또는 org.springframework.transaction.support.TransactionTemplate 을 import 하지 않음 |
cited raw 는 framework 의 권고만 보장 — ca-tmpl 내부 강제는 별도. code 확인: CleanArchitectureTest.application_does_not_use_spring_transactional_annotation (L167-175) = actually-implemented (app-port D3 소유) |
ArchUnit rule 존재 확인 완료; 의도적 위반 fixture 로 fail 검출은 ArchitectureViolationFixtureTest 에서 확인 |
locally-verified (app-port branch) |
isolation level READ_COMMITTED 가 PostgreSQL 과 MySQL InnoDB 에서 ca-tmpl 이 가정한 시맨틱과 동일 동작 (D3) |
#PG-ISO-C1 vs InnoDB REPEATABLE READ #MYSQL-ISO-C1). ca-tmpl 은 명시 pin 으로 vendor 차이 무력화 |
code 확인: SpringTransactionPort 가 ISOLATION_READ_COMMITTED pin (actually-implemented). 실 DB 에서 READ_COMMITTED 시맨틱(non-repeatable read 허용) 재현은 Testcontainers integration test 로 검증 필요 |
needs-confirmation (vendor 시맨틱 verified, ca-tmpl 실 DB 동작 미검증) |
| propagation REQUIRED 가 모든 ca-tmpl use case 의 default 시맨틱과 일치 (D4) | #SPRING-TX-MGR-C3 (PROPAGATION_REQUIRED default verbatim) + spring-tx-propagation-required-new-nested-official 수집. code: inWrite/inRead=REQUIRED (actually-implemented) |
SpringTransactionPortTest 가 모드별 propagation 설정값 단언(app-port branch, locally-verified) |
locally-verified (app-port branch) |
optimistic lock 실패가 application use case 에서 OptimisticLockingFailureException (또는 동등) 으로 식별되어 409 매핑 (D5) |
cited transaction raw 범위 밖 — persistence raw 의 #SDA-EX-C5 와 cross-reference. error-codes.yaml 에 optimistic 전용 code 부재(registry gap) |
integration test: @Version 충돌 시나리오에서 OptimisticLockingFailureException 발생 + handler 가 409 매핑 단언 |
planned |
| duplicate command idempotency 검증 (D6: 동일 idempotency key 로 retry 시 중복 row/write 없음) | contract test: 동일 idempotency key 로 5회 retry → DB row 1개만 생성 + 응답 동일 단언 (rate-limit branch 구현 후) | planned |
|
ArchUnit forbidden import test (application 의 @Transactional direct annotation) 가 실제 위반 검출 |
rule 정의 자체는 명확하지만 실제 적용 미검증. code 확인: rule + violation fixture 존재 | ArchitectureViolationFixtureTest 가 의도된 위반 fixture 를 잡아냄 (app-port branch) |
locally-verified (app-port branch) |
| Vassilis Soum / UNIL TransactionPort 참고 구현 (D1 의 company-case-study) 이 ca-tmpl 환경에서 동작 보장 | company-case-study 는 한 조직의 사례 — 우리 환경에서의 적합성 별도 검증 필요. code 확인: TransactionPort + SpringTransactionPort 실재(actually-implemented, app-port branch) |
모든 use case 가 TransactionPort.inWrite/inRead/inNew(...) 경유 — SpringTransactionPortTest 통과 (app-port branch, locally-verified) |
locally-verified (app-port branch) |
관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
/coverage가 채우는 생성물 — 손으로 유지하지 않는다. governing 문서(frontmattergoverning_docs:wiki/projects/ca-tmpl/transaction-boundary-abstraction)가 요구하는 관심사를 이 브랜치가 빠짐없이 덮는지의 결과. 기준:rules/coverage-gate.md. 상태:covered-here(이 브랜치 결정) /delegated(다른 owner 브랜치) /missing(아무도 안 맡음 → Blocking).
| 관심사 | 상태 | owner | 심각도 | 근거 |
|---|---|---|---|---|
C1: TransactionPort abstraction (inWrite/inRead/inNew) + @Transactional 금지 ArchUnit rule |
delegated | raw/branch-notes/feature-application-port-usecase-contract (D3/D11/D12) | OK | D1 Open Risk OWNERSHIP + §엣지·의존 링크 |
C2: SpringTransactionPort 내부 TransactionTemplate 사용 |
delegated | raw/branch-notes/feature-application-port-usecase-contract (D3) | OK | D2 Open Risk OWNERSHIP + §엣지·의존 링크 |
C3: Isolation enum — READ_COMMITTED pin, READ_UNCOMMITTED forbidden |
covered-here | — | — | D3 + §구현가이드 1; Isolation.java actually-implemented (code) |
| C4: Propagation 정책 — REQUIRED default, REQUIRES_NEW 조건, NESTED/NEVER forbidden | delegated | raw/branch-notes/feature-application-port-usecase-contract (D3/D12) | OK | D4 Open Risk OWNERSHIP; §엣지·의존 링크 |
| C5: Isolation 선택 정책 — 명시 pin 강제, vendor default forbidden, REPEATABLE_READ/SERIALIZABLE 경로 | covered-here | — | — | D3 + §구현가이드 1 (UNSUPPORTED_IMPL_DECISION 3옵션 기록) |
| C6: Lock-failure 분류 정책 — optimistic 409, deadlock/serialization retryable, generic-500 forbidden | covered-here | — | — | D5 + §구현가이드 2; @Version WorkLogEntity actually-implemented (code) |
C7: exception→error-code 매핑 (40001/40P01/optimistic @Version) |
delegated | raw/branch-notes/feature-persistence-failure-baseline (D6) | OK | D5 위임 명시; error-codes.yaml DB_SERIALIZATION_FAILURE/DB_DEADLOCK actually-implemented (code) |
| C8: Idempotency 요구 정책 — non-idempotent retryable write 금지 | covered-here | — | — | D6 고유 소유; @UseCaseCapability(idempotency=...) actually-implemented (code) |
| C9: Idempotency 메커니즘 — key scope/TTL/dedup/in-flight/mismatch | delegated | raw/branch-notes/feature-rate-limit-idempotency-contract (D2/D3/D6/D7/D8) | OK | D6 위임 명시; IdempotencyExecutor/IdempotencyStoreAdapter actually-implemented (code) |
| C10: Outbox trigger 정책 — external publish outbox 경유, lossy publish 금지 | covered-here | — | — | D7 고유 소유 |
| C11: Outbox 메커니즘 — SKIP LOCKED, at-least-once, publisher leadership | delegated | raw/branch-notes/feature-domain-event-outbox-contract (D2/D4/D9) | OK | D7 위임 명시; §엣지·의존 링크 |
C12: @UseCaseCapability(transactionMode/repositoryAccess) 어휘 + coherence ArchUnit rule |
delegated | raw/branch-notes/feature-repository-access-permission-contract (D2/D11/D12) | OK | §엣지·의존 링크; capabilities.yaml TRANSACTION_REQUIRED owner 코드 확인 |
Audit & Findings (2026-06-09 ground-truth 대조)
ca-tmpl
src/+docs/registries/+ sibling branch-notes 대조로 발견한 drift/ownership. 사용자 작성 결정 영역은 자동 rewrite 하지 않고 정합 권고만 기록(CLAUDE.md §2 ground-truth 절차).
- DRIFT-1 —
TransactionalUseCaseRunner미존재: D1·§결정사항·(이전)§외부근거 가TransactionalUseCaseRunner를 실행 경로로 언급하나, ca-tmplsrc/grep + governing doctransaction-boundary-abstractionL79 ("검토 후 미채택... 코드에 존재하지 않는다") 로 미채택 대안임을 확인. 권고: 실행 경로 표현에서 제거하고 "미채택 대안"으로만 유지. (§외부근거 대안 5 로 정정 기록함; D1 본문은 사용자 결정이라 verbatim 보존 + 본 finding 으로 정합 표시.) - DRIFT-2 — isolation "양쪽 동일 의미" 부정확: D3 의 "PostgreSQL/MySQL 양쪽 동일 의미" 는 vendor 공식과 불일치 — 기본값이 다름(Postgres=READ COMMITTED
#PG-ISO-C1, MySQL InnoDB=REPEATABLE READ#MYSQL-ISO-C1). 정확한 명제: "명시 pin 하면 양쪽에서 READ COMMITTED 동작을 강제할 수 있고, 묵시 default 는 vendor 마다 달라 위험". D3 row/§결정사항 보강으로 정정 반영. - OWNERSHIP-1 — TransactionPort 계약은 app-port branch 소유: TransactionPort abstraction +
@Transactional금지 ArchUnit rule + propagation 모드는 raw/branch-notes/feature-application-port-usecase-contract (D3/D11/D12) 가 SSOT 이며 이미 구현·로컬검증 완료(CleanArchitectureTest L167-175 주석이 "raw/branch-notes/feature-application-port-usecase-contract D3" 로 귀속). 본 branch D1/D2/D4 는 소비자 재진술 — §구현 가이드에서 OUT_OF_BRANCH_SCOPE 로 정제(메커니즘 명세는 app-port 로 위임, 본 branch 는 isolation/lock/idempotency/outbox 정책만). - REGISTRY-GAP-1 — optimistic-lock 전용 error code 부재: error-codes.yaml 에
DB_SERIALIZATION_FAILURE/DB_DEADLOCK/DB_UNIQUE_VIOLATION만 존재, optimistic@Version충돌 전용 code 없음. D5 의 "optimistic→409" 매핑의 정확한 code 는 owner_branch=feature-persistence-failure-baseline결정 영역 → 그 branch 로 신규 제안 이관 권고.
구현 진행 (2026-06-09 — Phase C2, 본 branch 고유 소유분)
위임분(D1/D2/D4 = app-port, C7 = persistence-baseline, C9 = rate-limit, C11 = outbox, C12 = repo-access, REGISTRY-GAP-1)은 구현 제외 — sibling SSOT 소유. 본 branch 고유 소유(C3/C5 isolation, C6 lock-policy)만 ca-tmpl 코드에 반영.
- C3/C5 (D3) —
actually-implemented:application-core/.../transaction/Isolation.javaenum 을READ_COMMITTED단일값 →READ_COMMITTED/REPEATABLE_READ/SERIALIZABLE3값으로 확장(app-portIsolation.javajavadoc 이 본 contract 로 위임한 항목).READ_UNCOMMITTED는 미선언(forbidden) 유지. call-path 라우팅(TransactionPort 시그니처/SpringTransactionPort isolation 별 라우팅)은 app-port 공동 PR 필요 →planned유지, vocabulary 만 ship.- test:
IsolationTest(app-core) — 3값 존재 +READ_UNCOMMITTED미선언 검증. - test:
SpringTransactionPortTest.every_mode_pins_an_explicit_isolation_never_the_vendor_default— 3 template 모두ISOLATION_DEFAULT아님(vendor default forbidden, D3 핵심 정책) 검증.
- test:
- C6 (D5) —
actually-implemented(정책 test):app-bootstrap/.../contract/LockFailureClassificationContractTest— deadlock/serialization = retryable CONFLICT, unique = non-retryable CONFLICT, DB conflict code 어느 것도 generic INTERNAL/500 아님(D5 forbidden "all locks generic 500") 검증 + REGISTRY-GAP-1(optimistic 전용 code 부재) 을 known-absent 로 pin. exception→code 매핑은 persistence-baseline 소유(소비만). - 검증:
:application-core:test,:adapter-persistence:test,:app-bootstrap:test(ArchUnit 포함),verifyCleanArchitectureDependencies전부 green (2026-06-09). - 제외(미구현, 의도적): REPEATABLE_READ/SERIALIZABLE call-path 라우팅(app-port 공동 PR), optimistic 전용 error code 신설(persistence-baseline), C8 non-idempotent-retryable-write 자동 금지(retry infra
planned), C10 outbox trigger(outbox branchraw).
마주친 문제
- 아직 없음.
묶음
- raw/company-tech-blogs/cache-woowahan-after-commit-invalidation
- raw/company-tech-blogs/custom-transaction-interceptor-catnipcoder
- raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement
- raw/company-tech-blogs/transaction-port-clean-ddd-spring-medium
- raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme
- raw/company-tech-blogs/woowahan-hexagonal-multimodule
- raw/official-docs/at-transactional-spring-official
- raw/official-docs/functional-tx-arrow-kt-resource-docs
- raw/official-docs/mysql-innodb-transaction-isolation-official
- raw/official-docs/postgres-transaction-isolation-official
- raw/official-docs/spring-tx-management-reference
- raw/official-docs/transaction-template-spring-official
본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
근거 자료
- raw/official-docs/postgres-transaction-isolation-official — PostgreSQL READ COMMITTED / REPEATABLE READ / SERIALIZABLE 보장 범위 vendor SSOT (D3 근거 — statement-level vs transaction-level snapshot, 직렬화 실패 에러)
- raw/official-docs/mysql-innodb-transaction-isolation-official — MySQL InnoDB vendor default (REPEATABLE READ) + READ COMMITTED / REPEATABLE READ consistent-read / locking-read 시맨틱 SSOT (D3 UNSUPPORTED_DECISION 해소)
오류 기록 (본 feature 작업 중 발생)
- (없음 — 2026-06-09 C3/C5/C6 구현 시 빌드/테스트 에러 없음.
raw/errors파생 노트 not needed.)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (후보, 미정제 —
raw/interviews파생 노트 not needed 현 시점) "isolation default 를 코드에서 명시 pin 하는 이유는?" → vendor 기본값 상이(Postgres READ COMMITTED vs MySQL InnoDB REPEATABLE READ), 묵시 위임 시 동일 코드가 DB 따라 다른 격리.
관련 일일 노트
이 브랜치를 작업한 날짜들. 양방향 nav 유지.
- 2026-06-09 — Phase C2 본 branch 고유 소유분(C3/C5 isolation enum + vendor-default-forbidden test, C6 lock-failure 분류 정책 test) 구현. 위임분 제외. 전 verification green. 상세 §구현 진행 (2026-06-09).
완료 후 정리
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:locally-verified항목:prod-verified항목:
- 추출하지 않을 항목 (planned / documented-only / abandoned):