Use central registries for Retry and CircuitBreaker instances

This commit is contained in:
Jon Chambers
2025-08-27 11:33:42 -04:00
committed by GitHub
parent a8c6fa93e0
commit f616612104
33 changed files with 326 additions and 349 deletions

View File

@@ -1,5 +1,6 @@
package org.whispersystems.textsecuregcm.redis;
import com.google.common.annotations.VisibleForTesting;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
@@ -15,8 +16,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
import org.whispersystems.textsecuregcm.util.CircuitBreakerUtil;
public class FaultTolerantRedisClient {
@@ -38,14 +39,17 @@ public class FaultTolerantRedisClient {
this(name, clientResourcesBuilder,
RedisUriUtil.createRedisUriWithTimeout(redisConfiguration.getUri(), redisConfiguration.getTimeout()),
redisConfiguration.getTimeout(),
redisConfiguration.getCircuitBreakerConfiguration());
redisConfiguration.getCircuitBreakerConfigurationName() != null
? CircuitBreakerUtil.getCircuitBreakerRegistry().circuitBreaker(name + "-breaker", redisConfiguration.getCircuitBreakerConfigurationName())
: CircuitBreakerUtil.getCircuitBreakerRegistry().circuitBreaker(name + "-breaker"));
}
@VisibleForTesting
FaultTolerantRedisClient(String name,
final ClientResources.Builder clientResourcesBuilder,
final RedisURI redisUri,
final Duration commandTimeout,
final CircuitBreakerConfiguration circuitBreakerConfiguration) {
final CircuitBreaker circuitBreaker) {
this.name = name;
@@ -73,7 +77,7 @@ public class FaultTolerantRedisClient {
this.stringConnection = redisClient.connect();
this.binaryConnection = redisClient.connect(ByteArrayCodec.INSTANCE);
this.circuitBreaker = CircuitBreaker.of(name + "-breaker", circuitBreakerConfiguration.toCircuitBreakerConfig());
this.circuitBreaker = circuitBreaker;
}
public void shutdown() {

View File

@@ -26,7 +26,7 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
import reactor.core.scheduler.Schedulers;
@@ -57,7 +57,7 @@ public class FaultTolerantRedisClusterClient {
Collections.singleton(RedisUriUtil.createRedisUriWithTimeout(clusterConfiguration.getConfigurationUri(),
clusterConfiguration.getTimeout())),
clusterConfiguration.getTimeout(),
clusterConfiguration.getCircuitBreakerConfiguration());
clusterConfiguration.getCircuitBreakerConfigurationName());
}
@@ -65,7 +65,7 @@ public class FaultTolerantRedisClusterClient {
final ClientResources.Builder clientResourcesBuilder,
final Iterable<RedisURI> redisUris,
final Duration commandTimeout,
final CircuitBreakerConfiguration circuitBreakerConfig) {
@Nullable final String circuitBreakerConfigurationName) {
this.name = name;
@@ -83,7 +83,7 @@ public class FaultTolerantRedisClusterClient {
});
final LettuceShardCircuitBreaker lettuceShardCircuitBreaker = new LettuceShardCircuitBreaker(name,
circuitBreakerConfig.toCircuitBreakerConfig(), Schedulers.newSingle("topology-changed-" + name, true));
circuitBreakerConfigurationName, Schedulers.newSingle("topology-changed-" + name, true));
this.clusterClient = RedisClusterClient.create(
clientResourcesBuilder.nettyCustomizer(lettuceShardCircuitBreaker).
build(),

View File

@@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.redis;
import com.google.common.annotations.VisibleForTesting;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.lettuce.core.RedisNoScriptException;
import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent;
import io.lettuce.core.cluster.models.partitions.RedisClusterNode;
@@ -31,6 +30,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,7 +54,8 @@ public class LettuceShardCircuitBreaker implements NettyCustomizer {
private static final Logger logger = LoggerFactory.getLogger(LettuceShardCircuitBreaker.class);
private final String clusterName;
private final CircuitBreakerConfig circuitBreakerConfig;
@Nullable
private final String circuitBreakerConfigurationName;
private final Scheduler scheduler;
// this set will be shared with all child channel breakers
private final Set<String> upstreamAddresses = ConcurrentHashMap.newKeySet();
@@ -62,10 +63,12 @@ public class LettuceShardCircuitBreaker implements NettyCustomizer {
// resources, which cannot be built without this NettyCustomizer
private EventBus eventBus;
public LettuceShardCircuitBreaker(final String clusterName, final CircuitBreakerConfig circuitBreakerConfig,
public LettuceShardCircuitBreaker(final String clusterName,
@Nullable final String circuitBreakerConfigurationName,
final Scheduler scheduler) {
this.clusterName = clusterName;
this.circuitBreakerConfig = circuitBreakerConfig;
this.circuitBreakerConfigurationName = circuitBreakerConfigurationName;
this.scheduler = scheduler;
}
@@ -110,7 +113,7 @@ public class LettuceShardCircuitBreaker implements NettyCustomizer {
}
final ChannelCircuitBreakerHandler channelCircuitBreakerHandler = new ChannelCircuitBreakerHandler(clusterName,
circuitBreakerConfig, upstreamAddresses, eventBus, scheduler);
circuitBreakerConfigurationName, upstreamAddresses, eventBus, scheduler);
final String commandHandlerName = StreamSupport.stream(channel.pipeline().spliterator(), false)
.filter(entry -> entry.getValue() instanceof CommandHandler)
@@ -127,7 +130,7 @@ public class LettuceShardCircuitBreaker implements NettyCustomizer {
private static final String CLUSTER_TAG_NAME = "cluster";
private final String clusterName;
private final CircuitBreakerConfig circuitBreakerConfig;
@Nullable private final String circuitBreakerConfigurationName;
private final AtomicBoolean registeredMetrics = new AtomicBoolean(false);
private final Set<String> upstreamAddresses;
@@ -136,11 +139,12 @@ public class LettuceShardCircuitBreaker implements NettyCustomizer {
@VisibleForTesting
CircuitBreaker breaker;
public ChannelCircuitBreakerHandler(final String name, final CircuitBreakerConfig circuitBreakerConfig,
public ChannelCircuitBreakerHandler(final String name,
@Nullable final String circuitBreakerConfigurationName,
final Set<String> upstreamAddresses,
final EventBus eventBus, final Scheduler scheduler) {
this.clusterName = name;
this.circuitBreakerConfig = circuitBreakerConfig;
this.circuitBreakerConfigurationName = circuitBreakerConfigurationName;
this.upstreamAddresses = upstreamAddresses;
eventBus.get()
@@ -183,7 +187,12 @@ public class LettuceShardCircuitBreaker implements NettyCustomizer {
// In some cases, like the default connection, the remote address includes the DNS hostname, which we want to exclude.
shardAddress = StringUtils.substringAfter(remoteAddress.toString(), "/");
breaker = CircuitBreaker.of("%s/%s-breaker".formatted(clusterName, shardAddress), circuitBreakerConfig);
final String circuitBreakerName = "%s/%s-breaker".formatted(clusterName, shardAddress);
breaker = circuitBreakerConfigurationName != null
? CircuitBreakerUtil.getCircuitBreakerRegistry().circuitBreaker(circuitBreakerName, circuitBreakerConfigurationName)
: CircuitBreakerUtil.getCircuitBreakerRegistry().circuitBreaker(circuitBreakerName);
if (upstreamAddresses.contains(shardAddress)) {
registerMetrics();