Skip to content

Commit

Permalink
Merge branch 'feat/#863' into glen/dev2
Browse files Browse the repository at this point in the history
  • Loading branch information
seokjin8678 committed May 28, 2024
2 parents 416c58e + 79fb628 commit 98ae09c
Show file tree
Hide file tree
Showing 12 changed files with 541 additions and 1 deletion.
4 changes: 4 additions & 0 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${swaggerVersion}")

Expand Down Expand Up @@ -86,6 +87,9 @@ dependencies {

// AWS S3
implementation("software.amazon.awssdk:s3:${awsS3Version}")

// Caffeine
implementation("com.github.ben-manes.caffeine:caffeine")
}

tasks.test {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.festago.common.cache;

import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
@Slf4j
public class CacheInvalidator {

private final CacheManager cacheManager;

public void invalidate(String cacheName) {
Optional.ofNullable(cacheManager.getCache(cacheName))
.ifPresentOrElse(cache -> {
cache.invalidate();
log.info("{} 캐시를 초기화 했습니다.", cacheName);
}, () -> log.error("{} 캐시를 찾을 수 없습니다.", cacheName));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.festago.common.cache;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.context.annotation.Profile;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Profile({"!test"})
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheStatsLogger {

private final CacheManager cacheManager;

@EventListener(ContextClosedEvent.class)
public void logCacheStats() {
for (String cacheName : cacheManager.getCacheNames()) {
Cache cache = cacheManager.getCache(cacheName);
if (cache instanceof CaffeineCache caffeineCache) {
log.info("CacheName={} CacheStats={}", cacheName, caffeineCache.getNativeCache().stats());
}
}
}
}
21 changes: 21 additions & 0 deletions backend/src/main/java/com/festago/config/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.festago.config;

import java.util.List;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

@Bean
public CacheManager cacheManager(List<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return cacheManager;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.festago.school.application.v1;

import com.festago.common.cache.CacheInvalidator;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class SchoolFestivalsV1CacheInvalidateScheduler {

private final CacheInvalidator cacheInvalidator;

// 매일 정각마다 캐시 초기화
@Scheduled(cron = "0 0 0 * * *")
public void invalidate() {
cacheInvalidator.invalidate(SchoolFestivalsV1QueryService.SCHOOL_FESTIVALS_V1_CACHE_NAME);
cacheInvalidator.invalidate(SchoolFestivalsV1QueryService.PAST_SCHOOL_FESTIVALS_V1_CACHE_NAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.festago.school.application.v1;

import com.festago.school.dto.v1.SchoolFestivalV1Response;
import com.festago.school.repository.v1.SchoolFestivalsV1QueryDslRepository;
import java.time.Clock;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SchoolFestivalsV1QueryService {

public static final String SCHOOL_FESTIVALS_V1_CACHE_NAME = "schoolFestivalsV1";
public static final String PAST_SCHOOL_FESTIVALS_V1_CACHE_NAME = "pastSchoolFestivalsV1";

private final SchoolFestivalsV1QueryDslRepository schoolFestivalsV1QueryDslRepository;
private final Clock clock;

@Cacheable(cacheNames = SCHOOL_FESTIVALS_V1_CACHE_NAME, key = "#schoolId")
public List<SchoolFestivalV1Response> findFestivalsBySchoolId(Long schoolId) {
LocalDate now = LocalDate.now(clock);
return schoolFestivalsV1QueryDslRepository.findFestivalsBySchoolId(schoolId, now);
}

@Cacheable(cacheNames = PAST_SCHOOL_FESTIVALS_V1_CACHE_NAME, key = "#schoolId")
public List<SchoolFestivalV1Response> findPastFestivalsBySchoolId(Long schoolId) {
LocalDate now = LocalDate.now(clock);
return schoolFestivalsV1QueryDslRepository.findPastFestivalsBySchoolId(schoolId, now);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.festago.school.infrastructure;

import com.festago.school.application.v1.SchoolFestivalsV1QueryService;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
import org.springframework.cache.Cache;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SchoolFestivalsV1CacheConfig {

private static final long EXPIRED_AFTER_WRITE = 30;
private static final long MAXIMUM_SIZE = 1_000;

@Bean
public Cache schoolFestivalsV1Cache() {
return new CaffeineCache(SchoolFestivalsV1QueryService.SCHOOL_FESTIVALS_V1_CACHE_NAME,
Caffeine.newBuilder()
.recordStats()
.expireAfterWrite(EXPIRED_AFTER_WRITE, TimeUnit.MINUTES)
.maximumSize(MAXIMUM_SIZE)
.build()
);
}

@Bean
public Cache pastSchoolFestivalsV1Cache() {
return new CaffeineCache(SchoolFestivalsV1QueryService.PAST_SCHOOL_FESTIVALS_V1_CACHE_NAME,
Caffeine.newBuilder()
.recordStats()
.expireAfterWrite(EXPIRED_AFTER_WRITE, TimeUnit.MINUTES)
.maximumSize(MAXIMUM_SIZE)
.build()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.festago.school.repository.v1;

import static com.festago.festival.domain.QFestival.festival;
import static com.festago.festival.domain.QFestivalQueryInfo.festivalQueryInfo;

import com.festago.common.querydsl.QueryDslHelper;
import com.festago.school.dto.v1.QSchoolFestivalV1Response;
import com.festago.school.dto.v1.SchoolFestivalV1Response;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class SchoolFestivalsV1QueryDslRepository {

private final QueryDslHelper queryDslHelper;

public List<SchoolFestivalV1Response> findFestivalsBySchoolId(
Long schoolId,
LocalDate today
) {
return queryDslHelper.select(
new QSchoolFestivalV1Response(
festival.id,
festival.name,
festival.festivalDuration.startDate,
festival.festivalDuration.endDate,
festival.posterImageUrl,
festivalQueryInfo.artistInfo
)
)
.from(festival)
.leftJoin(festivalQueryInfo).on(festivalQueryInfo.festivalId.eq(festival.id))
.where(festival.school.id.eq(schoolId).and(festival.festivalDuration.endDate.goe(today)))
.stream()
.sorted(Comparator.comparing(SchoolFestivalV1Response::startDate))
.toList();
}

public List<SchoolFestivalV1Response> findPastFestivalsBySchoolId(
Long schoolId,
LocalDate today
) {
return queryDslHelper.select(
new QSchoolFestivalV1Response(
festival.id,
festival.name,
festival.festivalDuration.startDate,
festival.festivalDuration.endDate,
festival.posterImageUrl,
festivalQueryInfo.artistInfo
)
)
.from(festival)
.leftJoin(festivalQueryInfo).on(festivalQueryInfo.festivalId.eq(festival.id))
.where(festival.school.id.eq(schoolId).and(festival.festivalDuration.endDate.lt(today)))
.stream()
.sorted(Comparator.comparing(SchoolFestivalV1Response::endDate).reversed())
.toList();
}
}
Loading

0 comments on commit 98ae09c

Please sign in to comment.