Skip to content

Commit

Permalink
#508 [feat] 글 인기 글 / 작가 도메인 캐시 구현
Browse files Browse the repository at this point in the history
Caffeine 캐시를 사용하였는데, (EhCache보다 사용하기 쉬움) 이는 Scheduling이 되지 않아서 스케줄러를 직접 등록해야하기 때문에 스케줄러를 직접 등록했습니다. 스케줄러가 스레드를 잡아먹어서 이에 대해 방법을 고민해보겠습니다!
  • Loading branch information
sohyundoh committed Sep 7, 2024
1 parent 04e1cd9 commit ee8d28d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 40 deletions.
2 changes: 2 additions & 0 deletions module-domain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ dependencies {

//Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.2'

// QueryDSL Implementation
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.mile.common.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Scheduler;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeineConfig());
return cacheManager;
}

private Scheduler getScheduler() {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Scheduler scheduler;
scheduler = Scheduler.forScheduledExecutorService(executor);
return scheduler;
}

private Caffeine<Object, Object> caffeineConfig() {
return Caffeine.newBuilder()
.maximumSize(200)
.expireAfterAccess(5, TimeUnit.DAYS)
.scheduler(getScheduler());
}
}
47 changes: 7 additions & 40 deletions module-domain/src/main/java/com/mile/moim/service/MoimService.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.mile.moim.service;

import com.mile.curious.repository.dto.PostAndCuriousCountInLastWeek;
import com.mile.common.utils.DateUtil;
import com.mile.common.utils.SecureUrlUtil;
import com.mile.curious.service.CuriousRetriever;
import com.mile.exception.message.ErrorMessage;
import com.mile.exception.model.BadRequestException;
import com.mile.exception.model.ForbiddenException;
import com.mile.moim.domain.Moim;
import com.mile.moim.domain.popular.MoimCuriousPost;
import com.mile.moim.domain.popular.MoimCuriousWriter;
import com.mile.moim.domain.popular.MoimPopularInfo;
import com.mile.moim.repository.MoimPopularInfoRepository;
import com.mile.moim.service.dto.request.MoimCreateRequest;
import com.mile.moim.service.dto.request.MoimInfoModifyRequest;
import com.mile.moim.service.dto.request.TopicCreateRequest;
Expand All @@ -35,15 +34,14 @@
import com.mile.moim.service.dto.response.TopicListResponse;
import com.mile.moim.service.dto.response.WriterNameConflictCheckResponse;
import com.mile.moim.service.lock.AtomicValidateUniqueMoimName;
import com.mile.moim.service.popular.MoimPopularInfoService;
import com.mile.post.domain.Post;
import com.mile.post.service.PostRetriever;
import com.mile.topic.service.TopicCreator;
import com.mile.topic.service.TopicRemover;
import com.mile.topic.service.TopicRetriever;
import com.mile.user.domain.User;
import com.mile.user.service.UserRetriever;
import com.mile.common.utils.DateUtil;
import com.mile.common.utils.SecureUrlUtil;
import com.mile.writername.domain.WriterName;
import com.mile.writername.service.WriterNameRemover;
import com.mile.writername.service.WriterNameRetriever;
Expand Down Expand Up @@ -73,7 +71,7 @@ public class MoimService {
private final TopicRemover topicRemover;
private final TopicRetriever topicRetriever;
private final TopicCreator topicCreator;
private final MoimPopularInfoRepository moimPopularInfoRepository;
private final MoimPopularInfoService moimPopularInfoService;

private static final int WRITER_NAME_MAX_VALUE = 8;
private static final int MOIM_NAME_MAX_VALUE = 10;
Expand Down Expand Up @@ -144,35 +142,6 @@ public MoimMostCuriousWriterResponse getMostCuriousWritersOfMoim(
}


private MoimPopularInfo setMostPopularInfoOfMoim(final Moim moim) {
List<PostAndCuriousCountInLastWeek> mostCuriousPostsInLastWeek = curiousRetriever.findMostCuriousPostsInLastWeek(moim);

List<MoimCuriousPost> moimCuriousPosts = getMoimCuriousPost(mostCuriousPostsInLastWeek);

List<MoimCuriousWriter> moimCuriousWriters = getMoimCuriousWriter(mostCuriousPostsInLastWeek);

MoimPopularInfo moimPopularInfo = MoimPopularInfo.of(moim.getId(), moimCuriousPosts, moimCuriousWriters);

return moimPopularInfoRepository.save(moimPopularInfo);
}

private List<MoimCuriousPost> getMoimCuriousPost(final List<PostAndCuriousCountInLastWeek> mostCuriousPostsInLastWeek) {
return mostCuriousPostsInLastWeek.stream().map(p ->
MoimCuriousPost.of(p.getPost())).limit(2).toList();
}

private List<MoimCuriousWriter> getMoimCuriousWriter(final List<PostAndCuriousCountInLastWeek> mostCuriousPostsInLastWeek) {
Map<WriterName, Long> writerNameCount = mostCuriousPostsInLastWeek.stream()
.collect(Collectors.groupingBy(p -> p.getPost().getWriterName(), Collectors.summingLong(PostAndCuriousCountInLastWeek::getCount)));

List<WriterName> topTwoWriters = writerNameCount.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(2)
.map(Map.Entry::getKey).toList();

return topTwoWriters.stream().map(MoimCuriousWriter::of).toList();
}

public MoimTopicResponse getTopicFromMoim(
final Long moimId
) {
Expand All @@ -193,13 +162,11 @@ public MoimInfoResponse getMoimInfo(
);
}


public MoimOverallInfoResponse getMoimTotalInformation(final Long moimId) {
Moim moim = moimRetriever.findById(moimId);
MoimInfoResponse moimInfoResponse = moimRetriever.getMoimInfoForTotal(moim,
writerNameRetriever.findNumbersOfWritersByMoim(moim));
MoimPopularInfo moimPopularInfo = moimPopularInfoRepository.findByMoimId(moimId).orElseGet(
() -> setMostPopularInfoOfMoim(moim)
);
MoimInfoResponse moimInfoResponse = moimRetriever.getMoimInfoForTotal(moim, writerNameRetriever.findNumbersOfWritersByMoim(moim));
MoimPopularInfo moimPopularInfo = moimPopularInfoService.getMoimPopularInfo(moim);
MoimMostCuriousWriterResponse mostCuriousWriterResponse = MoimMostCuriousWriterResponse.of(moimPopularInfo.getWriters());
MoimCuriousPostListResponse moimCuriousPostListResponse = MoimCuriousPostListResponse.of(
moimPopularInfo.getPosts().stream().map(MoimMostCuriousPostResponse::of).toList()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.mile.moim.service.popular;

import com.mile.curious.repository.dto.PostAndCuriousCountInLastWeek;
import com.mile.curious.service.CuriousRetriever;
import com.mile.moim.domain.Moim;
import com.mile.moim.domain.popular.MoimCuriousPost;
import com.mile.moim.domain.popular.MoimCuriousWriter;
import com.mile.moim.domain.popular.MoimPopularInfo;
import com.mile.moim.repository.MoimPopularInfoRepository;
import com.mile.writername.domain.WriterName;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public class MoimPopularInfoService {
private final MoimPopularInfoRepository moimPopularInfoRepository;
private final CuriousRetriever curiousRetriever;

@CachePut(value = "moimPopularInfo", key = "#moim.id")
public MoimPopularInfo setMostPopularInfoOfMoim(final Moim moim) {
List<PostAndCuriousCountInLastWeek> mostCuriousPostsInLastWeek = curiousRetriever.findMostCuriousPostsInLastWeek(moim);

List<MoimCuriousPost> moimCuriousPosts = getMoimCuriousPost(mostCuriousPostsInLastWeek);

List<MoimCuriousWriter> moimCuriousWriters = getMoimCuriousWriter(mostCuriousPostsInLastWeek);

MoimPopularInfo moimPopularInfo = MoimPopularInfo.of(moim.getId(), moimCuriousPosts, moimCuriousWriters);

return moimPopularInfoRepository.save(moimPopularInfo);
}

private List<MoimCuriousPost> getMoimCuriousPost(final List<PostAndCuriousCountInLastWeek> mostCuriousPostsInLastWeek) {
return mostCuriousPostsInLastWeek.stream().map(p ->
MoimCuriousPost.of(p.getPost())).limit(2).toList();
}

private List<MoimCuriousWriter> getMoimCuriousWriter(final List<PostAndCuriousCountInLastWeek> mostCuriousPostsInLastWeek) {
Map<WriterName, Long> writerNameCount = mostCuriousPostsInLastWeek.stream()
.collect(Collectors.groupingBy(p -> p.getPost().getWriterName(), Collectors.summingLong(PostAndCuriousCountInLastWeek::getCount)));

List<WriterName> topTwoWriters = writerNameCount.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(2)
.map(Map.Entry::getKey).toList();

return topTwoWriters.stream().map(MoimCuriousWriter::of).toList();
}

@Cacheable(value = "moimPopularInfo", key = "#moim.id")
public MoimPopularInfo getMoimPopularInfo(final Moim moim) {
return moimPopularInfoRepository.findByMoimId(moim.getId()).orElseGet(
() -> setMostPopularInfoOfMoim(moim)
);
}

}

0 comments on commit ee8d28d

Please sign in to comment.