세마포어 대신 Task Queue 도입 테스트 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
1. 테스트 목적
a. 가정
- Embedding 작업에 세마포어를 설정했을 때, 전체적으로 시간 개선은 많이 생겼지만 평균-최대 소요 시간 간의 편차가 큰 편이었다.
- VUs 30명 기준, 평균 소요시간 8.93s / 최대 소요시간 14.78s
- 세마포어는 실행되는 스레드는 제한할 수 있지만, 순차 처리는 보장하지 못해서 그로 인해 대기 시간이 길어지는 Embedding 작업이 있었을 것으로 판단
- 똑같이 스레드 수는 제한하되, 순차 처리를 보장할 수 있는 Task Queue를 도입하면 개선된 결과를 얻을 수 있을 것으로 예상
b. 예상 결과
- 요청 처리 시간의 분산이 줄어들 것으로 예상한다.
2. 테스트 설계
a. 도구
- 부하 테스트 도구:
k6
- 백엔드 서버: FastAPI
- 실행 환경: CPU 서버,
max_workers
고정(8) - 지표 수집: avg / p95 / max + http_req_failed 여부
b. 방식
- 실험 1:
max_workers=8
, embedding에 세마포어 4 적용 - 실험 2:
max_workers=8
, embedding에 worker 수가 4인 Task Queue 도입 - 각 실험에 대해 VUs(동시 사용자)를 10, 15, 20, 25, 30까지 순차적으로 증가시키며 테스트 진행
- 비교 지표:
- 각 작업별 처리 시간 (embedding / categories / duplicates / quality / score)
- 전체 요청 처리 시간 (total_duration)
- 실패율 (http_req_failed)
- iteration_duration을 통해 실질적 대기 시간 확인
3. 테스트 결과
-
세마포어 도입
VUs Embedding (avg / p95 / max) Categories (avg / p95 / max) Duplicates (avg / p95 / max) Quality (avg / p95 / max) Score (avg / p95 / max) Total (avg / p95 / max) http_req_failed 10 3.51s / 5.11s / 5.15s 157ms / 286ms / 290ms 130ms / 256ms / 258ms 138ms / 263ms / 268ms 85ms / 144ms / 148ms 4.02s / 5.31s / 5.35s 0.00% 15 4.56s / 7.23s / 7.33s 172ms / 303ms / 332ms 154ms / 258ms / 259ms 174ms / 295ms / 330ms 108ms / 181ms / 184ms 5.17s / 7.43s / 7.52s 0.00% 20 5.82s / 9.53s / 9.74s 158ms / 295ms / 318ms 152ms / 260ms / 320ms 157ms / 280ms / 300ms 111ms / 171ms / 199ms 6.40s / 9.74s / 9.93s 0.00% 25 7.00s / 11.61s / 12.44s 194ms / 305ms / 330ms 190ms / 280ms / 296ms 200ms / 305ms / 331ms 121ms / 251ms / 260ms 7.70s / 11.82s / 12.65s 0.00% 30 8.25s / 14.21s / 14.57s 193ms / 289ms / 316ms 179ms / 263ms / 285ms 187ms / 260ms / 273ms 124ms / 234ms / 264ms 8.93s / 14.44s / 14.78s 0.00% -
태스크큐 도입
VUs Embedding (avg / p95 / max) Categories (avg / p95 / max) Duplicates (avg / p95 / max) Quality (avg / p95 / max) Score (avg / p95 / max) Total (avg / p95 / max) 10 3.49s / 5.06s / 5.08s 149ms / 252ms / 274ms 137ms / 230ms / 235ms 137ms / 228ms / 233ms 94ms / 160ms / 162ms 4.00s / 5.31s / 5.34s 15 4.62s / 7.34s / 7.41s 180ms / 289ms / 322ms 161ms / 267ms / 294ms 180ms / 273ms / 332ms 108ms / 193ms / 212ms 5.25s / 7.59s / 7.67s 20 5.82s / 9.63s / 9.69s 194ms / 269ms / 277ms 157ms / 231ms / 267ms 180ms / 264ms / 310ms 133ms / 194ms / 235ms 6.49s / 9.89s / 10.00s 25 6.99s / 11.71s / 12.26s 202ms / 354ms / 360ms 186ms / 330ms / 356ms 201ms / 347ms / 401ms 134ms / 278ms / 379ms 7.71s / 11.91s / 12.49s 30 8.17s / 14.06s / 14.48s 170ms / 292ms / 348ms 163ms / 259ms / 344ms 164ms / 283ms / 348ms 146ms / 238ms / 463ms 8.81s / 14.31s / 14.78s
4. 결론: asyncio.Queue(worker=4)는 세마포어(4)와 비교해 성능상 유의미한 차이가 없다
a. Embedding 작업 시간 변화
avg/p95/max
기준으로 세마포어 제한 vs. asyncio.Queue 모두 VUs 증가에 따라 선형적으로 증가- 예를 들어, VU=30 기준:
-
세마포어: 8.25s / 14.21s / 14.64s
-
Queue: 8.17s / 14.06s / 14.48s
➤ 약 1~2% 미만의 차이로, 사실상 동일한 처리 성능
-
b. 다른 작업 항목들 (Categories / Duplicates / Quality / Score)
- 각 작업의 평균 처리 시간 및 분산도 거의 동일
- 오히려 일부 VU에서 Queue 방식이 약간 더 높은 분산(p95~max)을 보이는 경우도 있음
- Score나 Quality처럼 상대적으로 가벼운 작업에서도 크게 유리한 차이 없음
c. Total Duration
-
전체 요청 처리 시간도 각 VU 수준에서 1~2% 이내의 미미한 차이
→ 실질적으로 사용자 경험이나 처리량에 영향을 줄 수준은 아님
d. 선택 가이드
-
단순 제한을 원한다면 세마포어(4) 방식이 충분
→ 구현이 간단하고 효과도 안정적
-
정교한 워커 관리(큐 기반 디버깅 등)가 필요한 경우에만 asyncio.Queue 도입 고려
→ 성능 향상보다는 구조적 제어 목적