docs(n+1liner): adopt the decomposition contract and strip evaluative prose
- 계약 채택 — 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>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
b25357c48a
commit
62520a4dce
+37
-34
@@ -4,85 +4,88 @@ 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 종단을 거치지 않았다.
|
||||
- 지연 값은 warm cache 상태의 로컬 비교값이다. 표본은 7회 중 앞 2회를 버린 5개다.
|
||||
- 표본이 적어 백분위수 대신 중앙값과 최댓값으로 기록했다.
|
||||
- 왕복 수는 2,022개에서 23개로, 엔티티 로드는 1,569개에서 0개로, 자식 전송은 1,509행에서 최대 60행으로 줄었다.
|
||||
- 깊은 페이지 조회는 2,000행 대신 20행을 읽었다.
|
||||
- 문서 첫머리에 고트래픽 처리량, 커넥션 풀 안정성, 동시성이 범위 밖임을 명시했다.
|
||||
- 측정은 단일 스레드에서 이미 열린 테스트 트랜잭션 안의 어댑터 호출만 쟀고, 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. 현재 범위를 명시하고 운영 판단은 미룬다
|
||||
|
||||
조회 형태 개선까지만 주장하고 처리량은 다루지 않는다. 필요해질 때 부하 테스트를 연다.
|
||||
쿼리 수와 읽는 행수가 줄었다는 데까지만 주장하고 처리량은 다루지 않는다. 필요해지면 그때 부하 테스트를 연다.
|
||||
|
||||
운영에 올린 뒤 문제를 발견할 위험이 남는다.
|
||||
운영에 올린 뒤에야 문제를 발견할 수 있다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 목표 처리량과 허용 지연을 먼저 정한다. 기준이 없으면 결과를 판정할 수 없다.
|
||||
1. 목표 처리량과 허용 지연을 먼저 정한다. 이 두 값이 없으면 부하 결과가 통과인지 실패인지 판정할 수 없다.
|
||||
|
||||
2. 운영과 비슷한 데이터 규모를 준비한다. 편중 분포를 유지한다.
|
||||
2. 운영과 비슷한 데이터 규모를 준비하되 하이라이트 개수의 Zipf 형태 편중은 그대로 둔다.
|
||||
|
||||
3. 부하 테스트로 종단 지연과 처리량을 측정한다. 커넥션 풀 사용률과 대기 시간을 함께 본다.
|
||||
3. 부하 테스트로 HTTP 종단 지연과 처리량을 측정하고, 커넥션 풀 사용률과 대기 시간을 함께 본다.
|
||||
|
||||
4. 개선 전후 구조를 같은 조건에서 비교한다. 왕복 감소가 처리량으로 이어지는지 확인한다.
|
||||
4. 개선 전후 구조를 같은 조건에서 비교해 왕복이 줄어든 것이 처리량으로 이어지는지 확인한다.
|
||||
|
||||
5. 깊은 페이지와 얕은 페이지 요청을 섞어 지연 분포를 본다.
|
||||
5. 깊은 페이지 요청과 얕은 페이지 요청을 섞어 지연이 어디까지 벌어지는지 본다.
|
||||
|
||||
6. 운영 알람으로 쓸 지표를 정한다.
|
||||
|
||||
Reference in New Issue
Block a user