From 7a22d8ba7315033c855be90b76540ec0bc529781 Mon Sep 17 00:00:00 2001 From: teddy Date: Thu, 12 Feb 2026 15:39:12 +0900 Subject: [PATCH] =?UTF-8?q?containerName=20=EC=83=9D=EC=84=B1=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../training/model/dto/ModelTrainMngDto.java | 11 ++++ .../model/service/ModelTrainMngService.java | 19 ++++++ .../model/service/TmpDatasetService.java | 66 +++++++++++++++++++ .../core/ModelTrainMngCoreService.java | 30 +++++++++ .../dataset/DatasetRepositoryCustom.java | 2 + .../dataset/DatasetRepositoryImpl.java | 5 ++ .../model/ModelMngRepositoryImpl.java | 2 +- .../train/service/DockerTrainService.java | 6 +- 8 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/kamco/cd/training/model/service/TmpDatasetService.java diff --git a/src/main/java/com/kamco/cd/training/model/dto/ModelTrainMngDto.java b/src/main/java/com/kamco/cd/training/model/dto/ModelTrainMngDto.java index fa7f983..19202e6 100644 --- a/src/main/java/com/kamco/cd/training/model/dto/ModelTrainMngDto.java +++ b/src/main/java/com/kamco/cd/training/model/dto/ModelTrainMngDto.java @@ -155,6 +155,17 @@ public class ModelTrainMngDto { ModelConfig modelConfig; } + @Schema(name = "addReq", description = "모델학습 관리 등록 파라미터") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class UpdateReq { + + private String requestPath; + private String responsePath; + } + @Getter @Setter public static class TrainingDataset { diff --git a/src/main/java/com/kamco/cd/training/model/service/ModelTrainMngService.java b/src/main/java/com/kamco/cd/training/model/service/ModelTrainMngService.java index 2438c0d..8b4c832 100644 --- a/src/main/java/com/kamco/cd/training/model/service/ModelTrainMngService.java +++ b/src/main/java/com/kamco/cd/training/model/service/ModelTrainMngService.java @@ -13,6 +13,8 @@ import com.kamco.cd.training.model.dto.ModelTrainMngDto.SearchReq; import com.kamco.cd.training.postgres.core.HyperParamCoreService; import com.kamco.cd.training.postgres.core.ModelTrainMngCoreService; import com.kamco.cd.training.train.service.TrainJobService; +import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; @@ -31,6 +33,7 @@ public class ModelTrainMngService { private final TrainJobService trainJobService; private final ModelTrainMngCoreService modelTrainMngCoreService; private final HyperParamCoreService hyperParamCoreService; + private final TmpDatasetService tmpDatasetService; /** * 모델학습 조회 @@ -90,6 +93,22 @@ public class ModelTrainMngService { // 모델 config 저장 modelTrainMngCoreService.saveModelConfig(modelId, req.getModelConfig()); + UUID tmpUuid = UUID.randomUUID(); + String raw = tmpUuid.toString().replace("-", ""); + + List uids = + modelTrainMngCoreService.findDatasetUid(req.getTrainingDataset().getDatasetList()); + + try { + // 데이터셋 심볼링크 생성 + Path path = tmpDatasetService.buildTmpDatasetSymlink(raw, uids); + ModelTrainMngDto.UpdateReq updateReq = new ModelTrainMngDto.UpdateReq(); + updateReq.setRequestPath(path.toString()); + modelTrainMngCoreService.updateModelMaster(modelId, updateReq); + } catch (IOException e) { + throw new RuntimeException(e); + } + // 저장 다 끝난 뒤에 job enqueue if (Boolean.TRUE.equals(req.getIsStart())) { trainJobService.enqueue(modelId); // job 저장 + 이벤트 발행(실행은 AFTER_COMMIT) diff --git a/src/main/java/com/kamco/cd/training/model/service/TmpDatasetService.java b/src/main/java/com/kamco/cd/training/model/service/TmpDatasetService.java new file mode 100644 index 0000000..c58f135 --- /dev/null +++ b/src/main/java/com/kamco/cd/training/model/service/TmpDatasetService.java @@ -0,0 +1,66 @@ +package com.kamco.cd.training.model.service; + +import java.io.IOException; +import java.nio.file.*; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TmpDatasetService { + + @Value("${train.requestDir}") + private String requestDir; + + // 환경에 맞게 yml로 빼는 걸 추천 + private final Path BASE = Paths.get(requestDir); + + @Transactional(readOnly = true) + public Path buildTmpDatasetSymlink(String uid, List uids) throws IOException { + Path tmp = BASE.resolve("tmp").resolve(uid); + + // mkdir -p "$TMP"/train/{input1,input2,label} ... + for (String type : List.of("train", "val")) { + for (String part : List.of("input1", "input2", "label")) { + Files.createDirectories(tmp.resolve(type).resolve(part)); + } + } + + for (String id : uids) { + Path srcRoot = BASE.resolve(id); + + for (String type : List.of("train", "val")) { + for (String part : List.of("input1", "input2", "label")) { + + Path srcDir = srcRoot.resolve(type).resolve(part); + + // zsh NULL_GLOB: 폴더가 없으면 그냥 continue + if (!Files.isDirectory(srcDir)) continue; + + try (DirectoryStream stream = Files.newDirectoryStream(srcDir)) { + for (Path f : stream) { + if (!Files.isRegularFile(f)) continue; + + String dstName = id + "__" + f.getFileName(); + Path dst = tmp.resolve(type).resolve(part).resolve(dstName); + + // 이미 있으면 스킵(원하면 덮어쓰기 로직으로 바꿀 수 있음) + if (Files.exists(dst)) continue; + + // ln -s "$f" "$dst" 와 동일 + Files.createSymbolicLink(dst, f.toAbsolutePath()); + } + } + } + } + } + + log.info("tmp dataset created: {}", tmp); + return tmp; + } +} diff --git a/src/main/java/com/kamco/cd/training/postgres/core/ModelTrainMngCoreService.java b/src/main/java/com/kamco/cd/training/postgres/core/ModelTrainMngCoreService.java index b327132..692f475 100644 --- a/src/main/java/com/kamco/cd/training/postgres/core/ModelTrainMngCoreService.java +++ b/src/main/java/com/kamco/cd/training/postgres/core/ModelTrainMngCoreService.java @@ -149,6 +149,26 @@ public class ModelTrainMngCoreService { modelDatasetRepository.save(datasetEntity); } + /** + * 학습모델 수정 + * + * @param modelId + * @param req + */ + public void updateModelMaster(Long modelId, ModelTrainMngDto.UpdateReq req) { + ModelMasterEntity entity = + modelMngRepository + .findById(modelId) + .orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND)); + if (req.getRequestPath() != null && !req.getRequestPath().isEmpty()) { + entity.setRequestPath(req.getRequestPath()); + } + + if (req.getResponsePath() != null && !req.getResponsePath().isEmpty()) { + entity.setRequestPath(req.getResponsePath()); + } + } + /** * 모델 데이터셋 mapping 테이블 저장 * @@ -467,4 +487,14 @@ public class ModelTrainMngCoreService { entity.setBestEpoch(epoch); } + + /** + * 데이터셋 uid 조회 + * + * @param datasetIds + * @return + */ + public List findDatasetUid(List datasetIds) { + return datasetRepository.findDatasetUid(datasetIds); + } } diff --git a/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryCustom.java b/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryCustom.java index adf06e9..1916d58 100644 --- a/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryCustom.java @@ -22,4 +22,6 @@ public interface DatasetRepositoryCustom { Long getDatasetMaxStage(int compareYyyy, int targetYyyy); Long insertDatasetMngData(DatasetMngRegDto mngRegDto); + + List findDatasetUid(List datasetIds); } diff --git a/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryImpl.java b/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryImpl.java index 4ae1cee..c41fe37 100644 --- a/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/training/postgres/repository/dataset/DatasetRepositoryImpl.java @@ -242,4 +242,9 @@ public class DatasetRepositoryImpl implements DatasetRepositoryCustom { .where(dataset.uid.eq(mngRegDto.getUid())) .fetchOne(); } + + @Override + public List findDatasetUid(List datasetIds) { + return queryFactory.select(dataset.uid).from(dataset).where(dataset.id.in(datasetIds)).fetch(); + } } diff --git a/src/main/java/com/kamco/cd/training/postgres/repository/model/ModelMngRepositoryImpl.java b/src/main/java/com/kamco/cd/training/postgres/repository/model/ModelMngRepositoryImpl.java index 547d837..3ebbafc 100644 --- a/src/main/java/com/kamco/cd/training/postgres/repository/model/ModelMngRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/training/postgres/repository/model/ModelMngRepositoryImpl.java @@ -94,7 +94,7 @@ public class ModelMngRepositoryImpl implements ModelMngRepositoryCustom { .select( Projections.constructor( TrainRunRequest.class, - modelMasterEntity.uuid, // datasetFolder + modelMasterEntity.requestPath, // datasetFolder modelMasterEntity.uuid, // outputFolder modelHyperParamEntity.inputSize, modelHyperParamEntity.cropSize, diff --git a/src/main/java/com/kamco/cd/training/train/service/DockerTrainService.java b/src/main/java/com/kamco/cd/training/train/service/DockerTrainService.java index be9486b..f04e756 100644 --- a/src/main/java/com/kamco/cd/training/train/service/DockerTrainService.java +++ b/src/main/java/com/kamco/cd/training/train/service/DockerTrainService.java @@ -221,7 +221,7 @@ public class DockerTrainService { c.add("/workspace/change-detection-code/train_wrapper.py"); // ===== 기본 파라미터 ===== - addArg(c, "--dataset-folder", "4BDBBDF99D04477A927CC9EBA760B845" /*req.getDatasetFolder()*/); + addArg(c, "--dataset-folder", req.getDatasetFolder()); addArg(c, "--output-folder", req.getOutputFolder()); addArg(c, "--input-size", req.getInputSize()); addArg(c, "--crop-size", req.getCropSize()); @@ -281,8 +281,8 @@ public class DockerTrainService { if (value == null) return; String s = String.valueOf(value).trim(); if (s.isEmpty()) return; - c.add(key); - c.add(s); + + c.add(key + "=" + s); } /** 컨테이너 강제 종료 및 제거 */