Skip to content

Commit

Permalink
Merge pull request #60 from lotteon2/develop
Browse files Browse the repository at this point in the history
release
  • Loading branch information
CokeLee777 authored Jan 26, 2024
2 parents d81dda9 + cf3a7ea commit 55d239f
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 119 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ dependencies {
// aws
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.4.RELEASE'

implementation group: 'io.github.dailyon-maven', name: 'daily-on-common', version: '0.0.6'
implementation group: 'io.github.dailyon-maven', name: 'daily-on-common', version: '0.1.2'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,37 @@

import com.dailyon.snsservice.vo.PostCountVO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class PostCountRedisRepository {

private final RedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper;
private final RedisTemplate<String, PostCountVO> redisTemplate;

@Cacheable(value = "postCount", key = "#key", unless = "#result == null")
public PostCountVO findOrPutPostCountVO(String key, PostCountVO postCountVO)
throws JsonProcessingException {
String stringValue = redisTemplate.opsForValue().get(key);
if (isInValidValue(stringValue)) {
PostCountVO cachedPostCountVO = redisTemplate.opsForValue().get(key);
if (isInValidValue(cachedPostCountVO)) {
return postCountVO;
}
return objectMapper.readValue(stringValue, PostCountVO.class);
return cachedPostCountVO;
}

@CachePut(value = "postCount", key = "#key")
Expand All @@ -40,27 +44,28 @@ public PostCountVO modifyPostCountVOAboutLikeCount(String key, PostCountVO postC
@CacheEvict(value = "postCount", key = "#key")
public void deletePostCountVO(String key) {}

public List<Map<String, PostCountVO>> findPostCountVOs(String cacheName) {
Set<String> postIds = redisTemplate.keys(cacheName + ":*");
if (postIds != null && !postIds.isEmpty()) {
return postIds.stream()
.map(
postId -> {
String stringValue = redisTemplate.opsForValue().get(postId);
PostCountVO postCountVO;
try {
postCountVO = objectMapper.readValue(stringValue, PostCountVO.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return Map.of(postId, postCountVO);
})
.collect(Collectors.toList());
}
return null;
@Cacheable(value = "postCount", unless = "#result == null", key = "'*'")
public List<Map<String, PostCountVO>> findPostCountVOs() {
Set<String> allKeys = redisTemplate.keys("postCount::*");

// Fetch values for each key
List<Map<String, PostCountVO>> allValues =
allKeys.stream()
.map(
key -> {
String postId = key.split("::")[1];
PostCountVO cachedPostCountVO = redisTemplate.opsForValue().get(key);
if (isInValidValue(cachedPostCountVO)) {
return Map.of(postId, new PostCountVO(0, 0, 0));
}
return Map.of(postId, cachedPostCountVO);
})
.collect(Collectors.toList());

return allValues;
}

private Boolean isInValidValue(String value) {
private Boolean isInValidValue(PostCountVO value) {
return value == null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,21 @@
public class Top4OOTDRedisRepository {

private final PostRepository postRepository;
private final RedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper;
private final RedisTemplate<String, List<Top4OOTDVO>> redisTemplate;

@Cacheable(value = "top4OOTD", key = "#productId", unless = "#result == null")
public List<Top4OOTDVO> findOrPutTop4OOTDVO(String productId) throws JsonProcessingException {
String stringValue = redisTemplate.opsForValue().get(productId);
if (isInValidValue(stringValue)) {
List<Top4OOTDVO> cachedTop4OOTDVOs = redisTemplate.opsForValue().get(productId);
if (isInValidValue(cachedTop4OOTDVOs)) {
List<Post> posts = postRepository.findTop4ByOrderByLikeCountDesc(Long.parseLong(productId));
return posts.stream()
.map(post -> new Top4OOTDVO(post.getId(), post.getPostImage().getThumbnailImgUrl()))
.collect(Collectors.toList());
}
return objectMapper.readValue(
stringValue,
objectMapper.getTypeFactory().constructCollectionType(List.class, Top4OOTDVO.class));
return cachedTop4OOTDVOs;
}

private Boolean isInValidValue(String value) {
private Boolean isInValidValue(List<Top4OOTDVO> value) {
return value == null;
}
}
24 changes: 7 additions & 17 deletions src/main/java/com/dailyon/snsservice/config/CacheConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

import com.dailyon.snsservice.vo.PostCountVO;
import com.dailyon.snsservice.vo.Top4OOTDVO;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.time.Duration;
import java.util.List;

import dailyon.domain.utils.KryoRedisSerializer;
import dailyon.domain.utils.SnappyRedisSerializer;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

Expand Down Expand Up @@ -44,19 +45,7 @@ public RedisCacheConfiguration redisCacheConfiguration(ObjectMapper objectMapper
}

@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(
ObjectMapper objectMapper) {
Jackson2JsonRedisSerializer<PostCountVO> postCountVOJackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer<>(PostCountVO.class);

JavaType top4OOTDVOListType =
objectMapper.getTypeFactory().constructCollectionType(List.class, Top4OOTDVO.class);
Jackson2JsonRedisSerializer<List<Top4OOTDVO>> top4OOTDVOJackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer<>(top4OOTDVOListType);

postCountVOJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
top4OOTDVOJackson2JsonRedisSerializer.setObjectMapper(objectMapper);

public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder ->
builder
.withCacheConfiguration(
Expand All @@ -69,7 +58,7 @@ public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(
new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
postCountVOJackson2JsonRedisSerializer)))
new SnappyRedisSerializer<PostCountVO>(new KryoRedisSerializer<>()))))
.withCacheConfiguration(
"top4OOTD",
RedisCacheConfiguration.defaultCacheConfig()
Expand All @@ -80,6 +69,7 @@ public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(
new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
top4OOTDVOJackson2JsonRedisSerializer))));
new SnappyRedisSerializer<List<Top4OOTDVO>>(
new KryoRedisSerializer<>())))));
}
}
9 changes: 6 additions & 3 deletions src/main/java/com/dailyon/snsservice/config/RedisConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import dailyon.domain.utils.KryoRedisSerializer;
import dailyon.domain.utils.SnappyRedisSerializer;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -55,10 +58,10 @@ private Set<RedisNode> parseRedisNodes(String nodes) {
}

@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
public <T> RedisTemplate<String, T> redisTemplate() {
RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new SnappyRedisSerializer<T>(new KryoRedisSerializer<>()));

boolean isCluster = Objects.nonNull(env.getProperty("spring.redis.cluster.nodes"));
if (!isCluster) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class PostScheduler {
@Scheduled(cron = "0 5 * * * *", zone = "Asia/Seoul")
public void postCountCacheSyncToDB() {
List<Map<String, PostCountVO>> postCountVOStore =
postCountRedisRepository.findPostCountVOs("postCount");
postCountRedisRepository.findPostCountVOs();
if (postCountVOStore != null) {
postCountVOStore.forEach(
store ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class PostCountRedisRepositoryTest {

@Autowired private PostCountRedisRepository postCountRedisRepository;
@Autowired private PostRepository postRepository;
@Autowired private RedisTemplate<String, String> redisTemplate;
@Autowired private RedisTemplate<String, PostCountVO> redisTemplate;

@Test
@DisplayName("캐시 miss시 DB에서 캐시로 동기화 후 반환, 캐시 hit시 조회수, 좋아요수, 댓글수를 조회")
Expand Down Expand Up @@ -90,7 +90,7 @@ void findPostCountVOs() {

// when
List<Map<String, PostCountVO>> postCountVOStore =
postCountRedisRepository.findPostCountVOs(cacheName);
postCountRedisRepository.findPostCountVOs();

// then
assertThat(postCountVOStore.size()).isNotSameAs(0);
Expand All @@ -109,9 +109,8 @@ void findPostCountVOs() {
void deletePostCountVO() throws JsonProcessingException {
// given
Long postId = 1L;
ObjectMapper objectMapper = new ObjectMapper();
String stringValue = objectMapper.writeValueAsString(new PostCountVO(1, 2, 3));
redisTemplate.opsForValue().set(String.format("postCount::%s", postId), stringValue);
PostCountVO postCountVO = new PostCountVO(1, 2, 3);
redisTemplate.opsForValue().set(String.format("postCount::%s", postId), postCountVO);

// when
postCountRedisRepository.deletePostCountVO(String.valueOf(postId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ class CommentServiceTest {
@Autowired private CommentService commentService;

@Autowired private CommentReader commentReader;
@Autowired private RedisTemplate<String, String> redisTemplate;
@Autowired private ObjectMapper objectMapper;
@Autowired private RedisTemplate<String, PostCountVO> redisTemplate;

@Test
@DisplayName("댓글 등록")
Expand All @@ -47,10 +46,7 @@ void createComment() throws JsonProcessingException {
Comment savedComment = commentService.createComment(memberId, postId, createCommentRequest);

// then
PostCountVO afterPostCountVO =
objectMapper.readValue(
redisTemplate.opsForValue().get(String.format("postCount::%s", postId)),
PostCountVO.class);
PostCountVO afterPostCountVO = redisTemplate.opsForValue().get(String.format("postCount::%s", postId));

assertThat(savedComment.getDescription()).isEqualTo(commentDescription);
assertThat(savedComment.getPost().getId()).isEqualTo(postId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class PostLikeServiceTest {

@Autowired private PostLikeJpaRepository postLikeJpaRepository;

@Autowired private RedisTemplate<String, String> redisTemplate;
@Autowired private RedisTemplate<String, PostCountVO> redisTemplate;

@Autowired private ObjectMapper objectMapper;

Expand All @@ -48,16 +48,7 @@ void createPostLike() throws JsonProcessingException {

List<PostCountVO> beforePostCountVOs =
postIds.stream()
.map(
postId -> {
try {
return objectMapper.readValue(
redisTemplate.opsForValue().get(String.format("postCount::%s", postId)),
PostCountVO.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
})
.map(postId -> redisTemplate.opsForValue().get(String.format("postCount::%s", postId)))
.collect(Collectors.toList());

// when
Expand All @@ -68,42 +59,34 @@ void createPostLike() throws JsonProcessingException {
// then
postIds.forEach(
postId -> {
try {
Optional<PostLike> postLike =
postLikeJpaRepository.findById(new PostLikeId(memberId, postId));
if (postId.equals(2L)) {
assertThat(postLike.orElse(null)).isNull();
PostCountVO afterPostCountVO =
objectMapper.readValue(
redisTemplate.opsForValue().get(String.format("postCount::%s", postId)),
PostCountVO.class);
Optional<PostLike> postLike =
postLikeJpaRepository.findById(new PostLikeId(memberId, postId));
if (postId.equals(2L)) {
assertThat(postLike.orElse(null)).isNull();
PostCountVO afterPostCountVO =
redisTemplate.opsForValue().get(String.format("postCount::%s", postId));

assertThat(
beforePostCountVOs.stream()
.anyMatch(
beforePostCountVO ->
beforePostCountVO
.getLikeCount()
.equals(afterPostCountVO.getLikeCount() + 1)))
.isTrue();
} else {
assertThat(postLike.orElse(null)).isNotNull();
PostCountVO afterPostCountVO =
objectMapper.readValue(
redisTemplate.opsForValue().get(String.format("postCount::%s", postId)),
PostCountVO.class);
assertThat(
beforePostCountVOs.stream()
.anyMatch(
beforePostCountVO ->
beforePostCountVO
.getLikeCount()
.equals(afterPostCountVO.getLikeCount() + 1)))
.isTrue();
} else {
assertThat(postLike.orElse(null)).isNotNull();
PostCountVO afterPostCountVO =
redisTemplate.opsForValue().get(String.format("postCount::%s", postId));

assertThat(
beforePostCountVOs.stream()
.anyMatch(
beforePostCountVO ->
beforePostCountVO
.getLikeCount()
.equals(afterPostCountVO.getLikeCount() - 1)))
.isTrue();
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
assertThat(
beforePostCountVOs.stream()
.anyMatch(
beforePostCountVO ->
beforePostCountVO
.getLikeCount()
.equals(afterPostCountVO.getLikeCount() - 1)))
.isTrue();
}
});
}
Expand Down
Loading

0 comments on commit 55d239f

Please sign in to comment.