Fix: 트랜잭션 처리

This commit is contained in:
김용수 2024-09-29 21:09:11 +09:00
parent b94420a91f
commit 8649ae0a67
3 changed files with 27 additions and 16 deletions

View File

@ -11,7 +11,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -34,15 +33,14 @@ public class ImageAsyncService {
log.debug("현재 스레드 - {} 업로드 파일 개수 - {}, 현재 작업 큐 용량 - {}", log.debug("현재 스레드 - {} 업로드 파일 개수 - {}, 현재 작업 큐 용량 - {}",
Thread.currentThread().getName(), Thread.currentThread().getName(),
imageFileList.size(), imageFileList.size(),
imageUploadExecutor.getThreadPoolExecutor().getQueue().size()); // 큐에 쌓인 작업 출력); imageUploadExecutor.getThreadPoolExecutor().getQueue().size()
); // 큐에 쌓인 작업 출력);
imageFileList.forEach(file -> { imageFileList.forEach(file -> {
try{ try {
String extension = getExtension(file.getOriginalFilename()); String imageKey = imageUpload(projectId, file);
String imageKey = s3UploadService.uploadImageFile(file, extension, projectId);
createImage(file, imageKey, folder); createImage(file, imageKey, folder);
}catch (Exception e){ } catch (Exception e) {
log.error("이미지 업로드 실패: {}", file.getOriginalFilename(), e); log.error("이미지 업로드 실패: {}", file.getOriginalFilename(), e);
throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE);
} }
@ -52,15 +50,19 @@ public class ImageAsyncService {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED) @Transactional(propagation = Propagation.NOT_SUPPORTED)
public String imageUpload(Integer projectId, MultipartFile file) {
String extension = getExtension(file.getOriginalFilename());
return s3UploadService.uploadImageFile(file, extension, projectId);
}
public void createImage(MultipartFile file, String imageKey, Folder folder) { public void createImage(MultipartFile file, String imageKey, Folder folder) {
try { try {
String name = file.getOriginalFilename(); String name = file.getOriginalFilename();
String extension = getExtension(name); String extension = getExtension(name);
log.debug("image 파일 {} {} {} {}", name, extension, imageKey, folder.getId());
Image image = Image.of(name, imageKey, extension, folder); Image image = Image.of(name, imageKey, extension, folder);
imageRepository.save(image); imageRepository.save(image);
}catch (Exception e){ } catch (Exception e) {
log.debug("이미지 DB 저장 실패: ", e); log.debug("이미지 DB 저장 실패: ", e);
s3UploadService.deleteImageFromS3(imageKey); s3UploadService.deleteImageFromS3(imageKey);
throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE);

View File

@ -40,7 +40,6 @@ import java.util.stream.Stream;
@Slf4j @Slf4j
@Service @Service
@Transactional
@RequiredArgsConstructor @RequiredArgsConstructor
public class ImageService { public class ImageService {
@ -56,9 +55,8 @@ public class ImageService {
@CheckPrivilege(value = PrivilegeType.EDITOR) @CheckPrivilege(value = PrivilegeType.EDITOR)
public void uploadImageList(final List<MultipartFile> imageList, final Integer folderId, final Integer projectId) { public void uploadImageList(final List<MultipartFile> imageList, final Integer folderId, final Integer projectId) {
Folder folder = getOrCreateFolder(folderId, projectId); Folder folder = getOrCreateFolder(folderId, projectId);
folderRepository.flush();
log.debug("folder Id {}, Project Id {}",folder.getId(), folder.getProject().getId()); log.debug("folder Id {}, Project Id {}", folder.getId(), folder.getProject().getId());
long prev = System.currentTimeMillis(); long prev = System.currentTimeMillis();
// 동적 배치 크기 계산 // 동적 배치 크기 계산
@ -78,7 +76,6 @@ public class ImageService {
List<MultipartFile> batch = imageList.subList(i, Math.min(i + batchSize, imageList.size())); List<MultipartFile> batch = imageList.subList(i, Math.min(i + batchSize, imageList.size()));
CompletableFuture<Void> future = imageAsyncService.asyncImageUpload(batch, folder, projectId); CompletableFuture<Void> future = imageAsyncService.asyncImageUpload(batch, folder, projectId);
// 모든 비동기 작업이 완료될 때까지 기다림
futures.add(future); futures.add(future);
} }
@ -308,7 +305,8 @@ public class ImageService {
String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Project project = getProject(projectId); Project project = getProject(projectId);
Folder folder = Folder.of(currentDateTime, null, project); Folder folder = Folder.of(currentDateTime, null, project);
folderRepository.save(folder); // 새로운 폴더를 저장 folderRepository.saveAndFlush(folder); // 새로운 폴더를 저장
return folder; return folder;
} }
} }

View File

@ -1,6 +1,7 @@
package com.worlabel.domain.project.entity; package com.worlabel.domain.project.entity;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.worlabel.domain.folder.entity.Folder;
import com.worlabel.domain.labelcategory.entity.ProjectCategory; import com.worlabel.domain.labelcategory.entity.ProjectCategory;
import com.worlabel.domain.model.entity.AiModel; import com.worlabel.domain.model.entity.AiModel;
import com.worlabel.domain.workspace.entity.Workspace; import com.worlabel.domain.workspace.entity.Workspace;
@ -54,6 +55,12 @@ public class Project extends BaseEntity {
@OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProjectCategory> categoryList = new ArrayList<>(); private List<ProjectCategory> categoryList = new ArrayList<>();
/**
* 프로젝트에 속한 폴더
*/
@OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Folder> folderList = new ArrayList<>();
/** /**
* 프로젝트에 속한 모델 * 프로젝트에 속한 모델
*/ */
@ -74,4 +81,8 @@ public class Project extends BaseEntity {
this.title = title; this.title = title;
this.projectType = projectType; this.projectType = projectType;
} }
public void createFolder(final Folder folder) {
folderList.add(folder);
}
} }