Skip to content

Commit 46bb91a

Browse files
authored
Merge pull request #52 from JobDri-Developer/fix/#26-apply
[Feat] 비동기 처리 로직 (#38)
2 parents db97af0 + fd3e227 commit 46bb91a

7 files changed

Lines changed: 69 additions & 11 deletions

File tree

src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/request/JobPostingIngestCommand.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@ public class JobPostingIngestCommand {
1414
private byte[] imageBytes;
1515
private String imageContentType;
1616
private CompanySize companySize;
17-
private Integer candidateLimit;
1817
}

src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/request/JobPostingIngestMultipartRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
import org.springframework.web.multipart.MultipartFile;
55

66
public record JobPostingIngestMultipartRequest(String rawText, String sourceUrl, MultipartFile image,
7-
CompanySize companySize, Integer candidateLimit) {
7+
CompanySize companySize) {
88

99
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.jobdri.jobdri_api.domain.jobposting.service;
2+
3+
import com.jobdri.jobdri_api.domain.company.entity.Company;
4+
import com.jobdri.jobdri_api.domain.jobposting.dto.request.JobPostingMockGenerateRequest;
5+
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingMockGenerateResponse;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.scheduling.annotation.Async;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.util.List;
11+
import java.util.concurrent.CompletableFuture;
12+
13+
@Service
14+
@RequiredArgsConstructor
15+
public class JobPostingAiAsyncService {
16+
17+
private final JobPostingAiService jobPostingAiService;
18+
private final MockQuestionCacheService mockQuestionCacheService;
19+
20+
@Async("llmAsyncExecutor")
21+
public CompletableFuture<JobPostingMockGenerateResponse> generateMockJobPosting(
22+
JobPostingMockGenerateRequest request,
23+
Company company
24+
) {
25+
return CompletableFuture.completedFuture(
26+
jobPostingAiService.generateMockJobPosting(request, company)
27+
);
28+
}
29+
30+
@Async("llmAsyncExecutor")
31+
public CompletableFuture<List<String>> getRecommendedQuestions(JobPostingMockGenerateRequest request) {
32+
return CompletableFuture.completedFuture(
33+
mockQuestionCacheService.getRecommendedQuestions(request)
34+
);
35+
}
36+
}

src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncFacadeService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ private JobPostingIngestCommand snapshot(User user, JobPostingIngestMultipartReq
5555
.imageBytes(readBytes(request.image()))
5656
.imageContentType(readContentType(request.image()))
5757
.companySize(request.companySize())
58-
.candidateLimit(request.candidateLimit())
5958
.build();
6059
}
6160

src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingIngestService.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
@RequiredArgsConstructor
2525
public class JobPostingIngestService {
2626

27-
private static final int DEFAULT_CANDIDATE_LIMIT = 10;
27+
private static final int FIXED_CANDIDATE_LIMIT = 5;
2828

2929
@Value("${job-posting.ingest.classification-confidence-threshold:0.65}")
3030
private double classificationConfidenceThreshold;
@@ -40,7 +40,6 @@ public JobPostingIngestResponse ingestAndCreate(User user, JobPostingIngestMulti
4040
.rawText(request.rawText())
4141
.sourceUrl(request.sourceUrl())
4242
.companySize(request.companySize())
43-
.candidateLimit(request.candidateLimit())
4443
.build();
4544
return ingestAndCreate(command);
4645
}
@@ -54,9 +53,8 @@ public JobPostingIngestResponse ingestAndCreate(JobPostingIngestCommand command)
5453
command.getSourceUrl()
5554
);
5655

57-
int candidateLimit = command.getCandidateLimit() == null ? DEFAULT_CANDIDATE_LIMIT : command.getCandidateLimit();
5856
List<JobPostingClassificationCandidateResponse> candidates =
59-
jobPostingClassificationService.findCandidates(extracted, candidateLimit);
57+
jobPostingClassificationService.findCandidates(extracted, FIXED_CANDIDATE_LIMIT);
6058

6159
if (candidates.isEmpty()) {
6260
throw new GeneralException(

src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/MockJobPostingGenerationService.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
import org.springframework.stereotype.Service;
1111
import org.springframework.transaction.annotation.Transactional;
1212

13+
import java.util.concurrent.CompletableFuture;
14+
1315
@Service
1416
@RequiredArgsConstructor
1517
@Transactional(readOnly = true)
1618
public class MockJobPostingGenerationService {
1719

1820
private final CompanyRepository companyRepository;
19-
private final JobPostingAiService jobPostingAiService;
20-
private final MockQuestionCacheService mockQuestionCacheService;
21+
private final JobPostingAiAsyncService jobPostingAiAsyncService;
2122

2223
public JobPostingMockGenerateResponse generate(JobPostingMockGenerateRequest request) {
2324
Company company = companyRepository.findById(request.companyId())
@@ -26,15 +27,22 @@ public JobPostingMockGenerateResponse generate(JobPostingMockGenerateRequest req
2627
"해당 회사를 찾을 수 없습니다. companyId=" + request.companyId()
2728
));
2829

29-
JobPostingMockGenerateResponse generatedPosting = jobPostingAiService.generateMockJobPosting(request, company);
30+
CompletableFuture<JobPostingMockGenerateResponse> generatedPostingFuture =
31+
jobPostingAiAsyncService.generateMockJobPosting(request, company);
32+
CompletableFuture<java.util.List<String>> recommendedQuestionsFuture =
33+
jobPostingAiAsyncService.getRecommendedQuestions(request);
34+
35+
CompletableFuture.allOf(generatedPostingFuture, recommendedQuestionsFuture).join();
36+
JobPostingMockGenerateResponse generatedPosting = generatedPostingFuture.join();
37+
3038
return new JobPostingMockGenerateResponse(
3139
generatedPosting.companyName(),
3240
generatedPosting.jobTitle(),
3341
generatedPosting.task(),
3442
generatedPosting.requirement(),
3543
generatedPosting.preferred(),
3644
generatedPosting.summary(),
37-
mockQuestionCacheService.getRecommendedQuestions(request)
45+
recommendedQuestionsFuture.join()
3846
);
3947
}
4048
}

src/main/java/com/jobdri/jobdri_api/global/config/AsyncConfig.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,22 @@ public ThreadPoolTaskExecutor mailAsyncExecutor(
4545
executor.initialize();
4646
return executor;
4747
}
48+
49+
@Bean(name = "llmAsyncExecutor")
50+
public ThreadPoolTaskExecutor llmAsyncExecutor(
51+
@Value("${async.llm.core-pool-size:2}") int corePoolSize,
52+
@Value("${async.llm.max-pool-size:4}") int maxPoolSize,
53+
@Value("${async.llm.queue-capacity:20}") int queueCapacity
54+
) {
55+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
56+
executor.setThreadNamePrefix("llm-async-");
57+
executor.setCorePoolSize(corePoolSize);
58+
executor.setMaxPoolSize(maxPoolSize);
59+
executor.setQueueCapacity(queueCapacity);
60+
executor.setAllowCoreThreadTimeOut(true);
61+
executor.setWaitForTasksToCompleteOnShutdown(false);
62+
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
63+
executor.initialize();
64+
return executor;
65+
}
4866
}

0 commit comments

Comments
 (0)