Add narrative summaries to backend-read improvement report

- improvements-backend-reads.md 각 항목 제목 아래에 서사 요약 blockquote 추가
- R1·R4·R5·R7 항목의 Before→Fix 흐름을 한 문장으로 정리
This commit is contained in:
윤정민 2026-05-28 19:33:30 +09:00
parent 11d9fa9b5f
commit dafaacc446

View File

@ -15,6 +15,8 @@
## R1. `dailyArchive` 날짜별 `listByDate` 공유 ## R1. `dailyArchive` 날짜별 `listByDate` 공유
> **요약**: 기존에는 아카이브 run에서 `judgeDay``hasMissedGameDayBetween`**유저마다** 동일 날짜의 `games` 범위쿼리(`listByDate`)를 재실행하는 방식이라 `U × (1 + 최대 14)`회로 read가 U배 증폭됐으므로, run당 1개의 메모이징 캐시(`GameDayCache`)를 생성해 유저 루프 전체와 look-back 루프에 공유 주입함으로써 distinct 날짜당 1회로 수렴시키는 방식으로 수정했다.
### 문제 (Before) ### 문제 (Before)
- 아카이브 1회 run에서 `judgeDay``hasMissedGameDayBetween`**유저마다** 동일 날짜의 `games` 범위쿼리(`listByDate`)를 재실행했다. - 아카이브 1회 run에서 `judgeDay``hasMissedGameDayBetween`**유저마다** 동일 날짜의 `games` 범위쿼리(`listByDate`)를 재실행했다.
- 유저당 비용: `judgeDay`의 대상 날짜 1회 + `hasMissedGameDayBetween`의 직전 결석 판정 look-back **최대 14회** = 최대 15회. - 유저당 비용: `judgeDay`의 대상 날짜 1회 + `hasMissedGameDayBetween`의 직전 결석 판정 look-back **최대 14회** = 최대 15회.
@ -37,6 +39,8 @@
## R4. 캐시 스탬피드 폴링 read 완화 ## R4. 캐시 스탬피드 폴링 read 완화
> **요약**: 기존에는 락 보유자가 캐시를 채울 때까지 대기하는 3개 폴링 루프가 모두 **고정 500ms 간격**으로 폴링하는 방식이라 25s 윈도우 안에서 대기 요청마다 ~50회씩 read가 발생해 락 경합·콜드스타트 버스트에서 read가 증폭됐으므로, 공유 헬퍼 `backoffDelayMs`로 지수 백오프를 적용해 폴링 시도를 ~8~9회로 줄이는 방식으로 수정했다.
### 문제 (Before) ### 문제 (Before)
- 락 보유자가 캐시를 채울 때까지 대기하는 3개 폴링 루프가 모두 **고정 500ms 간격**으로 폴링했다. - 락 보유자가 캐시를 채울 때까지 대기하는 3개 폴링 루프가 모두 **고정 500ms 간격**으로 폴링했다.
- `kboCacheRepository.waitForCache` (≤25s → 최대 ~50회 `getCached`), - `kboCacheRepository.waitForCache` (≤25s → 최대 ~50회 `getCached`),
@ -65,6 +69,8 @@
## R5. 스코어보드 self-user read field-mask ## R5. 스코어보드 self-user read field-mask
> **요약**: 기존에는 `getScoreboard`가 본인 정보 계산에 `getUser(uid)`로 **전체 user doc**을 읽어 실제 사용하는 5개 필드 외 streak/티켓/notifications까지 폴링성 요청마다 전송하는 방식이라 bandwidth 낭비였으므로, `fieldMask`로 5개 필드만 페치하는 `getUserForScoreboard`를 도입하는 방식으로 수정했다.
### 문제 (Before) ### 문제 (Before)
- `getScoreboard`가 본인 정보 계산에 **전체 user doc**을 `getUser(uid)`로 읽었다(`scoreboardService.ts:54`). 실제 사용 필드는 5개뿐인데 streak/티켓/notifications 등 전체 doc을 전송받아 bandwidth 낭비. - `getScoreboard`가 본인 정보 계산에 **전체 user doc**을 `getUser(uid)`로 읽었다(`scoreboardService.ts:54`). 실제 사용 필드는 5개뿐인데 streak/티켓/notifications 등 전체 doc을 전송받아 bandwidth 낭비.
- 랭킹 화면은 폴링성 read(`Cache-Control: private, max-age=60`)라 요청마다 발생 (`backend-reads.md:86,168`). - 랭킹 화면은 폴링성 read(`Cache-Control: private, max-age=60`)라 요청마다 발생 (`backend-reads.md:86,168`).
@ -83,6 +89,8 @@
## R7. 다년도 rank 병렬 fetch ## R7. 다년도 rank 병렬 fetch
> **요약**: 기존에는 `fetchRankFromKbo(years)``for` 루프로 연도마다 `getOrFetch`를 **순차 await**하는 방식이라 연도별 캐시 키가 독립적인데도 동시 miss 시 외부 KBO fetch가 직렬화되어 다년도 조회 지연이 `N×T`로 누적됐으므로, `Promise.all`로 연도별 fetch를 병렬 실행해 `~1×T`로 단축하는 방식으로 수정했다.
### 문제 (Before) ### 문제 (Before)
- `fetchRankFromKbo(years)``for` 루프로 연도마다 `getOrFetch`를 **순차 await**했다(`kboRepository.ts:46`). 연도별 캐시 키(`rank__{year}`)가 독립적인데도 직렬 대기. - `fetchRankFromKbo(years)``for` 루프로 연도마다 `getOrFetch`를 **순차 await**했다(`kboRepository.ts:46`). 연도별 캐시 키(`rank__{year}`)가 독립적인데도 직렬 대기.
- 캐시 히트면 무해하나, **동시 miss(콜드/만료) 시 외부 KBO fetch가 직렬**이라 다년도 조회 지연 = N × 단년 지연 (`backend-reads.md:40,171`). - 캐시 히트면 무해하나, **동시 miss(콜드/만료) 시 외부 KBO fetch가 직렬**이라 다년도 조회 지연 = N × 단년 지연 (`backend-reads.md:40,171`).