From 02a086e08b63b52e7e0b92071d41199df1e7d88a Mon Sep 17 00:00:00 2001 From: youngreal <59333182+youngreal@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:27:13 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=9D=B4=EB=A6=84=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/post/PostSchedulerService.java | 170 ++++++++++-------- .../post/PostSchedulerServiceTest.java | 103 ----------- 2 files changed, 98 insertions(+), 175 deletions(-) delete mode 100644 src/main/java/com/example/inflearn/service/post/PostSchedulerServiceTest.java diff --git a/src/main/java/com/example/inflearn/service/post/PostSchedulerService.java b/src/main/java/com/example/inflearn/service/post/PostSchedulerService.java index 58ba3ea..ee565cd 100644 --- a/src/main/java/com/example/inflearn/service/post/PostSchedulerService.java +++ b/src/main/java/com/example/inflearn/service/post/PostSchedulerService.java @@ -1,72 +1,98 @@ -//package com.example.inflearn.service.post; -// -//import static java.lang.Boolean.FALSE; -// -//import com.example.inflearn.infra.redis.LikeCountRedisRepository; -//import com.example.inflearn.infra.redis.LettuceLockRepository; -//import java.time.LocalDateTime; -//import lombok.RequiredArgsConstructor; -//import lombok.extern.slf4j.Slf4j; -//import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; -//import org.springframework.scheduling.annotation.Scheduled; -//import org.springframework.stereotype.Service; -//import org.springframework.transaction.annotation.Transactional; -// -//@Slf4j -//@RequiredArgsConstructor -//@Service -//public class PostSchedulerService { -// -// private static final int MINUTE = 60 * 1_000; -// private final LettuceLockRepository lettuceLockRepository; -// private final LikeCountRedisRepository likeCountRedisRepository; -// private final PostQueryService postQueryService; -// private final PostService postService; -// -// //todo 로깅 AOP -// @SchedulerLock( -// name = "scheduler_lock", // 스케줄러 이름 같으면 락의 대상이 됨 -// lockAtLeastFor = "3s", // 락 유지시간(스케줄러 주기보다 약간 짧게 하는게 좋음) -// lockAtMostFor = "3s" // 타임아웃 느낌, 정상적으로 스케줄러가 종료되지않는경우 잠금 유지시간 9초 -// ) -// @Scheduled(fixedDelay = 5000) -// @Transactional -// public void updatePopularPosts() { -// log.info("1번 스케줄러 시작시간 ={}", LocalDateTime.now()); -// // select p.id from p , db -// postQueryService.updatePopularPosts(); -// // insert to 인기글테이블 -// } -// -//// @Scheduled(fixedDelay = 5 * MINUTE) -//// public void updatePopularPosts() { -//// // 락 획득에 실패한다면 재시도를 시도하지않고 리턴한다 -//// if (FALSE.equals(lettuceLockRepository.popularPostListUpdateLock())) { -//// log.info("The popularPostList lock has already been acquired from another server."); -//// return; -//// } -//// -//// // 락 획득에 성공한다면 인기게시글을 업데이트한다. -//// try { -//// log.info("Get Lock : update PopularPostLists"); -//// postQueryService.updatePopularPosts(); -//// } finally { -//// lettuceLockRepository.popularPostListUpdateUnLock(); -//// } -//// } -// -//// @Scheduled(fixedDelay = MINUTE) -//// public void updateViewCountToDatabase() { -//// if (FALSE.equals(lettuceLockRepository.updateViewCountLock())) { -//// log.info("The updateViewCount lock has already been acquired from another server."); -//// return; -//// } -//// -//// try { -//// log.info("Get Lock : update viewCCount to Database."); -//// postService.updateViewCountForPopularPosts(likeCountRedisRepository.getPopularPostEntries()); -//// } finally { -//// lettuceLockRepository.updateViewCountUnLock(); -//// } -//// } -//} +package com.example.inflearn.service.post; + +import com.example.inflearn.domain.post.domain.PopularPost; +import com.example.inflearn.infra.repository.post.PopularPostRepository; +import com.example.inflearn.infra.repository.post.PostRepository; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@RequiredArgsConstructor +@Service +public class PostSchedulerService { + + private static final int POPULAR_POST_SIZE = 5; + private final PostService postService; + private final PostQueryService postQueryService; + private final PostMemoryService postMemoryService; + private final PopularPostRepository popularPostRepository; + private final PostRepository postRepository; + + /** + * 기존 인기글과 인기 테이블에 있던 인기글 비교해서 테이블에 남기기 (insert to 인기글테이블) + */ + @SchedulerLock( + name = "scheduler_lock", // 스케줄러 이름 같으면 락의 대상이 됨 + lockAtLeastFor = "3s", // 락 유지시간(스케줄러 주기보다 약간 짧게 하는게 좋음) + lockAtMostFor = "3s" // 타임아웃 느낌, 정상적으로 스케줄러가 종료되지않는경우 잠금 유지시간 9초 + ) + @Scheduled(fixedDelay = 15000) + @Transactional + public void updatePopularPosts() { + log.info("1번 스케줄러 시작"); + // 7일간 좋아요 많은 5개 DB에서 가져오기 + Map newPopularPosts = postQueryService.updatePopularPostsForScheduler(); + Map beforePopularPosts = getBeforePopularPosts(); + updatePosts(sortEntriesByValue(beforePopularPosts, newPopularPosts)); + } + + @Scheduled(fixedDelay = 10000) + public void updateViewCountToDB() { + //memory에있는 viewCount들을 post엔티티에 30초마다 반영해준다 + log.info("스케줄러 2 시작"); + postService.updateViewCountForPopularPosts(); + } + + /** + * 3초마다 인기글 개수(현재5개 x 2) 만큼 count쿼리발생 vs 1초당 200번의 count쿼리 발생의 트레이드오프 + */ + @Scheduled(fixedDelay = 3000) + public void getLikeCountFromDB() { + //likes테이블과 + log.info("스케줄러 3 시작, 좋아요 카운트와 댓글카운트를 메모리에 저장해두기"); + Set popularPostKeys = postMemoryService.getEntry(); + for (Long popularPostKey : popularPostKeys) { + postMemoryService.saveLikeCount(popularPostKey, postRepository.likeCountWithScheduler(popularPostKey)); + postMemoryService.saveCommentCount(popularPostKey, postRepository.commentCountWithScheduler(popularPostKey)); + } + } + + + private void updatePosts(List> entries) { + int popularPostId = 1; + log.info("updatePosts entries = {}", entries); + for (Entry sortedEntry : entries) { + popularPostRepository.updatePopularIds(sortedEntry.getKey(), popularPostId); + popularPostRepository.updatePopularlikeCount(sortedEntry.getValue(), popularPostId); + popularPostId++; + } + } + + private List> sortEntriesByValue(Map beforePopularPosts, Map newPopularPosts) { + // 새롭게 맵을 만들지않고 두 Map을 add하게되면 서로의 Map의 변화에 서로 영향을 받기때문에 UnsupportedOperationException발생 + Map addMap = new HashMap<>(); + addMap.putAll(beforePopularPosts); + addMap.putAll(newPopularPosts); + + return newPopularPosts.entrySet().stream() + .sorted(Entry.comparingByValue().reversed()) + .limit(POPULAR_POST_SIZE) + .collect(Collectors.toList()); + } + + private Map getBeforePopularPosts() { + return popularPostRepository.findAll().stream() + .collect(Collectors.toMap(PopularPost::getPostId, PopularPost::getLikeCount, + (p1, p2) -> p1)); + } +} diff --git a/src/main/java/com/example/inflearn/service/post/PostSchedulerServiceTest.java b/src/main/java/com/example/inflearn/service/post/PostSchedulerServiceTest.java deleted file mode 100644 index bd6e1e9..0000000 --- a/src/main/java/com/example/inflearn/service/post/PostSchedulerServiceTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.example.inflearn.service.post; - -import com.example.inflearn.domain.post.domain.PopularPost; -import com.example.inflearn.infra.repository.post.PopularPostRepository; -import com.example.inflearn.infra.repository.post.PostRepository; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Slf4j -@RequiredArgsConstructor -@Service -public class PostSchedulerServiceTest { - - private static final int POPULAR_POST_SIZE = 5; - private final PostService postService; - private final PostQueryService postQueryService; - private final PostMemoryService postMemoryService; - private final PopularPostRepository popularPostRepository; - private final PostRepository postRepository; - - /** - * param : id - * param : likeCount - */ - /** - * 기존 인기글과 인기 테이블에 있던 인기글 비교해서 테이블에 남기기 (insert to 인기글테이블) - */ - //todo 로깅 AOP - @SchedulerLock( - name = "scheduler_lock", // 스케줄러 이름 같으면 락의 대상이 됨 - lockAtLeastFor = "3s", // 락 유지시간(스케줄러 주기보다 약간 짧게 하는게 좋음) - lockAtMostFor = "3s" // 타임아웃 느낌, 정상적으로 스케줄러가 종료되지않는경우 잠금 유지시간 9초 - ) - @Scheduled(fixedDelay = 15000) - @Transactional - public void updatePopularPosts() { - log.info("1번 스케줄러 시작"); - // 7일간 좋아요 많은 5개 DB에서 가져오기 - Map newPopularPosts = postQueryService.updatePopularPostsForSchedulerTest(); - Map beforePopularPosts = getBeforePopularPostMap(); - updatePosts(sortEntriesByValue(beforePopularPosts, newPopularPosts)); - } - - @Scheduled(fixedDelay = 10000) - public void updateViewCountToDB() { - //memory에있는 viewCount들을 post엔티티에 30초마다 반영해준다 - log.info("스케줄러 2 시작"); - postService.updateViewCountForPopularPosts(); - } - - /** - * 3초마다 인기글 개수(현재5개 x 2) 만큼 count쿼리발생 vs 1초당 200번의 count쿼리 발생의 트레이드오프 - */ - @Scheduled(fixedDelay = 3000) - public void getLikeCountFromDB() { - //likes테이블과 - log.info("스케줄러 3 시작, 좋아요 카운트와 댓글카운트를 메모리에 저장해두기"); - Set popularPostKeys = postMemoryService.getEntry(); - for (Long popularPostKey : popularPostKeys) { - postMemoryService.saveLikeCount(popularPostKey, postRepository.likeCountWithScheduler(popularPostKey)); - postMemoryService.saveCommentCount(popularPostKey, postRepository.commentCountWithScheduler(popularPostKey)); - } - } - - - private void updatePosts(List> entries) { - int popularPostId = 1; - log.info("updatePosts entries = {}", entries); - for (Entry sortedEntry : entries) { - popularPostRepository.updatePopularIds(sortedEntry.getKey(), popularPostId); - popularPostRepository.updatePopularlikeCount(sortedEntry.getValue(), popularPostId); - popularPostId++; - } - } - - private List> sortEntriesByValue(Map beforePopularPosts, Map newPopularPosts) { - // 새롭게 맵을 만들지않고 두 Map을 add하게되면 서로의 Map의 변화에 서로 영향을 받기때문에 UnsupportedOperationException발생 - Map addMap = new HashMap<>(); - addMap.putAll(beforePopularPosts); - addMap.putAll(newPopularPosts); - - return newPopularPosts.entrySet().stream() - .sorted(Entry.comparingByValue().reversed()) - .limit(POPULAR_POST_SIZE) - .collect(Collectors.toList()); - } - - private Map getBeforePopularPostMap() { - return popularPostRepository.findAll().stream() - .collect(Collectors.toMap(PopularPost::getPostId, PopularPost::getLikeCount, - (p1, p2) -> p1)); - } -}