Skip to content

Refactor/#70 AI 추천 룸메이트 조회 기능 성능 개선#71

Merged
1winhyun merged 1 commit into
mainfrom
refactor/#70
May 29, 2026
Merged

Refactor/#70 AI 추천 룸메이트 조회 기능 성능 개선#71
1winhyun merged 1 commit into
mainfrom
refactor/#70

Conversation

@1winhyun

@1winhyun 1winhyun commented May 29, 2026

Copy link
Copy Markdown
Collaborator

🔗 관련 이슈


✅ PR 유형

어떤 변경 사항이 있었나요?

  • 새로운 기능 추가
  • 버그 수정
  • 리팩토링
  • 코드에 영향을 주지 않는 변경사항(주석, 개행 등등..)
  • 문서 수정
  • 빌드 부분 혹은 패키지 매니저 수정
  • 테스트 코드 추가

✏️ 작업 내용

AI 추천 룸메이트 조회 기능을 청크 분할 병렬 호출로 개선했습니다.

  • 기존 444ms의 API 동작 시간을 224ms로 줄여 약 50%의 성능향상을 이루었습니다.
스크린샷 2026-05-29 오후 4 14 45 스크린샷 2026-05-29 오후 4 30 13

💡 추가 사항

Summary by CodeRabbit

  • Refactor

    • Enhanced the AI roommate recommendation system architecture to efficiently process larger candidate pools through optimized concurrent processing.
  • Tests

    • Added comprehensive test coverage for roommate recommendation processing workflows.

Review Change Stack

@1winhyun 1winhyun self-assigned this May 29, 2026
@1winhyun 1winhyun added ♻️Refactor 리팩토링 ✅Test 테스트 코드 작성 labels May 29, 2026
@1winhyun 1winhyun linked an issue May 29, 2026 that may be closed by this pull request
1 task
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements chunked parallel processing for AI roommate recommendations. It adds a configurable chunk size property, creates a thread pool executor bean, refactors the recommendation usecase to conditionally analyze candidates in parallel batches, and adds comprehensive test coverage for both single-batch and multi-chunk scenarios.

Changes

Chunked Parallel AI Recommendation Analysis

Layer / File(s) Summary
Configuration Setup and Executor Bean
src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchApiProperties.java, src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchClientConfig.java, src/main/resources/application-dev.yaml, src/main/resources/application-local.yaml
AiMatchApiProperties record adds recommendedRoommatesChunkSize property. AiMatchClientConfig introduces aiMatchTaskExecutor() bean with ThreadPoolTaskExecutor configured for core/max pool sizing, queue capacity, thread naming, and graceful shutdown. Both dev and local profiles set recommended-roommates-chunk-size: 5.
Usecase Refactoring with Parallel Chunking
src/main/java/com/project/bangjjack/domain/user/application/usecase/AiRecommendedRoommatesUseCase.java
Constructor updated from Lombok-generated to explicit, adding @Qualifier("aiMatchTaskExecutor") Executor dependency. getRecommended now reads topK and chunkSize from configuration and routes through new analyze(...) method. analyze(...) conditionally performs single-batch analysis or delegates to analyzeInParallel(...) for chunked execution. Parallel path splits candidates into chunks, runs CompletableFuture.supplyAsync calls via executor, merges results by match rate (descending) and candidate index, trims to topK, re-ranks sequentially, and maps CompletionException failures to AiServiceUnavailableException.
Test Setup and Parallel Execution Coverage
src/test/java/com/project/bangjjack/domain/user/application/usecase/AiRecommendedRoommatesUseCaseTest.java
Test setup refactored to explicit constructor with synchronous executor. New GetRecommendedParallel nested suite validates single-call behavior when candidates.size() <= chunkSize, multi-chunk execution with global merge and re-ranking, per-chunk candidate/topK partitioning, and exception propagation preventing cache save on chunk failure.

Sequence Diagram

sequenceDiagram
  participant Client
  participant AiRecommendedRoommatesUseCase
  participant analyze as analyze()
  participant analyzeInParallel as analyzeInParallel()
  participant Executor
  participant MatchBatchAnalysisPort
  Client->>AiRecommendedRoommatesUseCase: getRecommended(command)
  AiRecommendedRoommatesUseCase->>analyze: read topK and chunkSize
  alt chunkSize <= 0 or covers all candidates
    analyze->>MatchBatchAnalysisPort: single analyze call
  else chunked parallel path
    analyze->>analyzeInParallel: split into chunks
    analyzeInParallel->>Executor: supplyAsync for each chunk
    Executor->>MatchBatchAnalysisPort: analyze chunk 1
    Executor->>MatchBatchAnalysisPort: analyze chunk 2
    Executor->>MatchBatchAnalysisPort: analyze chunk N
    MatchBatchAnalysisPort-->>analyzeInParallel: results
    analyzeInParallel->>analyzeInParallel: merge + sort + trim + re-rank
  end
  analyze-->>AiRecommendedRoommatesUseCase: MatchBatchResult
  AiRecommendedRoommatesUseCase-->>Client: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • Bangjjack/Bangjjack-Backend#70: Implements the asynchronous, chunked AI roommate recommendation approach with executor-backed parallel analysis matching the issue's objective to refactor recommendation to async calls.

  • Bangjjack/Bangjjack-Backend#56: Extends the same AI roommate match-rate analysis infrastructure with configurable chunking and parallelization.

Possibly related PRs

  • Bangjjack/Bangjjack-Backend#60: Introduces foundational changes to AiRecommendedRoommatesUseCase and AiMatchApiProperties; this PR extends that implementation with executor-backed chunked parallel analysis.

Suggested reviewers

  • xEzIxX

Poem

🐰 A rabbit hops through chunks so clean,
Parallel dreams in executor streams,
Matching roommates swift and right,
Threading futures, futures bright! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: refactoring the AI recommended roommate feature with performance improvements through chunked parallel processing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#70

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchApiProperties.java`:
- Around line 13-14: AiMatchApiProperties binds recommendedRoommatesChunkSize as
a primitive int so profiles missing the recommended-roommates-chunk-size key
will silently get 0; to fix either add the recommended-roommates-chunk-size
property to every active Spring profile's config (ensure prod/other profiles
include the key with a sensible value) OR change the field in
AiMatchApiProperties from primitive int to Integer and add validation (e.g.,
`@NotNull` or an explicit check in the constructor) so missing bindings fail fast
and cannot default to 0.

In
`@src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchClientConfig.java`:
- Around line 21-32: The aiMatchTaskExecutor currently uses
ThreadPoolTaskExecutor with corePoolSize=5, maxPoolSize=10 and queueCapacity=50
which means the pool will not grow past 5 threads until the queue fills and will
use the default AbortPolicy on rejection; update aiMatchTaskExecutor to set an
explicit rejection handler (e.g., ThreadPoolExecutor.CallerRunsPolicy) via
setRejectedExecutionHandler(...) to degrade gracefully, and if you want
immediate parallelism up to maxPoolSize adjust the queuing strategy (e.g.,
reduce queueCapacity to 0 or use a synchronous queue) or set corePoolSize equal
to maxPoolSize so the pool can utilize all threads; refer to
aiMatchTaskExecutor, ThreadPoolTaskExecutor, setQueueCapacity, setCorePoolSize,
setMaxPoolSize and setRejectedExecutionHandler when making the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a34b0a9-481f-479e-ac23-673b31a31292

📥 Commits

Reviewing files that changed from the base of the PR and between ea6baf4 and ccfa2e3.

📒 Files selected for processing (6)
  • src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchApiProperties.java
  • src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchClientConfig.java
  • src/main/java/com/project/bangjjack/domain/user/application/usecase/AiRecommendedRoommatesUseCase.java
  • src/main/resources/application-dev.yaml
  • src/main/resources/application-local.yaml
  • src/test/java/com/project/bangjjack/domain/user/application/usecase/AiRecommendedRoommatesUseCaseTest.java

Comment on lines +13 to +14
long recommendedRoommatesCacheTtl,
int recommendedRoommatesChunkSize

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all Spring profile yaml/properties and check which define the chunk-size key.
fd -e yaml -e yml -e properties 'application' src/main/resources
echo '--- occurrences of recommended-roommates-chunk-size ---'
rg -n 'recommended-roommates-chunk-size|recommendedRoommatesChunkSize' src/main/resources

Repository: Bangjjack/Bangjjack-Backend

Length of output: 377


Ensure recommended-roommates-chunk-size is defined for all active Spring profiles.

AiMatchApiProperties binds recommendedRoommatesChunkSize as a primitive int, so any profile omitting recommended-roommates-chunk-size will bind it to 0. The key is currently present only in src/main/resources/application-dev.yaml and src/main/resources/application-local.yaml, so other profiles (e.g., prod, if used) may silently fall back to 0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchApiProperties.java`
around lines 13 - 14, AiMatchApiProperties binds recommendedRoommatesChunkSize
as a primitive int so profiles missing the recommended-roommates-chunk-size key
will silently get 0; to fix either add the recommended-roommates-chunk-size
property to every active Spring profile's config (ensure prod/other profiles
include the key with a sensible value) OR change the field in
AiMatchApiProperties from primitive int to Integer and add validation (e.g.,
`@NotNull` or an explicit check in the constructor) so missing bindings fail fast
and cannot default to 0.

Comment on lines +21 to +32
@Bean(name = "aiMatchTaskExecutor", destroyMethod = "shutdown")
public Executor aiMatchTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("ai-match-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(10);
executor.initialize();
return executor;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the default RejectedExecutionHandler for Spring ThreadPoolTaskExecutor, and does it grow beyond corePoolSize only after the queue is full?

💡 Result:

The default RejectedExecutionHandler for a Spring ThreadPoolTaskExecutor is the AbortPolicy [1]. This handler throws a RejectedExecutionException when a task cannot be accepted [1]. Regarding the thread pool scaling behavior, the ThreadPoolTaskExecutor indeed follows the standard rules of the underlying java.util.concurrent.ThreadPoolExecutor [2][3]. The pool grows beyond the corePoolSize only after the work queue is full [3][4][5]. Specifically: 1. If fewer than corePoolSize threads are running, the executor always creates a new thread for a new task [3][4][5]. 2. If corePoolSize or more threads are running, the executor prefers to queue the request rather than creating a new thread [3]. 3. Only if the queue is full and the current pool size is less than the maxPoolSize will the executor create a new thread beyond the corePoolSize [3][4][5]. 4. If the queue is full and the pool has already reached its maxPoolSize, the RejectedExecutionHandler is triggered [3][4]. It is important to note that by default, the queueCapacity of a ThreadPoolTaskExecutor is Integer.MAX_VALUE (effectively unbounded) [3][6]. Because the queue rarely becomes full with an unbounded capacity, the pool will typically never grow beyond its corePoolSize unless the queueCapacity is explicitly configured to a smaller, finite value [3][5].

Citations:


Update rejection/sizing guidance for aiMatchTaskExecutor (ThreadPoolTaskExecutor)

  • Default RejectedExecutionHandler for Spring ThreadPoolTaskExecutor is AbortPolicy, so with queueCapacity=50 and maxPoolSize=10 rejections can occur when the pool is saturated; consider setting CallerRunsPolicy (or explicitly mapping rejections) to degrade gracefully.
  • ThreadPoolTaskExecutor only grows beyond corePoolSize=5 once the queue is full, so with a finite queue this effectively limits parallelism to core threads until the queue fills.
Optional: graceful rejection handling
         executor.setThreadNamePrefix("ai-match-");
+        executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());
         executor.setWaitForTasksToCompleteOnShutdown(true);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchClientConfig.java`
around lines 21 - 32, The aiMatchTaskExecutor currently uses
ThreadPoolTaskExecutor with corePoolSize=5, maxPoolSize=10 and queueCapacity=50
which means the pool will not grow past 5 threads until the queue fills and will
use the default AbortPolicy on rejection; update aiMatchTaskExecutor to set an
explicit rejection handler (e.g., ThreadPoolExecutor.CallerRunsPolicy) via
setRejectedExecutionHandler(...) to degrade gracefully, and if you want
immediate parallelism up to maxPoolSize adjust the queuing strategy (e.g.,
reduce queueCapacity to 0 or use a synchronous queue) or set corePoolSize equal
to maxPoolSize so the pool can utilize all threads; refer to
aiMatchTaskExecutor, ThreadPoolTaskExecutor, setQueueCapacity, setCorePoolSize,
setMaxPoolSize and setRejectedExecutionHandler when making the changes.

@1winhyun 1winhyun merged commit 3ded562 into main May 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

♻️Refactor 리팩토링 ✅Test 테스트 코드 작성

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] AI 룸메이트 추천 기능 비동기 호출로 수정

1 participant