11 KiB
title, source_type, status, confidence, tags, related_projects, last_reviewed
| title | source_type | status | confidence | tags | related_projects | last_reviewed | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Data Layer Baseline (Persistence + Cache + Outbound HTTP) | llm-generated | draft | medium |
|
|
2026-05-22 |
Data Layer Baseline (Persistence + Cache + Outbound HTTP)
Layer:
wiki/concepts/— Phase E Group G-C 합성. 3개 sub-topic(Persistence failure / Cache consistency / Outbound HTTP)을 하나의 baseline canonical로 묶음. 프로젝트 적용 사실은wiki/projects/에 별도 작성하고 본 문서에서는 링크만 둠.
Summary
Data layer baseline은 세 가지 축으로 구성된다.
- Persistence: SQLState 매트릭스로 DB 실패를 분류하고, Spring
DataAccessException계층 위에 매핑하여PERSISTENCE / CONFLICT / TRANSIENT_DEPENDENCY카테고리를 만든다. OSIV는 off가 기본. - Cache: cache-aside default + Caffeine local lock(single-instance) + Redisson
RLockdistributed mutex(multi-instance HPA) + after-commit invalidation + eventual consistency window 5초. - Outbound HTTP: Spring RestClient를 baseline으로 두고, retry/circuit breaker는 Resilience4j로 일원화. timeout default = connect 2s / read 5s / global 10s.
Standard (공식 정의)
Persistence — SQLState + Spring DAO hierarchy
- SQLState (ISO/IEC 9075): 5-char code로 DB 오류를 표준 분류.
08*= connection exception,40001= serialization failure,40P01= deadlock(Postgres),23xxx= integrity constraint,57014= query canceled. - Spring
DataAccessExceptionhierarchy:TransientDataAccessException/NonTransientDataAccessException/RecoverableDataAccessException로 retryable/non-retryable 1차 분리. JPAPersistenceException은JpaSystemException으로 흡수. - OSIV (Open Session In View): Hibernate session을 view rendering까지 열어두는 패턴. Vlad Mihalcea가 anti-pattern으로 명시했고 Spring Boot는 활성화 시 startup WARN 로그를 출력. 운영 baseline은 off.
- HikariCP pool sizing: 공식 wiki는
connections = ((core_count * 2) + effective_spindle_count)공식과 단일 small pool 권장. pool wait p99 / pool exhaustion이 1차 alert 지표.
Cache — cache-aside + stampede control
- Cache-aside (Microsoft Cloud Design Patterns / AWS ElastiCache): application이 cache miss 시 DB 조회 → cache 채움. invalidation도 application 책임. write-through는 cache layer가 sync 책임, write-behind는 async, read-through는 cache layer가 loader를 안다. 책임 위치가 다름.
- Caffeine
AsyncLoadingCache/@Cacheable(sync = true): 동일 key 동시 miss를 단일 loader 호출로 직렬화 (in-process stampede 방지). - Redisson
RLock: Redis 기반 reentrant lock + watchdog lease extension. Kleppmann의 Redlock 비판을 회피하기 위해 단일 master 기반 RLock + fence token 사용. - after-commit invalidation: Spring
TransactionSynchronizationManager.registerSynchronization의afterCommit()hook에서만 cache mutation 수행. tx rollback 시 stale write 차단.
Outbound HTTP — RestClient + Resilience4j
- Spring RestClient (6.1+):
RestTemplate의 fluent 후속 API. RestTemplate은 Spring 공식 maintenance-only 상태로 신규 기능 추가 없음. - Resilience4j: Netflix Hystrix의 사실상 후속. Hystrix는 2018년 maintenance mode 진입. Retry / CircuitBreaker / TimeLimiter / Bulkhead / RateLimiter를 functional decorator로 제공.
- Circuit breaker 상태:
CLOSED→OPEN(failure rate threshold 초과) →HALF_OPEN(probe) →CLOSED복귀. Micrometer로 state transition을 metric으로 노출. - Timeout 계층: connect timeout(소켓 연결) < read timeout(응답 첫 바이트 대기) < global call timeout(전체 호출). 셋 중 하나라도 미설정이면 무한 대기 위험.
한계 / 주의점
Persistence
- SQLState 9-row matrix의 vendor-specific row(PostgreSQL
23505,40P01등)는 DB 변경 시 재검증 필요. MySQL은40001만 공유하고40P01대신 다른 코드를 사용. - OSIV off는 lazy loading exception을 presentation까지 새지 않게 막아주지만, application 경계에서 명시적 fetch 전략(
@EntityGraph, fetch join, DTO projection)을 강제한다. 익숙하지 않은 팀은 운영 부담이 늘 수 있음. - R2DBC reactive는 throughput 우위가 있으나 JPA tooling을 포기해야 한다. baseline은 JPA blocking으로 고정한 trade-off의 반대편.
Cache
- cache-aside의 eventual consistency window가 5초로 잡혀 있어 strict consistency가 요구되는 use case(잔액, 인증, idempotency 검증)에는 부적합. 해당 use case는 cache bypass를 명시.
- Caffeine local cache + Redisson 분산 mutex 조합은 노드 간 sync lag이 존재. 한 노드가 invalidation을 발행한 뒤 다른 노드의 local cache가 비워질 때까지 lag 발생.
- Redisson
RLock도 Kleppmann의 분산 lock 비판에서 완전히 자유롭지 않다. 정확한 fencing을 요구하는 경우 token + DB-level optimistic lock 병행이 필요. - negative cache(존재하지 않는 row, TTL 60s)는 invalidation 채널 적용 대상에서 제외 — 의도된 분리이지만 row가 실제로 생성된 직후 60초간 stale empty 응답이 나갈 수 있음.
Outbound HTTP
- RestClient는 Spring 6.1+ 한정. 기존 RestTemplate 코드는 마이그레이션 비용이 따른다.
- WebClient는 reactor event-loop 위에서 동작하므로 MVC(servlet) baseline에 강제 도입하면 blocking risk가 있다. baseline에서는 extension 문서로 분리.
- OpenFeign은 declarative interface로 편리하지만 Spring Cloud 의존이 붙는다. Spring 6.1+
@HttpExchange가 framework-level 대안. - Stripe engineering blog는 retry default-on을 옹호하지만 이는 idempotency-key 헤더 보장이 전제. 일반 API에 default-on retry를 적용하면 비-idempotent endpoint의 중복 write 위험이 생긴다.
- Resilience4j는 Spring Boot starter 통합이 매끄럽지만, Spring 외 환경(plain Java, Vert.x 등)에서는 verbose한 functional decorator 작성이 필요. "vendor-neutral"로 단언하기에는 일부 마찰이 있음.
Project Application
-
wiki/projects/ca-tmpl/data-layer-persistence-cache-outbound — ca-tmpl 의사결정 기록 (현재
documented-only, Phase C2 미진입). 실제 구현 여부는 project 문서 참조. ca-skeleton operational contract와 owning branch-notes: -
raw/branch-notes/feature-persistence-failure-baseline — SQLState 9-row matrix, Hikari alert threshold, OSIV off 결정
-
raw/branch-notes/feature-cache-consistency-contract — cache-aside default, Caffeine + Redisson, after-commit invalidation, 5s window
-
raw/branch-notes/feature-outbound-http-client-baseline — RestClient baseline, Resilience4j, timeout 2s/5s/10s, shutdown retry suppression
-
raw/project-notes/ca-skeleton-operational-contract — §6 Operational Error Category, §11 Adapter Failure Contract, §29 G-C 외부 근거
Interview Questions
- SQLState 코드를 어떻게 retryable / non-retryable로 매핑했고 그 분류가 Spring
DataAccessExceptionhierarchy와 어떻게 정합한가? - OSIV가 anti-pattern으로 평가되는 이유는 무엇이고 off로 두었을 때 lazy loading은 어떻게 해결하는가?
- cache-aside의 eventual consistency window 5초가 의미하는 바와, 그 안에서 stale read가 허용되지 않는 use case는 어떻게 분리하는가?
- Resilience4j를 Hystrix 대신 선택한 이유와 두 라이브러리의 차이는?
- outbound HTTP timeout을 connect 2s / read 5s / global 10s로 둔 의도와 셋 중 어떤 게 빠지면 어떤 위험이 생기는가?
- after-commit invalidation을 강제하는 이유와, transaction rollback 시 cache 일관성이 어떻게 보장되는가?
Do Not Overclaim
- "cache-aside면 항상 안전하다" — strict consistency가 요구되는 use case에서는 cache bypass가 필요하다. cache-aside는 eventual consistency 모델이다.
- "Resilience4j는 vendor-neutral이라 어디서나 동일하게 동작" — Spring Boot starter 통합 외 환경에서는 functional decorator를 직접 조립해야 하고 boilerplate가 늘어난다.
- "RestClient가 RestTemplate를 완전히 대체했다" — Spring 6.1+ 한정이고 기존 코드 마이그레이션 비용이 있다.
- "Redisson RLock이면 분산 lock 문제 해결" — Kleppmann 비판은 완화되었지만 fencing token / DB optimistic lock 병행이 필요한 경우가 있다.
- "Stripe처럼 retry default-on이 좋은 패턴이다" — Stripe는 idempotency-key 보장이 전제. 일반 API에 그대로 적용하면 위험하다.
Sources
공식 근거 (Persistence)
- raw/official-docs/persistence-spring-dataaccessexception-hierarchy — Spring
DataAccessException계층 (SQLState 분류의 framework-level anchor) - raw/official-docs/persistence-osiv-antipattern-hibernate-vladmihalcea — Hibernate 권위자의 OSIV anti-pattern 명시 + Spring Boot WARN
- raw/official-docs/persistence-hikaricp-pool-sizing-wiki — pool sizing 공식과 alert threshold 출처
- raw/official-docs/persistence-r2dbc-reactive-spring — JPA blocking baseline의 trade-off 반대편(R2DBC reactive)
공식 근거 (Cache)
- raw/official-docs/cache-aside-vs-write-through-aws — cache-aside / write-through / write-behind / read-through trade-off 공식 분류
- raw/official-docs/cache-caffeine-asyncloadingcache-readme — single-instance stampede 방지(
@Cacheable(sync = true),AsyncLoadingCache) 공식 매핑 - raw/official-docs/cache-redisson-rlock-vs-setnx — multi-instance HPA에서 RLock 채택 + SETNX/Redlock 배제 (Kleppmann 비판 포함)
사례 (Cache)
- raw/company-tech-blogs/cache-woowahan-after-commit-invalidation — after-commit invalidation의 한국 사례 + Spring
TransactionSynchronizationManager강제 근거 (회사 기술블로그 — 사례 취급)
공식 근거 (Outbound HTTP)
- raw/official-docs/outbound-spring-restclient-baseline — RestClient baseline + RestTemplate maintenance-only 명시
- raw/official-docs/outbound-resilience4j-vs-spring-retry — Resilience4j 채택 + Spring Retry 좁은 예외 허용 + Hystrix 배제
- raw/official-docs/outbound-webclient-vs-restclient-spring — WebClient baseline 배제 이유(reactor event-loop blocking risk)
- raw/official-docs/outbound-openfeign-declarative-client — Feign declarative 대안 + maintenance status + Spring 6.1+
@HttpExchange
사례 (Outbound HTTP)
- raw/company-tech-blogs/outbound-stripe-rate-limit-retry-engineering — retry + idempotency-key 결합, full-jitter backoff. ca-tmpl default-disabled의 보수성 대비 (회사 기술블로그 — 사례 취급)
Canonical contract
- raw/project-notes/ca-skeleton-operational-contract — §6 Operational Error Category, §11 Adapter Failure Contract, §29 Group G-C 외부 근거 인덱스