From 8fa3188a6b58603ec7b34751d198b76b25bc2963 Mon Sep 17 00:00:00 2001 From: hongo Date: Sat, 9 Dec 2023 20:57:22 +0900 Subject: [PATCH 01/30] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 6316eb203..73ab56986 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,6 @@ ![서비스요청흐름도](https://github.com/woowacourse-teams/2023-hang-log/assets/102305630/dc9c1562-068d-4c73-84ef-d93e9051b679) -## 인프라 구조도 - -![인프라 구조도](https://github.com/woowacourse-teams/2023-hang-log/assets/102305630/656caaa3-125d-48ed-b996-e2858be4d36c) - ## CI/CD ![CICD](https://github.com/woowacourse-teams/2023-hang-log/assets/64852591/a55b3a1c-ce12-49d2-b4da-5b394c4de6c1) From c033764c14b0878fa814c381a4d9b161b117bf0e Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Fri, 26 Jan 2024 17:43:03 +0900 Subject: [PATCH 02/30] =?UTF-8?q?fix:=20LikeCount=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/hanglog/like/service/LikeService.java | 9 +++++++-- .../service/LikeServiceIntegrationTest.java | 13 ++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index fc4799265..cb2bd493e 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -1,5 +1,7 @@ package hanglog.like.service; +import static java.lang.Boolean.TRUE; + import hanglog.like.domain.LikeCount; import hanglog.like.domain.Likes; import hanglog.like.domain.MemberLike; @@ -44,8 +46,11 @@ public void update(final Long memberId, final Long tripId, final LikeRequest lik private void updateLikeCountCache(final Long tripId, final LikeRequest likeRequest) { final Optional likeCount = likeCountRepository.findById(tripId); - if (Boolean.TRUE.equals(likeRequest.getIsLike())) { - likeCount.ifPresent(count -> likeCountRepository.save(new LikeCount(tripId, count.getCount() + 1))); + if (TRUE.equals(likeRequest.getIsLike())) { + likeCount.ifPresentOrElse( + count -> likeCountRepository.save(new LikeCount(tripId, count.getCount() + 1)), + () -> likeCountRepository.save(new LikeCount(tripId, 1L)) + ); return; } likeCount.ifPresent(count -> likeCountRepository.save(new LikeCount(tripId, count.getCount() - 1))); diff --git a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java index 843d661e9..4ba1b9954 100644 --- a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java +++ b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java @@ -3,8 +3,10 @@ import static hanglog.integration.IntegrationFixture.TRIP_CREATE_REQUEST; import static org.assertj.core.api.SoftAssertions.assertSoftly; -import hanglog.like.dto.request.LikeRequest; +import hanglog.like.domain.LikeCount; +import hanglog.like.domain.repository.LikeCountRepository; import hanglog.like.domain.repository.MemberLikeRepository; +import hanglog.like.dto.request.LikeRequest; import hanglog.like.service.LikeService; import hanglog.trip.service.TripService; import org.junit.jupiter.api.DisplayName; @@ -22,6 +24,9 @@ class LikeServiceIntegrationTest extends RedisServiceIntegrationTest { @Autowired private MemberLikeRepository memberLikeRepository; + @Autowired + private LikeCountRepository likeCountRepository; + @DisplayName("해당 게시물의 좋아요 여부를 변경할 수 있다.") @Test void update() { @@ -41,6 +46,9 @@ void update() { .get() .getLikeStatusForTrip() .get(tripId)).isTrue(); + softly.assertThat(likeCountRepository.findById(tripId)).isPresent(); + softly.assertThat(likeCountRepository.findById(tripId)).get().usingRecursiveComparison() + .isEqualTo(new LikeCount(tripId, 1L)); likeService.update(member.getId(), tripId, likeFalseRequest); softly.assertThat(memberLikeRepository.findById(member.getId())).isPresent(); @@ -48,6 +56,9 @@ void update() { .get() .getLikeStatusForTrip() .get(tripId)).isFalse(); + softly.assertThat(likeCountRepository.findById(tripId)).isPresent(); + softly.assertThat(likeCountRepository.findById(tripId)).get().usingRecursiveComparison() + .isEqualTo(new LikeCount(tripId, 0L)); }); } } From 52b1d06b31636929931d98ca5553689487265d34 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Fri, 26 Jan 2024 17:47:48 +0900 Subject: [PATCH 03/30] =?UTF-8?q?refactor:=20updateMemberLikeCache=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/hanglog/like/service/LikeService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index cb2bd493e..482e2b337 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -34,6 +34,11 @@ public class LikeService { private final CustomLikeRepository customLikeRepository; public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { + updateMemberLikeCache(memberId, tripId, likeRequest); + updateLikeCountCache(tripId, likeRequest); + } + + private void updateMemberLikeCache(final Long memberId, final Long tripId, final LikeRequest likeRequest) { Map tripLikeStatusMap = new HashMap<>(); final Optional memberLike = memberLikeRepository.findById(memberId); if (memberLike.isPresent()) { @@ -41,7 +46,6 @@ public void update(final Long memberId, final Long tripId, final LikeRequest lik } tripLikeStatusMap.put(tripId, likeRequest.getIsLike()); memberLikeRepository.save(new MemberLike(memberId, tripLikeStatusMap)); - updateLikeCountCache(tripId, likeRequest); } private void updateLikeCountCache(final Long tripId, final LikeRequest likeRequest) { From ccbc23b6bb03c268cec16ba7ce397103a378f2ce Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Fri, 26 Jan 2024 17:58:08 +0900 Subject: [PATCH 04/30] =?UTF-8?q?fix:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EC=97=AC=ED=96=89=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=8B=9C=20Redis=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/community/service/CommunityService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index c23bbc041..6ba32a9dc 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -79,10 +79,7 @@ private List getCommunityTripResponses(final Accessor acc ); final Map> citiesByTrip = tripCityElements.toCityMap(); - final LikeElements likeElements = new LikeElements(likeRepository.findLikeCountAndIsLikeByTripIds( - accessor.getMemberId(), - tripIds - )); + final LikeElements likeElements = new LikeElements(getLikeElements(accessor.getMemberId(), tripIds)); final Map likeInfoByTrip = likeElements.toLikeMap(); return trips.stream() @@ -94,6 +91,7 @@ private List getCommunityTripResponses(final Accessor acc )).toList(); } + private boolean isLike(final Map likeInfoByTrip, final Long tripId) { final LikeInfo likeInfo = likeInfoByTrip.get(tripId); if (likeInfo == null) { @@ -141,6 +139,12 @@ public TripDetailResponse getTripDetail(final Accessor accessor, final Long trip ); } + private List getLikeElements(final Long memberId, final List tripIds) { + return tripIds.stream() + .map(tripId -> getLikeElement(memberId, tripId)) + .toList(); + } + private LikeElement getLikeElement(final Long memberId, final Long tripId) { final Optional likeCount = likeCountRepository.findById(tripId); final Optional memberLike = memberLikeRepository.findById(memberId); From 968ca26a7ddc38258f4c9b5623fba4fd5ca3bd9a Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Sun, 28 Jan 2024 00:54:05 +0900 Subject: [PATCH 05/30] =?UTF-8?q?feat:=20RedisTemplate=20=EB=B9=88=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/hanglog/global/config/RedisConfig.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/backend/src/main/java/hanglog/global/config/RedisConfig.java b/backend/src/main/java/hanglog/global/config/RedisConfig.java index 8835a5f83..63c533f7d 100644 --- a/backend/src/main/java/hanglog/global/config/RedisConfig.java +++ b/backend/src/main/java/hanglog/global/config/RedisConfig.java @@ -5,7 +5,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.GenericToStringSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @EnableRedisRepositories @@ -20,4 +23,13 @@ public class RedisConfig { public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(host, port); } + + @Bean + public RedisTemplate redisTemplate() { + final RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Long.class)); + return redisTemplate; + } } From 568997699d9757c06aa53c0a527febee9e74c158 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Sun, 28 Jan 2024 19:48:53 +0900 Subject: [PATCH 06/30] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/like/service/LikeService.java | 38 +++++++- .../service/LikeServiceIntegrationTest.java | 90 +++++++++++++------ 2 files changed, 96 insertions(+), 32 deletions(-) diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index 482e2b337..baf13ca0b 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -5,19 +5,22 @@ import hanglog.like.domain.LikeCount; import hanglog.like.domain.Likes; import hanglog.like.domain.MemberLike; -import hanglog.like.dto.TripLikeCount; -import hanglog.like.dto.request.LikeRequest; import hanglog.like.domain.repository.CustomLikeRepository; import hanglog.like.domain.repository.LikeCountRepository; import hanglog.like.domain.repository.LikeRepository; import hanglog.like.domain.repository.MemberLikeRepository; +import hanglog.like.dto.TripLikeCount; +import hanglog.like.dto.request.LikeRequest; import hanglog.member.domain.repository.MemberRepository; +import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.SetOperations; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,12 +35,39 @@ public class LikeService { private final LikeCountRepository likeCountRepository; private final MemberRepository memberRepository; private final CustomLikeRepository customLikeRepository; + private final RedisTemplate redisTemplate; public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { - updateMemberLikeCache(memberId, tripId, likeRequest); - updateLikeCountCache(tripId, likeRequest); + final SetOperations opsForSet = redisTemplate.opsForSet(); + final String key = String.format("likes:%d", tripId); + if (TRUE.equals(opsForSet.isMember(key, memberId))) { + removeMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); + return; + } + addMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); } + private void removeMemberInLike(final String key, final Long memberId, final Boolean isLike, + final SetOperations opsForSet) { + if (!isLike) { + opsForSet.remove(key, memberId); + redisTemplate.expire(key, Duration.ofMinutes(90L)); + } + } + + private void addMemberInLike(final String key, final Long memberId, final Boolean isLike, + final SetOperations opsForSet) { + if (isLike) { + opsForSet.add(key, memberId); + redisTemplate.expire(key, Duration.ofMinutes(90L)); + } + } + +// public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { +// updateMemberLikeCache(memberId, tripId, likeRequest); +// updateLikeCountCache(tripId, likeRequest); +// } + private void updateMemberLikeCache(final Long memberId, final Long tripId, final LikeRequest likeRequest) { Map tripLikeStatusMap = new HashMap<>(); final Optional memberLike = memberLikeRepository.findById(memberId); diff --git a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java index 4ba1b9954..5d85d82c1 100644 --- a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java +++ b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java @@ -1,17 +1,19 @@ package hanglog.integration.service; import static hanglog.integration.IntegrationFixture.TRIP_CREATE_REQUEST; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; import static org.assertj.core.api.SoftAssertions.assertSoftly; -import hanglog.like.domain.LikeCount; -import hanglog.like.domain.repository.LikeCountRepository; -import hanglog.like.domain.repository.MemberLikeRepository; import hanglog.like.dto.request.LikeRequest; import hanglog.like.service.LikeService; import hanglog.trip.service.TripService; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.SetOperations; class LikeServiceIntegrationTest extends RedisServiceIntegrationTest { @@ -22,10 +24,14 @@ class LikeServiceIntegrationTest extends RedisServiceIntegrationTest { private LikeService likeService; @Autowired - private MemberLikeRepository memberLikeRepository; + private RedisTemplate redisTemplate; - @Autowired - private LikeCountRepository likeCountRepository; + private SetOperations opsForSet; + + @BeforeEach + void setUp() { + opsForSet = redisTemplate.opsForSet(); + } @DisplayName("해당 게시물의 좋아요 여부를 변경할 수 있다.") @Test @@ -34,31 +40,59 @@ void update() { final LikeRequest likeTrueRequest = new LikeRequest(true); final LikeRequest likeFalseRequest = new LikeRequest(false); - final Long tripId = tripService.save(member.getId(), TRIP_CREATE_REQUEST); + final Long memberId = member.getId(); + final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); + final String key = String.format("likes:%d", tripId); + + // when & then + assertSoftly(softly -> { + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); + + likeService.update(memberId, tripId, likeTrueRequest); + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(TRUE); + + likeService.update(memberId, tripId, likeFalseRequest); + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); + }); + } + + @DisplayName("이미 여행에 좋아요를 누른 유저가 다시 좋아요 추가 요청을 보내면 무시하고 값을 그대로 유지한다.") + @Test + void update_TrueRequestWhenIsMember() { + // given + final LikeRequest likeTrueRequest = new LikeRequest(true); + + final Long memberId = member.getId(); + final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); + final String key = String.format("likes:%d", tripId); + + likeService.update(memberId, tripId, likeTrueRequest); + + // when & then + assertSoftly(softly -> { + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(TRUE); + likeService.update(memberId, tripId, likeTrueRequest); + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(TRUE); + }); + } + + @DisplayName("여행에 좋아요를 누르지 않은 유저가 좋아요 삭제 요청을 보내면 무시하고 값을 그대로 유지한다.") + @Test + void update_FalseRequestWhenIsNotMember() { + // given + final LikeRequest likeFalseRequest = new LikeRequest(false); + + final Long memberId = member.getId(); + final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); + final String key = String.format("likes:%d", tripId); + + likeService.update(memberId, tripId, likeFalseRequest); // when & then assertSoftly(softly -> { - softly.assertThat(memberLikeRepository.findById(member.getId())).isEmpty(); - - likeService.update(member.getId(), tripId, likeTrueRequest); - softly.assertThat(memberLikeRepository.findById(member.getId())).isPresent(); - softly.assertThat(memberLikeRepository.findById(member.getId()) - .get() - .getLikeStatusForTrip() - .get(tripId)).isTrue(); - softly.assertThat(likeCountRepository.findById(tripId)).isPresent(); - softly.assertThat(likeCountRepository.findById(tripId)).get().usingRecursiveComparison() - .isEqualTo(new LikeCount(tripId, 1L)); - - likeService.update(member.getId(), tripId, likeFalseRequest); - softly.assertThat(memberLikeRepository.findById(member.getId())).isPresent(); - softly.assertThat(memberLikeRepository.findById(member.getId()) - .get() - .getLikeStatusForTrip() - .get(tripId)).isFalse(); - softly.assertThat(likeCountRepository.findById(tripId)).isPresent(); - softly.assertThat(likeCountRepository.findById(tripId)).get().usingRecursiveComparison() - .isEqualTo(new LikeCount(tripId, 0L)); + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); + likeService.update(memberId, tripId, likeFalseRequest); + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); }); } } From 12d48c87cbe34e68185107d545f71a54e4f0f4de Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Sun, 28 Jan 2024 21:05:22 +0900 Subject: [PATCH 07/30] =?UTF-8?q?feat:=20db=EC=99=80=20redis=20=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/LikeRepository.java | 12 ++++ .../like/service/LikeSyncScheduler.java | 62 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java diff --git a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java index 1cdbe97e4..bc47b1044 100644 --- a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java +++ b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java @@ -5,6 +5,7 @@ import hanglog.like.dto.TripLikeCount; import java.util.List; import java.util.Optional; +import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -37,4 +38,15 @@ Optional findLikeCountAndIsLikeByTripId(@Param("memberId") final Lo GROUP BY l.tripId """) List findCountByAllTrips(); + + @Query(""" + SELECT new hanglog.like.dto.TripLikeCount(l.tripId, COUNT(l.memberId)) + FROM Likes l + WHERE l.tripId = :tripId + GROUP BY l.tripId + """) + List findByTripId(@Param("tripId") final Long tripId); + + @Query("DELETE FROM Likes WHERE tripId IN :tripIds") + void deleteByTripIds(final Set tripIds); } diff --git a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java new file mode 100644 index 000000000..1d39db6b6 --- /dev/null +++ b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java @@ -0,0 +1,62 @@ +package hanglog.like.service; + +import hanglog.like.domain.Likes; +import hanglog.like.domain.repository.CustomLikeRepository; +import hanglog.like.domain.repository.LikeRepository; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.SetOperations; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class LikeSyncScheduler { + + private final LikeRepository likeRepository; + private final CustomLikeRepository customLikeRepository; + private final RedisTemplate redisTemplate; + + @Scheduled(cron = "0 0 * * * *") + public void writeBackLikeCache() { + final Set likeKeys = redisTemplate.keys("likes:"); + if (Objects.isNull(likeKeys)) { + return; + } + + final Set tripIds = extractTripIdsInRedisKeys(likeKeys); + likeRepository.deleteByTripIds(tripIds); + + final List likes = extractLikesInRedisValues(tripIds); + customLikeRepository.saveAll(likes); + } + + private Set extractTripIdsInRedisKeys(final Set likeKeys) { + return likeKeys.stream().map(key -> { + final int indexOfColon = key.indexOf(":"); + return Long.valueOf(key.substring(indexOfColon + 1)); + }).collect(Collectors.toSet()); + } + + private List extractLikesInRedisValues(final Set tripIds) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + return tripIds.stream() + .flatMap(tripId -> { + final String key = String.format("likes:%d", tripId); + final Set memberIds = opsForSet.members(key); + return Optional.ofNullable(memberIds) + .map(ids -> ids.stream() + .filter(memberId -> !"empty".equals(memberId)) + .map(memberId -> new Likes(tripId, (Long) memberId)) + ).orElseGet(Stream::empty); + }).toList(); + } +} From 5373b3040209c9280b4e10a26ea7911c2002a007 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Sun, 28 Jan 2024 21:08:55 +0900 Subject: [PATCH 08/30] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 6ba32a9dc..24ec61459 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -3,6 +3,7 @@ import static hanglog.community.domain.recommendstrategy.RecommendType.LIKE; import static hanglog.global.exception.ExceptionCode.NOT_FOUND_TRIP_ID; import static hanglog.trip.domain.type.PublishedStatusType.PUBLISHED; +import static java.lang.Boolean.TRUE; import hanglog.auth.domain.Accessor; import hanglog.city.domain.City; @@ -14,26 +15,27 @@ import hanglog.community.dto.response.CommunityTripResponse; import hanglog.community.dto.response.RecommendTripListResponse; import hanglog.global.exception.BadRequestException; -import hanglog.like.domain.LikeCount; import hanglog.like.domain.LikeInfo; -import hanglog.like.domain.MemberLike; -import hanglog.like.dto.LikeElement; -import hanglog.like.dto.LikeElements; import hanglog.like.domain.repository.LikeCountRepository; import hanglog.like.domain.repository.LikeRepository; import hanglog.like.domain.repository.MemberLikeRepository; +import hanglog.like.dto.LikeElement; +import hanglog.like.dto.LikeElements; import hanglog.trip.domain.Trip; import hanglog.trip.domain.repository.TripCityRepository; import hanglog.trip.domain.repository.TripRepository; import hanglog.trip.dto.TripCityElements; import hanglog.trip.dto.response.TripDetailResponse; +import java.time.Duration; import java.time.LocalDateTime; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.Objects; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.SetOperations; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,6 +55,8 @@ public class CommunityService { private final PublishedTripRepository publishedTripRepository; private final LikeCountRepository likeCountRepository; private final MemberLikeRepository memberLikeRepository; + private final RedisTemplate redisTemplate; + @Transactional(readOnly = true) public CommunityTripListResponse getCommunityTripsByPage(final Accessor accessor, final Pageable pageable) { @@ -145,6 +149,28 @@ private List getLikeElements(final Long memberId, final List .toList(); } + private LikeElement getLikeElement(final Long memberId, final Long tripId) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + final String key = "likes:" + tripId; + if (TRUE.equals(redisTemplate.hasKey(key))) { + final boolean isLike = TRUE.equals(opsForSet.isMember(key, memberId)); + final long count = Objects.requireNonNull(opsForSet.size(key)) - 1; + return new LikeElement(tripId, count, isLike); + } + final LikeElement likeElement = likeRepository.findLikeCountAndIsLikeByTripId(memberId, tripId) + .orElse(new LikeElement(tripId, 0, false)); + cachingLike(key, tripId, opsForSet); + return likeElement; + } + + private void cachingLike(final String key, final Long tripId, final SetOperations opsForSet) { + final List memberIds = likeRepository.findByTripId(tripId); + opsForSet.add(key, "empty"); + memberIds.forEach(memberId -> opsForSet.add(key, memberId)); + redisTemplate.expire(key, Duration.ofMinutes(90L)); + } + + /* private LikeElement getLikeElement(final Long memberId, final Long tripId) { final Optional likeCount = likeCountRepository.findById(tripId); final Optional memberLike = memberLikeRepository.findById(memberId); @@ -158,4 +184,5 @@ private LikeElement getLikeElement(final Long memberId, final Long tripId) { return likeRepository.findLikeCountAndIsLikeByTripId(memberId, tripId) .orElseGet(() -> new LikeElement(tripId, 0, false)); } + */ } From 23378f7f129bd7a293015f40c925eb6d5c4c726b Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Sun, 28 Jan 2024 21:24:15 +0900 Subject: [PATCH 09/30] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 21 ------ .../hanglog/like/service/LikeService.java | 68 ------------------- 2 files changed, 89 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 24ec61459..91947f286 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -16,9 +16,7 @@ import hanglog.community.dto.response.RecommendTripListResponse; import hanglog.global.exception.BadRequestException; import hanglog.like.domain.LikeInfo; -import hanglog.like.domain.repository.LikeCountRepository; import hanglog.like.domain.repository.LikeRepository; -import hanglog.like.domain.repository.MemberLikeRepository; import hanglog.like.dto.LikeElement; import hanglog.like.dto.LikeElements; import hanglog.trip.domain.Trip; @@ -53,11 +51,8 @@ public class CommunityService { private final CityRepository cityRepository; private final RecommendStrategies recommendStrategies; private final PublishedTripRepository publishedTripRepository; - private final LikeCountRepository likeCountRepository; - private final MemberLikeRepository memberLikeRepository; private final RedisTemplate redisTemplate; - @Transactional(readOnly = true) public CommunityTripListResponse getCommunityTripsByPage(final Accessor accessor, final Pageable pageable) { final List trips = tripRepository.findPublishedTripByPageable(pageable.previousOrFirst()); @@ -169,20 +164,4 @@ private void cachingLike(final String key, final Long tripId, final SetOperation memberIds.forEach(memberId -> opsForSet.add(key, memberId)); redisTemplate.expire(key, Duration.ofMinutes(90L)); } - - /* - private LikeElement getLikeElement(final Long memberId, final Long tripId) { - final Optional likeCount = likeCountRepository.findById(tripId); - final Optional memberLike = memberLikeRepository.findById(memberId); - if (likeCount.isPresent() && memberLike.isPresent()) { - final Map tripLikeStatusMap = memberLike.get().getLikeStatusForTrip(); - if (tripLikeStatusMap.containsKey(tripId)) { - return new LikeElement(tripId, likeCount.get().getCount(), tripLikeStatusMap.get(tripId)); - } - return new LikeElement(tripId, likeCount.get().getCount(), false); - } - return likeRepository.findLikeCountAndIsLikeByTripId(memberId, tripId) - .orElseGet(() -> new LikeElement(tripId, 0, false)); - } - */ } diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index baf13ca0b..4fefdcb51 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -2,26 +2,11 @@ import static java.lang.Boolean.TRUE; -import hanglog.like.domain.LikeCount; -import hanglog.like.domain.Likes; -import hanglog.like.domain.MemberLike; -import hanglog.like.domain.repository.CustomLikeRepository; -import hanglog.like.domain.repository.LikeCountRepository; -import hanglog.like.domain.repository.LikeRepository; -import hanglog.like.domain.repository.MemberLikeRepository; -import hanglog.like.dto.TripLikeCount; import hanglog.like.dto.request.LikeRequest; -import hanglog.member.domain.repository.MemberRepository; import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,11 +15,6 @@ @Transactional public class LikeService { - private final LikeRepository likeRepository; - private final MemberLikeRepository memberLikeRepository; - private final LikeCountRepository likeCountRepository; - private final MemberRepository memberRepository; - private final CustomLikeRepository customLikeRepository; private final RedisTemplate redisTemplate; public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { @@ -62,52 +42,4 @@ private void addMemberInLike(final String key, final Long memberId, final Boolea redisTemplate.expire(key, Duration.ofMinutes(90L)); } } - -// public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { -// updateMemberLikeCache(memberId, tripId, likeRequest); -// updateLikeCountCache(tripId, likeRequest); -// } - - private void updateMemberLikeCache(final Long memberId, final Long tripId, final LikeRequest likeRequest) { - Map tripLikeStatusMap = new HashMap<>(); - final Optional memberLike = memberLikeRepository.findById(memberId); - if (memberLike.isPresent()) { - tripLikeStatusMap = memberLike.get().getLikeStatusForTrip(); - } - tripLikeStatusMap.put(tripId, likeRequest.getIsLike()); - memberLikeRepository.save(new MemberLike(memberId, tripLikeStatusMap)); - } - - private void updateLikeCountCache(final Long tripId, final LikeRequest likeRequest) { - final Optional likeCount = likeCountRepository.findById(tripId); - if (TRUE.equals(likeRequest.getIsLike())) { - likeCount.ifPresentOrElse( - count -> likeCountRepository.save(new LikeCount(tripId, count.getCount() + 1)), - () -> likeCountRepository.save(new LikeCount(tripId, 1L)) - ); - return; - } - likeCount.ifPresent(count -> likeCountRepository.save(new LikeCount(tripId, count.getCount() - 1))); - } - - @Scheduled(cron = "0 0 * * * *") - public void writeBackMemberLikeCache() { - final List likes = memberRepository.findAll().stream() - .flatMap(member -> memberLikeRepository.findById(member.getId()) - .map(memberLike -> memberLike.getLikeStatusForTrip() - .entrySet().stream() - .filter(Map.Entry::getValue) - .map(entry -> new Likes(entry.getKey(), member.getId()))) - .orElseGet(Stream::empty)) - .toList(); - customLikeRepository.saveAll(likes); - } - - @Scheduled(cron = "0 0 0 * * *") - public void cacheLikeCount() { - final List tripLikeCounts = likeRepository.findCountByAllTrips(); - for (final TripLikeCount tripLikeCount : tripLikeCounts) { - likeCountRepository.save(new LikeCount(tripLikeCount.getTripId(), tripLikeCount.getCount())); - } - } } From 7109d2d585f42131dcadd4c5ddc63c791ad90e40 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 19:27:05 +0900 Subject: [PATCH 10/30] =?UTF-8?q?refactor:=20LikeElement=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 68 ++++++++++++------- .../domain/repository/LikeRepository.java | 16 +++++ .../java/hanglog/like/dto/LikeElement.java | 17 ++++- .../java/hanglog/like/dto/LikeElements.java | 6 +- 4 files changed, 79 insertions(+), 28 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 91947f286..6ce834331 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -17,8 +17,8 @@ import hanglog.global.exception.BadRequestException; import hanglog.like.domain.LikeInfo; import hanglog.like.domain.repository.LikeRepository; -import hanglog.like.dto.LikeElement; import hanglog.like.dto.LikeElements; +import hanglog.like.dto.LikeElement; import hanglog.trip.domain.Trip; import hanglog.trip.domain.repository.TripCityRepository; import hanglog.trip.domain.repository.TripRepository; @@ -26,6 +26,8 @@ import hanglog.trip.dto.response.TripDetailResponse; import java.time.Duration; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -49,8 +51,8 @@ public class CommunityService { private final TripRepository tripRepository; private final TripCityRepository tripCityRepository; private final CityRepository cityRepository; - private final RecommendStrategies recommendStrategies; private final PublishedTripRepository publishedTripRepository; + private final RecommendStrategies recommendStrategies; private final RedisTemplate redisTemplate; @Transactional(readOnly = true) @@ -77,9 +79,7 @@ private List getCommunityTripResponses(final Accessor acc tripCityRepository.findTripIdAndCitiesByTripIds(tripIds) ); final Map> citiesByTrip = tripCityElements.toCityMap(); - - final LikeElements likeElements = new LikeElements(getLikeElements(accessor.getMemberId(), tripIds)); - final Map likeInfoByTrip = likeElements.toLikeMap(); + final Map likeInfoByTrip = getLikeInfoByTripIds(accessor.getMemberId(), tripIds); return trips.stream() .map(trip -> CommunityTripResponse.of( @@ -90,7 +90,6 @@ private List getCommunityTripResponses(final Accessor acc )).toList(); } - private boolean isLike(final Map likeInfoByTrip, final Long tripId) { final LikeInfo likeInfo = likeInfoByTrip.get(tripId); if (likeInfo == null) { @@ -125,43 +124,62 @@ public TripDetailResponse getTripDetail(final Accessor accessor, final Long trip .orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID)) .getCreatedAt(); - final LikeElement likeElement = getLikeElement(accessor.getMemberId(), tripId); + final LikeInfo likeInfo = getLikeInfoByTripId(accessor.getMemberId(), tripId); final Boolean isWriter = trip.isWriter(accessor.getMemberId()); return TripDetailResponse.publishedTrip( trip, cities, isWriter, - likeElement.isLike(), - likeElement.getLikeCount(), + likeInfo.isLike(), + likeInfo.getLikeCount(), publishedDate ); } - private List getLikeElements(final Long memberId, final List tripIds) { - return tripIds.stream() - .map(tripId -> getLikeElement(memberId, tripId)) - .toList(); + private Map getLikeInfoByTripIds(final Long memberId, final List tripIds) { + final Map likeInfoByTrip = new HashMap<>(); + + final List nonCachedTripIds = new ArrayList<>(); + for (final Long tripId : tripIds) { + final String key = "likes:" + tripId; + if (TRUE.equals(redisTemplate.hasKey(key))) { + likeInfoByTrip.put(tripId, readLikeInfoFromCache(key, memberId)); + continue; + } + nonCachedTripIds.add(tripId); + } + + final List likeElementByTripIds = likeRepository.findLikeElementByTripIds(nonCachedTripIds); + likeElementByTripIds.forEach(this::cachingLike); + likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); + return likeInfoByTrip; } - private LikeElement getLikeElement(final Long memberId, final Long tripId) { - final SetOperations opsForSet = redisTemplate.opsForSet(); + private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { final String key = "likes:" + tripId; if (TRUE.equals(redisTemplate.hasKey(key))) { - final boolean isLike = TRUE.equals(opsForSet.isMember(key, memberId)); - final long count = Objects.requireNonNull(opsForSet.size(key)) - 1; - return new LikeElement(tripId, count, isLike); + return readLikeInfoFromCache(key, memberId); } - final LikeElement likeElement = likeRepository.findLikeCountAndIsLikeByTripId(memberId, tripId) - .orElse(new LikeElement(tripId, 0, false)); - cachingLike(key, tripId, opsForSet); - return likeElement; + + final LikeElement likeElement = likeRepository.findLikesElementByTripId(tripId) + .orElse(LikeElement.empty(tripId)); + cachingLike(likeElement); + return likeElement.toLikeMap(memberId); } - private void cachingLike(final String key, final Long tripId, final SetOperations opsForSet) { - final List memberIds = likeRepository.findByTripId(tripId); + private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + final boolean isLike = TRUE.equals(opsForSet.isMember(key, memberId)); + final long count = Objects.requireNonNull(opsForSet.size(key)) - 1; + return new LikeInfo(count, isLike); + } + + private void cachingLike(final LikeElement likeElement) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + final String key = "likes:" + likeElement.getTripId(); opsForSet.add(key, "empty"); - memberIds.forEach(memberId -> opsForSet.add(key, memberId)); + opsForSet.add(key, likeElement.getMemberIds()); redisTemplate.expire(key, Duration.ofMinutes(90L)); } } diff --git a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java index bc47b1044..f24c2fdfb 100644 --- a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java +++ b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java @@ -47,6 +47,22 @@ Optional findLikeCountAndIsLikeByTripId(@Param("memberId") final Lo """) List findByTripId(@Param("tripId") final Long tripId); + @Query(value = """ + SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds + FROM Likes l + WHERE l.trip_id IN :tripIds + GROUP BY l.trip_id + """, nativeQuery = true) + List findLikeElementByTripIds(@Param("tripIds") final List tripIds); + + @Query(value = """ + SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds + FROM Likes l + WHERE l.trip_id = :tripId + GROUP BY l.trip_id + """, nativeQuery = true) + Optional findLikesElementByTripId(@Param("tripId") final Long tripId); + @Query("DELETE FROM Likes WHERE tripId IN :tripIds") void deleteByTripIds(final Set tripIds); } diff --git a/backend/src/main/java/hanglog/like/dto/LikeElement.java b/backend/src/main/java/hanglog/like/dto/LikeElement.java index d94d9674f..093bd4557 100644 --- a/backend/src/main/java/hanglog/like/dto/LikeElement.java +++ b/backend/src/main/java/hanglog/like/dto/LikeElement.java @@ -1,5 +1,8 @@ package hanglog.like.dto; +import hanglog.like.domain.LikeInfo; +import java.util.Collections; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; @@ -9,5 +12,17 @@ public class LikeElement { private final Long tripId; private final long likeCount; - private final boolean isLike; + private final Set memberIds; + + public boolean isLike(final Long memberId) { + return memberIds.contains(memberId); + } + + public LikeInfo toLikeMap(final Long memberId) { + return new LikeInfo(this.getLikeCount(), this.isLike(memberId)); + } + + public static LikeElement empty(final Long tripId) { + return new LikeElement(tripId, 0, Collections.emptySet()); + } } diff --git a/backend/src/main/java/hanglog/like/dto/LikeElements.java b/backend/src/main/java/hanglog/like/dto/LikeElements.java index 2552e8a91..3cd756498 100644 --- a/backend/src/main/java/hanglog/like/dto/LikeElements.java +++ b/backend/src/main/java/hanglog/like/dto/LikeElements.java @@ -5,16 +5,18 @@ import java.util.List; import java.util.Map; import lombok.AllArgsConstructor; +import lombok.Getter; +@Getter @AllArgsConstructor public class LikeElements { private final List elements; - public Map toLikeMap() { + public Map toLikeMap(final Long memberId) { final Map map = new HashMap<>(); for (final LikeElement likeElement : elements) { - final LikeInfo likeInfo = new LikeInfo(likeElement.getLikeCount(), likeElement.isLike()); + final LikeInfo likeInfo = new LikeInfo(likeElement.getLikeCount(), likeElement.isLike(memberId)); map.put(likeElement.getTripId(), likeInfo); } return map; From d1c43644e808a70142b12366dc09254216a34694 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 19:54:35 +0900 Subject: [PATCH 11/30] =?UTF-8?q?refactor:=20Likes=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EC=97=90=20=EC=97=86=EB=8A=94=20tripId=EC=97=90=20def?= =?UTF-8?q?ault=20=EA=B0=92=20=ED=95=A0=EB=8B=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 6ce834331..08ff309a3 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -17,8 +17,8 @@ import hanglog.global.exception.BadRequestException; import hanglog.like.domain.LikeInfo; import hanglog.like.domain.repository.LikeRepository; -import hanglog.like.dto.LikeElements; import hanglog.like.dto.LikeElement; +import hanglog.like.dto.LikeElements; import hanglog.trip.domain.Trip; import hanglog.trip.domain.repository.TripCityRepository; import hanglog.trip.domain.repository.TripRepository; @@ -85,27 +85,11 @@ private List getCommunityTripResponses(final Accessor acc .map(trip -> CommunityTripResponse.of( trip, citiesByTrip.get(trip.getId()), - isLike(likeInfoByTrip, trip.getId()), - getLikeCount(likeInfoByTrip, trip.getId()) + likeInfoByTrip.get(trip.getId()).isLike(), + likeInfoByTrip.get(trip.getId()).getLikeCount() )).toList(); } - private boolean isLike(final Map likeInfoByTrip, final Long tripId) { - final LikeInfo likeInfo = likeInfoByTrip.get(tripId); - if (likeInfo == null) { - return false; - } - return likeInfo.isLike(); - } - - private Long getLikeCount(final Map likeInfoByTrip, final Long tripId) { - final LikeInfo likeInfo = likeInfoByTrip.get(tripId); - if (likeInfo == null) { - return 0L; - } - return likeInfo.getLikeCount(); - } - private Long getLastPageIndex(final int pageSize) { final Long totalTripCount = tripRepository.countTripByPublishedStatus(PUBLISHED); final long lastPageIndex = totalTripCount / pageSize; @@ -145,12 +129,13 @@ private Map getLikeInfoByTripIds(final Long memberId, final List final String key = "likes:" + tripId; if (TRUE.equals(redisTemplate.hasKey(key))) { likeInfoByTrip.put(tripId, readLikeInfoFromCache(key, memberId)); - continue; + } else { + nonCachedTripIds.add(tripId); } - nonCachedTripIds.add(tripId); } final List likeElementByTripIds = likeRepository.findLikeElementByTripIds(nonCachedTripIds); + likeElementByTripIds.addAll(getEmptyLikeElements(likeInfoByTrip, nonCachedTripIds)); likeElementByTripIds.forEach(this::cachingLike); likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); return likeInfoByTrip; @@ -168,6 +153,16 @@ private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { return likeElement.toLikeMap(memberId); } + private List getEmptyLikeElements( + final Map likeInfoByTrip, + final List nonCachedTripIds + ) { + return nonCachedTripIds.stream() + .filter(tripId -> likeInfoByTrip.get(tripId) == null) + .map(LikeElement::empty) + .toList(); + } + private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { final SetOperations opsForSet = redisTemplate.opsForSet(); final boolean isLike = TRUE.equals(opsForSet.isMember(key, memberId)); From 418b3e60712b6f2d7e1a31a7649c41d063ae3206 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 20:29:51 +0900 Subject: [PATCH 12/30] =?UTF-8?q?refactor:=20LikeRedisKeyConstants=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 10 ++++++---- .../java/hanglog/like/domain/LikeCount.java | 17 ----------------- .../like/domain/LikeRedisKeyConstants.java | 12 ++++++++++++ .../domain/repository/LikeCountRepository.java | 10 ---------- .../java/hanglog/like/service/LikeService.java | 3 ++- .../hanglog/like/service/LikeSyncScheduler.java | 13 +++++++++---- .../service/LikeServiceIntegrationTest.java | 7 ++++--- 7 files changed, 33 insertions(+), 39 deletions(-) delete mode 100644 backend/src/main/java/hanglog/like/domain/LikeCount.java create mode 100644 backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java delete mode 100644 backend/src/main/java/hanglog/like/domain/repository/LikeCountRepository.java diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 08ff309a3..a78e351c9 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -2,6 +2,8 @@ import static hanglog.community.domain.recommendstrategy.RecommendType.LIKE; import static hanglog.global.exception.ExceptionCode.NOT_FOUND_TRIP_ID; +import static hanglog.like.domain.LikeRedisKeyConstants.EMPTY_MARKER; +import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; import static hanglog.trip.domain.type.PublishedStatusType.PUBLISHED; import static java.lang.Boolean.TRUE; @@ -126,7 +128,7 @@ private Map getLikeInfoByTripIds(final Long memberId, final List final List nonCachedTripIds = new ArrayList<>(); for (final Long tripId : tripIds) { - final String key = "likes:" + tripId; + final String key = generateLikeKey(tripId); if (TRUE.equals(redisTemplate.hasKey(key))) { likeInfoByTrip.put(tripId, readLikeInfoFromCache(key, memberId)); } else { @@ -142,7 +144,7 @@ private Map getLikeInfoByTripIds(final Long memberId, final List } private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { - final String key = "likes:" + tripId; + final String key = generateLikeKey(tripId); if (TRUE.equals(redisTemplate.hasKey(key))) { return readLikeInfoFromCache(key, memberId); } @@ -172,8 +174,8 @@ private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { private void cachingLike(final LikeElement likeElement) { final SetOperations opsForSet = redisTemplate.opsForSet(); - final String key = "likes:" + likeElement.getTripId(); - opsForSet.add(key, "empty"); + final String key = generateLikeKey(likeElement.getTripId()); + opsForSet.add(key, EMPTY_MARKER); opsForSet.add(key, likeElement.getMemberIds()); redisTemplate.expire(key, Duration.ofMinutes(90L)); } diff --git a/backend/src/main/java/hanglog/like/domain/LikeCount.java b/backend/src/main/java/hanglog/like/domain/LikeCount.java deleted file mode 100644 index 62730449f..000000000 --- a/backend/src/main/java/hanglog/like/domain/LikeCount.java +++ /dev/null @@ -1,17 +0,0 @@ -package hanglog.like.domain; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; - -@Getter -@AllArgsConstructor -@RedisHash(value = "likeCount") -public class LikeCount { - - @Id - private Long tripId; - - private Long count; -} diff --git a/backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java b/backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java new file mode 100644 index 000000000..4632b5666 --- /dev/null +++ b/backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java @@ -0,0 +1,12 @@ +package hanglog.like.domain; + +public class LikeRedisKeyConstants { + + public static final String LIKE_KEY_PREFIX = "like:"; + public static final String SEPARATOR = ":"; + public static final String EMPTY_MARKER = "empty"; + + public static String generateLikeKey(final Long tripId) { + return LIKE_KEY_PREFIX + tripId; + } +} diff --git a/backend/src/main/java/hanglog/like/domain/repository/LikeCountRepository.java b/backend/src/main/java/hanglog/like/domain/repository/LikeCountRepository.java deleted file mode 100644 index de64fea80..000000000 --- a/backend/src/main/java/hanglog/like/domain/repository/LikeCountRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package hanglog.like.domain.repository; - -import hanglog.like.domain.LikeCount; -import java.util.List; -import org.springframework.data.repository.CrudRepository; - -public interface LikeCountRepository extends CrudRepository { - - List findLikeCountsByTripIdIn(final List tripIds); -} diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index 4fefdcb51..4d1e25a71 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -1,5 +1,6 @@ package hanglog.like.service; +import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; import static java.lang.Boolean.TRUE; import hanglog.like.dto.request.LikeRequest; @@ -19,7 +20,7 @@ public class LikeService { public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { final SetOperations opsForSet = redisTemplate.opsForSet(); - final String key = String.format("likes:%d", tripId); + final String key = generateLikeKey(tripId); if (TRUE.equals(opsForSet.isMember(key, memberId))) { removeMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); return; diff --git a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java index 1d39db6b6..02a6357b4 100644 --- a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java +++ b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java @@ -1,5 +1,10 @@ package hanglog.like.service; +import static hanglog.like.domain.LikeRedisKeyConstants.EMPTY_MARKER; +import static hanglog.like.domain.LikeRedisKeyConstants.LIKE_KEY_PREFIX; +import static hanglog.like.domain.LikeRedisKeyConstants.SEPARATOR; +import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; + import hanglog.like.domain.Likes; import hanglog.like.domain.repository.CustomLikeRepository; import hanglog.like.domain.repository.LikeRepository; @@ -27,7 +32,7 @@ public class LikeSyncScheduler { @Scheduled(cron = "0 0 * * * *") public void writeBackLikeCache() { - final Set likeKeys = redisTemplate.keys("likes:"); + final Set likeKeys = redisTemplate.keys(LIKE_KEY_PREFIX); if (Objects.isNull(likeKeys)) { return; } @@ -41,7 +46,7 @@ public void writeBackLikeCache() { private Set extractTripIdsInRedisKeys(final Set likeKeys) { return likeKeys.stream().map(key -> { - final int indexOfColon = key.indexOf(":"); + final int indexOfColon = key.indexOf(SEPARATOR); return Long.valueOf(key.substring(indexOfColon + 1)); }).collect(Collectors.toSet()); } @@ -50,11 +55,11 @@ private List extractLikesInRedisValues(final Set tripIds) { final SetOperations opsForSet = redisTemplate.opsForSet(); return tripIds.stream() .flatMap(tripId -> { - final String key = String.format("likes:%d", tripId); + final String key = generateLikeKey(tripId); final Set memberIds = opsForSet.members(key); return Optional.ofNullable(memberIds) .map(ids -> ids.stream() - .filter(memberId -> !"empty".equals(memberId)) + .filter(memberId -> !EMPTY_MARKER.equals(memberId)) .map(memberId -> new Likes(tripId, (Long) memberId)) ).orElseGet(Stream::empty); }).toList(); diff --git a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java index 5d85d82c1..d3e19f9c4 100644 --- a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java +++ b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java @@ -1,6 +1,7 @@ package hanglog.integration.service; import static hanglog.integration.IntegrationFixture.TRIP_CREATE_REQUEST; +import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static org.assertj.core.api.SoftAssertions.assertSoftly; @@ -42,7 +43,7 @@ void update() { final Long memberId = member.getId(); final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); - final String key = String.format("likes:%d", tripId); + final String key = generateLikeKey(tripId); // when & then assertSoftly(softly -> { @@ -64,7 +65,7 @@ void update_TrueRequestWhenIsMember() { final Long memberId = member.getId(); final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); - final String key = String.format("likes:%d", tripId); + final String key = generateLikeKey(tripId); likeService.update(memberId, tripId, likeTrueRequest); @@ -84,7 +85,7 @@ void update_FalseRequestWhenIsNotMember() { final Long memberId = member.getId(); final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); - final String key = String.format("likes:%d", tripId); + final String key = generateLikeKey(tripId); likeService.update(memberId, tripId, likeFalseRequest); From 64dbb6fda70bdfbfdf72c1b583ca4575498ec0f6 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 20:44:11 +0900 Subject: [PATCH 13/30] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20dto=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/hanglog/like/domain/MemberLike.java | 18 ---------- .../domain/repository/LikeRepository.java | 36 ------------------- .../repository/MemberLikeRepository.java | 7 ---- .../java/hanglog/like/dto/TripLikeCount.java | 12 ------- 4 files changed, 73 deletions(-) delete mode 100644 backend/src/main/java/hanglog/like/domain/MemberLike.java delete mode 100644 backend/src/main/java/hanglog/like/domain/repository/MemberLikeRepository.java delete mode 100644 backend/src/main/java/hanglog/like/dto/TripLikeCount.java diff --git a/backend/src/main/java/hanglog/like/domain/MemberLike.java b/backend/src/main/java/hanglog/like/domain/MemberLike.java deleted file mode 100644 index 50101bee5..000000000 --- a/backend/src/main/java/hanglog/like/domain/MemberLike.java +++ /dev/null @@ -1,18 +0,0 @@ -package hanglog.like.domain; - -import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; - -@Getter -@AllArgsConstructor -@RedisHash(value = "memberLike", timeToLive = 5400) -public class MemberLike { - - @Id - private Long memberId; - - private Map likeStatusForTrip; -} diff --git a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java index f24c2fdfb..27eec2818 100644 --- a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java +++ b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java @@ -2,7 +2,6 @@ import hanglog.like.domain.Likes; import hanglog.like.dto.LikeElement; -import hanglog.like.dto.TripLikeCount; import java.util.List; import java.util.Optional; import java.util.Set; @@ -12,41 +11,6 @@ public interface LikeRepository extends JpaRepository { - @Query(""" - SELECT new hanglog.like.dto.LikeElement - (l.tripId, COUNT(l.memberId), EXISTS(SELECT 1 FROM Likes l_1 WHERE l_1.memberId = :memberId AND l_1.tripId = l.tripId)) - FROM Likes l - WHERE l.tripId in :tripIds - GROUP BY l.tripId - """) - List findLikeCountAndIsLikeByTripIds(@Param("memberId") final Long memberId, - @Param("tripIds") final List tripIds); - - @Query(""" - SELECT new hanglog.like.dto.LikeElement - (l.tripId, COUNT(l.memberId), EXISTS(SELECT 1 FROM Likes l_1 WHERE l_1.memberId = :memberId AND l_1.tripId = l.tripId)) - FROM Likes l - WHERE l.tripId = :tripId - GROUP BY l.tripId - """) - Optional findLikeCountAndIsLikeByTripId(@Param("memberId") final Long memberId, - @Param("tripId") final Long tripId); - - @Query(""" - SELECT new hanglog.like.dto.TripLikeCount(l.tripId, COUNT(l.memberId)) - FROM Likes l - GROUP BY l.tripId - """) - List findCountByAllTrips(); - - @Query(""" - SELECT new hanglog.like.dto.TripLikeCount(l.tripId, COUNT(l.memberId)) - FROM Likes l - WHERE l.tripId = :tripId - GROUP BY l.tripId - """) - List findByTripId(@Param("tripId") final Long tripId); - @Query(value = """ SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds FROM Likes l diff --git a/backend/src/main/java/hanglog/like/domain/repository/MemberLikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/MemberLikeRepository.java deleted file mode 100644 index f338c37cd..000000000 --- a/backend/src/main/java/hanglog/like/domain/repository/MemberLikeRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package hanglog.like.domain.repository; - -import hanglog.like.domain.MemberLike; -import org.springframework.data.repository.CrudRepository; - -public interface MemberLikeRepository extends CrudRepository { -} diff --git a/backend/src/main/java/hanglog/like/dto/TripLikeCount.java b/backend/src/main/java/hanglog/like/dto/TripLikeCount.java deleted file mode 100644 index 5f43296c9..000000000 --- a/backend/src/main/java/hanglog/like/dto/TripLikeCount.java +++ /dev/null @@ -1,12 +0,0 @@ -package hanglog.like.dto; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public class TripLikeCount { - - private final long tripId; - private final long count; -} From 90984b383d6a3b119e29cb4268e1a0d4f3ef28ae Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 20:54:09 +0900 Subject: [PATCH 14/30] =?UTF-8?q?refactor:=20like=20ttl=20=EC=83=81?= =?UTF-8?q?=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/community/service/CommunityService.java | 8 ++++---- ...eRedisKeyConstants.java => LikeRedisConstants.java} | 7 +++++-- .../main/java/hanglog/like/service/LikeService.java | 8 ++++---- .../java/hanglog/like/service/LikeSyncScheduler.java | 10 +++++----- .../service/LikeServiceIntegrationTest.java | 2 +- 5 files changed, 19 insertions(+), 16 deletions(-) rename backend/src/main/java/hanglog/like/domain/{LikeRedisKeyConstants.java => LikeRedisConstants.java} (58%) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index a78e351c9..650ee0ea2 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -2,8 +2,9 @@ import static hanglog.community.domain.recommendstrategy.RecommendType.LIKE; import static hanglog.global.exception.ExceptionCode.NOT_FOUND_TRIP_ID; -import static hanglog.like.domain.LikeRedisKeyConstants.EMPTY_MARKER; -import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; +import static hanglog.like.domain.LikeRedisConstants.EMPTY_MARKER; +import static hanglog.like.domain.LikeRedisConstants.LIKE_TTL; +import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; import static hanglog.trip.domain.type.PublishedStatusType.PUBLISHED; import static java.lang.Boolean.TRUE; @@ -26,7 +27,6 @@ import hanglog.trip.domain.repository.TripRepository; import hanglog.trip.dto.TripCityElements; import hanglog.trip.dto.response.TripDetailResponse; -import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; @@ -177,6 +177,6 @@ private void cachingLike(final LikeElement likeElement) { final String key = generateLikeKey(likeElement.getTripId()); opsForSet.add(key, EMPTY_MARKER); opsForSet.add(key, likeElement.getMemberIds()); - redisTemplate.expire(key, Duration.ofMinutes(90L)); + redisTemplate.expire(key, LIKE_TTL); } } diff --git a/backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java similarity index 58% rename from backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java rename to backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java index 4632b5666..ca381710d 100644 --- a/backend/src/main/java/hanglog/like/domain/LikeRedisKeyConstants.java +++ b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java @@ -1,10 +1,13 @@ package hanglog.like.domain; -public class LikeRedisKeyConstants { +import java.time.Duration; + +public class LikeRedisConstants { public static final String LIKE_KEY_PREFIX = "like:"; - public static final String SEPARATOR = ":"; + public static final String KEY_SEPARATOR = ":"; public static final String EMPTY_MARKER = "empty"; + public static final Duration LIKE_TTL = Duration.ofMinutes(90L); public static String generateLikeKey(final Long tripId) { return LIKE_KEY_PREFIX + tripId; diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index 4d1e25a71..ec0183010 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -1,10 +1,10 @@ package hanglog.like.service; -import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; +import static hanglog.like.domain.LikeRedisConstants.LIKE_TTL; +import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; import static java.lang.Boolean.TRUE; import hanglog.like.dto.request.LikeRequest; -import java.time.Duration; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; @@ -32,7 +32,7 @@ private void removeMemberInLike(final String key, final Long memberId, final Boo final SetOperations opsForSet) { if (!isLike) { opsForSet.remove(key, memberId); - redisTemplate.expire(key, Duration.ofMinutes(90L)); + redisTemplate.expire(key, LIKE_TTL); } } @@ -40,7 +40,7 @@ private void addMemberInLike(final String key, final Long memberId, final Boolea final SetOperations opsForSet) { if (isLike) { opsForSet.add(key, memberId); - redisTemplate.expire(key, Duration.ofMinutes(90L)); + redisTemplate.expire(key, LIKE_TTL); } } } diff --git a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java index 02a6357b4..07c8d9870 100644 --- a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java +++ b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java @@ -1,9 +1,9 @@ package hanglog.like.service; -import static hanglog.like.domain.LikeRedisKeyConstants.EMPTY_MARKER; -import static hanglog.like.domain.LikeRedisKeyConstants.LIKE_KEY_PREFIX; -import static hanglog.like.domain.LikeRedisKeyConstants.SEPARATOR; -import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; +import static hanglog.like.domain.LikeRedisConstants.EMPTY_MARKER; +import static hanglog.like.domain.LikeRedisConstants.LIKE_KEY_PREFIX; +import static hanglog.like.domain.LikeRedisConstants.KEY_SEPARATOR; +import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; import hanglog.like.domain.Likes; import hanglog.like.domain.repository.CustomLikeRepository; @@ -46,7 +46,7 @@ public void writeBackLikeCache() { private Set extractTripIdsInRedisKeys(final Set likeKeys) { return likeKeys.stream().map(key -> { - final int indexOfColon = key.indexOf(SEPARATOR); + final int indexOfColon = key.indexOf(KEY_SEPARATOR); return Long.valueOf(key.substring(indexOfColon + 1)); }).collect(Collectors.toSet()); } diff --git a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java index d3e19f9c4..e410a9ac5 100644 --- a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java +++ b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java @@ -1,7 +1,7 @@ package hanglog.integration.service; import static hanglog.integration.IntegrationFixture.TRIP_CREATE_REQUEST; -import static hanglog.like.domain.LikeRedisKeyConstants.generateLikeKey; +import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static org.assertj.core.api.SoftAssertions.assertSoftly; From ee61595b0a3146d4f5f1e10b512c6c63c9ff3ffb Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 21:00:23 +0900 Subject: [PATCH 15/30] =?UTF-8?q?fix:=20likes=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=86=8C=EB=AC=B8=EC=9E=90=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/hanglog/like/domain/repository/LikeRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java index 27eec2818..13ffae5f4 100644 --- a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java +++ b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java @@ -13,7 +13,7 @@ public interface LikeRepository extends JpaRepository { @Query(value = """ SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds - FROM Likes l + FROM likes l WHERE l.trip_id IN :tripIds GROUP BY l.trip_id """, nativeQuery = true) @@ -21,7 +21,7 @@ SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) @Query(value = """ SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds - FROM Likes l + FROM likes l WHERE l.trip_id = :tripId GROUP BY l.trip_id """, nativeQuery = true) From 79335e8382103e8ba718fc1099c0969694a998ff Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 21:30:14 +0900 Subject: [PATCH 16/30] =?UTF-8?q?fix:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 8 ++- .../repository/CustomLikeRepository.java | 6 ++ .../domain/repository/LikeRepository.java | 22 +------- .../CustomLikeRepositoryImpl.java | 56 +++++++++++++++++++ 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 650ee0ea2..9dad81835 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -19,6 +19,7 @@ import hanglog.community.dto.response.RecommendTripListResponse; import hanglog.global.exception.BadRequestException; import hanglog.like.domain.LikeInfo; +import hanglog.like.domain.repository.CustomLikeRepository; import hanglog.like.domain.repository.LikeRepository; import hanglog.like.dto.LikeElement; import hanglog.like.dto.LikeElements; @@ -49,11 +50,12 @@ public class CommunityService { private static final int RECOMMEND_AMOUNT = 5; - private final LikeRepository likeRepository; private final TripRepository tripRepository; private final TripCityRepository tripCityRepository; private final CityRepository cityRepository; private final PublishedTripRepository publishedTripRepository; + private final LikeRepository likeRepository; + private final CustomLikeRepository customLikeRepository; private final RecommendStrategies recommendStrategies; private final RedisTemplate redisTemplate; @@ -136,7 +138,7 @@ private Map getLikeInfoByTripIds(final Long memberId, final List } } - final List likeElementByTripIds = likeRepository.findLikeElementByTripIds(nonCachedTripIds); + final List likeElementByTripIds = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); likeElementByTripIds.addAll(getEmptyLikeElements(likeInfoByTrip, nonCachedTripIds)); likeElementByTripIds.forEach(this::cachingLike); likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); @@ -149,7 +151,7 @@ private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { return readLikeInfoFromCache(key, memberId); } - final LikeElement likeElement = likeRepository.findLikesElementByTripId(tripId) + final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) .orElse(LikeElement.empty(tripId)); cachingLike(likeElement); return likeElement.toLikeMap(memberId); diff --git a/backend/src/main/java/hanglog/like/domain/repository/CustomLikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/CustomLikeRepository.java index 32e06b818..51c74b57e 100644 --- a/backend/src/main/java/hanglog/like/domain/repository/CustomLikeRepository.java +++ b/backend/src/main/java/hanglog/like/domain/repository/CustomLikeRepository.java @@ -1,9 +1,15 @@ package hanglog.like.domain.repository; import hanglog.like.domain.Likes; +import hanglog.like.dto.LikeElement; import java.util.List; +import java.util.Optional; public interface CustomLikeRepository { void saveAll(final List likes); + + Optional findLikesElementByTripId(final Long tripId); + + List findLikeElementByTripIds(final List tripIds); } diff --git a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java index 13ffae5f4..7f1338b46 100644 --- a/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java +++ b/backend/src/main/java/hanglog/like/domain/repository/LikeRepository.java @@ -1,32 +1,14 @@ package hanglog.like.domain.repository; import hanglog.like.domain.Likes; -import hanglog.like.dto.LikeElement; -import java.util.List; -import java.util.Optional; import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; public interface LikeRepository extends JpaRepository { - @Query(value = """ - SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds - FROM likes l - WHERE l.trip_id IN :tripIds - GROUP BY l.trip_id - """, nativeQuery = true) - List findLikeElementByTripIds(@Param("tripIds") final List tripIds); - - @Query(value = """ - SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds - FROM likes l - WHERE l.trip_id = :tripId - GROUP BY l.trip_id - """, nativeQuery = true) - Optional findLikesElementByTripId(@Param("tripId") final Long tripId); - + @Modifying @Query("DELETE FROM Likes WHERE tripId IN :tripIds") void deleteByTripIds(final Set tripIds); } diff --git a/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java b/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java index 98e983850..3517ed578 100644 --- a/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java +++ b/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java @@ -2,16 +2,30 @@ import hanglog.like.domain.Likes; import hanglog.like.domain.repository.CustomLikeRepository; +import hanglog.like.dto.LikeElement; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; +import org.springframework.util.StringUtils; @RequiredArgsConstructor @Repository public class CustomLikeRepositoryImpl implements CustomLikeRepository { + private static final RowMapper likeElementRowMapper = (rs, rowNum) -> + new LikeElement( + rs.getLong("tripId"), + rs.getLong("likeCount"), + parseMemberIds(rs.getString("memberIds"))); + private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; @Override @@ -34,4 +48,46 @@ private MapSqlParameterSource getLikeToSqlParameterSource(final Likes likes) { .addValue("tripId", likes.getTripId()) .addValue("memberId", likes.getMemberId()); } + + @Override + public Optional findLikesElementByTripId(final Long tripId) { + final String sql = """ + SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds + FROM likes l + WHERE l.trip_id = :tripId + GROUP BY l.trip_id + """; + + final MapSqlParameterSource parameters = new MapSqlParameterSource(); + parameters.addValue("tripId", tripId); + final List results = namedParameterJdbcTemplate.query(sql, parameters, likeElementRowMapper); + if (results.isEmpty()) { + return Optional.empty(); + } + return Optional.of(results.get(0)); + } + + @Override + public List findLikeElementByTripIds(final List tripIds) { + final String sql = """ + SELECT l.trip_id AS tripId, COUNT(l.id) AS likeCount, GROUP_CONCAT(l.member_id) AS memberIds + FROM likes l + WHERE l.trip_id IN (:tripIds) + GROUP BY l.trip_id + """; + + final MapSqlParameterSource parameters = new MapSqlParameterSource(); + parameters.addValue("tripIds", tripIds); + return namedParameterJdbcTemplate.query(sql, parameters, likeElementRowMapper); + } + + private static Set parseMemberIds(final String memberIds) { + if (!StringUtils.hasText(memberIds)) { + return Collections.emptySet(); + } + final String[] idArray = memberIds.split(","); + return Arrays.stream(idArray) + .map(Long::valueOf) + .collect(Collectors.toSet()); + } } From 56bb51199e5ec73c6f9029d5bf7c61f68307c0f8 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 22:53:16 +0900 Subject: [PATCH 17/30] =?UTF-8?q?fix:=20=EC=BA=90=EC=8B=9C=EB=90=9C=20trip?= =?UTF-8?q?Id=EA=B0=80=20=ED=95=98=EB=82=98=EB=9D=BC=EB=8F=84=20=EC=9E=88?= =?UTF-8?q?=EC=9D=84=20=EA=B2=BD=EC=9A=B0=EB=A7=8C=20db=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/community/service/CommunityService.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 9dad81835..a8f859db8 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -138,10 +138,12 @@ private Map getLikeInfoByTripIds(final Long memberId, final List } } - final List likeElementByTripIds = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); - likeElementByTripIds.addAll(getEmptyLikeElements(likeInfoByTrip, nonCachedTripIds)); - likeElementByTripIds.forEach(this::cachingLike); - likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); + if (!nonCachedTripIds.isEmpty()) { + final List likeElementByTripIds = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); + likeElementByTripIds.addAll(getEmptyLikeElements(likeInfoByTrip, nonCachedTripIds)); + likeElementByTripIds.forEach(this::cachingLike); + likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); + } return likeInfoByTrip; } From 7043b796d37e22f0ea4c44464acc480a83affaf2 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Mon, 29 Jan 2024 23:07:33 +0900 Subject: [PATCH 18/30] =?UTF-8?q?fix:=20likes=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=8B=9C=20memberIds=20=ED=8C=8C=EC=8B=B1=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java b/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java index 3517ed578..ef60e5057 100644 --- a/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java +++ b/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java @@ -87,6 +87,7 @@ private static Set parseMemberIds(final String memberIds) { } final String[] idArray = memberIds.split(","); return Arrays.stream(idArray) + .filter(StringUtils::hasText) .map(Long::valueOf) .collect(Collectors.toSet()); } From 2ea5b037ed275cd8df8489b793a5a07200bd8f98 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Tue, 30 Jan 2024 00:51:31 +0900 Subject: [PATCH 19/30] =?UTF-8?q?fix:=20redis=EC=97=90=20=EA=B0=80?= =?UTF-8?q?=EB=B3=80=EC=9D=B8=EC=9E=90=EB=A1=9C=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 19 +++++++++++++------ .../CustomLikeRepositoryImpl.java | 4 ++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index a8f859db8..12bfa34ab 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -20,7 +20,6 @@ import hanglog.global.exception.BadRequestException; import hanglog.like.domain.LikeInfo; import hanglog.like.domain.repository.CustomLikeRepository; -import hanglog.like.domain.repository.LikeRepository; import hanglog.like.dto.LikeElement; import hanglog.like.dto.LikeElements; import hanglog.trip.domain.Trip; @@ -34,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; @@ -54,7 +54,6 @@ public class CommunityService { private final TripCityRepository tripCityRepository; private final CityRepository cityRepository; private final PublishedTripRepository publishedTripRepository; - private final LikeRepository likeRepository; private final CustomLikeRepository customLikeRepository; private final RecommendStrategies recommendStrategies; private final RedisTemplate redisTemplate; @@ -140,7 +139,7 @@ private Map getLikeInfoByTripIds(final Long memberId, final List if (!nonCachedTripIds.isEmpty()) { final List likeElementByTripIds = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); - likeElementByTripIds.addAll(getEmptyLikeElements(likeInfoByTrip, nonCachedTripIds)); + likeElementByTripIds.addAll(getEmptyLikeElements(likeElementByTripIds, nonCachedTripIds)); likeElementByTripIds.forEach(this::cachingLike); likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); } @@ -160,15 +159,20 @@ private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { } private List getEmptyLikeElements( - final Map likeInfoByTrip, + final List likeElementByTripIds, final List nonCachedTripIds ) { return nonCachedTripIds.stream() - .filter(tripId -> likeInfoByTrip.get(tripId) == null) + .filter(tripId -> doesNotContainTripId(likeElementByTripIds, tripId)) .map(LikeElement::empty) .toList(); } + private boolean doesNotContainTripId(final List likeElementByTripIds, final Long tripId) { + return likeElementByTripIds.stream() + .noneMatch(likeElement -> likeElement.getTripId().equals(tripId)); + } + private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { final SetOperations opsForSet = redisTemplate.opsForSet(); final boolean isLike = TRUE.equals(opsForSet.isMember(key, memberId)); @@ -180,7 +184,10 @@ private void cachingLike(final LikeElement likeElement) { final SetOperations opsForSet = redisTemplate.opsForSet(); final String key = generateLikeKey(likeElement.getTripId()); opsForSet.add(key, EMPTY_MARKER); - opsForSet.add(key, likeElement.getMemberIds()); + final Set memberIds = likeElement.getMemberIds(); + if (!memberIds.isEmpty()) { + opsForSet.add(key, likeElement.getMemberIds().toArray()); + } redisTemplate.expire(key, LIKE_TTL); } } diff --git a/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java b/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java index ef60e5057..f4ba8c249 100644 --- a/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java +++ b/backend/src/main/java/hanglog/like/infrastrcutrue/CustomLikeRepositoryImpl.java @@ -31,7 +31,7 @@ public class CustomLikeRepositoryImpl implements CustomLikeRepository { @Override public void saveAll(final List likes) { final String sql = """ - INSERT INTO likes (trip_id, member_id) + INSERT INTO likes (trip_id, member_id) VALUES (:tripId, :memberId) """; namedParameterJdbcTemplate.batchUpdate(sql, getLikesToSqlParameterSources(likes)); @@ -85,7 +85,7 @@ private static Set parseMemberIds(final String memberIds) { if (!StringUtils.hasText(memberIds)) { return Collections.emptySet(); } - final String[] idArray = memberIds.split(","); + final String[] idArray = memberIds.strip().split(","); return Arrays.stream(idArray) .filter(StringUtils::hasText) .map(Long::valueOf) From 4583c0e62aa05a859577d9cc0c51e1a57d212565 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Tue, 30 Jan 2024 03:02:30 +0900 Subject: [PATCH 20/30] =?UTF-8?q?fix:=20like=20key=20prefix=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/hanglog/like/domain/LikeRedisConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java index ca381710d..4c59139c4 100644 --- a/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java +++ b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java @@ -4,7 +4,7 @@ public class LikeRedisConstants { - public static final String LIKE_KEY_PREFIX = "like:"; + public static final String LIKE_KEY_PREFIX = "like:*"; public static final String KEY_SEPARATOR = ":"; public static final String EMPTY_MARKER = "empty"; public static final Duration LIKE_TTL = Duration.ofMinutes(90L); From 68882fe0825ee9c339a1c659e3edb14cae574dc8 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Tue, 30 Jan 2024 16:28:03 +0900 Subject: [PATCH 21/30] =?UTF-8?q?fix:=20like=20key=20prefix=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/hanglog/like/domain/LikeRedisConstants.java | 3 ++- .../src/main/java/hanglog/like/service/LikeSyncScheduler.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java index 4c59139c4..929d2befa 100644 --- a/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java +++ b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java @@ -4,7 +4,8 @@ public class LikeRedisConstants { - public static final String LIKE_KEY_PREFIX = "like:*"; + public static final String LIKE_KEY_PREFIX = "like:"; + public static final String WILD_CARD = "*"; public static final String KEY_SEPARATOR = ":"; public static final String EMPTY_MARKER = "empty"; public static final Duration LIKE_TTL = Duration.ofMinutes(90L); diff --git a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java index 07c8d9870..687b91d92 100644 --- a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java +++ b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java @@ -3,6 +3,7 @@ import static hanglog.like.domain.LikeRedisConstants.EMPTY_MARKER; import static hanglog.like.domain.LikeRedisConstants.LIKE_KEY_PREFIX; import static hanglog.like.domain.LikeRedisConstants.KEY_SEPARATOR; +import static hanglog.like.domain.LikeRedisConstants.WILD_CARD; import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; import hanglog.like.domain.Likes; @@ -32,7 +33,7 @@ public class LikeSyncScheduler { @Scheduled(cron = "0 0 * * * *") public void writeBackLikeCache() { - final Set likeKeys = redisTemplate.keys(LIKE_KEY_PREFIX); + final Set likeKeys = redisTemplate.keys(LIKE_KEY_PREFIX + WILD_CARD); if (Objects.isNull(likeKeys)) { return; } From 2378fa40235487eb9dff0c7a740da69d76834c29 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Tue, 30 Jan 2024 20:36:21 +0900 Subject: [PATCH 22/30] =?UTF-8?q?fix:=20empty=5Fmarker=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/hanglog/like/domain/LikeRedisConstants.java | 2 +- .../src/main/java/hanglog/like/service/LikeSyncScheduler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java index 929d2befa..695b8e163 100644 --- a/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java +++ b/backend/src/main/java/hanglog/like/domain/LikeRedisConstants.java @@ -7,7 +7,7 @@ public class LikeRedisConstants { public static final String LIKE_KEY_PREFIX = "like:"; public static final String WILD_CARD = "*"; public static final String KEY_SEPARATOR = ":"; - public static final String EMPTY_MARKER = "empty"; + public static final Long EMPTY_MARKER = -1L; public static final Duration LIKE_TTL = Duration.ofMinutes(90L); public static String generateLikeKey(final Long tripId) { diff --git a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java index 687b91d92..2a4a81eba 100644 --- a/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java +++ b/backend/src/main/java/hanglog/like/service/LikeSyncScheduler.java @@ -1,8 +1,8 @@ package hanglog.like.service; import static hanglog.like.domain.LikeRedisConstants.EMPTY_MARKER; -import static hanglog.like.domain.LikeRedisConstants.LIKE_KEY_PREFIX; import static hanglog.like.domain.LikeRedisConstants.KEY_SEPARATOR; +import static hanglog.like.domain.LikeRedisConstants.LIKE_KEY_PREFIX; import static hanglog.like.domain.LikeRedisConstants.WILD_CARD; import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; From 7c2e8ff711c5522550ac178b7c5a6ecb6dbcc902 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 01:16:37 +0900 Subject: [PATCH 23/30] =?UTF-8?q?fix:=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=8B=9C=20=EC=BA=90=EC=8B=9C=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20DB=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=B4=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/like/service/LikeService.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index ec0183010..c6cb76a8b 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -1,10 +1,15 @@ package hanglog.like.service; +import static hanglog.like.domain.LikeRedisConstants.EMPTY_MARKER; import static hanglog.like.domain.LikeRedisConstants.LIKE_TTL; import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; +import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import hanglog.like.domain.repository.CustomLikeRepository; +import hanglog.like.dto.LikeElement; import hanglog.like.dto.request.LikeRequest; +import java.util.Set; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; @@ -16,11 +21,18 @@ @Transactional public class LikeService { + private final CustomLikeRepository customLikeRepository; private final RedisTemplate redisTemplate; public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { final SetOperations opsForSet = redisTemplate.opsForSet(); final String key = generateLikeKey(tripId); + if (FALSE.equals(redisTemplate.hasKey(key))) { + final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) + .orElse(LikeElement.empty(tripId)); + cachingLike(likeElement); + } + if (TRUE.equals(opsForSet.isMember(key, memberId))) { removeMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); return; @@ -28,6 +40,17 @@ public void update(final Long memberId, final Long tripId, final LikeRequest lik addMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); } + private void cachingLike(final LikeElement likeElement) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + final String key = generateLikeKey(likeElement.getTripId()); + opsForSet.add(key, EMPTY_MARKER); + final Set memberIds = likeElement.getMemberIds(); + if (!memberIds.isEmpty()) { + opsForSet.add(key, likeElement.getMemberIds().toArray()); + } + redisTemplate.expire(key, LIKE_TTL); + } + private void removeMemberInLike(final String key, final Long memberId, final Boolean isLike, final SetOperations opsForSet) { if (!isLike) { From d33ccade2c3145ee2b21bcbe792d28011deff560 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 01:41:00 +0900 Subject: [PATCH 24/30] =?UTF-8?q?refactor:=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9D=B8=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hanglog/like/service/LikeService.java | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index c6cb76a8b..cf628ec61 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -4,7 +4,6 @@ import static hanglog.like.domain.LikeRedisConstants.LIKE_TTL; import static hanglog.like.domain.LikeRedisConstants.generateLikeKey; import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; import hanglog.like.domain.repository.CustomLikeRepository; import hanglog.like.dto.LikeElement; @@ -25,19 +24,13 @@ public class LikeService { private final RedisTemplate redisTemplate; public void update(final Long memberId, final Long tripId, final LikeRequest likeRequest) { - final SetOperations opsForSet = redisTemplate.opsForSet(); final String key = generateLikeKey(tripId); if (FALSE.equals(redisTemplate.hasKey(key))) { final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) .orElse(LikeElement.empty(tripId)); cachingLike(likeElement); } - - if (TRUE.equals(opsForSet.isMember(key, memberId))) { - removeMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); - return; - } - addMemberInLike(key, memberId, likeRequest.getIsLike(), opsForSet); + update(key, memberId, likeRequest.getIsLike()); } private void cachingLike(final LikeElement likeElement) { @@ -51,19 +44,23 @@ private void cachingLike(final LikeElement likeElement) { redisTemplate.expire(key, LIKE_TTL); } - private void removeMemberInLike(final String key, final Long memberId, final Boolean isLike, - final SetOperations opsForSet) { - if (!isLike) { - opsForSet.remove(key, memberId); - redisTemplate.expire(key, LIKE_TTL); + private void update(final String key, final Long memberId, final Boolean isLike) { + if (isLike) { + addMemberInLike(key, memberId); + return; } + removeMemberInLike(key, memberId); } - private void addMemberInLike(final String key, final Long memberId, final Boolean isLike, - final SetOperations opsForSet) { - if (isLike) { - opsForSet.add(key, memberId); - redisTemplate.expire(key, LIKE_TTL); - } + private void addMemberInLike(final String key, final Long memberId) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + opsForSet.add(key, memberId); + redisTemplate.expire(key, LIKE_TTL); + } + + private void removeMemberInLike(final String key, final Long memberId) { + final SetOperations opsForSet = redisTemplate.opsForSet(); + opsForSet.remove(key, memberId); + redisTemplate.expire(key, LIKE_TTL); } } From 59edb00bd9a67311d5fc25b7a532818cb044472e Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 01:55:22 +0900 Subject: [PATCH 25/30] =?UTF-8?q?refactor:=20toLikeInfo=EB=A1=9C=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/hanglog/community/service/CommunityService.java | 4 ++-- backend/src/main/java/hanglog/like/dto/LikeElement.java | 2 +- backend/src/main/java/hanglog/like/dto/LikeElements.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 12bfa34ab..1cb5f5302 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -141,7 +141,7 @@ private Map getLikeInfoByTripIds(final Long memberId, final List final List likeElementByTripIds = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); likeElementByTripIds.addAll(getEmptyLikeElements(likeElementByTripIds, nonCachedTripIds)); likeElementByTripIds.forEach(this::cachingLike); - likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeMap(memberId)); + likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeInfo(memberId)); } return likeInfoByTrip; } @@ -155,7 +155,7 @@ private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) .orElse(LikeElement.empty(tripId)); cachingLike(likeElement); - return likeElement.toLikeMap(memberId); + return likeElement.toLikeInfo(memberId); } private List getEmptyLikeElements( diff --git a/backend/src/main/java/hanglog/like/dto/LikeElement.java b/backend/src/main/java/hanglog/like/dto/LikeElement.java index 093bd4557..72e48d352 100644 --- a/backend/src/main/java/hanglog/like/dto/LikeElement.java +++ b/backend/src/main/java/hanglog/like/dto/LikeElement.java @@ -18,7 +18,7 @@ public boolean isLike(final Long memberId) { return memberIds.contains(memberId); } - public LikeInfo toLikeMap(final Long memberId) { + public LikeInfo toLikeInfo(final Long memberId) { return new LikeInfo(this.getLikeCount(), this.isLike(memberId)); } diff --git a/backend/src/main/java/hanglog/like/dto/LikeElements.java b/backend/src/main/java/hanglog/like/dto/LikeElements.java index 3cd756498..e8bd70c5f 100644 --- a/backend/src/main/java/hanglog/like/dto/LikeElements.java +++ b/backend/src/main/java/hanglog/like/dto/LikeElements.java @@ -13,7 +13,7 @@ public class LikeElements { private final List elements; - public Map toLikeMap(final Long memberId) { + public Map toLikeInfo(final Long memberId) { final Map map = new HashMap<>(); for (final LikeElement likeElement : elements) { final LikeInfo likeInfo = new LikeInfo(likeElement.getLikeCount(), likeElement.isLike(memberId)); From c658576178db304efdbe666917609eecc326bb4d Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 01:57:49 +0900 Subject: [PATCH 26/30] =?UTF-8?q?refactor:=20likeElements=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index 1cb5f5302..bf1dc10ed 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -138,10 +138,10 @@ private Map getLikeInfoByTripIds(final Long memberId, final List } if (!nonCachedTripIds.isEmpty()) { - final List likeElementByTripIds = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); - likeElementByTripIds.addAll(getEmptyLikeElements(likeElementByTripIds, nonCachedTripIds)); - likeElementByTripIds.forEach(this::cachingLike); - likeInfoByTrip.putAll(new LikeElements(likeElementByTripIds).toLikeInfo(memberId)); + final List likeElements = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); + likeElements.addAll(getEmptyLikeElements(likeElements, nonCachedTripIds)); + likeElements.forEach(this::cachingLike); + likeInfoByTrip.putAll(new LikeElements(likeElements).toLikeInfo(memberId)); } return likeInfoByTrip; } @@ -159,17 +159,17 @@ private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { } private List getEmptyLikeElements( - final List likeElementByTripIds, + final List likeElements, final List nonCachedTripIds ) { return nonCachedTripIds.stream() - .filter(tripId -> doesNotContainTripId(likeElementByTripIds, tripId)) + .filter(tripId -> doesNotContainTripId(likeElements, tripId)) .map(LikeElement::empty) .toList(); } - private boolean doesNotContainTripId(final List likeElementByTripIds, final Long tripId) { - return likeElementByTripIds.stream() + private boolean doesNotContainTripId(final List likeElements, final Long tripId) { + return likeElements.stream() .noneMatch(likeElement -> likeElement.getTripId().equals(tripId)); } From 4a62d4ce8e2524633c043393cf529c78f6cc7905 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 02:07:01 +0900 Subject: [PATCH 27/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index bf1dc10ed..f98c07cb0 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -102,28 +102,6 @@ private Long getLastPageIndex(final int pageSize) { return lastPageIndex + 1; } - @Transactional(readOnly = true) - public TripDetailResponse getTripDetail(final Accessor accessor, final Long tripId) { - final Trip trip = tripRepository.findById(tripId) - .orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID)); - final List cities = cityRepository.findCitiesByTripId(tripId); - final LocalDateTime publishedDate = publishedTripRepository.findByTripId(tripId) - .orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID)) - .getCreatedAt(); - - final LikeInfo likeInfo = getLikeInfoByTripId(accessor.getMemberId(), tripId); - final Boolean isWriter = trip.isWriter(accessor.getMemberId()); - - return TripDetailResponse.publishedTrip( - trip, - cities, - isWriter, - likeInfo.isLike(), - likeInfo.getLikeCount(), - publishedDate - ); - } - private Map getLikeInfoByTripIds(final Long memberId, final List tripIds) { final Map likeInfoByTrip = new HashMap<>(); @@ -146,18 +124,6 @@ private Map getLikeInfoByTripIds(final Long memberId, final List return likeInfoByTrip; } - private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { - final String key = generateLikeKey(tripId); - if (TRUE.equals(redisTemplate.hasKey(key))) { - return readLikeInfoFromCache(key, memberId); - } - - final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) - .orElse(LikeElement.empty(tripId)); - cachingLike(likeElement); - return likeElement.toLikeInfo(memberId); - } - private List getEmptyLikeElements( final List likeElements, final List nonCachedTripIds @@ -173,6 +139,40 @@ private boolean doesNotContainTripId(final List likeElements, final .noneMatch(likeElement -> likeElement.getTripId().equals(tripId)); } + @Transactional(readOnly = true) + public TripDetailResponse getTripDetail(final Accessor accessor, final Long tripId) { + final Trip trip = tripRepository.findById(tripId) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID)); + final List cities = cityRepository.findCitiesByTripId(tripId); + final LocalDateTime publishedDate = publishedTripRepository.findByTripId(tripId) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID)) + .getCreatedAt(); + + final LikeInfo likeInfo = getLikeInfoByTripId(accessor.getMemberId(), tripId); + final Boolean isWriter = trip.isWriter(accessor.getMemberId()); + + return TripDetailResponse.publishedTrip( + trip, + cities, + isWriter, + likeInfo.isLike(), + likeInfo.getLikeCount(), + publishedDate + ); + } + + private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { + final String key = generateLikeKey(tripId); + if (TRUE.equals(redisTemplate.hasKey(key))) { + return readLikeInfoFromCache(key, memberId); + } + + final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) + .orElse(LikeElement.empty(tripId)); + cachingLike(likeElement); + return likeElement.toLikeInfo(memberId); + } + private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { final SetOperations opsForSet = redisTemplate.opsForSet(); final boolean isLike = TRUE.equals(opsForSet.isMember(key, memberId)); From babb2051d47dc1e636878a53ed3871a6e3a84e10 Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 02:54:17 +0900 Subject: [PATCH 28/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20LikeInfo=20dto=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/service/CommunityService.java | 10 +++++----- .../main/java/hanglog/like/dto/LikeElement.java | 5 ----- .../main/java/hanglog/like/dto/LikeElements.java | 1 - .../hanglog/like/{domain => dto}/LikeInfo.java | 2 +- .../java/hanglog/like/service/LikeService.java | 16 ++++++++-------- 5 files changed, 14 insertions(+), 20 deletions(-) rename backend/src/main/java/hanglog/like/{domain => dto}/LikeInfo.java (86%) diff --git a/backend/src/main/java/hanglog/community/service/CommunityService.java b/backend/src/main/java/hanglog/community/service/CommunityService.java index f98c07cb0..8a8bc5ef2 100644 --- a/backend/src/main/java/hanglog/community/service/CommunityService.java +++ b/backend/src/main/java/hanglog/community/service/CommunityService.java @@ -18,7 +18,7 @@ import hanglog.community.dto.response.CommunityTripResponse; import hanglog.community.dto.response.RecommendTripListResponse; import hanglog.global.exception.BadRequestException; -import hanglog.like.domain.LikeInfo; +import hanglog.like.dto.LikeInfo; import hanglog.like.domain.repository.CustomLikeRepository; import hanglog.like.dto.LikeElement; import hanglog.like.dto.LikeElements; @@ -118,7 +118,7 @@ private Map getLikeInfoByTripIds(final Long memberId, final List if (!nonCachedTripIds.isEmpty()) { final List likeElements = customLikeRepository.findLikeElementByTripIds(nonCachedTripIds); likeElements.addAll(getEmptyLikeElements(likeElements, nonCachedTripIds)); - likeElements.forEach(this::cachingLike); + likeElements.forEach(this::storeLikeInCache); likeInfoByTrip.putAll(new LikeElements(likeElements).toLikeInfo(memberId)); } return likeInfoByTrip; @@ -169,8 +169,8 @@ private LikeInfo getLikeInfoByTripId(final Long memberId, final Long tripId) { final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) .orElse(LikeElement.empty(tripId)); - cachingLike(likeElement); - return likeElement.toLikeInfo(memberId); + storeLikeInCache(likeElement); + return new LikeInfo(likeElement.getLikeCount(), likeElement.isLike(memberId)); } private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { @@ -180,7 +180,7 @@ private LikeInfo readLikeInfoFromCache(final String key, final Long memberId) { return new LikeInfo(count, isLike); } - private void cachingLike(final LikeElement likeElement) { + private void storeLikeInCache(final LikeElement likeElement) { final SetOperations opsForSet = redisTemplate.opsForSet(); final String key = generateLikeKey(likeElement.getTripId()); opsForSet.add(key, EMPTY_MARKER); diff --git a/backend/src/main/java/hanglog/like/dto/LikeElement.java b/backend/src/main/java/hanglog/like/dto/LikeElement.java index 72e48d352..ef55bb780 100644 --- a/backend/src/main/java/hanglog/like/dto/LikeElement.java +++ b/backend/src/main/java/hanglog/like/dto/LikeElement.java @@ -1,6 +1,5 @@ package hanglog.like.dto; -import hanglog.like.domain.LikeInfo; import java.util.Collections; import java.util.Set; import lombok.AllArgsConstructor; @@ -18,10 +17,6 @@ public boolean isLike(final Long memberId) { return memberIds.contains(memberId); } - public LikeInfo toLikeInfo(final Long memberId) { - return new LikeInfo(this.getLikeCount(), this.isLike(memberId)); - } - public static LikeElement empty(final Long tripId) { return new LikeElement(tripId, 0, Collections.emptySet()); } diff --git a/backend/src/main/java/hanglog/like/dto/LikeElements.java b/backend/src/main/java/hanglog/like/dto/LikeElements.java index e8bd70c5f..7c3aac67e 100644 --- a/backend/src/main/java/hanglog/like/dto/LikeElements.java +++ b/backend/src/main/java/hanglog/like/dto/LikeElements.java @@ -1,6 +1,5 @@ package hanglog.like.dto; -import hanglog.like.domain.LikeInfo; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/backend/src/main/java/hanglog/like/domain/LikeInfo.java b/backend/src/main/java/hanglog/like/dto/LikeInfo.java similarity index 86% rename from backend/src/main/java/hanglog/like/domain/LikeInfo.java rename to backend/src/main/java/hanglog/like/dto/LikeInfo.java index c968fffa8..d418acdae 100644 --- a/backend/src/main/java/hanglog/like/domain/LikeInfo.java +++ b/backend/src/main/java/hanglog/like/dto/LikeInfo.java @@ -1,4 +1,4 @@ -package hanglog.like.domain; +package hanglog.like.dto; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/backend/src/main/java/hanglog/like/service/LikeService.java b/backend/src/main/java/hanglog/like/service/LikeService.java index cf628ec61..fd29deb5c 100644 --- a/backend/src/main/java/hanglog/like/service/LikeService.java +++ b/backend/src/main/java/hanglog/like/service/LikeService.java @@ -28,12 +28,12 @@ public void update(final Long memberId, final Long tripId, final LikeRequest lik if (FALSE.equals(redisTemplate.hasKey(key))) { final LikeElement likeElement = customLikeRepository.findLikesElementByTripId(tripId) .orElse(LikeElement.empty(tripId)); - cachingLike(likeElement); + storeLikeInCache(likeElement); } - update(key, memberId, likeRequest.getIsLike()); + updateToCache(key, memberId, likeRequest.getIsLike()); } - private void cachingLike(final LikeElement likeElement) { + private void storeLikeInCache(final LikeElement likeElement) { final SetOperations opsForSet = redisTemplate.opsForSet(); final String key = generateLikeKey(likeElement.getTripId()); opsForSet.add(key, EMPTY_MARKER); @@ -44,21 +44,21 @@ private void cachingLike(final LikeElement likeElement) { redisTemplate.expire(key, LIKE_TTL); } - private void update(final String key, final Long memberId, final Boolean isLike) { + private void updateToCache(final String key, final Long memberId, final Boolean isLike) { if (isLike) { - addMemberInLike(key, memberId); + addMember(key, memberId); return; } - removeMemberInLike(key, memberId); + removeMember(key, memberId); } - private void addMemberInLike(final String key, final Long memberId) { + private void addMember(final String key, final Long memberId) { final SetOperations opsForSet = redisTemplate.opsForSet(); opsForSet.add(key, memberId); redisTemplate.expire(key, LIKE_TTL); } - private void removeMemberInLike(final String key, final Long memberId) { + private void removeMember(final String key, final Long memberId) { final SetOperations opsForSet = redisTemplate.opsForSet(); opsForSet.remove(key, memberId); redisTemplate.expire(key, LIKE_TTL); From 8530e2683c79b85430c650d77251928296a679ae Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 02:54:40 +0900 Subject: [PATCH 29/30] =?UTF-8?q?test:=20LikeService=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/LikeServiceIntegrationTest.java | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java index e410a9ac5..815b702ac 100644 --- a/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java +++ b/backend/src/test/java/hanglog/integration/service/LikeServiceIntegrationTest.java @@ -6,6 +6,8 @@ import static java.lang.Boolean.TRUE; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import hanglog.like.domain.Likes; +import hanglog.like.domain.repository.LikeRepository; import hanglog.like.dto.request.LikeRequest; import hanglog.like.service.LikeService; import hanglog.trip.service.TripService; @@ -27,11 +29,20 @@ class LikeServiceIntegrationTest extends RedisServiceIntegrationTest { @Autowired private RedisTemplate redisTemplate; + @Autowired + private LikeRepository likeRepository; + private SetOperations opsForSet; + private Long memberId; + private Long tripId; + private String key; @BeforeEach void setUp() { opsForSet = redisTemplate.opsForSet(); + memberId = member.getId(); + tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); + key = generateLikeKey(tripId); } @DisplayName("해당 게시물의 좋아요 여부를 변경할 수 있다.") @@ -41,10 +52,6 @@ void update() { final LikeRequest likeTrueRequest = new LikeRequest(true); final LikeRequest likeFalseRequest = new LikeRequest(false); - final Long memberId = member.getId(); - final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); - final String key = generateLikeKey(tripId); - // when & then assertSoftly(softly -> { softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); @@ -63,13 +70,10 @@ void update_TrueRequestWhenIsMember() { // given final LikeRequest likeTrueRequest = new LikeRequest(true); - final Long memberId = member.getId(); - final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); - final String key = generateLikeKey(tripId); - + // when likeService.update(memberId, tripId, likeTrueRequest); - // when & then + // then assertSoftly(softly -> { softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(TRUE); likeService.update(memberId, tripId, likeTrueRequest); @@ -83,17 +87,31 @@ void update_FalseRequestWhenIsNotMember() { // given final LikeRequest likeFalseRequest = new LikeRequest(false); - final Long memberId = member.getId(); - final Long tripId = tripService.save(memberId, TRIP_CREATE_REQUEST); - final String key = generateLikeKey(tripId); - + // when likeService.update(memberId, tripId, likeFalseRequest); - // when & then + // then assertSoftly(softly -> { softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); likeService.update(memberId, tripId, likeFalseRequest); softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); }); } + + @DisplayName("TripId 키가 Redis에 존재하지 않을 경우 DB에 조회해서 업데이트한다.") + @Test + void update_WhenTripIdIsNotExistInRedis() { + // given + final LikeRequest likeTrueRequest = new LikeRequest(true); + + final Likes likes = new Likes(tripId, memberId); + likeRepository.save(likes); + + // when & then + assertSoftly(softly -> { + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(FALSE); + likeService.update(memberId, tripId, likeTrueRequest); + softly.assertThat(opsForSet.isMember(key, memberId)).isEqualTo(TRUE); + }); + } } From 339be7b369a110a5b9cb8bcb97ec34504dab26ee Mon Sep 17 00:00:00 2001 From: mcodnjs Date: Thu, 1 Feb 2024 02:56:03 +0900 Subject: [PATCH 30/30] =?UTF-8?q?test:=20LikeSyncScheduler=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../like/service/LikeSyncSchedulerTest.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 backend/src/test/java/hanglog/like/service/LikeSyncSchedulerTest.java diff --git a/backend/src/test/java/hanglog/like/service/LikeSyncSchedulerTest.java b/backend/src/test/java/hanglog/like/service/LikeSyncSchedulerTest.java new file mode 100644 index 000000000..e5eb1364e --- /dev/null +++ b/backend/src/test/java/hanglog/like/service/LikeSyncSchedulerTest.java @@ -0,0 +1,64 @@ +package hanglog.like.service; + +import static hanglog.integration.IntegrationFixture.TRIP_CREATE_REQUEST; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import hanglog.integration.service.RedisServiceIntegrationTest; +import hanglog.like.domain.Likes; +import hanglog.like.domain.repository.LikeRepository; +import hanglog.like.dto.request.LikeRequest; +import hanglog.trip.service.TripService; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class LikeSyncSchedulerTest extends RedisServiceIntegrationTest { + + @Autowired + private LikeSyncScheduler likeSyncScheduler; + + @Autowired + private LikeRepository likeRepository; + + @Autowired + private TripService tripService; + + @Autowired + private LikeService likeService; + + private Long memberId; + private Long tripId1; + private Long tripId2; + + @BeforeEach + void setUp() { + memberId = member.getId(); + tripId1 = tripService.save(memberId, TRIP_CREATE_REQUEST); + tripId2 = tripService.save(memberId, TRIP_CREATE_REQUEST); + } + + @DisplayName("Redis에서 업데이트 된 좋아요 값을 DB에 저장한다. (기존에 저장되어 있는 TripId 1 삭제하고 TripId 2 데이터만 남는다.)") + @Test + void writeBackLikeCache() { + // given + final Likes likeTripId1 = new Likes(tripId1, memberId); + likeRepository.save(likeTripId1); + + likeService.update(memberId, tripId1, new LikeRequest(false)); + likeService.update(memberId, tripId2, new LikeRequest(true)); + + // when + likeSyncScheduler.writeBackLikeCache(); + final List likes = likeRepository.findAll(); + + // then + assertSoftly(softly -> { + softly.assertThat(likes.size()).isEqualTo(1); + softly.assertThat(likes.get(0)).usingRecursiveComparison() + .ignoringFields("id") + .isEqualTo(new Likes(tripId2, memberId)); + }); + } +}