Improve Redis exception handling

This commit is contained in:
Jon Chambers
2021-09-22 10:31:39 -04:00
committed by GitHub
parent 6a71d369e2
commit 98e41f9a37
12 changed files with 49 additions and 407 deletions

View File

@@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
import org.whispersystems.textsecuregcm.redis.RedisException;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
@@ -67,7 +66,7 @@ class ApnFallbackManagerTest {
}
@Test
void testClusterInsert() throws RedisException {
void testClusterInsert() {
final String endpoint = apnFallbackManager.getEndpointKey(account, device);
assertTrue(apnFallbackManager.getPendingDestinations(SlotHash.getSlot(endpoint), 1).isEmpty());
@@ -88,7 +87,7 @@ class ApnFallbackManagerTest {
}
@Test
void testProcessNextSlot() throws RedisException {
void testProcessNextSlot() {
final ApnFallbackManager.NotificationWorker worker = apnFallbackManager.new NotificationWorker();
apnFallbackManager.schedule(account, device, System.currentTimeMillis() - 30_000);

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.redis;
import io.lettuce.core.RedisClient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
import redis.embedded.RedisServer;
import java.time.Duration;
import java.util.List;
import static org.junit.Assume.assumeFalse;
public class AbstractRedisSingletonTest {
private static RedisServer redisServer;
private FaultTolerantRedisClient redisClient;
private ReplicatedJedisPool replicatedJedisPool;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
assumeFalse(System.getProperty("os.name").equalsIgnoreCase("windows"));
redisServer = RedisServer.builder()
.setting("appendonly no")
.setting("dir " + System.getProperty("java.io.tmpdir"))
.port(AbstractRedisClusterTest.getNextRedisClusterPort())
.build();
redisServer.start();
}
@Before
public void setUp() throws Exception {
final String redisUrl = String.format("redis://127.0.0.1:%d", redisServer.ports().get(0));
redisClient = new FaultTolerantRedisClient("test-client",
RedisClient.create(redisUrl),
Duration.ofSeconds(2),
new CircuitBreakerConfiguration());
replicatedJedisPool = new RedisClientFactory("test-pool",
redisUrl,
List.of(redisUrl),
new CircuitBreakerConfiguration()).getRedisClientPool();
redisClient.useClient(connection -> connection.sync().flushall());
}
protected FaultTolerantRedisClient getRedisClient() {
return redisClient;
}
protected ReplicatedJedisPool getJedisPool() {
return replicatedJedisPool;
}
@After
public void tearDown() throws Exception {
redisClient.shutdown();
}
@AfterClass
public static void tearDownAfterClass() {
redisServer.stop();
}
}

View File

@@ -19,6 +19,7 @@ import org.whispersystems.textsecuregcm.configuration.RetryConfiguration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -56,15 +57,17 @@ public class FaultTolerantPubSubConnectionTest {
public void testBreaker() {
when(pubSubCommands.get(anyString()))
.thenReturn("value")
.thenThrow(new io.lettuce.core.RedisException("Badness has ensued."));
.thenThrow(new RuntimeException("Badness has ensued."));
assertEquals("value", faultTolerantPubSubConnection.withPubSubConnection(connection -> connection.sync().get("key")));
assertThrows(RedisException.class,
() -> faultTolerantPubSubConnection.withPubSubConnection(connection -> connection.sync().get("OH NO")));
assertThrows(CallNotPermittedException.class,
final RedisException redisException = assertThrows(RedisException.class,
() -> faultTolerantPubSubConnection.withPubSubConnection(connection -> connection.sync().get("OH NO")));
assertTrue(redisException.getCause() instanceof CallNotPermittedException);
}
@Test

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.redis;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisException;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import org.junit.Before;
import org.junit.Test;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import java.time.Duration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class FaultTolerantRedisClientTest {
private RedisCommands<String, String> commands;
private FaultTolerantRedisClient faultTolerantRedisClient;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
final RedisClient redisClient = mock(RedisClient.class);
final StatefulRedisConnection<String, String> clusterConnection = mock(StatefulRedisConnection.class);
commands = mock(RedisCommands.class);
when(redisClient.connect()).thenReturn(clusterConnection);
when(clusterConnection.sync()).thenReturn(commands);
final CircuitBreakerConfiguration breakerConfiguration = new CircuitBreakerConfiguration();
breakerConfiguration.setFailureRateThreshold(100);
breakerConfiguration.setRingBufferSizeInClosedState(1);
breakerConfiguration.setWaitDurationInOpenStateInSeconds(Integer.MAX_VALUE);
faultTolerantRedisClient = new FaultTolerantRedisClient("test", redisClient, Duration.ofSeconds(2), breakerConfiguration);
}
@Test
public void testBreaker() {
when(commands.get(anyString()))
.thenReturn("value")
.thenThrow(new io.lettuce.core.RedisException("Badness has ensued."));
assertEquals("value", faultTolerantRedisClient.withClient(connection -> connection.sync().get("key")));
assertThrows(RedisException.class,
() -> faultTolerantRedisClient.withClient(connection -> connection.sync().get("OH NO")));
assertThrows(CallNotPermittedException.class,
() -> faultTolerantRedisClient.withClient(connection -> connection.sync().get("OH NO")));
}
}

View File

@@ -66,15 +66,17 @@ public class FaultTolerantRedisClusterTest {
public void testBreaker() {
when(clusterCommands.get(anyString()))
.thenReturn("value")
.thenThrow(new RedisException("Badness has ensued."));
.thenThrow(new RuntimeException("Badness has ensued."));
assertEquals("value", faultTolerantCluster.withCluster(connection -> connection.sync().get("key")));
assertThrows(RedisException.class,
() -> faultTolerantCluster.withCluster(connection -> connection.sync().get("OH NO")));
assertThrows(CallNotPermittedException.class,
final RedisException redisException = assertThrows(RedisException.class,
() -> faultTolerantCluster.withCluster(connection -> connection.sync().get("OH NO")));
assertTrue(redisException.getCause() instanceof CallNotPermittedException);
}
@Test