Skip to content

Feat/170 : 로컬 k6 기반 부하 테스트 환경 구성 및 테스트 스크립트 작성 Ai 피드백 처리 멀티 워커 구조 도입#171

Merged
HeeMang-Lee merged 2 commits intodevfrom
feat/170
Jun 25, 2025
Merged

Feat/170 : 로컬 k6 기반 부하 테스트 환경 구성 및 테스트 스크립트 작성 Ai 피드백 처리 멀티 워커 구조 도입#171
HeeMang-Lee merged 2 commits intodevfrom
feat/170

Conversation

@HeeMang-Lee
Copy link
Copy Markdown
Member

@HeeMang-Lee HeeMang-Lee commented Jun 25, 2025

🔎 작업 내용

  • SSE 기반 AI 피드백 처리에 큐 + 멀티 워커 구조를 도입하여 대량 동시 요청을 안정적으로 처리할 수 있도록 개선
  • 로컬에서 k6 + Prometheus + Grafana를 활용한 부하 테스트 환경 구성 완료 및 테스트 스크립트 작성
  • LLM 호출 우회를 위한 MOCK_AI=true 환경변수 도입 → 테스트 시 실제 API 호출 차단

🛠️ 변경 사항

✅ 피드백 처리 구조 개선

  • AiFeedbackQueueService
    • BlockingQueue 용량: 100 → 500 증가
    • 워커 수: 1 → 16개로 병렬 처리 가능하도록 개선
    • ExecutorServiceFixedThreadPool 전환
  • AiFeedbackStreamProcessor
    • 실제 AI 호출 대신 하드코딩된 응답 사용 (테스트 시)
    • Thread.sleep(300) 으로 응답 간 지연 시뮬레이션

✅ 테스트 데이터 및 환경

  • TestDataInitializer
    • @Profile("loadtest") 활성 시, UserQuizAnswer 1,000건 bulk insert
    • 테스트용 유저/퀴즈 자동 생성
  • FallbackAiChatClient
    • System.getenv("MOCK_AI") == "true" 시 OpenAI 호출 생략하고 테스트용 응답 반환

✅ 부하 테스트 스크립트

  • k6/scripts/sse-test.js
    • 60초간 VU 500명으로 /quizzes/{answerId}/feedback SSE 요청 부하 발생

🧩 트러블 슈팅

  • 문제: 단일 워커로는 1000건 동시 요청 처리 불가
    • 해결: FixedThreadPool 16개로 병렬 처리 구조 확장
  • 문제: 실제 OpenAI/Claude API 호출로 비용 문제로 테스트 불가능
    • 해결: MOCK_AI=true 설정 시 하드코딩 응답 반환

📌 참고 사항

  • 로컬 부하 테스트 명령 예시 (MOCK_AI 설정 포함):
export MOCK_AI=true
./gradlew build -x test
docker-compose --profile loadtest up --build

closed #170

Summary by CodeRabbit

  • 신규 기능

    • 로드 테스트용 테스트 데이터 자동 초기화 기능이 추가되었습니다.
    • 다중 서비스 및 모니터링 환경을 위한 Docker Compose 설정 파일이 도입되었습니다.
    • SSE 피드백 엔드포인트 부하 테스트를 위한 k6 스크립트가 추가되었습니다.
  • 버그 수정

    • AI 피드백 큐의 처리 용량이 100에서 500으로 증가하고, 동시 처리 워커가 16개로 확장되어 처리 성능이 향상되었습니다.
  • 테스트

    • AI 응답을 모킹할 수 있는 환경변수(MOCK_AI) 지원이 추가되어 테스트가 용이해졌습니다.
    • SSE 피드백 엔드포인트의 부하 테스트가 가능해졌습니다.
  • 기타

    • AI 피드백 스트림 응답이 테스트용 고정 메시지로 일시 변경되었습니다.

@HeeMang-Lee HeeMang-Lee linked an issue Jun 25, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jun 25, 2025

Walkthrough

이 변경 사항은 AI 피드백 처리의 동시성 향상, 테스트 및 부하 테스트를 위한 환경 지원, 그리고 도커 기반 통합 환경 및 부하 테스트 스크립트 도입을 포함합니다. 또한, AI 피드백 스트림 처리 로직에 테스트용 하드코딩 응답을 적용하고, 테스트 데이터 자동 초기화 기능이 추가되었습니다.

Changes

파일/경로 변경 요약
cs25-service/.../FallbackAiChatClient.java MOCK_AI 환경 변수 설정 시 고정 테스트 문자열 반환 로직 추가
cs25-service/.../AiFeedbackQueueService.java 큐 용량 100→500, 단일 스레드→16개 고정 스레드 풀로 변경, 16개 워커 태스크 동시 실행
cs25-service/.../AiFeedbackStreamProcessor.java AI 피드백 호출 대신 300ms 대기 및 하드코딩 응답 사용, 중간 상태 메시지 및 완료 이벤트 일부 제거
cs25-service/.../test/TestDataInitializer.java "loadtest" 프로필에서 1000개 테스트 데이터 자동 생성 컴포넌트 추가
docker-compose.yml 애플리케이션, 인프라, 모니터링, CI/CD 등 멀티 서비스 도커 컴포즈 환경 신규 추가
k6/scripts/sse-test.js 500 VU, 60초 동안 SSE 피드백 엔드포인트 부하테스트 스크립트 신규 추가
cs25-entity/.../UserRepository.java 이메일 존재 여부 확인용 메서드 existsByEmail 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant k6(VU)
    participant cs25-service
    participant AiFeedbackStreamProcessor

    User->>k6(VU): 부하 테스트 시작
    k6(VU)->>cs25-service: GET /feedback/stream/{answerId} (SSE)
    cs25-service->>AiFeedbackStreamProcessor: stream(answerId, emitter)
    AiFeedbackStreamProcessor->>AiFeedbackStreamProcessor: (300ms 대기, 하드코딩 응답 생성)
    AiFeedbackStreamProcessor-->>cs25-service: 응답 전송 (SSE)
    cs25-service-->>k6(VU): SSE 응답 전송
    k6(VU)->>User: 결과 확인
Loading

Assessment against linked issues

Objective Addressed Explanation
SSE 기반 ai 피드백 워커 수를 16개로 증가하여 병렬 처리 구현 (#170)
k6, Prometheus, Grafana 연동 설정 및 부하 테스트 스크립트 추가 (#170)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
TestDataInitializer 클래스 추가 (cs25-service/.../test/TestDataInitializer.java) 테스트 데이터 초기화는 부하 테스트 지원 목적 외 별도 기능으로 보임.
FallbackAiChatClient의 MOCK_AI 환경 변수 처리 추가 (cs25-service/.../FallbackAiChatClient.java) 부하 테스트 목적 외 테스트용 AI 응답 처리 기능 추가임.

Possibly related PRs

Poem

🐇
부하를 견디는 토끼의 힘,
스레드가 열여섯, 큐도 넓어짐!
MOCK_AI라면 답도 뚝딱,
도커와 k6로 테스트는 착착.
데이터도 자동, 피드백은 빠름,
오늘도 코드는 한층 더 아름!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ebb82b and 22b8e95.

📒 Files selected for processing (3)
  • cs25-entity/src/main/java/com/example/cs25entity/domain/user/repository/UserRepository.java (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/FallbackAiChatClient.java (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/test/TestDataInitializer.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/FallbackAiChatClient.java
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/test/TestDataInitializer.java
🔇 Additional comments (2)
cs25-entity/src/main/java/com/example/cs25entity/domain/user/repository/UserRepository.java (2)

47-47: 메서드 추가 승인 - 성능 최적화에 도움됩니다

existsByEmail 메서드 추가가 적절합니다. 이 메서드는 전체 User 엔티티를 로드하지 않고 존재 여부만 확인하므로 findByEmail(...).isPresent() 보다 성능상 효율적입니다.


26-32: 기존 코드 최적화 고려사항

validateSocialJoinEmail 메서드에서 findByEmail(email).ifPresent(...)를 사용하고 있습니다. 하지만 여기서는 User 객체의 socialType을 확인해야 하므로 새로운 existsByEmail 메서드로 대체할 수 없습니다. 현재 구현이 적절합니다.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
docker-compose.yml (1)

134-134: YAML 파일 끝에 개행 문자를 추가하세요.

YAMLlint에서 파일 끝에 개행 문자가 없다고 경고하고 있습니다.

파일 끝에 빈 줄을 추가하세요:

 networks:
   monitoring:
+
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/AiFeedbackQueueService.java (2)

24-24: 워커 수를 설정 가능하게 만드는 것을 고려해보세요.

고정된 16개의 워커는 현재 부하 테스트에는 적합하지만, 운영 환경에서는 다른 값이 더 적절할 수 있습니다.

-private final int WORKER_COUNT = 16;
+@Value("${ai.feedback.worker.count:16}")
+private int workerCount;

application.yml에서 설정할 수 있도록 하면 더 유연한 구성이 가능합니다.


26-29: 스레드 이름 지정 방식을 개선해보세요.

현재 r.hashCode()를 사용한 스레드 이름은 가독성이 떨어집니다.

-private final ExecutorService executor = Executors.newFixedThreadPool(
-    WORKER_COUNT,
-    r -> new Thread(r, "ai-feedback-worker-" + r.hashCode())
-);
+private final ExecutorService executor = Executors.newFixedThreadPool(
+    WORKER_COUNT,
+    new ThreadFactoryBuilder()
+        .setNameFormat("ai-feedback-worker-%d")
+        .setDaemon(true)
+        .build()
+);

또는 AtomicInteger를 사용하여 순차적인 번호를 부여할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 272e29c and 1ebb82b.

⛔ Files ignored due to path filters (1)
  • .DS_Store is excluded by !**/.DS_Store
📒 Files selected for processing (6)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/FallbackAiChatClient.java (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/service/AiFeedbackQueueService.java (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/service/AiFeedbackStreamProcessor.java (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/test/TestDataInitializer.java (1 hunks)
  • docker-compose.yml (1 hunks)
  • k6/scripts/sse-test.js (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
docker-compose.yml

[error] 134-134: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (7)
k6/scripts/sse-test.js (1)

1-23: 로드 테스트 스크립트가 잘 구성되었습니다.

500명의 동시 사용자로 60초간 SSE 엔드포인트를 테스트하는 설정이 적절합니다. SSE 헤더와 타임아웃 설정도 올바르게 구성되어 있고, 1-1000 범위의 랜덤 answer ID 생성이 테스트 데이터와 일치합니다.

cs25-service/src/main/java/com/example/cs25service/domain/ai/service/AiFeedbackStreamProcessor.java (2)

33-33: 사용자 답변 조회 로직 변경이 적절합니다.

findWithQuizAndUserById에서 findById로 변경하여 불필요한 조인을 제거했습니다. 이후 코드에서 answer.getQuiz()answer.getUser()를 통해 필요한 데이터에 접근하므로 로직상 문제없습니다.


42-49: 테스트용 AI 응답 모킹이 잘 구현되었습니다.

실제 AI 호출을 300ms 대기와 하드코딩된 응답으로 대체하여 로드 테스트 중 API 비용을 절약하면서도 실제 응답 시간을 시뮬레이션합니다. FallbackAiChatClient의 모킹 로직과 일관성을 유지하고 있습니다.

docker-compose.yml (1)

108-125: k6 로드 테스트 설정이 잘 구성되었습니다.

k6 서비스가 Prometheus와 통합되어 메트릭을 수집하고, host.docker.internal을 통해 호스트의 애플리케이션에 접근할 수 있도록 설정되어 있습니다. 네이티브 히스토그램 기능도 활성화되어 있어 상세한 성능 메트릭 수집이 가능합니다.

cs25-service/src/main/java/com/example/cs25service/domain/ai/service/AiFeedbackQueueService.java (3)

23-23: 큐 용량 증가가 적절합니다.

부하 테스트 요구사항에 맞춰 큐 용량을 100에서 500으로 증가시킨 것은 적절한 변경입니다.


35-37: 멀티 워커 초기화 로직이 올바릅니다.

여러 워커를 동시에 시작하는 로직이 정확하게 구현되었습니다. BlockingQueue는 스레드 안전하므로 여러 워커가 동시에 큐를 처리하는데 문제없습니다.


52-63: 동시성 처리 시 주의사항을 확인해보세요.

현재 구현은 올바르지만, AiFeedbackStreamProcessor.stream() 메서드가 멀티 스레드 환경에서 안전한지 확인이 필요합니다.

다음 스크립트로 스트림 프로세서의 스레드 안전성을 확인해보세요:

#!/bin/bash
# Description: AiFeedbackStreamProcessor의 thread-safety 관련 구현을 확인합니다.

# AiFeedbackStreamProcessor 클래스의 stream 메서드 구현 확인
ast-grep --pattern 'class AiFeedbackStreamProcessor {
  $$$
  public $_ stream($_, $_) {
    $$$
  }
  $$$
}'

# 공유 상태나 동시성 이슈 가능성 확인
rg -A 10 -B 5 "class AiFeedbackStreamProcessor"

@HeeMang-Lee HeeMang-Lee merged commit 1523163 into dev Jun 25, 2025
1 check passed
@HeeMang-Lee HeeMang-Lee deleted the feat/170 branch June 25, 2025 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AiSerivce 피드백 부하 테스트 및 병렬 워커 풀 증가

3 participants