diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/config/RedisConfig.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/config/RedisConfig.java index f43baf00..ecbcc5a2 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/config/RedisConfig.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/config/RedisConfig.java @@ -11,7 +11,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @@ -41,11 +40,13 @@ public LettuceConnectionFactory lettuceConnectionFactory() { } @Bean - public RedisTemplate redistemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); + public RedisTemplate redistemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper())); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); return redisTemplate; } } diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/persistence/domain/Paint.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/persistence/domain/Paint.java index 5c66008c..a524f6e3 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/persistence/domain/Paint.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/persistence/domain/Paint.java @@ -16,13 +16,13 @@ public class Paint { private Long id; - private Boolean isReply; + private Boolean isReply = false; private Long authorId; private String authorUsername; private String authorNickname; private String authorImagePath; private String authorStatus; - private Paint quotePaint; + private Paint quotePaint = null; private LocalDateTime createdAt; private String text; private List hashtagRecords; diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/FollowerPaintMapService.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/FollowerPaintMapService.java index 3ebfb4bd..b8abaa0b 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/FollowerPaintMapService.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/FollowerPaintMapService.java @@ -15,23 +15,23 @@ @RequiredArgsConstructor public class FollowerPaintMapService { - private final RedisTemplate redistemplate; + private final RedisTemplate redistemplate; public void addPaintToFollowersTimeline(List followerIds, Long paintId) { followerIds.forEach(userId -> { final String key = RedisKeyUtil.constructKey(FOLLOWER_PAINT_TIMELINE_PREFIX.getKey(), userId); redistemplate.opsForList() - .leftPush(key, paintId); + .leftPush(key, paintId.toString()); redistemplate.expire(key, Duration.ofDays(5)); }); } public List getFollowingTimelinePaintIds(final Long userId) { final String key = RedisKeyUtil.constructKey(FOLLOWER_PAINT_TIMELINE_PREFIX.getKey(), userId); - return Optional.ofNullable(redistemplate.opsForList().range(key, 0, -1)) + return Optional.ofNullable(redistemplate.opsForList().range(key, 1, -1)) .orElse(List.of()) .stream() - .map(object -> Long.valueOf(object.toString())) + .map(Long::valueOf) .toList(); } } diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintCacheService.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintCacheService.java index 9ede6277..405b26a1 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintCacheService.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintCacheService.java @@ -1,5 +1,7 @@ package org.palette.easeltimelineservice.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import org.palette.easeltimelineservice.persistence.domain.Paint; import org.palette.easeltimelineservice.util.RedisKeyUtil; @@ -16,27 +18,44 @@ @RequiredArgsConstructor public class PaintCacheService { - private final RedisTemplate redistemplate; + private final RedisTemplate redistemplate; + private final ObjectMapper objectMapper; public void cachePaint(Long paintId, Paint paint) { String key = RedisKeyUtil.constructKey(PAINT_PREFIX.getKey(), paintId); - redistemplate.opsForValue().set(key, paint); + String paintJson; + try { + paintJson = objectMapper.writeValueAsString(paint); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + redistemplate.opsForValue().set(key, paintJson); + redistemplate.opsForSet().add("paint_keys", paintId.toString()); } public List getPaints(final List paintIds) { final List keys = RedisKeyUtil.constructKeys(PAINT_PREFIX.getKey(), paintIds); - return Optional.ofNullable(redistemplate.opsForValue().multiGet(keys)) + final List value = redistemplate.opsForValue().multiGet(keys); + return Optional.ofNullable(value) .orElseGet(Collections::emptyList) .stream() - .map(Paint.class::cast) + .map(json -> { + try { + return objectMapper.readValue(json, Paint.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) .toList(); } public List getRandomPaints() { - return Optional.ofNullable(redistemplate.opsForSet().randomMembers(PAINT_PREFIX.getKey(), 200)) + List paintIds = Optional.ofNullable(redistemplate.opsForSet().randomMembers("paint_keys", 200)) .orElseGet(Collections::emptyList) .stream() - .map(Paint.class::cast) + .map(Long::valueOf) + .distinct() .toList(); + return getPaints(paintIds); } } diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetrics.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetrics.java index c403e37f..e19c092a 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetrics.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetrics.java @@ -1,8 +1,11 @@ package org.palette.easeltimelineservice.service; +import java.util.Map; + public record PaintMetrics(Integer replyCount, Integer repaintCount, Integer likeCount) { private static final Integer DEFAULT_COUNT = 0; + public static final PaintMetrics DEFAULT_METRICS = new PaintMetrics(DEFAULT_COUNT, DEFAULT_COUNT, DEFAULT_COUNT); public static PaintMetrics of(Integer replyCount, Integer repaintCount, Integer likeCount) { return new PaintMetrics( @@ -11,4 +14,13 @@ public static PaintMetrics of(Integer replyCount, Integer repaintCount, Integer likeCount == null ? DEFAULT_COUNT : likeCount ); } + + + public Map asMap() { + return Map.of( + "replyCount", String.valueOf(replyCount), + "repaintCount", String.valueOf(repaintCount), + "likeCount", String.valueOf(likeCount) + ); + } } diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetricsService.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetricsService.java index 25284dcc..c0dc6aa1 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetricsService.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/service/PaintMetricsService.java @@ -5,6 +5,8 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import java.util.Optional; + @Service @RequiredArgsConstructor public class PaintMetricsService { @@ -53,14 +55,21 @@ public PaintMetrics getPaintMetrics(final Long pid) { final Integer replyCount = getPaintMetric(pid, REPLY_COUNT); final Integer repaintCount = getPaintMetric(pid, REPAINT_COUNT); final Integer likeCount = getPaintMetric(pid, LIKE_COUNT); - return PaintMetrics.of(replyCount, repaintCount, likeCount); } public Integer getPaintMetric(final Long pid, final String metric) { - return (Integer) redistemplate.opsForHash().get( + final String string = Optional.ofNullable(redistemplate.opsForHash().get( RedisKeyUtil.constructKey(RedisKeyConstants.METRICS_PREFIX.getKey(), pid), metric + )).orElse("0").toString(); + return Integer.parseInt(string); + } + + public void create(final Long id) { + redistemplate.opsForHash().putAll( + RedisKeyUtil.constructKey(RedisKeyConstants.METRICS_PREFIX.getKey(), id), + PaintMetrics.DEFAULT_METRICS.asMap() ); } } diff --git a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/usecase/TimelineUsecase.java b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/usecase/TimelineUsecase.java index 61c8867e..4e181e69 100644 --- a/src/timeline-service/src/main/java/org/palette/easeltimelineservice/usecase/TimelineUsecase.java +++ b/src/timeline-service/src/main/java/org/palette/easeltimelineservice/usecase/TimelineUsecase.java @@ -26,6 +26,7 @@ public class TimelineUsecase extends GSocialServiceGrpc.GSocialServiceImplBase { public void handlePaintCreatedEvent(PaintCreatedEvent paintCreatedEvent) { final GFollowerIdsResponse followerIds = gRPCSocialClient.getFollowerIds(paintCreatedEvent.authorId()); paintCacheService.cachePaint(paintCreatedEvent.id(), Paint.from(paintCreatedEvent)); + paintMetricsService.create(paintCreatedEvent.id()); Optional.ofNullable(paintCreatedEvent.inReplyToPaintId()) .ifPresent(paintMetricsService::incrementReplyCount); followerPaintMapService.addPaintToFollowersTimeline(followerIds.getFollowerIdsList(), paintCreatedEvent.id());