From 3f7dd05dcf72e8aed88c0e8e0d03c6c50fd8adbe Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Tue, 28 May 2024 18:17:02 +0900 Subject: [PATCH 1/7] =?UTF-8?q?#346=20[feat]=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EA=B4=80=EB=A0=A8=20setnx=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +- .../mile/controller/moim/MoimController.java | 2 + .../java/com/mile/UniqueNameLockTest.java | 92 +++++++++++++++++++ .../mile/exception/message/ErrorMessage.java | 3 +- module-domain/build.gradle | 10 ++ .../com/mile/moim/lock/AopForTransaction.java | 15 +++ .../lock/AtomicValidateUniqueMoimName.java | 11 +++ .../mile/moim/lock/MoimNameRequestAspect.java | 41 +++++++++ .../com/mile/moim/service/MoimService.java | 15 ++- 9 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 module-api/src/test/java/com/mile/UniqueNameLockTest.java create mode 100644 module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java create mode 100644 module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java create mode 100644 module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java diff --git a/build.gradle b/build.gradle index ceeb29f5..021370a3 100644 --- a/build.gradle +++ b/build.gradle @@ -25,8 +25,6 @@ dependencies { //Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' - //Test - testImplementation 'org.springframework.boot:spring-boot-starter-test' } @@ -55,6 +53,10 @@ subprojects { annotationProcessor "org.projectlombok:lombok" testCompileOnly("org.projectlombok:lombok") testAnnotationProcessor("org.projectlombok:lombok") + + //Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' + // VALIDATION implementation 'org.springframework.boot:spring-boot-starter-validation' diff --git a/module-api/src/main/java/com/mile/controller/moim/MoimController.java b/module-api/src/main/java/com/mile/controller/moim/MoimController.java index b3c51b9c..67b2f056 100644 --- a/module-api/src/main/java/com/mile/controller/moim/MoimController.java +++ b/module-api/src/main/java/com/mile/controller/moim/MoimController.java @@ -3,6 +3,7 @@ import com.mile.config.filter.PrincipalHandler; import com.mile.dto.SuccessResponse; import com.mile.exception.message.SuccessMessage; +import com.mile.moim.lock.AtomicValidateUniqueMoimName; import com.mile.moim.service.MoimService; import com.mile.moim.service.dto.BestMoimListResponse; import com.mile.moim.service.dto.ContentListResponse; @@ -222,6 +223,7 @@ public ResponseEntity> validateMo @PostMapping @Override + @AtomicValidateUniqueMoimName public ResponseEntity> createMoim( @RequestBody final MoimCreateRequest creatRequest ) { diff --git a/module-api/src/test/java/com/mile/UniqueNameLockTest.java b/module-api/src/test/java/com/mile/UniqueNameLockTest.java new file mode 100644 index 00000000..4b73cf95 --- /dev/null +++ b/module-api/src/test/java/com/mile/UniqueNameLockTest.java @@ -0,0 +1,92 @@ +package com.mile; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mile.authentication.UserAuthentication; +import com.mile.moim.service.dto.MoimCreateRequest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +@SpringBootTest +@AutoConfigureMockMvc +public class UniqueNameLockTest { + @Autowired + private MockMvc mockMvc; + @Autowired + private ObjectMapper objectMapper; + + + @Test + @DisplayName("중복 요청에 대한 Lock으로 인해 동시에 같은 제목으로 요청을 보내면 400 에러를 반환한다.") + public void uniqueMoimNameTest() throws Exception { + // given + int numberOfThread = 4; + ExecutorService executorService = Executors.newFixedThreadPool(numberOfThread); + CountDownLatch latch = new CountDownLatch(numberOfThread); + + MoimCreateRequest bodyDto = new + MoimCreateRequest("de", "string", false, "string", "string", "string", "string", "str", "string"); + + String body = objectMapper.writeValueAsString(bodyDto); + + // when + List results = new ArrayList<>(); + UserAuthentication testUser = new UserAuthentication(1L, null, null); + + for (int i = 0; i < numberOfThread; i++) { + executorService.submit(() -> { + try { + MvcResult result = mockMvc.perform( + post("/api/moim") + .contentType(MediaType.APPLICATION_JSON) + .content(body) + .with(authentication(testUser)) + ) + .andDo(print()) + .andReturn(); + synchronized (results) { + results.add(result); + } + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + latch.countDown(); + } + }); + } + latch.await(); + + // then + int count200 = 0; + int count400 = 0; + for (MvcResult mvcResult : results) { + if (mvcResult.getResponse().getStatus() == HttpStatus.OK.value()) count200++; + else if (mvcResult.getResponse().getStatus() == HttpStatus.BAD_REQUEST.value()) count400++; + } + System.out.println(count400); + + assertThat(count200).isEqualTo(1); + assertThat(count400).isEqualTo(numberOfThread - 1); + } + +} + diff --git a/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java b/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java index 9120a05d..3347fc6a 100644 --- a/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java +++ b/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java @@ -4,7 +4,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.http.HttpStatus; -import org.springframework.web.client.HttpClientErrorException.Unauthorized; @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) @@ -49,7 +48,7 @@ public enum ErrorMessage { LEAST_TOPIC_SIZE_OF_MOIM_ERROR(HttpStatus.BAD_REQUEST.value(), "모임에는 최소 하나의 글감이 있어야 합니다."), USER_MOIM_ALREADY_JOIN(HttpStatus.BAD_REQUEST.value(), "사용자는 이미 모임에 가입했습니다."), WRITER_NAME_LENGTH_WRONG(HttpStatus.BAD_REQUEST.value(), "사용 불가능한 필명입니다."), - MOIM_NAME_LENGTH_WRONG(HttpStatus.BAD_REQUEST.value(), "사용 불가능한 모임명입니다."), + MOIM_NAME_VALIDATE_ERROR(HttpStatus.BAD_REQUEST.value(), "사용 불가능한 모임명입니다."), EXCEED_MOIM_MAX_SIZE(HttpStatus.BAD_REQUEST.value(), "최대 가입 가능 모임 개수(5개)를 초과하였습니다."), /* Conflict diff --git a/module-domain/build.gradle b/module-domain/build.gradle index acfa2a5d..c3787542 100644 --- a/module-domain/build.gradle +++ b/module-domain/build.gradle @@ -17,4 +17,14 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + //Redisson + implementation "org.redisson:redisson:3.29.0" + //Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' } + + +tasks.named("test") { + useJUnitPlatform() +} \ No newline at end of file diff --git a/module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java b/module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java new file mode 100644 index 00000000..d10e082c --- /dev/null +++ b/module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java @@ -0,0 +1,15 @@ +package com.mile.moim.lock; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class AopForTransaction { + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { + return joinPoint.proceed(); + } +} \ No newline at end of file diff --git a/module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java b/module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java new file mode 100644 index 00000000..7c99cbf9 --- /dev/null +++ b/module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java @@ -0,0 +1,11 @@ +package com.mile.moim.lock; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AtomicValidateUniqueMoimName { +} diff --git a/module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java b/module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java new file mode 100644 index 00000000..6b5f68f4 --- /dev/null +++ b/module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java @@ -0,0 +1,41 @@ +package com.mile.moim.lock; + +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; + +@Aspect +@RequiredArgsConstructor +public class MoimNameRequestAspect { + + private final RedissonClient redissonClient; + private final static String MOIM_NAME_LOCK = "MOIM NAME"; + private final AopForTransaction aopForTransaction; + + @Pointcut("execution(* com.mile.moim.service.MoimService.*(..))") + public void uniqueMoimNameCut() { + } + + @Around("uniqueMoimNameCut()") + public Object validateUniqueName(final ProceedingJoinPoint joinPoint) throws Throwable { + final RLock lock = redissonClient.getLock(MOIM_NAME_LOCK); + try { + checkAvailability(lock.tryLock(3, 4, TimeUnit.SECONDS)); + return aopForTransaction.proceed(joinPoint); + } finally { + lock.unlock(); + } + } + + + public void checkAvailability(final Boolean available) { + if (!available) throw new RuntimeException("Lock is Unavailable"); + } + +} diff --git a/module-domain/src/main/java/com/mile/moim/service/MoimService.java b/module-domain/src/main/java/com/mile/moim/service/MoimService.java index 15ffef5d..d6e04499 100644 --- a/module-domain/src/main/java/com/mile/moim/service/MoimService.java +++ b/module-domain/src/main/java/com/mile/moim/service/MoimService.java @@ -4,6 +4,7 @@ import com.mile.exception.model.BadRequestException; import com.mile.exception.model.ForbiddenException; import com.mile.exception.model.NotFoundException; +import com.mile.moim.lock.AtomicValidateUniqueMoimName; import com.mile.moim.domain.Moim; import com.mile.moim.repository.MoimRepository; import com.mile.moim.service.dto.BestMoimListResponse; @@ -264,15 +265,25 @@ public void modifyMoimInforation( authenticateOwnerOfMoim(moim, userId); } + @AtomicValidateUniqueMoimName public MoimNameConflictCheckResponse validateMoimName( final String moimName ) { if (moimName.length() > MOIM_NAME_MAX_VALUE) { - throw new BadRequestException(ErrorMessage.MOIM_NAME_LENGTH_WRONG); + throw new BadRequestException(ErrorMessage.MOIM_NAME_VALIDATE_ERROR); } return MoimNameConflictCheckResponse.of(!moimRepository.existsByName(moimName)); } + private void checkMoimNameUnique( + final String moimName + ) { + if(moimRepository.existsByName(moimName)) { + log.info("------------"); + throw new BadRequestException(ErrorMessage.MOIM_NAME_VALIDATE_ERROR); + } + } + public InvitationCodeGetResponse getInvitationCode( final Long moimId, final Long userId @@ -292,11 +303,13 @@ public String createTopic( return topicService.createTopicOfMoim(moim, createRequest).toString(); } + @AtomicValidateUniqueMoimName @Transactional public MoimCreateResponse createMoim( final Long userId, final MoimCreateRequest createRequest ) { + checkMoimNameUnique(createRequest.moimName()); Moim moim = moimRepository.saveAndFlush(Moim.create(createRequest)); User user = userService.findById(userId); From 7a05a3881d593406be34a062bea733a11ce63017 Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Thu, 30 May 2024 15:24:38 +0900 Subject: [PATCH 2/7] =?UTF-8?q?#346=20[feat]=20=EB=A1=9C=EA=B9=85=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mile/moim/lock/AopForTransaction.java | 15 --------------- .../java/com/mile/moim/service/MoimService.java | 11 +++++------ 2 files changed, 5 insertions(+), 21 deletions(-) delete mode 100644 module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java diff --git a/module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java b/module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java deleted file mode 100644 index d10e082c..00000000 --- a/module-domain/src/main/java/com/mile/moim/lock/AopForTransaction.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.mile.moim.lock; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -@Component -public class AopForTransaction { - - @Transactional(propagation = Propagation.REQUIRES_NEW) - public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { - return joinPoint.proceed(); - } -} \ No newline at end of file diff --git a/module-domain/src/main/java/com/mile/moim/service/MoimService.java b/module-domain/src/main/java/com/mile/moim/service/MoimService.java index d6e04499..4a07fcb1 100644 --- a/module-domain/src/main/java/com/mile/moim/service/MoimService.java +++ b/module-domain/src/main/java/com/mile/moim/service/MoimService.java @@ -4,7 +4,6 @@ import com.mile.exception.model.BadRequestException; import com.mile.exception.model.ForbiddenException; import com.mile.exception.model.NotFoundException; -import com.mile.moim.lock.AtomicValidateUniqueMoimName; import com.mile.moim.domain.Moim; import com.mile.moim.repository.MoimRepository; import com.mile.moim.service.dto.BestMoimListResponse; @@ -28,6 +27,7 @@ import com.mile.moim.service.dto.TopicListResponse; import com.mile.moim.service.dto.WriterMemberJoinRequest; import com.mile.moim.service.dto.WriterNameConflictCheckResponse; +import com.mile.moim.service.lock.AtomicValidateUniqueMoimName; import com.mile.post.domain.Post; import com.mile.post.service.PostAuthenticateService; import com.mile.post.service.PostDeleteService; @@ -41,7 +41,6 @@ import com.mile.writername.service.WriterNameService; import com.mile.writername.service.dto.WriterNameShortResponse; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -52,7 +51,6 @@ import java.util.stream.Collectors; @Service -@Slf4j @RequiredArgsConstructor public class MoimService { @@ -275,11 +273,12 @@ public MoimNameConflictCheckResponse validateMoimName( return MoimNameConflictCheckResponse.of(!moimRepository.existsByName(moimName)); } - private void checkMoimNameUnique( + + @AtomicValidateUniqueMoimName + public void checkMoimNameUnique( final String moimName ) { - if(moimRepository.existsByName(moimName)) { - log.info("------------"); + if (moimRepository.existsByName(moimName)) { throw new BadRequestException(ErrorMessage.MOIM_NAME_VALIDATE_ERROR); } } From 29a153cac7e78521397c3a7dced789790cb5e644 Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Thu, 30 May 2024 15:25:07 +0900 Subject: [PATCH 3/7] =?UTF-8?q?#346=20[test]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/mile/{ => cocurrency}/UniqueNameLockTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename module-api/src/test/java/com/mile/{ => cocurrency}/UniqueNameLockTest.java (94%) diff --git a/module-api/src/test/java/com/mile/UniqueNameLockTest.java b/module-api/src/test/java/com/mile/cocurrency/UniqueNameLockTest.java similarity index 94% rename from module-api/src/test/java/com/mile/UniqueNameLockTest.java rename to module-api/src/test/java/com/mile/cocurrency/UniqueNameLockTest.java index 4b73cf95..711c1dab 100644 --- a/module-api/src/test/java/com/mile/UniqueNameLockTest.java +++ b/module-api/src/test/java/com/mile/cocurrency/UniqueNameLockTest.java @@ -1,4 +1,4 @@ -package com.mile; +package com.mile.cocurrency; import com.fasterxml.jackson.databind.ObjectMapper; import com.mile.authentication.UserAuthentication; @@ -13,9 +13,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -44,7 +42,7 @@ public void uniqueMoimNameTest() throws Exception { CountDownLatch latch = new CountDownLatch(numberOfThread); MoimCreateRequest bodyDto = new - MoimCreateRequest("de", "string", false, "string", "string", "string", "string", "str", "string"); + MoimCreateRequest("이정해봅시다", "string", false, "string", "string", "string", "string", "str", "string"); String body = objectMapper.writeValueAsString(bodyDto); From f6c159bf376f92c1ed61f37bf3fd43366233da75 Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Thu, 30 May 2024 15:25:28 +0900 Subject: [PATCH 4/7] =?UTF-8?q?#346=20[feat]=20=EB=B6=84=EC=82=B0=EB=9D=BD?= =?UTF-8?q?=20=EB=B0=8F=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EA=B2=A9?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mile/controller/moim/MoimController.java | 2 -- .../mile/moim/service/lock/AopForTransaction.java | 15 +++++++++++++++ .../lock/AtomicValidateUniqueMoimName.java | 2 +- .../{ => service}/lock/MoimNameRequestAspect.java | 14 +++++++++----- 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 module-domain/src/main/java/com/mile/moim/service/lock/AopForTransaction.java rename module-domain/src/main/java/com/mile/moim/{ => service}/lock/AtomicValidateUniqueMoimName.java (88%) rename module-domain/src/main/java/com/mile/moim/{ => service}/lock/MoimNameRequestAspect.java (68%) diff --git a/module-api/src/main/java/com/mile/controller/moim/MoimController.java b/module-api/src/main/java/com/mile/controller/moim/MoimController.java index 67b2f056..b3c51b9c 100644 --- a/module-api/src/main/java/com/mile/controller/moim/MoimController.java +++ b/module-api/src/main/java/com/mile/controller/moim/MoimController.java @@ -3,7 +3,6 @@ import com.mile.config.filter.PrincipalHandler; import com.mile.dto.SuccessResponse; import com.mile.exception.message.SuccessMessage; -import com.mile.moim.lock.AtomicValidateUniqueMoimName; import com.mile.moim.service.MoimService; import com.mile.moim.service.dto.BestMoimListResponse; import com.mile.moim.service.dto.ContentListResponse; @@ -223,7 +222,6 @@ public ResponseEntity> validateMo @PostMapping @Override - @AtomicValidateUniqueMoimName public ResponseEntity> createMoim( @RequestBody final MoimCreateRequest creatRequest ) { diff --git a/module-domain/src/main/java/com/mile/moim/service/lock/AopForTransaction.java b/module-domain/src/main/java/com/mile/moim/service/lock/AopForTransaction.java new file mode 100644 index 00000000..545d1a34 --- /dev/null +++ b/module-domain/src/main/java/com/mile/moim/service/lock/AopForTransaction.java @@ -0,0 +1,15 @@ +package com.mile.moim.service.lock; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class AopForTransaction { + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { + return joinPoint.proceed(); + } +} \ No newline at end of file diff --git a/module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java b/module-domain/src/main/java/com/mile/moim/service/lock/AtomicValidateUniqueMoimName.java similarity index 88% rename from module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java rename to module-domain/src/main/java/com/mile/moim/service/lock/AtomicValidateUniqueMoimName.java index 7c99cbf9..3b0579f1 100644 --- a/module-domain/src/main/java/com/mile/moim/lock/AtomicValidateUniqueMoimName.java +++ b/module-domain/src/main/java/com/mile/moim/service/lock/AtomicValidateUniqueMoimName.java @@ -1,4 +1,4 @@ -package com.mile.moim.lock; +package com.mile.moim.service.lock; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java b/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java similarity index 68% rename from module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java rename to module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java index 6b5f68f4..d67cda64 100644 --- a/module-domain/src/main/java/com/mile/moim/lock/MoimNameRequestAspect.java +++ b/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java @@ -1,4 +1,4 @@ -package com.mile.moim.lock; +package com.mile.moim.service.lock; import lombok.RequiredArgsConstructor; import org.aspectj.lang.ProceedingJoinPoint; @@ -7,33 +7,37 @@ import org.aspectj.lang.annotation.Pointcut; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Aspect @RequiredArgsConstructor +@Component public class MoimNameRequestAspect { private final RedissonClient redissonClient; - private final static String MOIM_NAME_LOCK = "MOIM NAME"; + private final static String MOIM_NAME_LOCK = "MOIM_NAME_LOCK : "; private final AopForTransaction aopForTransaction; - @Pointcut("execution(* com.mile.moim.service.MoimService.*(..))") + @Pointcut("@annotation(com.mile.moim.service.lock.AtomicValidateUniqueMoimName)") public void uniqueMoimNameCut() { } @Around("uniqueMoimNameCut()") public Object validateUniqueName(final ProceedingJoinPoint joinPoint) throws Throwable { - final RLock lock = redissonClient.getLock(MOIM_NAME_LOCK); + final String key = MOIM_NAME_LOCK + joinPoint.getSignature().getName(); + final RLock lock = redissonClient.getLock(key); try { checkAvailability(lock.tryLock(3, 4, TimeUnit.SECONDS)); return aopForTransaction.proceed(joinPoint); + } catch (InterruptedException e) { + throw new RuntimeException(e); } finally { lock.unlock(); } } - public void checkAvailability(final Boolean available) { if (!available) throw new RuntimeException("Lock is Unavailable"); } From 49181a122dbfc5c125bc68167d93e7fbd7bc3170 Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Sun, 2 Jun 2024 23:35:13 +0900 Subject: [PATCH 5/7] =?UTF-8?q?#346=20[feat]=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?&=20Transactional=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/mile/moim/service/MoimService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module-domain/src/main/java/com/mile/moim/service/MoimService.java b/module-domain/src/main/java/com/mile/moim/service/MoimService.java index 4a07fcb1..abd34f32 100644 --- a/module-domain/src/main/java/com/mile/moim/service/MoimService.java +++ b/module-domain/src/main/java/com/mile/moim/service/MoimService.java @@ -252,12 +252,13 @@ private void getAuthenticateOwnerOfMoim( } } - @Transactional + @AtomicValidateUniqueMoimName public void modifyMoimInforation( final Long moimId, final Long userId, final MoimInfoModifyRequest modifyRequest ) { + validateMoimName(modifyRequest.moimTitle()); Moim moim = findById(moimId); moim.modifyMoimInfo(modifyRequest); authenticateOwnerOfMoim(moim, userId); @@ -303,7 +304,6 @@ public String createTopic( } @AtomicValidateUniqueMoimName - @Transactional public MoimCreateResponse createMoim( final Long userId, final MoimCreateRequest createRequest From 19c3459456ddc088085c77db9a09a4fae7f93a15 Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Tue, 11 Jun 2024 18:04:53 +0900 Subject: [PATCH 6/7] =?UTF-8?q?#346=20[feat]=20Lock=20key=EC=97=90?= =?UTF-8?q?=EC=84=9C=20signature=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/mile/moim/service/lock/MoimNameRequestAspect.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java b/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java index d67cda64..fc4384d4 100644 --- a/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java +++ b/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java @@ -26,8 +26,7 @@ public void uniqueMoimNameCut() { @Around("uniqueMoimNameCut()") public Object validateUniqueName(final ProceedingJoinPoint joinPoint) throws Throwable { - final String key = MOIM_NAME_LOCK + joinPoint.getSignature().getName(); - final RLock lock = redissonClient.getLock(key); + final RLock lock = redissonClient.getLock(MOIM_NAME_LOCK); try { checkAvailability(lock.tryLock(3, 4, TimeUnit.SECONDS)); return aopForTransaction.proceed(joinPoint); From d77a5975edba57c16b26857fbb655824e5f5ca02 Mon Sep 17 00:00:00 2001 From: sohyundoh Date: Tue, 11 Jun 2024 18:12:22 +0900 Subject: [PATCH 7/7] =?UTF-8?q?#346=20[refactor]=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/mile/exception/message/ErrorMessage.java | 1 + .../com/mile/moim/service/lock/MoimNameRequestAspect.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java b/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java index 3347fc6a..85f26d86 100644 --- a/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java +++ b/module-common/src/main/java/com/mile/exception/message/ErrorMessage.java @@ -87,6 +87,7 @@ public enum ErrorMessage { IMAGE_DELETE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "S3 버킷으로부터 이미지를 삭제하는 데 실패했습니다."), INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버 내부 오류입니다."), DISCORD_LOG_APPENDER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "디스코드 로그 전송에 실패하였습니다"), + TIME_OUT_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR.value(), "락을 획득하는 과정에서 Time Out이 발생했습니다."), ; final int status; diff --git a/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java b/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java index fc4384d4..719b6ed6 100644 --- a/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java +++ b/module-domain/src/main/java/com/mile/moim/service/lock/MoimNameRequestAspect.java @@ -1,5 +1,7 @@ package com.mile.moim.service.lock; +import com.mile.exception.message.ErrorMessage; +import com.mile.exception.model.MileException; import lombok.RequiredArgsConstructor; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -38,7 +40,7 @@ public Object validateUniqueName(final ProceedingJoinPoint joinPoint) throws Thr } public void checkAvailability(final Boolean available) { - if (!available) throw new RuntimeException("Lock is Unavailable"); + if (!available) throw new MileException(ErrorMessage.TIME_OUT_EXCEPTION); } }