데이터셋 1개일 때 심볼릭 링크 생성하지 않게 수정

This commit is contained in:
2026-04-09 16:14:31 +09:00
parent ca080bf77b
commit ecca537670
11 changed files with 103 additions and 5 deletions

View File

@@ -176,6 +176,8 @@ public class ModelTrainMngDto {
private String requestPath; private String requestPath;
private String responsePath; private String responsePath;
private String tmpFileStatus;
private ZonedDateTime tmpFileEndDttm;
} }
@Getter @Getter

View File

@@ -305,7 +305,19 @@ public class ModelTrainMngService {
modelTrainMngCoreService.saveModelConfig(modelId, req.getModelConfig()); modelTrainMngCoreService.saveModelConfig(modelId, req.getModelConfig());
// 데이터셋 임시파일 생성 // 데이터셋 임시파일 생성
List<Long> datasetList = null;
if (req.getTrainingDataset() != null) {
datasetList = req.getTrainingDataset().getDatasetList();
}
boolean isSingleDataset = datasetList != null && datasetList.size() == 1;
// 데이터셋 1개만 선택한 경우는 symbolic link 미생성 해도 됨 -> train 호출 시 그냥 데이터셋 request 경로로 호출
if (isSingleDataset) {
trainJobService.updateRequestPath(modelUuid, datasetList);
} else {
trainJobService.createTmpFile(modelUuid); trainJobService.createTmpFile(modelUuid);
}
return modelUuid; return modelUuid;
} }

View File

@@ -180,6 +180,9 @@ public class ModelTrainMngCoreService {
if (req.getRequestPath() != null && !req.getRequestPath().isEmpty()) { if (req.getRequestPath() != null && !req.getRequestPath().isEmpty()) {
entity.setRequestPath(req.getRequestPath()); entity.setRequestPath(req.getRequestPath());
} }
entity.setTmpFileStatus(req.getTmpFileStatus());
entity.setTmpFileEndDttm(req.getTmpFileEndDttm());
} }
/** /**
@@ -673,4 +676,36 @@ public class ModelTrainMngCoreService {
public List<ModelTrainLinkDto> findDatasetTestPath(Long modelId) { public List<ModelTrainLinkDto> findDatasetTestPath(Long modelId) {
return modelDatasetMapRepository.findDatasetTestPath(modelId); return modelDatasetMapRepository.findDatasetTestPath(modelId);
} }
public void updateTrainRequestPath(Long modelId, String datasetUid) {
ModelMasterEntity entity =
modelMngRepository
.findById(modelId)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
// 임시폴더 UID업데이트
entity.setRequestPath(datasetUid);
entity.setReqTmpYn(false); // false 인 것은 train, test 실행 시 docker 명령어에 request 폴더를 바라보게 할 예정
}
public void updateTmpFileStatusStart(Long modelId) {
ModelMasterEntity entity =
modelMngRepository
.findById(modelId)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
entity.setReqTmpYn(true);
entity.setTmpFileStatus("IN_PROGRESS");
entity.setTmpFileStartDttm(ZonedDateTime.now());
}
public void updateTmpFileStatusFail(Long modelId, String message) {
ModelMasterEntity entity =
modelMngRepository
.findById(modelId)
.orElseThrow(() -> new CustomApiException("NOT_FOUND_DATA", HttpStatus.NOT_FOUND));
entity.setTmpFileStatus("FAIL");
entity.setTmpFileErrMessage(message);
}
} }

View File

@@ -121,6 +121,21 @@ public class ModelMasterEntity {
@Column(name = "packing_end_dttm") @Column(name = "packing_end_dttm")
private ZonedDateTime packingEndDttm; private ZonedDateTime packingEndDttm;
@Column(name = "req_tmp_yn")
private Boolean reqTmpYn;
@Column(name = "tmp_file_status")
private String tmpFileStatus;
@Column(name = "tmp_file_start_dttm")
private ZonedDateTime tmpFileStartDttm;
@Column(name = "tmp_file_end_dttm")
private ZonedDateTime tmpFileEndDttm;
@Column(name = "tmp_file_err_message", columnDefinition = "TEXT")
private String tmpFileErrMessage;
public ModelTrainMngDto.Basic toDto() { public ModelTrainMngDto.Basic toDto() {
return new ModelTrainMngDto.Basic( return new ModelTrainMngDto.Basic(
this.id, this.id,

View File

@@ -189,7 +189,8 @@ public class ModelMngRepositoryImpl implements ModelMngRepositoryCustom {
modelHyperParamEntity.hueDelta, modelHyperParamEntity.hueDelta,
Expressions.nullExpression(Integer.class), Expressions.nullExpression(Integer.class),
Expressions.nullExpression(String.class), Expressions.nullExpression(String.class),
modelHyperParamEntity.uuid)) modelHyperParamEntity.uuid,
modelMasterEntity.reqTmpYn))
.from(modelMasterEntity) .from(modelMasterEntity)
.leftJoin(modelHyperParamEntity) .leftJoin(modelHyperParamEntity)
.on(modelHyperParamEntity.id.eq(modelMasterEntity.hyperParamId)) .on(modelHyperParamEntity.id.eq(modelMasterEntity.hyperParamId))

View File

@@ -15,6 +15,7 @@ public class EvalRunRequest {
private Integer timeoutSeconds; private Integer timeoutSeconds;
private String datasetFolder; private String datasetFolder;
private String outputFolder; private String outputFolder;
private Boolean reqTmpYn;
public String getOutputFolder() { public String getOutputFolder() {
return this.outputFolder.toString(); return this.outputFolder.toString();

View File

@@ -84,6 +84,8 @@ public class TrainRunRequest {
private UUID uuid; private UUID uuid;
private Boolean reqTmpYn; // tmp 심볼릭 링크를 쓰는지 아닌지 여부
public String getOutputFolder() { public String getOutputFolder() {
return String.valueOf(this.outputFolder); return String.valueOf(this.outputFolder);
} }

View File

@@ -271,7 +271,11 @@ public class DockerTrainService {
c.add("-v"); c.add("-v");
c.add(basePath + ":" + basePath); // 심볼릭 링크와 연결되는 실제 파일 경로도 마운트를 해줘야 함 c.add(basePath + ":" + basePath); // 심볼릭 링크와 연결되는 실제 파일 경로도 마운트를 해줘야 함
c.add("-v"); c.add("-v");
c.add(symbolicDir + ":/data"); // 요청할경로 if (req.getReqTmpYn()) {
c.add(symbolicDir + ":/data"); // 요청할경로 : tmp 심볼릭 사용하는 것이니 symbolicDir로 호출
} else {
c.add(requestDir + ":/data"); // 요청할경로 : tmp 심볼릭 사용하지 않으니 request로 호출
}
c.add("-v"); c.add("-v");
c.add(responseDir + ":/checkpoints"); // 저장될경로 c.add(responseDir + ":/checkpoints"); // 저장될경로
@@ -472,8 +476,13 @@ public class DockerTrainService {
c.add("-v"); c.add("-v");
c.add(basePath + ":" + basePath); // 심볼릭 링크와 연결되는 실제 파일 경로도 마운트를 해줘야 함 c.add(basePath + ":" + basePath); // 심볼릭 링크와 연결되는 실제 파일 경로도 마운트를 해줘야 함
c.add("-v"); c.add("-v");
c.add(basePath + "/tmp:/data"); if (req.getReqTmpYn()) {
c.add(symbolicDir + ":/data"); // tmp 사용하는 모델은 심볼릭 링크
} else {
c.add(requestDir + ":/data"); // tmp 사용하지 않는 모델은 request 경로
}
c.add("-v"); c.add("-v");
c.add(responseDir + ":/checkpoints"); c.add(responseDir + ":/checkpoints");

View File

@@ -269,7 +269,7 @@ public class ModelTestMetricsJobService {
zipFiles(zipFileList, individualZipPath); zipFiles(zipFileList, individualZipPath);
log.info( log.info(
"개별 ZIP 생성 완료: fileName={}, pthFile={}, size={} bytes", "개별 ZIP 생성 완료: fileName={}, pthFile={}, size={} bytes",
individualZipName, individualZipName,
pthFileName, pthFileName,
Files.size(individualZipPath)); Files.size(individualZipPath));

View File

@@ -204,6 +204,18 @@ public class TrainJobService {
return jobId; return jobId;
} }
/**
* 데이터셋 1개일 때, 파일 경로 업데이트
*
* @param modelUuid
* @param datasetList
*/
public void updateRequestPath(UUID modelUuid, List<Long> datasetList) {
Long modelId = modelTrainMngCoreService.findModelIdByUuid(modelUuid);
List<String> datasetUid = modelTrainMngCoreService.findDatasetUid(datasetList);
modelTrainMngCoreService.updateTrainRequestPath(modelId, datasetUid.getFirst());
}
private enum ResumeMode { private enum ResumeMode {
NONE, // 새로 시작 NONE, // 새로 시작
REQUIRE // 이어하기 REQUIRE // 이어하기
@@ -274,6 +286,9 @@ public class TrainJobService {
List<String> uids = modelTrainMngCoreService.findDatasetUid(datasetIds); List<String> uids = modelTrainMngCoreService.findDatasetUid(datasetIds);
try { try {
// 1. 시작 상태 업데이트
modelTrainMngCoreService.updateTmpFileStatusStart(modelId);
// 데이터셋 심볼링크 생성 // 데이터셋 심볼링크 생성
// String pathUid = tmpDatasetService.buildTmpDatasetSymlink(raw, uids); // String pathUid = tmpDatasetService.buildTmpDatasetSymlink(raw, uids);
// train path 모델 클래스별 조회 // train path 모델 클래스별 조회
@@ -298,6 +313,8 @@ public class TrainJobService {
ModelTrainMngDto.UpdateReq updateReq = new ModelTrainMngDto.UpdateReq(); ModelTrainMngDto.UpdateReq updateReq = new ModelTrainMngDto.UpdateReq();
updateReq.setRequestPath(raw); updateReq.setRequestPath(raw);
updateReq.setTmpFileStatus("COMPLETE");
updateReq.setTmpFileEndDttm(ZonedDateTime.now());
// 학습모델을 수정한다. // 학습모델을 수정한다.
modelTrainMngCoreService.updateModelMaster(modelId, updateReq); modelTrainMngCoreService.updateModelMaster(modelId, updateReq);
@@ -311,6 +328,9 @@ public class TrainJobService {
(uids == null ? null : uids.size()), (uids == null ? null : uids.size()),
e); e);
// 3. 실패 처리
modelTrainMngCoreService.updateTmpFileStatusFail(modelId, e.getMessage());
// 런타임 예외로 래핑하되, 메시지에 핵심 정보 포함 // 런타임 예외로 래핑하되, 메시지에 핵심 정보 포함
throw new CustomApiException( throw new CustomApiException(
"INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR, "임시 데이터셋 생성에 실패했습니다."); "INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR, "임시 데이터셋 생성에 실패했습니다.");

View File

@@ -87,6 +87,7 @@ public class TrainJobWorker {
evalReq.setTimeoutSeconds(null); evalReq.setTimeoutSeconds(null);
evalReq.setDatasetFolder(datasetFolder); evalReq.setDatasetFolder(datasetFolder);
evalReq.setOutputFolder(outputFolder); evalReq.setOutputFolder(outputFolder);
evalReq.setReqTmpYn((Boolean) params.get("reqTmpYn"));
log.info("[JOB] selected test epoch={}", epoch); log.info("[JOB] selected test epoch={}", epoch);
// 도커 실행 후 로그 수집 // 도커 실행 후 로그 수집