diff --git a/kamco-make-dataset-generation/build/libs/generator-dataset-for-training.jar b/kamco-make-dataset-generation/build/libs/generator-dataset-for-training.jar index 52ec8f2..347b389 100644 Binary files a/kamco-make-dataset-generation/build/libs/generator-dataset-for-training.jar and b/kamco-make-dataset-generation/build/libs/generator-dataset-for-training.jar differ diff --git a/kamco-make-dataset-generation/src/main/java/com/kamco/cd/geojsonscheduler/batch/LaunchChildJobsTasklet.java b/kamco-make-dataset-generation/src/main/java/com/kamco/cd/geojsonscheduler/batch/LaunchChildJobsTasklet.java index d29e2ef..c12f6d9 100644 --- a/kamco-make-dataset-generation/src/main/java/com/kamco/cd/geojsonscheduler/batch/LaunchChildJobsTasklet.java +++ b/kamco-make-dataset-generation/src/main/java/com/kamco/cd/geojsonscheduler/batch/LaunchChildJobsTasklet.java @@ -18,35 +18,44 @@ import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; /** * Child Job 실행 Tasklet (Parent Job용) * - *

진행 중인 모든 분석 회차(AnalCntInfo)를 조회하여 각 회차마다 독립적인 Child Job을 실행합니다. 각 Child Job은 3개의 + *

+ * 진행 중인 모든 분석 회차(AnalCntInfo)를 조회하여 각 회차마다 독립적인 Child Job을 실행합니다. 각 Child Job은 + * 3개의 * Step(makeGeoJson → dockerRun → zipResponse)을 순차적으로 실행합니다. * - *

주요 기능: + *

+ * 주요 기능: * *

* - *

실행 조건: + *

+ * 실행 조건: * *

* - *

실패 정책: + *

+ * 실패 정책: * *

* * @author KAMCO Development Team @@ -69,10 +78,15 @@ public class LaunchChildJobsTasklet implements Tasklet { @Qualifier("processAnalCntInfoJob") private final Job processAnalCntInfoJob; + /** 트랜잭션 매니저 (트랜잭션 제어용) */ + private final PlatformTransactionManager transactionManager; + /** * Parent Job의 메인 로직 실행 * - *

진행 중인 모든 분석 회차를 조회하여 각 회차마다 Child Job을 실행합니다. 한 회차가 실패해도 다른 회차는 계속 처리되며, 최종적으로 통계를 + *

+ * 진행 중인 모든 분석 회차를 조회하여 각 회차마다 Child Job을 실행합니다. 한 회차가 실패해도 다른 회차는 계속 처리되며, + * 최종적으로 통계를 * 로깅합니다. * * @param contribution Step 실행 정보를 담는 객체 @@ -133,12 +147,11 @@ public class LaunchChildJobsTasklet implements Tasklet { try { // Child Job Parameters 생성 - JobParameters jobParameters = - new JobParametersBuilder() - .addLong("analUid", info.getAnalUid()) - .addString("resultUid", info.getResultUid()) - .addLong("timestamp", System.currentTimeMillis()) // JobInstance 고유성 보장 - .toJobParameters(); + JobParameters jobParameters = new JobParametersBuilder() + .addLong("analUid", info.getAnalUid()) + .addString("resultUid", info.getResultUid()) + .addLong("timestamp", System.currentTimeMillis()) // JobInstance 고유성 보장 + .toJobParameters(); log.info("[Child Job 실행] processAnalCntInfoJob 시작..."); log.info(" - JobParameters: analUid={}, resultUid={}", info.getAnalUid(), @@ -148,7 +161,21 @@ public class LaunchChildJobsTasklet implements Tasklet { // asyncJobLauncher를 사용하여 별도 쓰레드에서 실행 // 내부적으로 makeGeoJsonStep → dockerRunStep → zipResponseStep 순차 실행 long startTime = System.currentTimeMillis(); - JobExecution jobExecution = asyncJobLauncher.run(processAnalCntInfoJob, jobParameters); + + // 트랜잭션 일시 정지 후 실행 (Existing transaction detected 에러 방지) + JobExecution jobExecution = new TransactionTemplate(transactionManager, + new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NOT_SUPPORTED)) + .execute(status -> { + try { + return asyncJobLauncher.run(processAnalCntInfoJob, jobParameters); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + if (jobExecution == null) { + throw new RuntimeException("JobExecution is null after launching child job"); + } // Child Job 완료 대기 (비동기 실행이므로 완료를 폴링) log.info("[Child Job 대기] 실행 완료 대기 중... (JobExecutionId={})", @@ -222,8 +249,7 @@ public class LaunchChildJobsTasklet implements Tasklet { // 성공률 계산 if (analList.size() > 0) { - double successRate = - (double) processedCount / (analList.size() - skippedCount) * 100; + double successRate = (double) processedCount / (analList.size() - skippedCount) * 100; log.info(" - 성공률: {}% (건너뛴 회차 제외)", String.format("%.2f", successRate)); } @@ -239,8 +265,8 @@ public class LaunchChildJobsTasklet implements Tasklet { // 실패가 있어도 Parent Job은 성공으로 처리 (부분 성공 정책) // 만약 하나라도 실패하면 Parent Job도 실패로 처리하려면 아래 주석 해제 // throw new RuntimeException( - // String.format("%d 개의 Child Job 실행이 실패했습니다. (성공: %d, 실패: %d)", - // failedCount, processedCount, failedCount)); + // String.format("%d 개의 Child Job 실행이 실패했습니다. (성공: %d, 실패: %d)", + // failedCount, processedCount, failedCount)); } else { log.info("[완료] 모든 Child Job이 정상적으로 완료되었습니다."); }