- 계약 채택 — schema 2 → 4. 독자 질문, 후보 36건(PROMOTE 24 · MERGE_INTO 8 · KEEP_IN_SSOT 4), 종류별 칸, kind:slug 관계. 저장소 github-project/ca-tmpl @ 761384d (문서가 인용한 IT 7종이 그 커밋에 있다) - 재선별 결과 기존 24건이 전부 살아남았다. 측정이 Reference 안에 들어 있었지만 독립성 검사를 통과하지 못해 Case 로 빼지 않았다 - 평문 칸의 백틱 제거, 「기준선」을 실제 이름으로, frontmatter 에 source·sourceRevision - 리뷰 39건 반영 — 설명 뒤에 붙은 평가·예고·되풀이·독자 오해 가정을 지웠다. 사실을 담은 절반이 있는 문장은 평가만 뺐다 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5.4 KiB
id, kind, slug, title, topic, topicName, topicName, project, status, studio, questionStatus, sourceRevision, source
| id | kind | slug | title | topic | topicName | topicName | project | status | studio | questionStatus | sourceRevision | source | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 6cbe963f-86f8-4df6-be5a-900712970d01 | QUESTION | concurrency-stability | 실제 동시 트래픽에서도 이 구조가 안정적인가 | jpa-feed-query-performance | JPA 피드 조회 성능 — N+1 진단과 조회 전략의 진화 | JPA 피드 조회 성능 | Liner N + 1문제 | 게시 전 | https://hyeonworks.com/studio/documents/6cbe963f-86f8-4df6-be5a-900712970d01/edit | OPEN | n+1liner-lab@2026-08 |
|
id: 6cbe963f-86f8-4df6-be5a-900712970d01 kind: QUESTION slug: concurrency-stability title: 실제 동시 트래픽에서도 이 구조가 안정적인가 topic: jpa-feed-query-performance topicName: JPA 피드 조회 성능 — N+1 진단과 조회 전략의 진화 topicName: JPA 피드 조회 성능 project: Liner N + 1문제 status: 게시 전 studio: "https://hyeonworks.com/studio/documents/6cbe963f-86f8-4df6-be5a-900712970d01/edit" questionStatus: OPEN sourceRevision: n+1liner-lab@2026-08 source:
- final/document.md#4-5
- final/document.md#18
실제 동시 트래픽에서도 이 구조가 안정적인가
지금까지의 측정은 단일 스레드 퍼시스턴스 통합 테스트에서 아이템 수 N을 키울 때 쿼리 수와 읽는 행수가 얼마나 늘어나는지만 확인했다. 여러 요청이 겹칠 때의 처리량과 커넥션 풀 안정성은 이 테스트가 재지 않았으므로, 조회 구조가 실제 부하를 견디는지는 아직 모른다.
한 요청만 놓고 보면 왕복 수는 페이지 크기에 묶여 있다. 그 요청당 왕복이 트래픽에 곱해진 값은 요청을 동시에 보내야 나오고, 그 측정은 하지 않았다.
관계
- PostgreSQL Query Plan 측정 기준 무엇을 어디까지 재고 어떤 도구로 잴지 그 기록에서 정했다.
- feed_visible을 Production CQRS로 승격할 것인가 고트래픽 읽기에서 사전계산이 실제로 필요한지는 부하 결과를 봐야 정할 수 있다.
사실
- 측정은 단일 스레드에서 이미 열린 테스트 트랜잭션 안의 어댑터 호출만 쟀고, HTTP 종단은 거치지 않았다.
- 지연 값은 DB 캐시가 이미 채워진 상태에서 잰 로컬 비교값이고, 7회 반복 중 앞 2회를 워밍업으로 버린 5개가 표본이다.
- 표본이 5개뿐이어서 p50·p99 대신 중앙값과 최댓값으로 적었다.
- N=1,000에서 왕복 수는 2,022개에서 23개로, 엔티티 로드는 1,569개에서 0개로, 자식 전송은 1,509행에서 최대 60행으로 줄었다.
- 100번째 페이지에서 OFFSET은 20행을 만들려고 2,000행을 읽었고 keyset은 20행만 읽었다.
- 고트래픽 처리량과 커넥션 풀 안정성, 동시성은 이 측정의 범위 밖이라고 문서 첫머리에 적어 두었다.
- 커넥션 풀과 Little's Law는 이번 조회 문제를 해결한 뒤 별도 주제로 검증할 수 있다고 다음 단계에 함께 적어 두었다.
가정
- 왕복 수가 줄면 같은 트래픽에서 DB 부하도 줄어든다.
- 요청마다 반복되는 왕복이 처리량에 곱해지므로, 왕복이 줄면 처리량 한계도 올라간다.
- 단일 스레드에서 잰 요청당 쿼리 수와 읽는 행수는 여러 요청이 동시에 돌아도 달라지지 않는다.
미지수
- 목표 처리량에서 커넥션 풀이 포화되는가. 포화되지 않으려면 풀을 몇 개로 잡아야 하고 요청은 얼마나 기다리는가.
- 요청이 동시에 몰릴 때 지연이 어느 구간에 몰리는가. 꼬리 지연은 어디까지 벌어지는가.
- hibernate.default_batch_fetch_size=100은 세션 전체에 걸리는 설정인데, 여러 세션이 동시에 도는 동안에도 세션마다 따로 적용되는가.
- 깊은 페이지 요청이 섞이면 전체 지연이 얼마나 밀리는가.
- DB 캐시가 비어 있거나 ANALYZE를 돌리지 않아 통계가 낡은 상태에서 실행계획이 달라지는가.
- 운영 알람의 기준으로 어느 지표를 삼을 것인가.
제약
- 단일 스레드에서 잰 값으로는 동시 실행을 말할 수 없어서, 부하 테스트와 APM(Application Performance Monitoring, 애플리케이션 성능 모니터링)으로 도구를 바꿔야 한다.
- 안정적인 꼬리 지연(p99)을 말하려면 워밍업 뒤 100회 넘게 반복한 독립 세트가 여러 개 있어야 한다.
- 부하 테스트 환경의 데이터 규모와 하드웨어를 운영에 맞추지 않으면 거기서 나온 결과를 운영으로 그대로 옮길 수 없다.
선택지
1. 부하 테스트와 APM으로 확인한다
목표 처리량을 먼저 정하고 HTTP 종단 지연과 처리량, 커넥션 풀 사용률을 함께 잰다. 왕복이 줄어든 만큼 처리량이 올라가는지 부하에서 확인한다.
2. 반복 횟수를 늘린 지연 측정부터 한다
같은 단일 스레드 조건에서 반복을 100회 넘게 늘려 꼬리 지연부터 안정시킨다. 동시성은 아직 다루지 않는다.
3. 현재 범위를 명시하고 운영 판단은 미룬다
쿼리 수와 읽는 행수가 줄었다는 데까지만 주장하고 처리량은 다루지 않는다. 필요해지면 그때 부하 테스트를 연다.
운영에 올린 뒤에야 문제를 발견할 수 있다.
다음 검증
-
목표 처리량과 허용 지연을 먼저 정한다. 이 두 값이 없으면 부하 결과가 통과인지 실패인지 판정할 수 없다.
-
운영과 비슷한 데이터 규모를 준비하되 하이라이트 개수의 Zipf 형태 편중은 그대로 둔다.
-
부하 테스트로 HTTP 종단 지연과 처리량을 측정하고, 커넥션 풀 사용률과 대기 시간을 함께 본다.
-
개선 전후 구조를 같은 조건에서 비교해 왕복이 줄어든 것이 처리량으로 이어지는지 확인한다.
-
깊은 페이지 요청과 얕은 페이지 요청을 섞어 지연이 어디까지 벌어지는지 본다.
-
운영 알람으로 쓸 지표를 정한다.