18 KiB
title, source_type, status, branch, related_projects, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, parent_branch, contract_packet_sha256
| title | source_type | status | branch | related_projects | tags | created | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | parent_branch | contract_packet_sha256 | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-cache-consistency-contract | branch-note | raw | feature-cache-consistency-contract |
|
|
2026-05-22 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-024 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-024 |
|
1 | 6d4978b50ddf3cab75e6803a198f9ca53753f9812e8f3a158f96c3544c843008 |
branch: feature-cache-consistency-contract
Layer:
raw/branch-notes/— cache consistency와 Redis/cache adapter 기준을 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (§<관련 섹션>) 의 결정/근거/금지 사항을 정제한다.
묶음
- raw/company-tech-blogs/cache-woowahan-after-commit-invalidation
- raw/official-docs/cache-aside-vs-write-through-aws
- raw/official-docs/cache-caffeine-asyncloadingcache-readme
- raw/official-docs/cache-redisson-rlock-vs-setnx
본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
오류 기록 (본 feature 작업 중 발생)
- (없음 — 현재 documented-only 단계)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase C2 실 구현 단계에 누적)
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: after-commit invalidation·stampede failure fixture가 통과한다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
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 |
|---|
목표
cache miss/unavailable만으로는 cache 운영 기준이 부족합니다. stale cache, stampede, key naming, TTL, invalidation 실패를 skeleton 기준에 포함해야 합니다.
- 이슈:
- PR:
범위
포함 범위
- cache aside 기준.
- stale cache 허용 범위.
- cache stampede 방지 기준.
- cache key naming.
- TTL 기준.
- invalidation 실패 분류.
- Redis unavailable degrade 기준과 연결.
제외 범위
- business-specific cache policy.
- distributed lock 기본 구현.
- Redis cluster 운영 설정.
TODO
TODO drained — 결정은 아래 표/결정 사항 참조.
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 승급 위치 |
진행 중 메모
- cache consistency는 optional adapter이지만, 붙였을 때 같은 실패 계약을 따라야 합니다.
결정 사항 (decisions)
- 2026-05-22: cache consistency를 Redis adapter 내부 세부사항으로만 두지 않음.
- 2026-05-22: core는 single-instance/local cache policy만 완결. distributed cache lock/stampede 방지는 multi-instance package 활성화 시 필요.
- 2026-05-22: Redis cluster 운영은 out of core이나 cluster mode 활성화 시 key hash/tag policy와 failover runbook이 필요.
- 2026-05-22: cache invalidation = after-commit only. Spring
TransactionSynchronizationManager.registerSynchronization으로 강제. tx 내부 또는 tx 미참여 상태에서의 cache mutation은 forbidden. - 2026-05-22: stampede 방지 default = single-instance Caffeine local lock, multi-instance HPA 시 Redisson RLock distributed mutex. application 별 override 금지.
- 2026-05-22: cache value serialization = JSON (Jackson). 스키마 변경 시 key version suffix(
v{n}접미사) 필수. - 2026-05-22: negative cache 정책 = 존재하지 않는 row는 짧은 TTL(60s) 캐싱 허용. application별 override 가능.
- 2026-05-22: eventual consistency window default = 5s (TTL과는 별개로 invalidation propagation 허용 한계).
- 2026-05-22: negative cache TTL(60s)와 invalidation propagation window(5s)는 독립 축. negative cache는 invalidation 채널 적용 대상에서 제외 (적용 시 정상화). 두 수치는 의도된 분리.
결정-근거 매핑
각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. company-tech-blog 는
company-case-study로만 라벨 (best practice 단정 금지).
| Decision ID | Decision (요약) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|
| D1 | cache pattern default = cache-aside (write-through without consistency contract 는 forbidden) | raw/official-docs/cache-aside-vs-write-through-aws.md#CACHE-PAT-C1, #CACHE-PAT-C2, #CACHE-PAT-C3 |
official-vendor-doc (AWS + Redis 공식 — lazy caching 정의 + application 책임 + write-through latency tradeoff) |
"write-through 가 결제/주문 도메인에 부적합" 은 cited raw 가 직접 prescribe 안 함 — ca-tmpl 내부 결정. write-behind 의 data loss 메커니즘 (#CACHE-PAT-C5) 은 needs-confirmation — AWS Database Blog 또는 Redis docs 별도 raw 필요 |
| D2 | cache invalidation = after-commit only. Spring TransactionSynchronizationManager.registerSynchronization 으로 강제. tx 내부/tx 미참여 상태 cache mutation forbidden |
raw/company-tech-blogs/cache-woowahan-after-commit-invalidation.md (company-case-study — 우아한형제들 한국 사례) |
company-case-study (NOT official best practice) |
Spring TransactionSynchronizationManager 공식 reference 의 after-commit hook 시맨틱 verbatim 미수집 — 별도 official-doc raw 필요. 우아한형제들 사례는 한 회사의 결정이며 official-standard 가 아님 |
| D3 | stampede 방지 default = single-instance Caffeine local lock, multi-instance HPA 시 Redisson RLock distributed mutex | raw/official-docs/cache-redisson-rlock-vs-setnx.md#LOCK-C1 (Redis SET NX PX 단순 패턴, 단일 인스턴스 efficiency lock), #LOCK-C4 (Kleppmann: efficiency vs correctness lock 분리), raw/official-docs/cache-caffeine-asyncloadingcache-readme.md (single-instance LoadingCache stampede 방지) |
official-vendor-doc (LOCK-C1 — Redis 공식 verbatim 확인) + engineering-blog (LOCK-C4 — Kleppmann 비판, WebFetch 차단으로 재확인 보류) |
LOCK-C3 (Redisson RLock watchdog + j.u.c.locks.Lock 호환) 은 needs-confirmation — redisson.org → redisson.pro redirect 차단. Redisson Javadoc 직접 다운로드 필요. Caffeine raw 의 claim ID 매핑 미확인 (본 세션 mandatory read 범위 밖) |
| D4 | core 는 single-instance/local cache policy 만 완결. distributed cache lock/stampede 방지는 multi-instance package 활성화 시 필요 | raw/official-docs/cache-redisson-rlock-vs-setnx.md#LOCK-C4 (efficiency vs correctness lock 분리 — cache stampede = efficiency lock) |
engineering-blog (Kleppmann, 재확인 보류) |
"stampede = efficiency lock" 의 분류가 모든 cache 시나리오 (token bucket, rate limit 등) 에 적용되는지 미검증 — correctness 가 필요한 endpoint 식별 필요 |
| D5 | cache value serialization = JSON (Jackson). 스키마 변경 시 key version suffix (v{n}) 필수 |
(UNSUPPORTED_DECISION — cited raw 4종 중 직접 verbatim claim 없음. Jackson docs / Redis serialization 공식 raw 별도 필요) | internal-policy |
schema versioning 컨벤션은 ca-tmpl 내부 결정 — 외부 권위 근거 미수집 |
| D6 | negative cache 정책 = 존재하지 않는 row 는 짧은 TTL(60s) 캐싱 허용 | (UNSUPPORTED_DECISION — cited raw 4종에 negative cache TTL verbatim 없음) | internal-policy |
60s 값은 ca-tmpl 내부 SLO — 외부 근거 미수집 |
| D7 | eventual consistency window default = 5s (TTL 과는 별개로 invalidation propagation 허용 한계) | (UNSUPPORTED_DECISION — cited raw 4종에 propagation window 수치 verbatim 없음) | internal-policy |
5s 값은 ca-tmpl 내부 SLO — 외부 근거 미수집 |
| D8 | negative cache TTL(60s) 와 invalidation propagation window(5s) 는 독립 축 (D6/D7 분리) | (UNSUPPORTED_DECISION — 위 두 값 자체가 internal policy) | internal-policy |
두 수치 모두 외부 근거 없음 |
| D9 | Redis cluster 운영은 out of core. cluster mode 활성화 시 key hash/tag policy + failover runbook 필요 | (UNSUPPORTED_DECISION — cited raw 4종에 Redis cluster key hashtag 시맨틱 verbatim 없음. Redis 공식 cluster spec 별도 필요) | internal-policy |
Redis cluster keyspace 분배 권고 (hashtag {}) verbatim raw 별도 수집 필요 |
검증해야 할 주장
공식 문서 인용이 결정의 근거이지만, ca-tmpl 자체 구현에서의 동작은 별도 검증이 필요한 주장.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
LOCK-C3 (Redisson RLock watchdog + j.u.c.locks.Lock 호환) 의 verbatim 재확인 |
1차 URL redisson.org → redisson.pro 301 redirect, redirect 호스트 호출 차단으로 verbatim 재확인 불가 | Redisson Javadoc 직접 다운로드 또는 archive.org 스냅샷으로 verbatim 격상 | needs-confirmation |
| LOCK-C4 (Kleppmann fencing token) verbatim 재확인 | martin.kleppmann.com WebFetch permission denied | archive.org Kleppmann "How to do distributed locking" 스냅샷 verbatim 확보 | needs-confirmation |
stampede 방지 contract test (ArchUnit methodsThat().areAnnotatedWith(@Cacheable)... withAttribute("sync", "true")또는AsyncLoadingCache또는RLock` wrap) 가 실제로 위반 검출 |
cited raw 는 stampede 방지 도구 비교까지만 보장 — ArchUnit rule 동작은 별도 | ArchUnit test 작성 + 의도적 위반 case (sync=false 한 @Cacheable 추가) 로 fail 확인 |
planned |
multi-instance cache claim consistency (env APP_MULTI_INSTANCE_ENABLED=true + APP_CACHE_REDIS_ENABLED=true 시 Redisson bean 등록 + 모든 hot cache 메서드 RLock wrap) |
LOCK-C3 가 needs-confirmation 인 상태에서 RLock wrap 의 실제 효과 미보증 |
MultiInstanceCacheStampedeContractTest 작성 + 두 flag true 일 때 Redisson bean verify + 동시 cache miss 1회 backend 호출 확인 |
planned |
| after-commit invalidation 이 tx rollback 시 cache 에 stale write 를 남기지 않음 | 우아한형제들 사례 (D2) 는 company-case-study — 우리 환경에서의 동작 별도 보장 필요 | TransactionTemplate rollback 시나리오 integration test + Redis key 미존재 단언 | planned |
| Redis unavailable 시 degrade 가능 endpoint 가 declared 된 경우에만 fail-fast 회피 (generic INTERNAL 금지) | cited raw 는 degrade 정책 자체를 prescribe 안 함 — 내부 결정 | contract test: Redis down 시 declared degrade endpoint 는 fallback 응답, undeclared 는 503/CACHE_UNAVAILABLE 반환 |
planned |
| negative cache TTL 60s + invalidation propagation 5s 값의 적절성 (D6/D7) | UNSUPPORTED_DECISION — 외부 근거 없음 | 도메인별 stale tolerance SLO 측정 + p99 user-visible staleness 추적 | planned |
Spring TransactionSynchronizationManager.registerSynchronization 의 after-commit hook 시맨틱 (D2 메커니즘) |
공식 reference verbatim raw 미수집 | Spring Framework Reference §Transaction Synchronization raw 수집 후 afterCommit hook 보장 verbatim 확인 |
needs-confirmation |
Decisionized Work Items
| item | Decision | Allowed | Forbidden | Required test |
|---|---|---|---|---|
| cache pattern | cache-aside default | read-through if adapter owns it | write-through without consistency contract | cache behavior test |
| TTL | explicit per key family | no-cache for sensitive data | immortal cache | TTL test |
| stampede | local lock in single-instance | distributed lock for HPA | hot key without guard | stampede test |
| key scope | app/profile/operation/tenant-if-enabled | hash compact key | PII/raw user id | key naming test |
| Redis unavailable | degrade only if declared | fail-fast for required cache | generic INTERNAL | unavailable mapping |
테스트 계약
- cache key naming에 operation/tenant/profile 기준이 없으면 실패.
- invalidation 실패가 조용히 무시되면 실패.
- stampede 방지 검증:
@Cacheable이 적용된 모든 메서드는 (a)sync=true명시 또는 (b) Caffeine의AsyncLoadingCache사용 또는 (c) RedissonRLockwrap 중 하나여야 함. 측정 방법: ArchUnitmethodsThat().areAnnotatedWith(@Cacheable).should().beAnnotatedWith(@Cacheable.class).withAttribute("sync", "true")또는 동등 reflection check. 위반 시 fail. - Redis unavailable이 degrade 가능 여부 없이 INTERNAL로 처리되면 실패.
- multi-instance cache claim consistency: env property
APP_MULTI_INSTANCE_ENABLED=true이고APP_CACHE_REDIS_ENABLED=true이면 RedissonRedissonClientbean이 등록되어 있고 모든 hot cache 메서드가 RLock으로 wrap되어 있어야 함. 측정 방법: 두 flag 모두 true일 때 contract testMultiInstanceCacheStampedeContractTest.java에서 Redisson bean verify + RLock 사용 검증. - tx rollback 시 cache에 stale write가 남으면 실패.
- 동일 key에 대해 동시 cache miss 시 backend 호출이 1회로 제한되는지 verify (stampede). 미충족 시 실패.
마주친 문제
- 아직 없음.
근거 (필수, 최소 1개+)
본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조.
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/cache-aside-vs-write-through-aws | cache-aside default 채택의 trade-off 표 + AWS 공식 분류 |
| raw/official-docs/cache-caffeine-asyncloadingcache-readme | single-instance stampede 방지를 LoadingCache / `@Cacheable(sync=true |
| raw/official-docs/cache-redisson-rlock-vs-setnx | multi-instance HPA에서 Redisson RLock 채택 + SETNX/Redlock 배제 근거 (Kleppmann 비판 포함 |
| raw/company-tech-blogs/cache-woowahan-after-commit-invalidation | after-commit invalidation 결정의 한국 사례 + Spring TransactionSynchronizationManager 강제 근거 |
외부 근거 (Group G-C — Cache consistency)
ca-tmpl cache 결정 backbone + stampede 방지 도구 비교 자료.
- 채택 결정의 공식 근거:
- raw/official-docs/cache-aside-vs-write-through-aws — cache-aside default 채택의 trade-off 표 + AWS 공식 분류.
- raw/official-docs/cache-caffeine-asyncloadingcache-readme — single-instance stampede 방지를
LoadingCache/@Cacheable(sync=true)에 매핑하는 공식 근거. - raw/official-docs/cache-redisson-rlock-vs-setnx — multi-instance HPA에서 Redisson RLock 채택 + SETNX/Redlock 배제 근거 (Kleppmann 비판 포함).
- 사례 / 한국 도메인:
- raw/company-tech-blogs/cache-woowahan-after-commit-invalidation — after-commit invalidation 결정의 한국 사례 + Spring
TransactionSynchronizationManager강제 근거. (회사 기술블로그 — 사례 취급)
- raw/company-tech-blogs/cache-woowahan-after-commit-invalidation — after-commit invalidation 결정의 한국 사례 + Spring
검색 키워드 기록: cache-aside vs write-through trade-off, Caffeine AsyncLoadingCache stampede, Redisson RLock vs SETNX, Kleppmann Redlock unsafe, 우아한형제들 캐시 무효화 트랜잭션.
구현 가이드
- write transaction commit 이후에만 invalidate하고 rollback 시 cache를 변경하지 않는다.
- TTL·key namespace·stampede 방지 정책은 registry 값으로 고정하고 backend adapter가 적용한다.
- hit·miss·eviction·fallback을 contract test와 metric으로 함께 검증한다.
엣지·실패·의존
- commit 전 eviction은 rollback 뒤 stale miss를, eviction 실패 무시는 stale read를 만들 수 있다.
- database transaction·multi-backend router·runtime context 계약에 의존한다.
관련 일일 노트
- 별도 일일 노트 없음.
완료 후 정리
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:locally-verified항목:prod-verified항목:
- 추출하지 않을 항목 (planned / documented-only / abandoned):