Refactor/#70 AI 추천 룸메이트 조회 기능 성능 개선#71
Conversation
📝 WalkthroughWalkthroughThis 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. ChangesChunked Parallel AI Recommendation Analysis
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
src/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchApiProperties.javasrc/main/java/com/project/bangjjack/domain/post/external/aimatch/AiMatchClientConfig.javasrc/main/java/com/project/bangjjack/domain/user/application/usecase/AiRecommendedRoommatesUseCase.javasrc/main/resources/application-dev.yamlsrc/main/resources/application-local.yamlsrc/test/java/com/project/bangjjack/domain/user/application/usecase/AiRecommendedRoommatesUseCaseTest.java
| long recommendedRoommatesCacheTtl, | ||
| int recommendedRoommatesChunkSize |
There was a problem hiding this comment.
🧩 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/resourcesRepository: 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.
| @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; | ||
| } |
There was a problem hiding this comment.
🧩 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:
- 1: https://www.baeldung.com/java-rejectedexecutionhandler
- 2: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html
- 3: https://stackoverflow.com/questions/44638056/whats-springs-default-queue-size-with-a-threadpooltaskexecutor
- 4: https://medium.com/@AlexanderObregon/thread-pool-sizing-in-spring-boot-async-executors-c880eeccf765
- 5: https://www.baeldung.com/java-threadpooltaskexecutor-core-vs-max-poolsize
- 6: https://docs.spring.io/spring-framework/docs/6.2.13/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html
Update rejection/sizing guidance for aiMatchTaskExecutor (ThreadPoolTaskExecutor)
- Default
RejectedExecutionHandlerfor SpringThreadPoolTaskExecutorisAbortPolicy, so withqueueCapacity=50andmaxPoolSize=10rejections can occur when the pool is saturated; consider settingCallerRunsPolicy(or explicitly mapping rejections) to degrade gracefully. - ThreadPoolTaskExecutor only grows beyond
corePoolSize=5once 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.
🔗 관련 이슈
✅ PR 유형
어떤 변경 사항이 있었나요?
✏️ 작업 내용
AI 추천 룸메이트 조회 기능을 청크 분할 병렬 호출로 개선했습니다.
💡 추가 사항
Summary by CodeRabbit
Refactor
Tests