From e4a385351ebc3ecb756cb66452a95283b511e00d Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Wed, 11 Mar 2026 22:46:38 +0900 Subject: [PATCH] =?UTF-8?q?label-to-review=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../label-to-review/.gradle/file-system.probe | Bin 8 -> 8 bytes .../service/TrainingDataLabelJobService.class | Bin 9594 -> 10362 bytes .../service/TrainingDataLabelJobService.java | 118 +++++++++++++----- 4 files changed, 84 insertions(+), 34 deletions(-) diff --git a/label/label-to-review/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/label/label-to-review/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 4d27b0fd9b0a50d06cec0b0ca005931a9be22773..8b68d2326d8045c315a435b39105a6c86d2d6d78 100644 GIT binary patch literal 17 UcmZSfJm9soWWiG(1_<~D05xR=Hvj+t literal 17 UcmZSfJm9soWWiG(1_-zW05w4c6951J diff --git a/label/label-to-review/.gradle/file-system.probe b/label/label-to-review/.gradle/file-system.probe index e860189107ab818ef90aa9dfcd98bce6db39beed..e5ffdf6c7088ecb53bad0479cc0715751e6db2ee 100644 GIT binary patch literal 8 PcmZQzV4QPTxiA|52t)#U literal 8 PcmZQzV4SmQq2o#b2=D^i diff --git a/label/label-to-review/build/classes/java/main/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.class b/label/label-to-review/build/classes/java/main/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.class index 0ba19d4ecb4c7275f85f812c12f81ca519a55cd4..dd6f141335244d1becf8a05ab1cf42309c4f26b7 100644 GIT binary patch literal 10362 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 literal 9594 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 diff --git a/label/label-to-review/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java b/label/label-to-review/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java index 82663b1..cbbd270 100644 --- a/label/label-to-review/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java +++ b/label/label-to-review/src/main/java/com/kamco/cd/kamcoback/scheduler/service/TrainingDataLabelJobService.java @@ -33,7 +33,6 @@ public class TrainingDataLabelJobService { } public void runTask() { - // 프록시를 통해 호출해야 @Transactional이 적용됨 applicationContext .getBean(TrainingDataLabelJobService.class) .assignReviewerYesterdayLabelComplete(null); @@ -42,87 +41,136 @@ public class TrainingDataLabelJobService { @Transactional public void assignReviewerYesterdayLabelComplete(LocalDate baseDate) { + long jobStart = System.currentTimeMillis(); + log.info("[JOB-START] TrainingDataLabelJob start profile={}", profile); + try { - log.info("[Step 1-1] 라벨링 완료된 데이터 목록 조회한다."); - log.info("=== baseDate : {}", baseDate); - log.info("=== baseDate 있으면 해당 일자, 없으면 어제일자로 조회"); + + log.info("[Step 1-1] 라벨링 완료된 데이터 조회 시작 baseDate={}", baseDate); + List tasks = trainingDataLabelJobCoreService.findCompletedYesterdayUnassigned(baseDate); - log.info("[Step 1-2] 목록 객체 건수 count : {}", tasks == null ? 0 : tasks.size()); - if (tasks.isEmpty()) { - log.info("[Step 1-3] 조회된 것 없어 return"); + int totalTasks = tasks == null ? 0 : tasks.size(); + + log.info("[Step 1-2] 조회된 작업 건수={}", totalTasks); + + if (tasks == null || tasks.isEmpty()) { + log.info("[Step 1-3] 조회된 데이터 없음 → 종료"); return; } - // 회차별로 그룹핑 - log.info("[Step 2-1] 회차별로 그룹핑 시작"); + log.info("[Step 2-1] 회차별 그룹핑 시작"); + Map> taskByRound = tasks.stream().collect(Collectors.groupingBy(Tasks::getAnalUid)); - // 회차별 분배 - log.info("[Step 3-1] 회차별로 분배 시작"); + log.info("[Step 2-2] 회차 수={}", taskByRound.size()); + + int successRound = 0; + int skipRound = 0; + for (Map.Entry> entry : taskByRound.entrySet()) { + + long roundStart = System.currentTimeMillis(); + Long analUid = entry.getKey(); List analTasks = entry.getValue(); - // pending 계산 - log.info("[Step 3-2] 수행하는 회차 analUid: {}", analUid); - log.info("해당 회차에 라벨링 할당받은 검수자별 완료 건수 count(), 완료한 게 적은 순으로 해야 일이 한 사람에게 몰리지 않음"); + log.info( + "[ROUND-START] analUid={} tasks={}", analUid, analTasks == null ? 0 : analTasks.size()); + + log.info("[Step 3-2] 해당 회차 검수자 pending 조회"); + List pendings = trainingDataLabelJobCoreService.findInspectorPendingByRound(analUid); - log.info("검수자 수: {}", pendings == null ? 0 : pendings.size()); - if (pendings.isEmpty()) { - log.info("[Step 3-3] 할당된 검수자가 없으면 return"); + int reviewerCount = pendings == null ? 0 : pendings.size(); + + log.info("[Step 3-3] 검수자 수={}", reviewerCount); + + if (pendings == null || pendings.isEmpty()) { + log.warn("[ROUND-SKIP] 검수자 없음 analUid={}", analUid); + skipRound++; continue; } - log.info("[Step 4-1] 검수자 사번 List 생성"); List reviewerIds = pendings.stream().map(InspectorPendingDto::getInspectorUid).toList(); - // Lock 걸릴 수 있기 때문에 엔티티 조회하는 Repository 에서 구현 - log.info("[Step 4-2] 검수자 테이블 lock 걸리지 않게 처리"); + log.info("[Step 4-1] 검수자 목록={}", reviewerIds); + + log.info("[Step 4-2] 검수자 row lock 수행"); + trainingDataLabelJobCoreService.lockInspectors(analUid, reviewerIds); - // 균등 분배 - log.info("[Step 5-1] 검수자에게 라벨 작업 균등분배 시작"); - Map> assignMap = distributeByLeastPending(analTasks, reviewerIds); - log.info("[Step 5-2] 검수자에게 라벨 작업 균등분배 완료"); + log.info("[Step 5-1] 라벨 작업 균등 분배 시작"); + + Map> assignMap = distributeByLeastPending(analTasks, reviewerIds); + + log.info("[Step 5-2] 라벨 작업 균등 분배 완료"); + + assignMap.forEach( + (reviewerId, assignedTasks) -> + log.info( + "[Step 5-3] reviewerId={} assignedCount={}", + reviewerId, + assignedTasks == null ? 0 : assignedTasks.size())); + + log.info("[Step 5-4] reviewer batch update 시작"); - // reviewer별 batch update - log.info("[Step 5-3] 검수자별 할당 데이터를 batch update 시작"); assignMap.forEach( (reviewerId, assignedTasks) -> { - if (assignedTasks.isEmpty()) { - log.info("[Step 5-4] 할당된 데이터 없으면 return"); + if (assignedTasks == null || assignedTasks.isEmpty()) { + log.debug("[Step 5-5] reviewer={} 할당 없음", reviewerId); return; } List assignmentUids = assignedTasks.stream().map(Tasks::getAssignmentUid).toList(); - log.info("[Step 6-1] 할당 작업에 검수자 아이디 update"); - log.info("==== 검수자 사번: {}", reviewerId); - log.info("==== 할당 갯수: {}", assignmentUids == null ? 0 : assignmentUids.size()); + + log.info( + "[Step 6-1] reviewer assignment update reviewerId={}, count={}", + reviewerId, + assignmentUids.size()); + trainingDataLabelJobCoreService.assignReviewerBatch(assignmentUids, reviewerId); - log.info("[Step 7-1] geom 테이블에 검수 상태 update"); List geomUids = assignedTasks.stream().map(Tasks::getInferenceUid).toList(); + + log.info("[Step 7-1] geom 상태 업데이트 geomCount={}", geomUids.size()); + trainingDataLabelJobCoreService.updateGeomUidTestState(geomUids); }); + + successRound++; + + log.info( + "[ROUND-END] analUid={} elapsed={}ms", + analUid, + System.currentTimeMillis() - roundStart); } + + log.info( + "[JOB-SUMMARY] rounds={}, successRounds={}, skipRounds={}, elapsed={}ms", + taskByRound.size(), + successRound, + skipRound, + System.currentTimeMillis() - jobStart); + } catch (Exception e) { - log.error("배치 처리 중 예외", e); + + log.error("[JOB-ERROR] 배치 처리 중 예외 발생", e); + throw e; } } private Map> distributeByLeastPending( List tasks, List reviewerIds) { + Map> result = new LinkedHashMap<>(); - // 순서 유지 중요 (ASC 정렬된 상태) for (String reviewerId : reviewerIds) { result.put(reviewerId, new ArrayList<>()); } @@ -130,7 +178,9 @@ public class TrainingDataLabelJobService { int reviewerCount = reviewerIds.size(); for (int i = 0; i < tasks.size(); i++) { + String reviewerId = reviewerIds.get(i % reviewerCount); + result.get(reviewerId).add(tasks.get(i)); }