From 8ceb42834e6f27dd247b2f418f1451b33e87abc7 Mon Sep 17 00:00:00 2001 From: Nycz-lab Date: Thu, 2 Apr 2026 14:34:02 +0200 Subject: [PATCH 1/3] feat: implement sentineled connection for redis cache --- .../common/cache/types/RedisCache.java | 55 +++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java b/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java index 532c6bb..2e9ec84 100644 --- a/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java +++ b/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java @@ -5,26 +5,60 @@ import ls.ni.networkfilter.common.filter.FilterResult; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; +import redis.clients.jedis.*; import java.time.Duration; +import java.util.List; +import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; public class RedisCache implements Cache { private final ObjectMapper objectMapper = new ObjectMapper(); - private final JedisPool jedisPool; + private final UnifiedJedis unifiedJedis; private final Duration expireAfterWrite; + private static final int SENTINEL_DEFAULT_PORT = 26379; + public RedisCache(@NotNull String uri, @NotNull Duration expireAfterWrite) { - this.jedisPool = new JedisPool(uri); + this.unifiedJedis = new JedisPooled(uri); this.expireAfterWrite = expireAfterWrite; // Close the JedisPool when the JVM is shutting down - Runtime.getRuntime().addShutdownHook(new Thread(this.jedisPool::close)); + Runtime.getRuntime().addShutdownHook(new Thread(this.unifiedJedis::close)); + } + + public RedisCache(String masterName, List sentinelAddresses, String username, String password, + boolean ssl, String sentinelUsername, String sentinelPassword, Duration expireAfterWrite){ + + this.expireAfterWrite = expireAfterWrite; + + Set sentinels = sentinelAddresses.stream() + .map(addr -> parseAddress(addr)) + .collect(Collectors.toSet()); + + this.unifiedJedis = new JedisSentineled(masterName, jedisConfig(username, password, ssl), + sentinels, jedisConfig(sentinelUsername, sentinelPassword, ssl)); + } + + private static HostAndPort parseAddress(String address) { + return parseAddress(address, Protocol.DEFAULT_PORT); + } + + private static HostAndPort parseAddress(String address, int defaultPort) { + return new HostAndPort(address, defaultPort); + } + + private static JedisClientConfig jedisConfig(String username, String password, boolean ssl) { + return DefaultJedisClientConfig.builder() + .user(username) + .password(password) + .ssl(ssl) + .timeoutMillis(Protocol.DEFAULT_TIMEOUT) + .build(); } @Override @@ -36,12 +70,12 @@ public RedisCache(@NotNull String uri, @NotNull Duration expireAfterWrite) { public @Nullable FilterResult getIfPresent(@NotNull String key) { String cacheKey = this.cacheKey(key); - try (Jedis resource = this.jedisPool.getResource()) { - if (!resource.exists(cacheKey)) { + try { + if (!this.unifiedJedis.exists(cacheKey)) { return null; } - return this.objectMapper.readValue(resource.get(cacheKey), FilterResult.class); + return this.objectMapper.readValue(this.unifiedJedis.get(cacheKey), FilterResult.class); } catch (Throwable cause) { throw new RuntimeException("Failed to get value from Redis", cause); } @@ -64,10 +98,11 @@ public RedisCache(@NotNull String uri, @NotNull Duration expireAfterWrite) { public void put(@NotNull String key, @NotNull FilterResult value) { String cacheKey = this.cacheKey(key); - try (Jedis resource = this.jedisPool.getResource()) { + try { String mappedValue = this.objectMapper.writeValueAsString(value); - resource.setex(cacheKey, (int) this.expireAfterWrite.getSeconds(), mappedValue); + this.unifiedJedis.setex(cacheKey, (int) this.expireAfterWrite.getSeconds(), mappedValue); + } catch (Throwable cause) { throw new RuntimeException("Failed to put value to Redis", cause); } From 2dc9e7605267a9f71152dbc40a337076eed233fc Mon Sep 17 00:00:00 2001 From: Nycz-lab Date: Tue, 7 Apr 2026 14:00:44 +0200 Subject: [PATCH 2/3] fix: correct address parsing + fixed identifier --- .../common/cache/types/RedisCache.java | 15 +++++++++++---- .../cache/types/RedisSentineledCacheSettings.java | 4 ++++ 2 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java diff --git a/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java b/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java index 2e9ec84..3617d98 100644 --- a/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java +++ b/common/src/main/java/ls/ni/networkfilter/common/cache/types/RedisCache.java @@ -18,6 +18,7 @@ public class RedisCache implements Cache { private final ObjectMapper objectMapper = new ObjectMapper(); private final UnifiedJedis unifiedJedis; + private boolean sentineled = false; private final Duration expireAfterWrite; private static final int SENTINEL_DEFAULT_PORT = 26379; @@ -26,7 +27,6 @@ public RedisCache(@NotNull String uri, @NotNull Duration expireAfterWrite) { this.unifiedJedis = new JedisPooled(uri); this.expireAfterWrite = expireAfterWrite; - // Close the JedisPool when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(this.unifiedJedis::close)); } @@ -42,13 +42,19 @@ public RedisCache(String masterName, List sentinelAddresses, String user this.unifiedJedis = new JedisSentineled(masterName, jedisConfig(username, password, ssl), sentinels, jedisConfig(sentinelUsername, sentinelPassword, ssl)); + + this.sentineled = true; } private static HostAndPort parseAddress(String address) { - return parseAddress(address, Protocol.DEFAULT_PORT); + return parseAddress(address, SENTINEL_DEFAULT_PORT); } - private static HostAndPort parseAddress(String address, int defaultPort) { + private static HostAndPort parseAddress(@NotNull String address, int defaultPort) { + if(address.lastIndexOf(':') > 0){ + return HostAndPort.from(address); + } + return new HostAndPort(address, defaultPort); } @@ -63,7 +69,8 @@ private static JedisClientConfig jedisConfig(String username, String password, b @Override public @NotNull String getName() { - return "redis"; + + return sentineled ? "redis_sentineled" : "redis"; } @Override diff --git a/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java b/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java new file mode 100644 index 0000000..94541cf --- /dev/null +++ b/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java @@ -0,0 +1,4 @@ +package ls.ni.networkfilter.common.config.cache.types; + +public class RedisSentineledCacheSettings { +} From 1c0d61bb2fa440046672add23d54f938541aff80 Mon Sep 17 00:00:00 2001 From: Nycz-lab Date: Tue, 7 Apr 2026 14:01:10 +0200 Subject: [PATCH 3/3] feat: add config and set correct parameters --- .../common/cache/CacheFactory.java | 14 ++++++++++ .../common/config/cache/CacheSettings.java | 5 ++++ .../common/config/cache/CacheType.java | 1 + .../types/RedisSentineledCacheSettings.java | 26 +++++++++++++++++++ common/src/main/resources/config.yml | 10 +++++++ 5 files changed, 56 insertions(+) diff --git a/common/src/main/java/ls/ni/networkfilter/common/cache/CacheFactory.java b/common/src/main/java/ls/ni/networkfilter/common/cache/CacheFactory.java index 48cf0f9..70b0d5e 100644 --- a/common/src/main/java/ls/ni/networkfilter/common/cache/CacheFactory.java +++ b/common/src/main/java/ls/ni/networkfilter/common/cache/CacheFactory.java @@ -4,6 +4,7 @@ import ls.ni.networkfilter.common.cache.types.NoopCache; import ls.ni.networkfilter.common.cache.types.RedisCache; import ls.ni.networkfilter.common.config.Config; +import ls.ni.networkfilter.common.config.cache.types.RedisSentineledCacheSettings; import org.jetbrains.annotations.NotNull; import java.time.Duration; @@ -25,6 +26,19 @@ yield new RedisCache( Duration.ofMinutes(config.getCaches().getRedis().getCacheTimeMinutes()) ); } + case REDIS_SENTINELED -> { + RedisSentineledCacheSettings cacheSettings = config.getCaches().getSentinel(); + yield new RedisCache( + cacheSettings.getMaster(), + cacheSettings.getAddresses(), + cacheSettings.getUsername(), + cacheSettings.getPassword(), + cacheSettings.isSsl(), + cacheSettings.getUsername(), + cacheSettings.getPassword(), + Duration.ofMinutes(cacheSettings.getCacheTimeMinutes()) + ); + } default -> throw new IllegalStateException("Cache '" + config.getCache() + "' is not supported!"); }; } diff --git a/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheSettings.java b/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheSettings.java index ed63710..0abebf4 100644 --- a/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheSettings.java +++ b/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheSettings.java @@ -8,6 +8,7 @@ import ls.ni.networkfilter.common.config.cache.types.DisabledCacheSettings; import ls.ni.networkfilter.common.config.cache.types.LocalCacheSettings; import ls.ni.networkfilter.common.config.cache.types.RedisCacheSettings; +import ls.ni.networkfilter.common.config.cache.types.RedisSentineledCacheSettings; @Data @NoArgsConstructor @@ -26,4 +27,8 @@ public class CacheSettings { @NotNull private RedisCacheSettings redis; + @Valid + @NotNull + private RedisSentineledCacheSettings sentinel; + } diff --git a/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheType.java b/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheType.java index f61a0db..adfe660 100644 --- a/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheType.java +++ b/common/src/main/java/ls/ni/networkfilter/common/config/cache/CacheType.java @@ -7,6 +7,7 @@ public enum CacheType { DISABLED("disabled"), LOCAL("local"), REDIS("redis"), + REDIS_SENTINELED("redis_sentineled") ; private final String key; diff --git a/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java b/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java index 94541cf..c0f5129 100644 --- a/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java +++ b/common/src/main/java/ls/ni/networkfilter/common/config/cache/types/RedisSentineledCacheSettings.java @@ -1,4 +1,30 @@ package ls.ni.networkfilter.common.config.cache.types; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor public class RedisSentineledCacheSettings { + + @NotNull + private String master; + + private List addresses; + + private String username; + private String password; + + @NotNull + @Positive + private Long cacheTimeMinutes; + + @NotNull + private boolean ssl; } diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index e6a7382..4668d98 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -17,6 +17,16 @@ caches: uri: "redis://user:password@localhost:6379" cacheTimeMinutes: 15 + sentinel: + master: mymaster + addresses: + - localhost:26379 + username: '' + password: '' + cacheTimeMinutes: 15 + ssl: false + + services: # https://nf.ni.ls networkfilter: