- ChatCharacterService.getPopularCharacters()에 @Cacheable 추가 - 키: popular-chat-character:<windowStartEpoch>:<limit> - 윈도우(매일 20:00 UTC) 전환 시 자동으로 신규 키 사용 → 전일 순위 캐시와 분리 보장 Why: 동일 윈도우 내 반복 요청의 DB 부하를 줄이고, 경계 전환 시 자연스러운 캐시 갱신을 보장.
142 lines
6.3 KiB
Kotlin
142 lines
6.3 KiB
Kotlin
package kr.co.vividnext.sodalive.configs
|
|
|
|
import org.redisson.Redisson
|
|
import org.redisson.api.RedissonClient
|
|
import org.redisson.config.Config
|
|
import org.springframework.beans.factory.annotation.Value
|
|
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.cache.RedisCacheManager
|
|
import org.springframework.data.redis.connection.RedisConnectionFactory
|
|
import org.springframework.data.redis.connection.RedisStandaloneConfiguration
|
|
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration
|
|
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
|
|
import org.springframework.data.redis.core.RedisTemplate
|
|
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories
|
|
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
|
|
import org.springframework.data.redis.serializer.RedisSerializationContext
|
|
import org.springframework.data.redis.serializer.StringRedisSerializer
|
|
import java.time.Duration
|
|
|
|
@Configuration
|
|
@EnableCaching
|
|
@EnableRedisRepositories
|
|
class RedisConfig(
|
|
@Value("\${spring.redis.host}")
|
|
private val host: String,
|
|
@Value("\${spring.redis.port}")
|
|
private val port: Int
|
|
) {
|
|
@Bean(destroyMethod = "shutdown")
|
|
fun redissonClient(): RedissonClient {
|
|
val config = Config()
|
|
config.useSingleServer()
|
|
.setAddress("rediss://$host:$port")
|
|
.setSslEnableEndpointIdentification(true)
|
|
.setSslTruststore(null)
|
|
.setDnsMonitoringInterval(30000)
|
|
.setConnectionMinimumIdleSize(0)
|
|
.setConnectionPoolSize(5)
|
|
return Redisson.create(config)
|
|
}
|
|
|
|
@Bean
|
|
fun redisConnectionFactory(): RedisConnectionFactory {
|
|
val clientConfiguration = LettuceClientConfiguration.builder()
|
|
.useSsl()
|
|
.disablePeerVerification()
|
|
.build()
|
|
|
|
return LettuceConnectionFactory(RedisStandaloneConfiguration(host, port), clientConfiguration)
|
|
}
|
|
|
|
@Bean
|
|
fun redisTemplate(): RedisTemplate<*, *> {
|
|
val redisTemplate: RedisTemplate<*, *> = RedisTemplate<Any, Any>()
|
|
redisTemplate.setConnectionFactory(redisConnectionFactory())
|
|
return redisTemplate
|
|
}
|
|
|
|
@Bean
|
|
fun cacheManager(redisConnectionFactory: RedisConnectionFactory): RedisCacheManager {
|
|
val defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofMinutes(30))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
|
|
val cacheConfigMap = mutableMapOf<String, RedisCacheConfiguration>()
|
|
cacheConfigMap["default"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofMinutes(30))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
cacheConfigMap["cache_ttl_3_days"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofDays(3))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
cacheConfigMap["cache_ttl_3_hours"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofHours(3))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
|
|
cacheConfigMap["cache_ttl_10_minutes"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofMinutes(10))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
|
|
cacheConfigMap["cache_ttl_5_minutes"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofMinutes(5))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
|
|
cacheConfigMap["cache_ttl_3_minutes"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofMinutes(3))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
|
|
// 24시간 TTL 캐시: 인기 캐릭터 집계용
|
|
cacheConfigMap["popularCharacters_24h"] = RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofHours(24))
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
|
|
.serializeValuesWith(
|
|
RedisSerializationContext.SerializationPair.fromSerializer(
|
|
GenericJackson2JsonRedisSerializer()
|
|
)
|
|
)
|
|
|
|
return RedisCacheManager.builder(redisConnectionFactory)
|
|
.cacheDefaults(defaultCacheConfig)
|
|
.withInitialCacheConfigurations(cacheConfigMap)
|
|
.build()
|
|
}
|
|
}
|