From 5fc3567987e15fbef75d0c5afc866f45dcf154d5 Mon Sep 17 00:00:00 2001 From: gnlslfflffl Date: Sun, 15 Sep 2024 18:42:25 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20=EB=A1=9C=EC=A7=81=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=EB=A1=9C=20=EB=B3=80=ED=99=98,=20scheduled=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=EB=A1=9C=20=EC=BB=A4=EB=A7=A8?= =?UTF-8?q?=EB=93=9C=20=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/message/InMemoryMessageQueue.java | 41 ++++++++----------- .../out/post/message/InMemoryPostQueue.java | 3 +- .../config/InMemoryQueueConfig.java | 8 ++-- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java index ec4c0c1..3b867d8 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java @@ -3,38 +3,31 @@ import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; import com.flab.ccinside.api.trendingpost.application.port.in.PostSystemUsecase; import com.flab.ccinside.api.trendingpost.application.port.in.UpdateViewCountCommand; -import jakarta.annotation.PostConstruct; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; +import java.util.Queue; import java.util.concurrent.Executors; -import lombok.RequiredArgsConstructor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component -@RequiredArgsConstructor public class InMemoryMessageQueue { - private final PostSystemUsecase postSystemUsecase; - private final BlockingQueue queue; - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + public InMemoryMessageQueue(PostSystemUsecase postSystemUsecase, Queue queue) { + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); - @PostConstruct - public void init() { - executorService.submit( - () -> { - try { - while (true) { - var event = queue.take(); - log.info("View post event consumed. postId: {}", event.postId()); - var command = new UpdateViewCountCommand(event.postId()); - postSystemUsecase.updateViewCount(command); - } - } catch (InterruptedException e) { - log.error("consume error: {}", e.getMessage()); - throw new RuntimeException(e); - } - }); + executorService.scheduleWithFixedDelay(() -> { + try { + ViewPostEvent event; + while ((event = queue.poll()) != null) { + log.info("View post event consumed. postId: {}", event.postId()); + var command = new UpdateViewCountCommand(event.postId()); + postSystemUsecase.updateViewCount(command); + } + } catch (Exception e) { + log.error("error: {}", e.getMessage()); + } +}, 10, 100, TimeUnit.MILLISECONDS); } } diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java index 86546b6..2e8fa84 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java @@ -2,6 +2,7 @@ import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; import com.flab.ccinside.api.trendingpost.application.port.out.post.AsyncPublishAddViewCountPort; +import java.util.Queue; import java.util.concurrent.BlockingQueue; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,7 +13,7 @@ @RequiredArgsConstructor public class InMemoryPostQueue implements AsyncPublishAddViewCountPort { - private final BlockingQueue queue; + private final Queue queue; @Override public void add(ViewPostEvent event) { diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java index ae4ebbb..5d3f684 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java @@ -1,16 +1,18 @@ package com.flab.ccinside.api.trendingpost.config; import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; +import java.util.Queue; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class InMemoryQueueConfig { - @Bean - public BlockingQueue queue() { - return new LinkedBlockingQueue<>(); + public Queue queue() { + return new ConcurrentLinkedQueue<>(); } } From 9f654d805ee1acb5feeb787d0f13e6591fb0f163 Mon Sep 17 00:00:00 2001 From: gnlslfflffl Date: Sun, 15 Sep 2024 22:05:40 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20=EC=A1=B0=ED=9A=8C=EC=88=98=20=EC=98=81?= =?UTF-8?q?=EC=86=8D=ED=99=94=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/message/InMemoryMessageQueue.java | 33 ++++++++++++------- .../out/post/message/InMemoryPostQueue.java | 1 - .../post/persistence/PostJpaRepository.java | 2 +- .../persistence/PostPersistenceAdapter.java | 6 ++++ .../persistence/PostRepositorySupport.java | 8 +++++ .../PostRepositorySupportImpl.java | 21 ++++++++++++ .../application/PostSystemService.java | 30 ++++++++++------- .../port/in/PostSystemUsecase.java | 4 +++ .../port/out/post/PersistPostViewPort.java | 3 ++ .../config/InMemoryQueueConfig.java | 3 -- 10 files changed, 82 insertions(+), 29 deletions(-) create mode 100644 api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupport.java create mode 100644 api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupportImpl.java diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java index 3b867d8..19a0ec8 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java @@ -3,6 +3,8 @@ import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; import com.flab.ccinside.api.trendingpost.application.port.in.PostSystemUsecase; import com.flab.ccinside.api.trendingpost.application.port.in.UpdateViewCountCommand; +import java.util.ArrayList; +import java.util.List; import java.util.Queue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -17,17 +19,24 @@ public class InMemoryMessageQueue { public InMemoryMessageQueue(PostSystemUsecase postSystemUsecase, Queue queue) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); - executorService.scheduleWithFixedDelay(() -> { - try { - ViewPostEvent event; - while ((event = queue.poll()) != null) { - log.info("View post event consumed. postId: {}", event.postId()); - var command = new UpdateViewCountCommand(event.postId()); - postSystemUsecase.updateViewCount(command); - } - } catch (Exception e) { - log.error("error: {}", e.getMessage()); - } -}, 10, 100, TimeUnit.MILLISECONDS); + executorService.scheduleWithFixedDelay( + () -> { + try { + List commands = new ArrayList<>(); + ViewPostEvent event; + while ((event = queue.poll()) != null) { + log.info("View post event consumed. postId: {}", event.postId()); + var command = new UpdateViewCountCommand(event.postId()); + postSystemUsecase.updateViewCount(command); + commands.add(command); + } + postSystemUsecase.persistViewCountsInBatch(commands); + } catch (Exception e) { + log.error("error: {}", e.getMessage()); + } + }, + 10, + 100, + TimeUnit.MILLISECONDS); } } diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java index 2e8fa84..13ce6c5 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/message/InMemoryPostQueue.java @@ -3,7 +3,6 @@ import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; import com.flab.ccinside.api.trendingpost.application.port.out.post.AsyncPublishAddViewCountPort; import java.util.Queue; -import java.util.concurrent.BlockingQueue; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostJpaRepository.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostJpaRepository.java index d406e7f..f216719 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostJpaRepository.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostJpaRepository.java @@ -5,7 +5,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -public interface PostJpaRepository extends JpaRepository { +public interface PostJpaRepository extends JpaRepository, PostRepositorySupport { List findByGalleryNo(Long galleryNo); diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostPersistenceAdapter.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostPersistenceAdapter.java index 2f3e9c8..4c8b2d0 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostPersistenceAdapter.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostPersistenceAdapter.java @@ -57,4 +57,10 @@ public void modify(Post post) { postRepository.save(entity); }); } + + @Override + public void modifyInBatch(List posts) { + var postEntities = posts.stream().map(mapper::map).toList(); + postRepository.saveAllByBatch(postEntities); + } } diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupport.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupport.java new file mode 100644 index 0000000..8321b0d --- /dev/null +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupport.java @@ -0,0 +1,8 @@ +package com.flab.ccinside.api.trendingpost.adapter.out.post.persistence; + +import java.util.List; + +public interface PostRepositorySupport { + + void saveAllByBatch(List posts); +} diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupportImpl.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupportImpl.java new file mode 100644 index 0000000..07666d7 --- /dev/null +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/out/post/persistence/PostRepositorySupportImpl.java @@ -0,0 +1,21 @@ +package com.flab.ccinside.api.trendingpost.adapter.out.post.persistence; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public class PostRepositorySupportImpl implements PostRepositorySupport { + + @PersistenceContext private final EntityManager em; + + @Override + public void saveAllByBatch(List posts) { + posts.forEach(em::merge); + em.flush(); + em.clear(); + } +} diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/application/PostSystemService.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/application/PostSystemService.java index f47a033..9de8f41 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/application/PostSystemService.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/application/PostSystemService.java @@ -6,9 +6,10 @@ import com.flab.ccinside.api.trendingpost.application.port.out.post.HandlePostViewPort; import com.flab.ccinside.api.trendingpost.application.port.out.post.LoadPostPort; import com.flab.ccinside.api.trendingpost.application.port.out.post.PersistPostViewPort; +import com.flab.ccinside.api.trendingpost.exception.PostNotFoundException; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,17 +28,22 @@ public void updateViewCount(UpdateViewCountCommand command) { } @Transactional - @Scheduled(fixedRate = 30000) - public void persistViewCounts() { - var allPosts = loadPostPort.loadAllPosts(); - log.info("Posts view count persisted. size: {}", allPosts.size()); + @Override + public void persistViewCountsInBatch(List commands) { + var posts = + commands.stream() + .map( + command -> { + var post = + loadPostPort + .loadPost(command.postId()) + .orElseThrow(PostNotFoundException::new); + var view = handlePostViewPort.getView(command.postId()); + var postViewPersistCommand = new PostViewPersistCommand(view); + return post.persistViewCount(postViewPersistCommand); + }) + .toList(); - allPosts.forEach( - post -> { - var view = handlePostViewPort.getView(post.getId()); - var command = new PostViewPersistCommand(view); - var persistedPost = post.persistViewCount(command); - persistPostViewPort.modify(persistedPost); - }); + persistPostViewPort.modifyInBatch(posts); } } diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/in/PostSystemUsecase.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/in/PostSystemUsecase.java index febdd30..a0df316 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/in/PostSystemUsecase.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/in/PostSystemUsecase.java @@ -1,6 +1,10 @@ package com.flab.ccinside.api.trendingpost.application.port.in; +import java.util.List; + public interface PostSystemUsecase { void updateViewCount(UpdateViewCountCommand command); + + void persistViewCountsInBatch(List commands); } diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/out/post/PersistPostViewPort.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/out/post/PersistPostViewPort.java index 0e608a8..42ad5a5 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/out/post/PersistPostViewPort.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/application/port/out/post/PersistPostViewPort.java @@ -1,8 +1,11 @@ package com.flab.ccinside.api.trendingpost.application.port.out.post; import com.flab.ccinside.api.trendingpost.domain.Post; +import java.util.List; public interface PersistPostViewPort { void modify(Post post); + + void modifyInBatch(List posts); } diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java index 5d3f684..62f114c 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java @@ -2,10 +2,7 @@ import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.LinkedBlockingQueue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; From a2987e196e2c9fac31112d6e632e1b88ffc0e319 Mon Sep 17 00:00:00 2001 From: gnlslfflffl Date: Mon, 16 Sep 2024 17:04:30 +0900 Subject: [PATCH 3/5] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/message/InMemoryMessageQueue.java | 7 +-- .../config/InMemoryQueueConfig.java | 7 +++ .../application/PostUserServiceSpec.groovy | 23 ++++++++-- .../message/InMemoryMessageQueueSpec.groovy | 44 +++++++++++++++++++ .../test/groovy/fixture/PostFixture.groovy | 2 +- 5 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java index 19a0ec8..5ce13b4 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Queue; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @@ -16,8 +15,10 @@ @Component public class InMemoryMessageQueue { - public InMemoryMessageQueue(PostSystemUsecase postSystemUsecase, Queue queue) { - ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + public InMemoryMessageQueue( + PostSystemUsecase postSystemUsecase, + Queue queue, + ScheduledExecutorService executorService) { executorService.scheduleWithFixedDelay( () -> { diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java index 62f114c..b160b19 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/config/InMemoryQueueConfig.java @@ -3,6 +3,8 @@ import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -12,4 +14,9 @@ public class InMemoryQueueConfig { public Queue queue() { return new ConcurrentLinkedQueue<>(); } + + @Bean + public ScheduledExecutorService executorService() { + return Executors.newScheduledThreadPool(1); + } } diff --git a/api/src/test/groovy/application/PostUserServiceSpec.groovy b/api/src/test/groovy/application/PostUserServiceSpec.groovy index 9f9185f..e4cf694 100644 --- a/api/src/test/groovy/application/PostUserServiceSpec.groovy +++ b/api/src/test/groovy/application/PostUserServiceSpec.groovy @@ -2,6 +2,7 @@ package application import com.flab.ccinside.api.trendingpost.application.PostMapper import com.flab.ccinside.api.trendingpost.application.PostUserService +import com.flab.ccinside.api.trendingpost.application.port.in.CreatePostCommand import com.flab.ccinside.api.trendingpost.application.port.out.PostId import com.flab.ccinside.api.trendingpost.application.port.out.post.AsyncPublishAddViewCountPort import com.flab.ccinside.api.trendingpost.application.port.out.post.CreatePostPort @@ -30,8 +31,6 @@ class PostUserServiceSpec extends Specification { @SpringBean AsyncPublishAddViewCountPort publishAddViewCountPort = Mock() - - def "게시글 조회시, 조회 이벤트 발행 - 정상"() { given: var event = ViewPostEventFixture.VIEW_POSE_EVENT @@ -42,9 +41,27 @@ class PostUserServiceSpec extends Specification { def got = postUserService.viewPostDetail(PostId.from(1L)); then: - 1 * publishAddViewCountPort.add({it == event}) + 1 * publishAddViewCountPort.add({ it == event }) got.postTitle() == "test post title" got.authorNo() == 1L got.postNo() == 1L } + + def "게시글 생성 - 정상"() { + given: + def post = PostFixture.POST + var command = new CreatePostCommand(post.getPostTitle(), post.getAuthorNo(), post.getGalleryNo(), post.getViewCount()) + + when: + postUserService.create(command) + + then: + 1 * createPostPort.createPost({ + it.postTitle == post.getPostTitle() + it.authorNo == post.getAuthorNo() + it.galleryNo == post.getGalleryNo() + it.viewCount == post.viewCount + }) + + } } diff --git a/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy b/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy new file mode 100644 index 0000000..6805d64 --- /dev/null +++ b/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy @@ -0,0 +1,44 @@ +package com.flab.ccinside.api.trendingpost.adapter.in.message + +import com.flab.ccinside.api.trendingpost.application.port.ViewPostEvent +import com.flab.ccinside.api.trendingpost.application.port.in.PostSystemUsecase +import com.flab.ccinside.api.trendingpost.application.port.in.UpdateViewCountCommand +import com.flab.ccinside.api.trendingpost.application.port.out.PostId +import com.flab.ccinside.api.trendingpost.config.InMemoryQueueConfig +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +@ContextConfiguration(classes = [InMemoryMessageQueue, InMemoryQueueConfig]) +class InMemoryMessageQueueSpec extends Specification { + + @Autowired + Queue queue + + @SpringBean + ScheduledExecutorService executorService = Mock() + + @SpringBean + PostSystemUsecase postSystemUsecase = Mock() + + def "인메모리 메시지큐 이벤트 소비 및 행위 검증- 정상"() { + given: + def event = new ViewPostEvent(PostId.from(1L)) + + postSystemUsecase.updateViewCount(_) >> {} + postSystemUsecase.persistViewCountsInBatch(_) >> {} + 1 * executorService.scheduleWithFixedDelay(_, _, _, _) >> { Runnable runnable, long l1, long l2, TimeUnit unit -> runnable.run()} + + queue.add(event) + + when: + def inMemoryMessageQueue = new InMemoryMessageQueue(postSystemUsecase, queue, executorService) + + then: + 1 * postSystemUsecase.updateViewCount({it == new UpdateViewCountCommand(PostId.from(1L))}) + 1 * postSystemUsecase.persistViewCountsInBatch({it == List.of(new UpdateViewCountCommand(PostId.from(1L)))}) + } +} diff --git a/api/src/test/groovy/fixture/PostFixture.groovy b/api/src/test/groovy/fixture/PostFixture.groovy index dae8c69..17e2e12 100644 --- a/api/src/test/groovy/fixture/PostFixture.groovy +++ b/api/src/test/groovy/fixture/PostFixture.groovy @@ -7,5 +7,5 @@ import java.time.format.DateTimeFormatter class PostFixture { - static final Post POST = new Post(PostId.from(1L), "test post title", 1L, 1L, LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)) + static final Post POST = new Post(PostId.from(1L), "test post title", 1L, 1L, 0, LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)) } From b632707c41099aee2b6193476d17e2d1bf09b21c Mon Sep 17 00:00:00 2001 From: gnlslfflffl Date: Sun, 22 Sep 2024 19:28:11 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/message/InMemoryMessageQueue.java | 43 +++++++++++-------- .../message/InMemoryMessageQueueSpec.groovy | 34 +++++++++++++++ 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java index 5ce13b4..c3e8216 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java @@ -15,29 +15,36 @@ @Component public class InMemoryMessageQueue { + private static final Long INITIAL_DELAY = 10L; + private static final Long FIXED_DELAY = 100L; + private static final int THRESHOLD = 100; + public InMemoryMessageQueue( PostSystemUsecase postSystemUsecase, Queue queue, ScheduledExecutorService executorService) { executorService.scheduleWithFixedDelay( - () -> { - try { - List commands = new ArrayList<>(); - ViewPostEvent event; - while ((event = queue.poll()) != null) { - log.info("View post event consumed. postId: {}", event.postId()); - var command = new UpdateViewCountCommand(event.postId()); - postSystemUsecase.updateViewCount(command); - commands.add(command); - } - postSystemUsecase.persistViewCountsInBatch(commands); - } catch (Exception e) { - log.error("error: {}", e.getMessage()); - } - }, - 10, - 100, - TimeUnit.MILLISECONDS); + createEventConsumerRunnable(postSystemUsecase, queue), INITIAL_DELAY, FIXED_DELAY, TimeUnit.MILLISECONDS); + } + + private Runnable createEventConsumerRunnable(PostSystemUsecase postSystemUsecase, Queue queue) { + return () -> { + try { + List commands = new ArrayList<>(); + ViewPostEvent event; + int count = 0; + while ((event = queue.poll()) != null && count < THRESHOLD) { + log.info("View post event consumed. postId: {}", event.postId()); + var command = new UpdateViewCountCommand(event.postId()); + postSystemUsecase.updateViewCount(command); + commands.add(command); + count++; + } + postSystemUsecase.persistViewCountsInBatch(commands); + } catch (Exception e) { + log.error("error: {}", e.getMessage()); + } + }; } } diff --git a/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy b/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy index 6805d64..b73e2bf 100644 --- a/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy +++ b/api/src/test/groovy/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueueSpec.groovy @@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.ContextConfiguration import spock.lang.Specification + @ContextConfiguration(classes = [InMemoryMessageQueue, InMemoryQueueConfig]) class InMemoryMessageQueueSpec extends Specification { @@ -41,4 +42,37 @@ class InMemoryMessageQueueSpec extends Specification { 1 * postSystemUsecase.updateViewCount({it == new UpdateViewCountCommand(PostId.from(1L))}) 1 * postSystemUsecase.persistViewCountsInBatch({it == List.of(new UpdateViewCountCommand(PostId.from(1L)))}) } + + def "executorService 실제 동작 여부 - 정상" () { + when: + def inMemoryMessageQueue = new InMemoryMessageQueue(postSystemUsecase, queue, executorService) + + then: + 1 * executorService.scheduleWithFixedDelay(_, 10L, 100L, TimeUnit.MILLISECONDS) + + } + + def "임계치를 초과하는 이벤트 처리 테스트 - 정상"() { + given: + int eventCount = 101 + (0..eventCount).each { i -> + queue.add(new ViewPostEvent(PostId.from(i))) + } + + executorService.scheduleWithFixedDelay(_, _, _, _) >> { Runnable runnable, long l1, long l2, TimeUnit unit -> + runnable.run() + } + + when: + def inMemoryMessageQueue = new InMemoryMessageQueue(postSystemUsecase, queue, executorService) + + then: + 100 * postSystemUsecase.updateViewCount(_) + 1 * postSystemUsecase.persistViewCountsInBatch({ commands -> + commands.size() == 100 + }) + + queue.size() == 1 + } + } From f9783c1231456458affc82b740d55a9a8060730d Mon Sep 17 00:00:00 2001 From: gnlslfflffl Date: Sun, 22 Sep 2024 19:28:34 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20spotless=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/message/InMemoryMessageQueue.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java index c3e8216..9d5e965 100644 --- a/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java +++ b/api/src/main/java/com/flab/ccinside/api/trendingpost/adapter/in/message/InMemoryMessageQueue.java @@ -25,10 +25,14 @@ public InMemoryMessageQueue( ScheduledExecutorService executorService) { executorService.scheduleWithFixedDelay( - createEventConsumerRunnable(postSystemUsecase, queue), INITIAL_DELAY, FIXED_DELAY, TimeUnit.MILLISECONDS); + createEventConsumerRunnable(postSystemUsecase, queue), + INITIAL_DELAY, + FIXED_DELAY, + TimeUnit.MILLISECONDS); } - private Runnable createEventConsumerRunnable(PostSystemUsecase postSystemUsecase, Queue queue) { + private Runnable createEventConsumerRunnable( + PostSystemUsecase postSystemUsecase, Queue queue) { return () -> { try { List commands = new ArrayList<>();