Skip to content

Conversation

@Bumnote
Copy link
Member

@Bumnote Bumnote commented Sep 13, 2025

#️⃣ 연관된 이슈

#52

📝 작업 내용

  • 기존 동기 방식 -> Async + CompletableFuture 비동기 방식으로 전환
  • 가상 사용자 30명 기준 1초 동안 동시 요청을 했을 때, 전후 비교는 다음과 같습니다.

개선 전

image (2) image (5)

개선 후

image (3) image (4)

결론

  • 개선 후, 실패률이 더 높았을 뿐 아니라, Heap 사용량 또한 대폭 증가한 것을 확인할 수 있었습니다.

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요


Bumnote and others added 4 commits July 21, 2025 01:13
Release: 전반적인 코드 리팩터링 및 IP 화이트 리스트를 통한 모니터링 도구 접근 제한
- Async 비동기 방식 전환을 위한 Config 설정했습니다.
- 코어 풀 사이즈를 20으로 설정했습니다.
- 맥스 풀 사이즈를 50으로 설정했습니다.
- 대기 큐 용량을 100으로 설정했습니다.
issue #52
- Async 어노테이션을 활용하여 비동기 통신으로 전환했습니다.
- CompletableFuture 반환형을 모든 메서드에 적용했습니다.
- 트랜잭션을 분리하기 위해서 Persistence 작업을 위한 서비스를 따로 분리했습니다.
issue #52
@Bumnote Bumnote requested review from Oilwoo and Copilot September 13, 2025 06:32
@Bumnote Bumnote self-assigned this Sep 13, 2025
@Bumnote Bumnote added ♻️ refactor 리팩터링 이슈 🔥 feat 기능 구현 이슈 labels Sep 13, 2025
@Bumnote Bumnote merged commit 374957a into dev Sep 13, 2025
1 check passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR converts the travel analysis service from synchronous to asynchronous processing using @Async and CompletableFuture. The goal was to improve performance under concurrent load, though the PR description notes that the change actually resulted in higher failure rates and increased heap usage.

  • Introduced async processing with custom thread pool configuration
  • Refactored service layer to return CompletableFuture types
  • Added new persistence service to separate concerns

Reviewed Changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
application.yml Increased max-request-size and updated OpenAI model version
CustomRejectedExecutionHandler.java New custom handler for thread pool saturation with caller-runs fallback
AsyncConfig.java Thread pool configuration for OpenAI operations
RetripService.java Converted to async processing, removed business logic moved to persistence service
RetripPersistenceService.java New service handling database operations and entity building
OpenAiClient.java Added async annotation and CompletableFuture return type
GptImageAnalysisService.java Updated to work with async CompletableFuture pattern
RetripController.java Modified to handle async responses
README.md Updated project documentation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +16 to +21
try {
log.info("호출 스레드에서 작업 실행 재시도");
r.run();
} catch (Exception e) {
log.error("거부된 작업 재실행 중 오류 발생", e);
}
Copy link

Copilot AI Sep 13, 2025

Choose a reason for hiding this comment

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

Running rejected tasks synchronously on the caller thread defeats the purpose of async processing and can cause thread blocking. Consider implementing a proper backpressure mechanism or circuit breaker pattern instead.

Suggested change
try {
log.info("호출 스레드에서 작업 실행 재시도");
r.run();
} catch (Exception e) {
log.error("거부된 작업 재실행 중 오류 발생", e);
}
log.error("거부된 작업이 드롭되었습니다. (Rejected task dropped)");

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +58
.exceptionally(throwable -> {
log.error("전체 처리 과정에서 오류 발생", throwable);
throw new RuntimeException("여행 분석 처리 중 오류가 발생했습니다: " + throwable.getMessage(), throwable);
Copy link

Copilot AI Sep 13, 2025

Choose a reason for hiding this comment

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

Throwing an exception inside exceptionally() creates an unhandled exception that will be wrapped in a CompletionException. This should return a default value or use handle() method instead to properly manage the exception flow.

Suggested change
.exceptionally(throwable -> {
log.error("전체 처리 과정에서 오류 발생", throwable);
throw new RuntimeException("여행 분석 처리 중 오류가 발생했습니다: " + throwable.getMessage(), throwable);
.handle((result, throwable) -> {
if (throwable != null) {
log.error("전체 처리 과정에서 오류 발생", throwable);
throw new RuntimeException("여행 분석 처리 중 오류가 발생했습니다: " + throwable.getMessage(), throwable);
}
return result;

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +79
return CompletableFuture.failedFuture(
new RuntimeException("GPT API 호출 중 오류가 발생했습니다: " + e.getMessage(), e)
);
Copy link

Copilot AI Sep 13, 2025

Choose a reason for hiding this comment

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

Wrapping the original exception in a RuntimeException loses the specific exception type information. Consider returning the original exception using CompletableFuture.failedFuture(e) or creating a more specific custom exception type.

Suggested change
return CompletableFuture.failedFuture(
new RuntimeException("GPT API 호출 중 오류가 발생했습니다: " + e.getMessage(), e)
);
return CompletableFuture.failedFuture(e);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔥 feat 기능 구현 이슈 ♻️ refactor 리팩터링 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants