6.5 KiB
6.5 KiB
title, source_type, status, related_branches, related_projects, tags, created, status_label
| title | source_type | status | related_branches | related_projects | tags | created | status_label | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| error / idempotency-column-definition-base-check-failure-2026-07-15 | error-note | raw |
|
|
|
2026-07-15 | open |
error: idempotency-column-definition-base-check-failure-2026-07-15
Layer:
raw/errors/— N+1 replay branch의 최종check에서 남은 단일 architecture failure를, 랩 기능 실패와 분리해 보존한다. 원본은 raw에 영구 보관한다.
Parent / 부모
- raw/branch-notes/experiment-nplus1-feed-api-replay — 11개 N+1 replay checkpoint의 최종 검증에서 발견했으며, 수정 소유권은 replay 범위 밖의 persistence base에 있다.
증상 / Symptom
- 에러 메시지 (ArchUnit condition이 만드는 원문):
Field dev.caskeleton.adapter.outbound.persistence.idempotency.entity.IdempotencyRecordEntity.requestHash pins vendor SQL columnDefinition='char(64)' in adapter:outbound:persistence-jpa; move the physical type to the vendor migration. - 발생 컨텍스트:
lab/nplus1-api-replay의 최종cd src && ./gradlew check. - 발생 시점: 2026-07-15 (최종 검증; 시각은 별도 캡처하지 않음).
- 발생 환경: local Gradle /
app-bootstrap의CleanArchitectureTest. - 재현 가능 여부:
always— 해당@Column(columnDefinition = "char(64)")가 non-PostgreSQL persistence package에 남아 있는 한. - 범위 구분: 이는 L1의 lazy highlights 재현이나 L12의 CQRS-lite read-model 기능 실패가 아니다. final
check에서 남은 base architecture failure 하나이며, L1/L12 replay 변경이 이 entity를 수정하거나 도입하지 않았다.
재현 절차 / Reproduction
lab/nplus1-api-replay의nplus1-replay-l12tag에서cd src && ./gradlew check를 실행한다.CleanArchitectureTest.PERSISTENCE_RDBMS_ENTITIES_DO_NOT_PIN_VENDOR_COLUMN_DEFINITIONS가IdempotencyRecordEntity.requestHash의 non-blankcolumnDefinition을 검사한다.- 기대 결과: non-PostgreSQL persistence entity에는 vendor SQL
columnDefinition문자열이 없다. - 실제 결과:
request_hash에columnDefinition = "char(64)"가 있어 위 Architecture violation으로check가 실패한다.
조사 단계 / Investigation log
- 2026-07-15 — final
./gradlew check의 잔여 failure가 하나뿐임을 raw/branch-notes/experiment-nplus1-feed-api-replay의Full check의 기준선 실패기록으로 확인했다. - 2026-07-15 —
git show 6f0b0d6:src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/idempotency/entity/IdempotencyRecordEntity.java에서requestHash의@Column(... columnDefinition = "char(64)")를 확인했다. - 2026-07-15 —
git diff --exit-code 6f0b0d6..nplus1-replay-l12 -- src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/idempotency/entity/IdempotencyRecordEntity.java가 변경 없음으로 끝났다. base와 replay tag의 해당 file blob은 모두4b0f51783a9db312b09b181c0245734599f7ced7이다. - 2026-07-15 —
CleanArchitectureTest의 rule은dev.caskeleton.adapter.outbound.persistence.postgresql..밖의@Columnfield에 non-blankcolumnDefinition이 있으면 위 원문을 생성하도록 확인했다. 따라서 failure는 replay의 L1/L12 기능을 대상으로 하지 않는다.
근본 원인 / Root cause
- 직접 원인:
IdempotencyRecordEntity.requestHash가 vendor-neutral persistence package 안에서@Column(columnDefinition = "char(64)")로 물리 SQL type을 고정했다. - 근본 원인: RDBMS base entity의 portable mapping과 PostgreSQL 물리 schema 소유권을 분리하는 architecture rule이 이미 base commit
6f0b0d6의 기존 entity 선언과 충돌한다. - 트리거 조건: full
check가CleanArchitectureTest를 실행해 non-PostgreSQL package의 모든@Columnfield를 검사할 때.
Sources / 근거
- raw/branch-notes/experiment-nplus1-feed-api-replay —
Full check의 기준선 실패가 finalcheck의 유일한 잔여 failure와 replay scope 밖이라는 판단을 기록한다. - local code evidence:
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureTest.java의PERSISTENCE_RDBMS_ENTITIES_DO_NOT_PIN_VENDOR_COLUMN_DEFINITIONS와notDeclareColumnDefinition()— violation 조건과 원문을 보유한다. - local Git evidence: base
6f0b0d6와nplus1-replay-l12의IdempotencyRecordEntity.javablob SHA가 동일하다. 이는 replay history가 해당 선언을 건드리지 않았다는 근거다.
해결 / Resolution
- 적용한 조치: replay branch에서는 수정하지 않았다.
nplus1-replay-l12가 학습 checkpoint history를 보존해야 하므로, 이 failure의 소유권을 별도 persistence base-fix 작업으로 분리했다. - 권고 조치 및 소유권: persistence base owner가 entity의 non-empty
columnDefinition을 제거하고,char(64)물리 type이 PostgreSQL vendor Flyway migration에만 남는지 확인한다. portable@JdbcTypeCode사용 여부는 기존 mapping/integration test와 함께 검토한다. - 검증 방법: base-fix branch에서
cd src && ./gradlew check를 다시 실행하고, idempotency migration 및 persistence integration test로 schema/mapping을 확인한다. - 잔여 위험 / 후속 작업: migration이 실제 physical type을 충분히 소유하지 않으면 entity annotation만 제거한 뒤 schema와 runtime mapping이 어긋날 수 있다. base-fix가 완료되기 전에는 replay branch의 full
check를 green이라고 주장할 수 없다.
회고 / Lessons
- 빨리 감지하는 신호:
pins vendor SQL columnDefinition=또는move the physical type to the vendor migration메시지가 보이면, 랩 변경 파일부터 추측하지 말고 baseline blob과 replay diff를 먼저 비교한다. - 예방 체크리스트 항목 후보: vendor-neutral JPA entity에
@Column(columnDefinition = ...)를 추가하거나 유지할 때는 architecture test와 vendor migration의 schema ownership을 같은 change에서 확인한다. - wiki로 끌어올릴 가치가 있는 일반화된 교훈: full-suite failure를 feature regression으로 귀속하기 전에 base/replay diff와 architecture-rule 대상 범위를 대조하는 방법.
Related / 관련
- raw/branch-notes/experiment-nplus1-feed-api-replay — focused lab suite, L1/L12 replay 검증, 그리고 이 base failure의 범위 구분을 함께 보존한다.