Skip to content
Open
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
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
projectVersion=6.7.0-SNAPSHOT
projectVersion=6.7.1-SNAPSHOT
projectGroup=io.micronaut.redis

title=Micronaut Redis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public interface RedisSetting {
* Default configuration for Redis caches.
*/
String REDIS_CACHE = PREFIX + ".cache";

/**
* Configuration for dynamic Redis caches.
*/
String REDIS_DYNAMIC_CACHE = REDIS_CACHE + ".dynamic";

/**
* Configured Redis caches.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@
/**
* An implementation of {@link SyncCache} for Lettuce / Redis.
*
* @author Graeme Rocher
* @author Graeme Rocher, Ferdinand Armbruster
* @since 1.0
*/
@EachBean(RedisCacheConfiguration.class)
@Requires(classes = SyncCache.class, property = RedisSetting.PREFIX + ".enabled", defaultValue = StringUtils.TRUE, notEquals = StringUtils.FALSE)
@Requires(classes = SyncCache.class, property = RedisSetting.REDIS_POOL + ".enabled", defaultValue = StringUtils.FALSE, notEquals = StringUtils.TRUE)
public class RedisCache extends AbstractRedisCache<StatefulConnection<byte[], byte[]>> {
private final RedisAsyncCache asyncCache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@
/**
* An implementation of {@link SyncCache} for Lettuce / Redis using connection pooling.
*
* @author Graeme Rocher, Kovalov Illia
* @author Graeme Rocher, Kovalov Illia, Ferdinand Armbruster
* @since 5.3.0
*/
@EachBean(RedisCacheConfiguration.class)
@Requires(classes = SyncCache.class, property = RedisSetting.PREFIX + ".enabled", defaultValue = StringUtils.TRUE, notEquals = StringUtils.FALSE)
@Requires(classes = SyncCache.class, property = RedisSetting.REDIS_POOL + ".enabled", defaultValue = StringUtils.FALSE, notEquals = StringUtils.FALSE)
public class RedisConnectionPoolCache extends AbstractRedisCache<AsyncPool<StatefulConnection<byte[], byte[]>>> {
private static final Logger LOG = LoggerFactory.getLogger(RedisConnectionPoolCache.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.configuration.lettuce.cache.bypass;

import io.micronaut.cache.DynamicCacheManager;
import io.micronaut.cache.SyncCache;
import io.micronaut.configuration.lettuce.RedisSetting;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.StringUtils;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;
import java.util.function.Supplier;

/**
* An implementation of {@link DynamicCacheManager} to bypass the caching mechanism, if redis is deactivated.
*
* @author Armbruster Ferdinand
* @since 6.7.1
*/
@Singleton
@Requires(classes = SyncCache.class, property = RedisSetting.PREFIX + ".enabled", defaultValue = StringUtils.TRUE, notEquals = StringUtils.TRUE)
public class BypassDynamicCacheManager implements DynamicCacheManager<Object> {

private static final Logger LOG = LoggerFactory.getLogger(BypassDynamicCacheManager.class);

/**
* Creates a new bypass cache for the given arguments.
*
* @param name The name of the dynamic cache
*/
@Override
public @NonNull SyncCache<Object> getCache(String name) {
if (LOG.isDebugEnabled()) {
LOG.debug("Create BypassCache for {}", name);
}
return new BypassCache(name);
}

/**
* An implementation of {@link SyncCache} to bypass the caching mechanism.
*
* @author Armbruster Ferdinand
* @since 6.7.1
*/
protected class BypassCache implements SyncCache<Object> {

private final String name;

public BypassCache(String name) {
this.name = name;
}

@Override
public @NonNull <T> Optional<T> get(@NonNull Object key, @NonNull Argument<T> requiredType) {
return Optional.empty();
}

@Override
public <T> T get(@NonNull Object key, @NonNull Argument<T> requiredType, @NonNull Supplier<T> supplier) {
return null;
}

@Override
public @NonNull <T> Optional<T> putIfAbsent(@NonNull Object key, @NonNull T value) {
return Optional.empty();
}

@Override
public void put(@NonNull Object key, @NonNull Object value) { }

@Override
public void invalidate(@NonNull Object key) { }

@Override
public void invalidateAll() { }

@Override
public String getName() {
return name;
}

@Override
public Object getNativeCache() {
return null;
}
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.configuration.lettuce.cache.dynamic;

import io.lettuce.core.api.StatefulConnection;
import io.micronaut.cache.DynamicCacheManager;
import io.micronaut.cache.SyncCache;
import io.micronaut.configuration.lettuce.RedisSetting;
import io.micronaut.configuration.lettuce.cache.DefaultRedisCacheConfiguration;
import io.micronaut.configuration.lettuce.cache.RedisCache;
import io.micronaut.configuration.lettuce.cache.RedisCacheConfiguration;
import io.micronaut.configuration.lettuce.cache.bypass.BypassDynamicCacheManager;
import io.micronaut.context.BeanLocator;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.util.StringUtils;
import io.micronaut.runtime.ApplicationConfiguration;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* An implementation of {@link DynamicCacheManager} for to create caches without having a configuration entry.
* By using default {@link DefaultRedisCacheConfiguration}.
*
* @author Armbruster Ferdinand
* @since 6.7.1
*/
@Singleton
@Requires(classes = SyncCache.class, property = RedisSetting.PREFIX + ".enabled", defaultValue = StringUtils.TRUE, notEquals = StringUtils.FALSE)
@Requires(classes = SyncCache.class, property = RedisSetting.REDIS_DYNAMIC_CACHE + ".enabled", defaultValue = StringUtils.FALSE, notEquals = StringUtils.FALSE)
public class RedisDynamicCacheManager implements DynamicCacheManager<StatefulConnection<byte[], byte[]>> {

private static final Logger LOG = LoggerFactory.getLogger(BypassDynamicCacheManager.class);

private ApplicationConfiguration applicationConfiguration;
private DefaultRedisCacheConfiguration defaultRedisCacheConfiguration;
private ConversionService conversionService;
private BeanLocator beanLocator;

public RedisDynamicCacheManager(
ApplicationConfiguration applicationConfiguration,
DefaultRedisCacheConfiguration defaultRedisCacheConfiguration,
ConversionService conversionService,
BeanLocator beanLocator
) {
this.applicationConfiguration = applicationConfiguration;
this.defaultRedisCacheConfiguration = defaultRedisCacheConfiguration;
this.conversionService = conversionService;
this.beanLocator = beanLocator;
}

/**
* Creates a new dynamic redis cache for the given arguments.
*
* @param name The name of the dynamic cache
*/
@Override
public @NonNull SyncCache<StatefulConnection<byte[], byte[]>> getCache(String name) {

if (LOG.isDebugEnabled()) {
LOG.debug("Create DynamicRedisCache for {}", name);
}

return new RedisCache(
defaultRedisCacheConfiguration,
new RedisCacheConfiguration(name, applicationConfiguration),
conversionService,
beanLocator
);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.micronaut.configuration.lettuce.RedisSpec
import io.micronaut.context.ApplicationContext
import io.micronaut.context.BeanLocator
import io.micronaut.context.exceptions.ConfigurationException
import io.micronaut.context.exceptions.NoSuchBeanException
import io.micronaut.core.convert.ConversionService
import io.micronaut.core.type.Argument
import io.micronaut.inject.qualifiers.Qualifiers
Expand All @@ -21,17 +22,31 @@ import java.nio.charset.Charset
import java.util.concurrent.ExecutionException

/**
* @author Graeme Rocher
* @author Graeme Rocher, Ferdinand Armbruster
* @since 1.0
*/
class RedisCacheSpec extends RedisSpec {

ApplicationContext createApplicationContext() {
ApplicationContext.run(
ApplicationContext createApplicationContext(Map options = [:]) {
ApplicationContext.run([
'redis.port': RedisContainerUtils.getRedisPort(),
'redis.caches.test.enabled': 'true',
'redis.caches.test.invalidate-scan-count': 2
)
] + options)
}

void "test bean is not instantiated if redis is disabled"(){
setup:
ApplicationContext applicationContext = createApplicationContext(['redis.enabled': false])

when:
applicationContext.getBean(RedisCache, Qualifiers.byName("test"))

then:
thrown(NoSuchBeanException)

cleanup:
applicationContext.stop()
}

void "test read/write object from redis sync cache"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import io.micronaut.runtime.ApplicationConfiguration
import java.nio.charset.Charset

/**
* @author Kovalov Illia
* @author Kovalov Illia, Ferdinand Armbruster
*/
class RedisPoolCacheSpec extends RedisSpec {

Expand All @@ -29,6 +29,20 @@ class RedisPoolCacheSpec extends RedisSpec {
] + options).environments("test").eagerInitSingletons(eagerInit).start()
}

void "test bean is not instantiated if redis is disabled"(){
setup:
ApplicationContext applicationContext = createApplicationContext('redis.enabled': 'false')

when:
applicationContext.getBean(RedisCache, Qualifiers.byName("test"))

then:
thrown(NoSuchBeanException)

cleanup:
applicationContext.stop()
}

void "can be disabled where initialization is #description"() {
setup:
ApplicationContext applicationContext = createApplicationContext('redis.pool.enabled': 'false', eager)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.micronaut.configuration.lettuce.cache.bypass


import io.micronaut.configuration.lettuce.RedisSpec
import io.micronaut.context.ApplicationContext
import io.micronaut.context.exceptions.NoSuchBeanException

/**
* @author Ferdinand Armbruster
*
* @since 6.7.1
*/
class BypassDynamicCacheManagerSpec extends RedisSpec {


ApplicationContext createApplicationContext(Boolean enabled) {
ApplicationContext.run(["redis.enabled": enabled])
}


void "test BypassDynamicCacheManager should not be available if redis is active"() {
setup:
ApplicationContext applicationContext = createApplicationContext(true)

when:
applicationContext.getBean(BypassDynamicCacheManager)

then:
thrown(NoSuchBeanException)
}

void "test bypassCache"() {
setup:
ApplicationContext applicationContext = createApplicationContext(false)

when:
BypassDynamicCacheManager bypassDynamicCacheManager = applicationContext.getBean(BypassDynamicCacheManager)
BypassDynamicCacheManager.BypassCache bypassCache = bypassDynamicCacheManager.getCache("test")

then:
bypassDynamicCacheManager != null
bypassCache != null
}

}
Loading