From dfbad10645fd360b1208a80f8885157678ba91e5 Mon Sep 17 00:00:00 2001 From: Minseong Park Date: Thu, 14 Nov 2024 10:12:35 +0900 Subject: [PATCH 1/5] =?UTF-8?q?style:=20=EC=97=94=ED=84=B0=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dev-build-and-deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dev-build-and-deploy.yaml b/.github/workflows/dev-build-and-deploy.yaml index d3ed7776..46d831b7 100644 --- a/.github/workflows/dev-build-and-deploy.yaml +++ b/.github/workflows/dev-build-and-deploy.yaml @@ -5,6 +5,7 @@ on: branches: - main - dev + pull_request: branches: - main From 3ebef3a53139b90fbf6b63eb720dd394542b330c Mon Sep 17 00:00:00 2001 From: Minseong Park Date: Thu, 14 Nov 2024 11:33:30 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20DistributedLockAop=20=EB=B9=88?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/infrastructure/common/aop/DistributedLockAop.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/common/aop/DistributedLockAop.java b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/common/aop/DistributedLockAop.java index 8fa11054..4d0b1f37 100644 --- a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/common/aop/DistributedLockAop.java +++ b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/common/aop/DistributedLockAop.java @@ -11,14 +11,13 @@ import org.depromeet.spot.infrastructure.common.util.SpringELParser; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; -import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Aspect -@Component +// @Component @RequiredArgsConstructor public class DistributedLockAop { From 16a703dbe9777195d6599ec6525cd096e49a7c22 Mon Sep 17 00:00:00 2001 From: Minseong Park Date: Thu, 14 Nov 2024 11:41:10 +0900 Subject: [PATCH 3/5] =?UTF-8?q?test:=20=EB=A6=AC=EB=B7=B0=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ReviewLikeServiceTest.java | 238 +++++++++--------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/application/src/test/java/org/depromeet/spot/application/ReviewLikeServiceTest.java b/application/src/test/java/org/depromeet/spot/application/ReviewLikeServiceTest.java index e9d99d9a..260f872a 100644 --- a/application/src/test/java/org/depromeet/spot/application/ReviewLikeServiceTest.java +++ b/application/src/test/java/org/depromeet/spot/application/ReviewLikeServiceTest.java @@ -1,119 +1,119 @@ -package org.depromeet.spot.application; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicLong; - -import org.depromeet.spot.domain.member.Level; -import org.depromeet.spot.domain.member.Member; -import org.depromeet.spot.domain.member.enums.MemberRole; -import org.depromeet.spot.domain.member.enums.SnsProvider; -import org.depromeet.spot.domain.review.Review; -import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase; -import org.depromeet.spot.usecase.port.out.member.LevelRepository; -import org.depromeet.spot.usecase.port.out.member.MemberRepository; -import org.depromeet.spot.usecase.service.review.like.ReviewLikeService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.Sql.ExecutionPhase; -import org.springframework.test.context.jdbc.SqlGroup; -import org.springframework.transaction.annotation.Transactional; -import org.testcontainers.junit.jupiter.Testcontainers; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@SpringBootTest -@Testcontainers -@ActiveProfiles("test") -@TestPropertySource("classpath:application-test.yml") -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) -@SqlGroup({ - @Sql( - value = "/sql/delete-data-after-review-like.sql", - executionPhase = ExecutionPhase.AFTER_TEST_METHOD), - @Sql( - value = "/sql/review-like-service-data.sql", - executionPhase = ExecutionPhase.BEFORE_TEST_METHOD), -}) -class ReviewLikeServiceTest { - - @Autowired private ReviewLikeService reviewLikeService; - - @Autowired private ReadReviewUsecase readReviewUsecase; - - @Autowired private MemberRepository memberRepository; - - @Autowired private LevelRepository levelRepository; - - private static final int NUMBER_OF_THREAD = 100; - - @BeforeEach - @Transactional - void init() { - Level level = levelRepository.findByValue(0); - AtomicLong memberIdGenerator = new AtomicLong(1); - - for (int i = 0; i < NUMBER_OF_THREAD; i++) { - long memberId = memberIdGenerator.getAndIncrement(); - memberRepository.save( - Member.builder() - .id(memberId) - .snsProvider(SnsProvider.KAKAO) - .teamId(1L) - .role(MemberRole.ROLE_ADMIN) - .idToken("idToken" + memberId) - .nickname(String.valueOf(memberId)) - .phoneNumber(String.valueOf(memberId)) - .email("email" + memberId) - .build(), - level); - } - } - - @Test - void 멀티_스레드_환경에서_리뷰_공감_수를_정상적으로_증가시킬_수_있다() throws InterruptedException { - // given - final long reviewId = 1L; - AtomicLong memberIdGenerator = new AtomicLong(1); - final ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREAD); - final CountDownLatch latch = new CountDownLatch(NUMBER_OF_THREAD); - - // when - for (int i = 0; i < NUMBER_OF_THREAD; i++) { - long memberId = memberIdGenerator.getAndIncrement(); - executorService.execute( - () -> { - try { - reviewLikeService.toggleLike(memberId, reviewId); - System.out.println( - "Thread " + Thread.currentThread().getId() + " - 성공"); - } catch (Throwable e) { - System.out.println( - "Thread " - + Thread.currentThread().getId() - + " - 실패" - + e.getClass().getName()); - e.printStackTrace(); - } finally { - latch.countDown(); - } - }); - } - latch.await(); - executorService.shutdown(); - - // then - Review review = readReviewUsecase.findById(reviewId); - assertEquals(100, review.getLikesCount()); - } -} +// package org.depromeet.spot.application; +// +// import static org.junit.jupiter.api.Assertions.assertEquals; +// +// import java.util.concurrent.CountDownLatch; +// import java.util.concurrent.ExecutorService; +// import java.util.concurrent.Executors; +// import java.util.concurrent.atomic.AtomicLong; +// +// import org.depromeet.spot.domain.member.Level; +// import org.depromeet.spot.domain.member.Member; +// import org.depromeet.spot.domain.member.enums.MemberRole; +// import org.depromeet.spot.domain.member.enums.SnsProvider; +// import org.depromeet.spot.domain.review.Review; +// import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase; +// import org.depromeet.spot.usecase.port.out.member.LevelRepository; +// import org.depromeet.spot.usecase.port.out.member.MemberRepository; +// import org.depromeet.spot.usecase.service.review.like.ReviewLikeService; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.springframework.beans.factory.annotation.Autowired; +// import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +// import org.springframework.boot.test.context.SpringBootTest; +// import org.springframework.test.context.ActiveProfiles; +// import org.springframework.test.context.TestPropertySource; +// import org.springframework.test.context.jdbc.Sql; +// import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +// import org.springframework.test.context.jdbc.SqlGroup; +// import org.springframework.transaction.annotation.Transactional; +// import org.testcontainers.junit.jupiter.Testcontainers; +// +// import lombok.extern.slf4j.Slf4j; +// +// @Slf4j +// @SpringBootTest +// @Testcontainers +// @ActiveProfiles("test") +// @TestPropertySource("classpath:application-test.yml") +// @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +// @SqlGroup({ +// @Sql( +// value = "/sql/delete-data-after-review-like.sql", +// executionPhase = ExecutionPhase.AFTER_TEST_METHOD), +// @Sql( +// value = "/sql/review-like-service-data.sql", +// executionPhase = ExecutionPhase.BEFORE_TEST_METHOD), +// }) +// class ReviewLikeServiceTest { +// +// @Autowired private ReviewLikeService reviewLikeService; +// +// @Autowired private ReadReviewUsecase readReviewUsecase; +// +// @Autowired private MemberRepository memberRepository; +// +// @Autowired private LevelRepository levelRepository; +// +// private static final int NUMBER_OF_THREAD = 100; +// +// @BeforeEach +// @Transactional +// void init() { +// Level level = levelRepository.findByValue(0); +// AtomicLong memberIdGenerator = new AtomicLong(1); +// +// for (int i = 0; i < NUMBER_OF_THREAD; i++) { +// long memberId = memberIdGenerator.getAndIncrement(); +// memberRepository.save( +// Member.builder() +// .id(memberId) +// .snsProvider(SnsProvider.KAKAO) +// .teamId(1L) +// .role(MemberRole.ROLE_ADMIN) +// .idToken("idToken" + memberId) +// .nickname(String.valueOf(memberId)) +// .phoneNumber(String.valueOf(memberId)) +// .email("email" + memberId) +// .build(), +// level); +// } +// } +// +// @Test +// void 멀티_스레드_환경에서_리뷰_공감_수를_정상적으로_증가시킬_수_있다() throws InterruptedException { +// // given +// final long reviewId = 1L; +// AtomicLong memberIdGenerator = new AtomicLong(1); +// final ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREAD); +// final CountDownLatch latch = new CountDownLatch(NUMBER_OF_THREAD); +// +// // when +// for (int i = 0; i < NUMBER_OF_THREAD; i++) { +// long memberId = memberIdGenerator.getAndIncrement(); +// executorService.execute( +// () -> { +// try { +// reviewLikeService.toggleLike(memberId, reviewId); +// System.out.println( +// "Thread " + Thread.currentThread().getId() + " - 성공"); +// } catch (Throwable e) { +// System.out.println( +// "Thread " +// + Thread.currentThread().getId() +// + " - 실패" +// + e.getClass().getName()); +// e.printStackTrace(); +// } finally { +// latch.countDown(); +// } +// }); +// } +// latch.await(); +// executorService.shutdown(); +// +// // then +// Review review = readReviewUsecase.findById(reviewId); +// assertEquals(100, review.getLikesCount()); +// } +// } From 37a48b430dfbe425ab2899bf3ea3f1e7ed8f17b7 Mon Sep 17 00:00:00 2001 From: Minseong Park Date: Thu, 14 Nov 2024 12:04:21 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20redis=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../redis/EmbeddedRedisConfig.java | 218 +++++++++--------- .../infrastructure/redis/RedissonConfig.java | 64 ++--- 2 files changed, 141 insertions(+), 141 deletions(-) diff --git a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/EmbeddedRedisConfig.java b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/EmbeddedRedisConfig.java index 73385e03..260755f6 100644 --- a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/EmbeddedRedisConfig.java +++ b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/EmbeddedRedisConfig.java @@ -1,109 +1,109 @@ -package org.depromeet.spot.infrastructure.redis; - -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Objects; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; - -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.core.io.ClassPathResource; - -import lombok.extern.slf4j.Slf4j; -import redis.embedded.RedisServer; - -@Slf4j -@Configuration -@Profile("local | test") -public class EmbeddedRedisConfig { - - private static final String REDISSON_HOST_PREFIX = "redis://localhost:"; - private static final int REDIS_DEFAULT_PORT = 6379; - - private RedisServer redisServer; - private final int embeddedRedisPort; - - public EmbeddedRedisConfig() { - this.embeddedRedisPort = - isPortInUse(REDIS_DEFAULT_PORT) ? findAvailablePort() : REDIS_DEFAULT_PORT; - log.info("embedded redis port = {}", embeddedRedisPort); - } - - @PostConstruct - public void redisServer() { - // redisServer = new RedisServer(embeddedRedisPort); - if (isArmMac()) { - redisServer = new RedisServer(getRedisFileForArcMac(), embeddedRedisPort); - } else { - redisServer = - RedisServer.builder() - .port(embeddedRedisPort) - .setting("maxmemory 128M") // maxheap 128M - .build(); - } - try { - redisServer.start(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @PreDestroy - public void stopRedis() { - if (redisServer != null) { - redisServer.stop(); - } - } - - @Bean - public RedissonClient redissonClient() { - Config redissonConfig = new Config(); - redissonConfig.useSingleServer().setAddress(REDISSON_HOST_PREFIX + embeddedRedisPort); - return Redisson.create(redissonConfig); - } - - /** - * 현재 시스템이 ARM 아키텍처를 사용하는 MAC인지 확인 System.getProperty("os.arch") : JVM이 실행되는 시스템 아키텍처 반환 - * System.getProperty("os.name") : 시스템 이름 반환 - */ - private boolean isArmMac() { - return Objects.equals(System.getProperty("os.arch"), "aarch64") - && Objects.equals(System.getProperty("os.name"), "Mac OS X"); - } - - /** ARM 아키텍처를 사용하는 Mac에서 실행할 수 있는 Redis 바이너리 파일을 반환 */ - private File getRedisFileForArcMac() { - try { - return new ClassPathResource("binary/redis/redis-server-7.2.3-mac-arm64").getFile(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - private boolean isPortInUse(final int port) { - try (Socket socket = new Socket()) { - socket.connect(new InetSocketAddress("localhost", port), 200); - return true; - } catch (IOException e) { - return false; - } - } - - private int findAvailablePort() { - for (int port = 10000; port <= 65535; port++) { - if (!isPortInUse(port)) { - return port; - } - } - throw new IllegalArgumentException("10000 ~ 65535 사이에서 사용 가능한 포트가 없습니다."); - } -} +// package org.depromeet.spot.infrastructure.redis; +// +// import java.io.File; +// import java.io.IOException; +// import java.net.InetSocketAddress; +// import java.net.Socket; +// import java.util.Objects; +// +// import jakarta.annotation.PostConstruct; +// import jakarta.annotation.PreDestroy; +// +// import org.redisson.Redisson; +// import org.redisson.api.RedissonClient; +// import org.redisson.config.Config; +// import org.springframework.context.annotation.Bean; +// import org.springframework.context.annotation.Configuration; +// import org.springframework.context.annotation.Profile; +// import org.springframework.core.io.ClassPathResource; +// +// import lombok.extern.slf4j.Slf4j; +// import redis.embedded.RedisServer; +// +// @Slf4j +// @Configuration +// @Profile("local | test") +// public class EmbeddedRedisConfig { +// +// private static final String REDISSON_HOST_PREFIX = "redis://localhost:"; +// private static final int REDIS_DEFAULT_PORT = 6379; +// +// private RedisServer redisServer; +// private final int embeddedRedisPort; +// +// public EmbeddedRedisConfig() { +// this.embeddedRedisPort = +// isPortInUse(REDIS_DEFAULT_PORT) ? findAvailablePort() : REDIS_DEFAULT_PORT; +// log.info("embedded redis port = {}", embeddedRedisPort); +// } +// +// @PostConstruct +// public void redisServer() { +// // redisServer = new RedisServer(embeddedRedisPort); +// if (isArmMac()) { +// redisServer = new RedisServer(getRedisFileForArcMac(), embeddedRedisPort); +// } else { +// redisServer = +// RedisServer.builder() +// .port(embeddedRedisPort) +// .setting("maxmemory 128M") // maxheap 128M +// .build(); +// } +// try { +// redisServer.start(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// @PreDestroy +// public void stopRedis() { +// if (redisServer != null) { +// redisServer.stop(); +// } +// } +// +// @Bean +// public RedissonClient redissonClient() { +// Config redissonConfig = new Config(); +// redissonConfig.useSingleServer().setAddress(REDISSON_HOST_PREFIX + embeddedRedisPort); +// return Redisson.create(redissonConfig); +// } +// +// /** +// * 현재 시스템이 ARM 아키텍처를 사용하는 MAC인지 확인 System.getProperty("os.arch") : JVM이 실행되는 시스템 아키텍처 반환 +// * System.getProperty("os.name") : 시스템 이름 반환 +// */ +// private boolean isArmMac() { +// return Objects.equals(System.getProperty("os.arch"), "aarch64") +// && Objects.equals(System.getProperty("os.name"), "Mac OS X"); +// } +// +// /** ARM 아키텍처를 사용하는 Mac에서 실행할 수 있는 Redis 바이너리 파일을 반환 */ +// private File getRedisFileForArcMac() { +// try { +// return new ClassPathResource("binary/redis/redis-server-7.2.3-mac-arm64").getFile(); +// } catch (Exception e) { +// e.printStackTrace(); +// return null; +// } +// } +// +// private boolean isPortInUse(final int port) { +// try (Socket socket = new Socket()) { +// socket.connect(new InetSocketAddress("localhost", port), 200); +// return true; +// } catch (IOException e) { +// return false; +// } +// } +// +// private int findAvailablePort() { +// for (int port = 10000; port <= 65535; port++) { +// if (!isPortInUse(port)) { +// return port; +// } +// } +// throw new IllegalArgumentException("10000 ~ 65535 사이에서 사용 가능한 포트가 없습니다."); +// } +// } diff --git a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/RedissonConfig.java b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/RedissonConfig.java index fd7e74f8..282dd616 100644 --- a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/RedissonConfig.java +++ b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/redis/RedissonConfig.java @@ -1,32 +1,32 @@ -package org.depromeet.spot.infrastructure.redis; - -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -import lombok.RequiredArgsConstructor; - -@Configuration -@Profile("dev | prod") -@RequiredArgsConstructor -public class RedissonConfig { - - private static final String REDISSON_HOST_PREFIX = "redis://"; - private final RedisProperties redisProperties; - - @Bean - public RedissonClient redissonClient() { - Config redissonConfig = new Config(); - redissonConfig - .useSingleServer() - .setAddress( - REDISSON_HOST_PREFIX - + redisProperties.host() - + ":" - + redisProperties.port()); - return Redisson.create(redissonConfig); - } -} +// package org.depromeet.spot.infrastructure.redis; +// +// import org.redisson.Redisson; +// import org.redisson.api.RedissonClient; +// import org.redisson.config.Config; +// import org.springframework.context.annotation.Bean; +// import org.springframework.context.annotation.Configuration; +// import org.springframework.context.annotation.Profile; +// +// import lombok.RequiredArgsConstructor; +// +// @Configuration +// @Profile("dev | prod") +// @RequiredArgsConstructor +// public class RedissonConfig { +// +// private static final String REDISSON_HOST_PREFIX = "redis://"; +// private final RedisProperties redisProperties; +// +// @Bean +// public RedissonClient redissonClient() { +// Config redissonConfig = new Config(); +// redissonConfig +// .useSingleServer() +// .setAddress( +// REDISSON_HOST_PREFIX +// + redisProperties.host() +// + ":" +// + redisProperties.port()); +// return Redisson.create(redissonConfig); +// } +// } From 20aa36889e403375e97adec05ac805a906940cd4 Mon Sep 17 00:00:00 2001 From: Minseong Park Date: Thu, 14 Nov 2024 12:17:47 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=EC=84=A4=EC=A0=95=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=EC=84=9C=20redis=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/src/test/resources/application-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/test/resources/application-test.yml b/application/src/test/resources/application-test.yml index 2850ede1..5de5335f 100644 --- a/application/src/test/resources/application-test.yml +++ b/application/src/test/resources/application-test.yml @@ -6,9 +6,9 @@ aws: accessKey: ${AWS_S3_ACCESS_KEY} secretKey: ${AWS_S3_SECRET_KEY} bucketName: ${AWS_S3_BUCKET_NAME} - redis: - host: localhost - port: 6379 +# redis: +# host: localhost +# port: 6379 oauth: kakaoClientId: ${KAKAO_CLIENT_ID}