Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PO-449: Integrate Redis cache for opal-fines-service #470

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/main/java/uk/gov/hmcts/opal/cache/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package uk.gov.hmcts.opal.cache;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;

import java.time.Duration;

@Slf4j
@Configuration
@EnableCaching
public class CacheConfig {

@Value("${opal.redis.enabled}")
private boolean redisEnabled;

@Value("${spring.data.redis.host}")
private String redisHost;

@Value("${spring.data.redis.port}")
private int redisPort;

@Value("${opal.redis.ttl-hours}")
private long redisTtlHours;

private CacheManager cacheManager;

@Bean
@ConditionalOnProperty(name = "opal.redis.enabled", havingValue = "true")
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisHost, redisPort);
return new LettuceConnectionFactory(config);
}

@Bean
@ConditionalOnProperty(name = "opal.redis.enabled", havingValue = "true")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@Primary
@ConditionalOnProperty(name = "opal.redis.enabled", havingValue = "true")
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(redisTtlHours))
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

this.cacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfig)
.build();
logCacheDetails(cacheManager);
return cacheManager;
}

@Bean
@ConditionalOnProperty(name = "opal.redis.enabled", havingValue = "false", matchIfMissing = true)
public CacheManager simpleCacheManager() {
this.cacheManager = new ConcurrentMapCacheManager();
logCacheDetails(cacheManager);
return cacheManager;
}

public void logCacheDetails(CacheManager cacheManager) {
log.info("------------------------------");
log.info("Cache Configuration Details:");
log.info("Redis Enabled: {}", redisEnabled);
log.info("Redis Host: {}", redisHost);
log.info("Redis Port: {}", redisPort);
log.info("Redis TTL (hours): {}", redisTtlHours);
if (cacheManager != null) {
log.info("Cache Manager: {}", cacheManager.getClass().getName());
if (cacheManager instanceof RedisCacheManager) {
log.info("Using Redis Cache Manager");
} else if (cacheManager instanceof ConcurrentMapCacheManager) {
log.info("Using Concurrent Map Cache Manager (local cache)");
}
} else {
log.warn("Cache Manager is null. This might indicate a configuration issue.");
}

log.info("Available Caches:");
if (cacheManager != null) {
cacheManager.getCacheNames().forEach(cacheName -> log.info("- {}", cacheName));
}
log.info("------------------------------");
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ opal:
file-handler-job:
cron: ${OPAL_FILE_HANDLER_JOB_CRON:0 0 * * * ?}
frontend:
url: ${OPAL_FRONTEND_URL:http://localhost:4200}
url: ${OPAL_FRONTEND_URL:http://localhost:4200}
azure:
active-directory-justice-auth-uri: https://login.microsoftonline.com
testing-support-endpoints:
Expand Down
68 changes: 68 additions & 0 deletions src/test/java/uk/gov/hmcts/opal/cache/CacheConfigTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package uk.gov.hmcts.opal.cache;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

@SpringBootTest(classes = {CacheConfig.class, CacheConfigTest.TestConfig.class})
class CacheConfigTest {

@Autowired
private CacheManager cacheManager;

@Autowired
private RedisConnectionFactory redisConnectionFactory;

@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", () -> "localhost");
registry.add("spring.data.redis.port", () -> "6379");
registry.add("opal.redis.ttl.hours", () -> "8");
registry.add("opal.redis.enabled", () -> "false");
}

@Test
void whenRedisEnabled_thenReturnRedisCacheManager() {
assertTrue(cacheManager instanceof RedisCacheManager);
}

@Test
void whenRedisDisabled_thenReturnSimpleCacheManager() {
CacheConfig config = new CacheConfig();
CacheManager simpleCacheManager = config.simpleCacheManager();
assertTrue(simpleCacheManager instanceof ConcurrentMapCacheManager);
}

@Test
void testRedisConnectionFactory() {
assertNotNull(redisConnectionFactory);
}

@TestConfiguration
static class TestConfig {
@Bean
@Primary
public RedisConnectionFactory redisConnectionFactory() {
return mock(RedisConnectionFactory.class);
}

@Bean
@Primary
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.builder(redisConnectionFactory).build();
}
}
}
Loading