Skip to content

Commit 22a7865

Browse files
committed
✨ feat: 이벤트 all-time 조회 성능 개선을 위한 Redis 캐시 도입
- EventScheduleCacheService를 추가하여 AllTimeScheduleResponse 캐싱 - 10분 TTL 설정 - 캐시 무효화 로직 추가 (createOrUpdateMyTime, completeMyTime, joinEvent) - 자주 조회되는 이벤트 시간대 데이터의 DB 부하 감소 - 캐시 실패 시 DB 조회로 자동 fallback 처리 ✨ feat: 이벤트 all-time 조회 성능 개선을 위한 Redis 캐시 도입 - EventScheduleCacheService를 추가하여 AllTimeScheduleResponse 캐싱 - 10분 TTL 설정 - 캐시 무효화 로직 추가 (createOrUpdateMyTime, completeMyTime, joinEvent) - 자주 조회되는 이벤트 시간대 데이터의 DB 부하 감소 - 캐시 실패 시 DB 조회로 자동 fallback 처리
1 parent 5961b93 commit 22a7865

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

src/main/java/com/grepp/spring/app/model/event/service/EventCommandService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class EventCommandService {
4646
private final TempScheduleRepository tempScheduleRepository;
4747
private final EventScheduleResultService eventScheduleResultService;
4848
private final EventCreationStrategyFactory strategyFactory;
49+
private final EventScheduleCacheService cacheService;
4950

5051
public CreateEventResponse createEvent(CreateEventRequest webRequest, String currentMemberId) {
5152
CreateEventDto serviceRequest = CreateEventDto.toDto(webRequest, currentMemberId);
@@ -77,6 +78,8 @@ public void joinEvent(Long eventId, Long groupId, String currentMemberId) {
7778

7879
EventMemberDto memberDto = EventMemberDto.toDto(dto.getEventId(), dto.getMemberId(), Role.ROLE_MEMBER);
7980
createEventMember(memberDto);
81+
82+
cacheService.invalidateEventCache(eventId);
8083
}
8184

8285
public void createOrUpdateMyTime(MyTimeScheduleRequest request, Long eventId, String currentMemberId) {
@@ -90,6 +93,8 @@ public void createOrUpdateMyTime(MyTimeScheduleRequest request, Long eventId, St
9093
for (MyTimeScheduleDto.DailyTimeSlotDto slot : dto.getDailyTimeSlots()) {
9194
updateOrCreateTempSchedule(eventMember, slot);
9295
}
96+
97+
cacheService.invalidateEventCache(eventId);
9398
}
9499

95100
private void updateOrCreateTempSchedule(EventMember eventMember, MyTimeScheduleDto.DailyTimeSlotDto slot) {
@@ -126,6 +131,8 @@ public void completeMyTime(Long eventId, String currentMemberId) {
126131

127132
eventMember.confirmScheduleOrThrow();
128133
eventMemberRepository.save(eventMember);
134+
135+
cacheService.invalidateEventCache(eventId);
129136
}
130137

131138
public void createScheduleResult(Long eventId, String currentMemberId) {

src/main/java/com/grepp/spring/app/model/event/service/EventQueryService.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class EventQueryService {
4545
private final TempScheduleRepository tempScheduleRepository;
4646
private final ScheduleQueryRepository scheduleQueryRepository;
4747
private final ScheduleMemberQueryRepository scheduleMemberQueryRepository;
48+
private final EventScheduleCacheService cacheService;
4849

4950
public ShowEventResponse getEvent(Long eventId, String currentMemberId) {
5051
Event event = eventRepository.findById(eventId)
@@ -75,6 +76,14 @@ public AllTimeScheduleResponse getAllTimeSchedules(Long eventId, String currentM
7576
throw new NotEventMemberException(EventErrorCode.NOT_EVENT_MEMBER);
7677
}
7778

79+
// 캐시에서 조회 시도
80+
AllTimeScheduleResponse cachedResponse = cacheService.getCachedAllTimeSchedule(eventId);
81+
82+
if (cachedResponse != null) {
83+
return cachedResponse;
84+
}
85+
86+
// 캐시 미스 시 데이터베이스에서 조회
7887
List<CandidateDate> candidateDates = candidateDateRepository
7988
.findAllByEventIdAndActivatedTrueOrderByDate(eventId);
8089

@@ -107,7 +116,11 @@ public AllTimeScheduleResponse getAllTimeSchedules(Long eventId, String currentM
107116
.participantCounts(participantCounts)
108117
.build();
109118

110-
return AllTimeScheduleDto.fromDto(dto);
119+
AllTimeScheduleResponse response = AllTimeScheduleDto.fromDto(dto);
120+
121+
cacheService.cacheAllTimeSchedule(eventId, response);
122+
123+
return response;
111124
}
112125

113126

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.grepp.spring.app.model.event.service;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.grepp.spring.app.controller.api.event.payload.response.AllTimeScheduleResponse;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.data.redis.core.RedisTemplate;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.time.Duration;
11+
12+
@Service
13+
@RequiredArgsConstructor
14+
@Slf4j
15+
public class EventScheduleCacheService {
16+
17+
private final RedisTemplate<String, Object> redisTemplate;
18+
private final ObjectMapper objectMapper;
19+
20+
private static final String CACHE_KEY_PREFIX = "event:all-time:";
21+
22+
private static final Duration DEFAULT_TTL = Duration.ofMinutes(10);
23+
24+
// 캐시에서 전체 시간대 조회 결과를 가져옴. 캐시 조회 실패 시 null 반환
25+
public AllTimeScheduleResponse getCachedAllTimeSchedule(Long eventId) {
26+
try {
27+
String key = buildCacheKey(eventId);
28+
Object cached = redisTemplate.opsForValue().get(key);
29+
30+
if (cached == null) {
31+
return null;
32+
}
33+
34+
AllTimeScheduleResponse response = objectMapper.convertValue(cached, AllTimeScheduleResponse.class);
35+
36+
if (response != null) {
37+
log.debug("Cache hit for eventId: {}", eventId);
38+
return response;
39+
}
40+
41+
return null;
42+
43+
} catch (Exception e) {
44+
log.warn("Cache get failed for eventId: {}, falling back to DB: {}", eventId, e.getMessage());
45+
return null;
46+
}
47+
}
48+
49+
// 전체 시간대 조회 결과를 캐시에 저장
50+
public void cacheAllTimeSchedule(Long eventId, AllTimeScheduleResponse response) {
51+
String key = buildCacheKey(eventId);
52+
redisTemplate.opsForValue().set(key, response, DEFAULT_TTL);
53+
54+
log.debug("Cached data for eventId: {}", eventId);
55+
}
56+
57+
// 특정 이벤트의 캐시를 무효화
58+
public void invalidateEventCache(Long eventId) {
59+
String key = buildCacheKey(eventId);
60+
Boolean deleted = redisTemplate.delete(key);
61+
62+
log.debug("Cache invalidated for eventId: {}, deleted: {}", eventId, deleted);
63+
}
64+
65+
// 캐시 키 생성
66+
private String buildCacheKey(Long eventId) {
67+
return CACHE_KEY_PREFIX + eventId;
68+
}
69+
}

0 commit comments

Comments
 (0)