On a cache miss, every concurrent caller recomputes the same expensive value (N computes for N callers). Fix it with single-flight so exactly one caller computes and the rest share the result.
You'll predict the failure first, then write the fix and watch your own code drive the behavior.
캐시는 한번 계산한 값을 저장해 두고 다음부터 빨리 주는 창고다. 그 계산은 비싸다(느리고 무겁다).
Integer v = store.get(key); if (v != null) return v; // cheap warm read int result = compute(key); // expensive: key.length() * 10 store.put(key, result);
“A cache stores an expensive result so later reads are fast.”
checking microphone…
문제 상황: 그 값이 아직 창고에 없다(콜드). 그런데 같은 값을 찾는 요청 10개가 거의 동시에 들어온다.
⚖️ Trade-off: 다들 창고를 봤더니 비어 있다. 그래서 다들 '내가 계산해야지' 한다.
🔧 도구:read-through cache
Integer v = store.get(key);
if (v == null) {
// cold: nothing published yet, this caller missed
}“While the key is still cold, ten callers ask for the same value at once.”
checking microphone…
나이브한 코드는 비어 있으면 각자 계산을 시작한다. 그래서 요청 10개면 똑같은 비싼 계산을 10번 돌린다.
⚖️ Trade-off: 이게 캐시 스탬피드(cache stampede). 한 번이면 될 일을 N번 하니 자원 폭발 + 느려짐.
🔧 도구:cache stampede
int request(String key) {
Integer v = store.get(key);
if (v != null) return v;
if (inFlight.contains(key)) return joinInFlight(key); // don't recompute
return startCompute(key);
}“The naive cache lets each caller compute on a miss, so N callers do N computes.”
checking microphone…
해결: '지금 누가 이 값 계산 중'이라는 표시(진행중 마커)를 둔다. 제일 먼저 온 요청만 계산을 시작하고 그 표시를 건다.
🔧 도구:single-flightin-flight map (Map<key, Promise>)
if (!inFlight.contains(key)) {
inFlight.add(key); // first caller becomes the leader
return compute(key); // only one compute is launched
}“I add a marker that says someone is already computing this key.”
checking microphone…
계산 도중에 온 나머지 요청은 새로 계산하지 않는다. 표시를 보고 '이미 하는 사람 있네' 하며 그 결과를 같이 기다렸다 받는다.
⚖️ Trade-off: 진행중인 키를 담아 둘 맵(map) 하나가 더 필요하고, 끝나면 그 표시를 지우는 뒷정리도 해야 한다.
✅ Fix: 맵 하나, 마커 정리 정도는 싼 값이다. 그 대신 계산 N번을 1번으로 줄인다 → 충분히 남는 장사.
🔧 도구:single-flightshared Promise / futurepublish() clears the marker
if (inFlight.contains(key)) {
return awaitInFlight(key); // share the leader's pending result
}“Callers that arrive mid-flight join that one compute instead of starting their own.”
checking microphone…
위험 하나: 계산하던 그 첫 요청이 도중에 실패하면? 표시는 그대로 남고, 기다리던 나머지는 영원히 못 받는다.
⚖️ Trade-off: 리더(첫 요청)가 죽으면 대기자들이 무한정 멈춰 버린다.
✅ Fix: 실패하면 그 진행중 표시를 반드시 지운다(에러도 같이 전달). 그래야 다음 요청이 다시 깨끗하게 계산할 수 있다.
🔧 도구:error propagationfinally/cleanup on the in-flight entry(prod) timeout
try {
int result = compute(key);
publish(key, result); // publish() removes the inFlight marker
} catch (RuntimeException e) {
inFlight.remove(key); // leader failed: clear marker so waiters retry
throw e;
}“If the leader fails, I must clear the marker so the waiters don't hang forever.”
checking microphone…
값이 한번 창고에 들어간(퍼블리시된) 뒤에는 모두 그 저장된 값을 바로 읽는다. 다시는 계산 안 한다.
🔧 도구:published store / warm read
Integer v = store.get(key); if (v != null) return v; // warm hit after publish; computeCount stays 1
“After publish, every read just returns the stored value and never recomputes.”
checking microphone…
7단계 영어를 다 말하면 → 이 메커니즘 전체를 영어로 설명할 수 있게 된다.
Say it out loud in your own words — hit these terms — then check against the gold answer.
Say it in your own words — but hit these: caching · concurrency · single flight · thundering herd · java
checking microphone…