Restore Redis retries for select operations

This commit is contained in:
Jon Chambers
2025-08-27 11:52:16 -04:00
committed by GitHub
parent f616612104
commit 8825396fc1
46 changed files with 449 additions and 262 deletions

View File

@@ -14,6 +14,7 @@ import static org.mockito.Mockito.when;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -39,8 +40,10 @@ class DisconnectionRequestManagerTest {
void setUp() {
grpcClientConnectionManager = mock(GrpcClientConnectionManager.class);
disconnectionRequestManager =
new DisconnectionRequestManager(REDIS_EXTENSION.getRedisClient(), grpcClientConnectionManager, Runnable::run);
disconnectionRequestManager = new DisconnectionRequestManager(REDIS_EXTENSION.getRedisClient(),
grpcClientConnectionManager,
Runnable::run,
mock(ScheduledExecutorService.class));
disconnectionRequestManager.start();
}

View File

@@ -19,10 +19,13 @@ import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -36,6 +39,7 @@ import org.whispersystems.textsecuregcm.util.TestClock;
class DynamicRateLimiterTest {
private ClusterLuaScript validateRateLimitScript;
private ScheduledExecutorService retryExecutor;
private static final TestClock CLOCK = TestClock.pinned(Instant.now());
@@ -44,10 +48,17 @@ class DynamicRateLimiterTest {
@BeforeEach
void setUp() throws IOException {
retryExecutor = Executors.newSingleThreadScheduledExecutor();
validateRateLimitScript = ClusterLuaScript.fromResource(
REDIS_CLUSTER_EXTENSION.getRedisCluster(), "lua/validate_rate_limit.lua", ScriptOutputType.INTEGER);
}
@AfterEach
void tearDown() {
retryExecutor.shutdown();
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void validate(final boolean failOpen) {
@@ -56,6 +67,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, Duration.ofHours(1), failOpen),
validateRateLimitScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -72,6 +84,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, Duration.ofHours(1), failOpen),
validateRateLimitScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -94,6 +107,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, Duration.ofHours(1), failOpen),
failingScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -116,6 +130,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, Duration.ofHours(1), failOpen),
failingScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -138,6 +153,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, refillRate.get(), false),
validateRateLimitScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -161,6 +177,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, refillRate.get(), false),
validateRateLimitScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -187,6 +204,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(bucketSize.get(), Duration.ofMinutes(1), false),
validateRateLimitScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -211,6 +229,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(bucketSize.get(), Duration.ofMinutes(1), false),
validateRateLimitScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -238,6 +257,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, Duration.ofMinutes(1), failOpen.get()),
failingScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);
@@ -260,6 +280,7 @@ class DynamicRateLimiterTest {
() -> new RateLimiterConfig(1, Duration.ofMinutes(1), failOpen.get()),
failingScript,
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
retryExecutor,
CLOCK);
final String key = RandomStringUtils.insecure().nextAlphanumeric(16);

View File

@@ -21,6 +21,7 @@ import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
@@ -65,6 +66,7 @@ public class RateLimitersLuaScriptTest {
dynamicConfig,
RateLimiters.defaultScript(redisCluster),
redisCluster,
mock(ScheduledExecutorService.class),
Clock.systemUTC());
final RateLimiter rateLimiter = limiters.forDescriptor(descriptor);
@@ -84,6 +86,7 @@ public class RateLimitersLuaScriptTest {
dynamicConfig,
RateLimiters.defaultScript(redisCluster),
redisCluster,
mock(ScheduledExecutorService.class),
Clock.systemUTC());
final RateLimiter rateLimiter = limiters.forDescriptor(descriptor);
@@ -138,6 +141,7 @@ public class RateLimitersLuaScriptTest {
dynamicConfig,
RateLimiters.defaultScript(redisCluster),
redisCluster,
mock(ScheduledExecutorService.class),
Clock.systemUTC());
when(redisCluster.withCluster(any())).thenThrow(new RedisException("fail"));
final RateLimiter rateLimiter = limiters.forDescriptor(descriptor);

View File

@@ -13,6 +13,7 @@ import static org.mockito.Mockito.when;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.redis.ClusterLuaScript;
@@ -36,7 +37,7 @@ public class RateLimitersTest {
private final MutableClock clock = MockUtils.mutableClock(0);
@Test
public void testValidateDuplicates() throws Exception {
public void testValidateDuplicates() {
final TestDescriptor td1 = new TestDescriptor("id1");
final TestDescriptor td2 = new TestDescriptor("id2");
final TestDescriptor td3 = new TestDescriptor("id3");
@@ -47,6 +48,7 @@ public class RateLimitersTest {
dynamicConfig,
validateScript,
redisCluster,
mock(ScheduledExecutorService.class),
clock) {});
new BaseRateLimiters<>(
@@ -54,12 +56,13 @@ public class RateLimitersTest {
dynamicConfig,
validateScript,
redisCluster,
mock(ScheduledExecutorService.class),
clock) {};
}
@Test
void testUnchangingConfiguration() {
final RateLimiters rateLimiters = new RateLimiters(dynamicConfig, validateScript, redisCluster, clock);
final RateLimiters rateLimiters = new RateLimiters(dynamicConfig, validateScript, redisCluster, mock(ScheduledExecutorService.class), clock);
final RateLimiter limiter = rateLimiters.getRateLimitResetLimiter();
final RateLimiterConfig expected = RateLimiters.For.RATE_LIMIT_RESET.defaultConfig();
assertEquals(expected, limiter.config());
@@ -78,7 +81,7 @@ public class RateLimitersTest {
when(configuration.getLimits()).thenReturn(limitsConfigMap);
final RateLimiters rateLimiters = new RateLimiters(dynamicConfig, validateScript, redisCluster, clock);
final RateLimiters rateLimiters = new RateLimiters(dynamicConfig, validateScript, redisCluster, mock(ScheduledExecutorService.class), clock);
final RateLimiter limiter = rateLimiters.getRateLimitResetLimiter();
limitsConfigMap.put(RateLimiters.For.RATE_LIMIT_RESET.id(), initialRateLimiterConfig);
@@ -104,7 +107,7 @@ public class RateLimitersTest {
when(configuration.getLimits()).thenReturn(mapForDynamic);
final RateLimiters rateLimiters = new RateLimiters(dynamicConfig, validateScript, redisCluster, clock);
final RateLimiters rateLimiters = new RateLimiters(dynamicConfig, validateScript, redisCluster, mock(ScheduledExecutorService.class), clock);
final RateLimiter limiter = rateLimiters.forDescriptor(descriptor);
// test only default is present

View File

@@ -23,6 +23,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -90,7 +91,7 @@ class PushNotificationSchedulerTest {
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(true, Optional.empty(), false, Optional.empty())));
pushNotificationScheduler = new PushNotificationScheduler(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
apnSender, fcmSender, accountsManager, clock, 1, 1);
apnSender, fcmSender, accountsManager, clock, 1, 1, mock(ScheduledExecutorService.class));
}
@ParameterizedTest
@@ -267,7 +268,7 @@ class PushNotificationSchedulerTest {
final AccountsManager accountsManager = mock(AccountsManager.class);
pushNotificationScheduler = new PushNotificationScheduler(redisCluster, apnSender, fcmSender,
accountsManager, dedicatedThreadCount, 1);
accountsManager, dedicatedThreadCount, 1, mock(ScheduledExecutorService.class));
pushNotificationScheduler.start();
pushNotificationScheduler.stop();

View File

@@ -173,6 +173,7 @@ public class AccountCreationDeletionIntegrationTest {
clientPublicKeysManager,
executor,
executor,
executor,
CLOCK,
"link-device-secret".getBytes(StandardCharsets.UTF_8),
dynamicConfigurationManager);

View File

@@ -161,6 +161,7 @@ class AccountsManagerChangeNumberIntegrationTest {
clientPublicKeysManager,
executor,
executor,
executor,
mock(Clock.class),
"link-device-secret".getBytes(StandardCharsets.UTF_8),
dynamicConfigurationManager);

View File

@@ -140,6 +140,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
mock(ClientPublicKeysManager.class),
mock(Executor.class),
mock(ScheduledExecutorService.class),
mock(ScheduledExecutorService.class),
mock(Clock.class),
"link-device-secret".getBytes(StandardCharsets.UTF_8),
dynamicConfigurationManager

View File

@@ -78,6 +78,7 @@ public class AccountsManagerDeviceTransferIntegrationTest {
mock(ClientPublicKeysManager.class),
mock(ExecutorService.class),
mock(ScheduledExecutorService.class),
mock(ScheduledExecutorService.class),
Clock.systemUTC(),
"link-device-secret".getBytes(StandardCharsets.UTF_8),
mock(DynamicConfigurationManager.class));

View File

@@ -260,6 +260,7 @@ class AccountsManagerTest {
clientPublicKeysManager,
mock(Executor.class),
mock(ScheduledExecutorService.class),
mock(ScheduledExecutorService.class),
CLOCK,
LINK_DEVICE_SECRET,
dynamicConfigurationManager);

View File

@@ -165,6 +165,7 @@ class AccountsManagerUsernameIntegrationTest {
mock(ClientPublicKeysManager.class),
Executors.newSingleThreadExecutor(),
Executors.newSingleThreadScheduledExecutor(),
Executors.newSingleThreadScheduledExecutor(),
mock(Clock.class),
"link-device-secret".getBytes(StandardCharsets.UTF_8),
dynamicConfigurationManager);

View File

@@ -77,7 +77,7 @@ public class AddRemoveDeviceIntegrationTest {
static final S3LocalStackExtension S3_EXTENSION = new S3LocalStackExtension("testbucket");
private ExecutorService accountLockExecutor;
private ScheduledExecutorService messagePollExecutor;
private ScheduledExecutorService scheduledExecutorService;
private KeysManager keysManager;
private ClientPublicKeysManager clientPublicKeysManager;
@@ -123,7 +123,7 @@ public class AddRemoveDeviceIntegrationTest {
DynamoDbExtensionSchema.Tables.USED_LINK_DEVICE_TOKENS.tableName());
accountLockExecutor = Executors.newSingleThreadExecutor();
messagePollExecutor = mock(ScheduledExecutorService.class);
scheduledExecutorService = mock(ScheduledExecutorService.class);
final AccountLockManager accountLockManager = new AccountLockManager(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DynamoDbExtensionSchema.Tables.DELETED_ACCOUNTS_LOCK.tableName());
@@ -172,7 +172,8 @@ public class AddRemoveDeviceIntegrationTest {
mock(RegistrationRecoveryPasswordsManager.class),
clientPublicKeysManager,
accountLockExecutor,
messagePollExecutor,
scheduledExecutorService,
scheduledExecutorService,
clock,
"link-device-secret".getBytes(StandardCharsets.UTF_8),
dynamicConfigurationManager);
@@ -635,7 +636,7 @@ public class AddRemoveDeviceIntegrationTest {
.thenReturn(CompletableFuture.completedFuture(Optional.empty()));
clock.pin(Instant.ofEpochMilli(10_000));
// Run any scheduled job right away
when(messagePollExecutor.schedule(any(Runnable.class), anyLong(), any())).thenAnswer(x -> {
when(scheduledExecutorService.schedule(any(Runnable.class), anyLong(), any())).thenAnswer(x -> {
x.getArgument(0, Runnable.class).run();
return null;
});

View File

@@ -23,6 +23,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang3.RandomStringUtils;
@@ -84,7 +85,7 @@ class MessagePersisterIntegrationTest {
final AccountsManager accountsManager = mock(AccountsManager.class);
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
messageDeliveryScheduler, messageDeletionExecutorService, Clock.systemUTC(), experimentEnrollmentManager);
messageDeliveryScheduler, messageDeletionExecutorService, mock(ScheduledExecutorService.class), Clock.systemUTC(), experimentEnrollmentManager);
messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, mock(RedisMessageAvailabilityManager.class),
mock(ReportMessageManager.class), messageDeletionExecutorService, Clock.systemUTC());

View File

@@ -116,7 +116,7 @@ class MessagePersisterTest {
resubscribeRetryExecutorService = Executors.newSingleThreadScheduledExecutor();
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
messageDeliveryScheduler, sharedExecutorService, mock(ScheduledExecutorService.class), Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
messagePersister = new MessagePersister(messagesCache, messagesManager, accountsManager,
dynamicConfigurationManager, PERSIST_DELAY, 1);

View File

@@ -17,6 +17,7 @@ import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.entities.MessageProtos;
@@ -32,7 +33,8 @@ class MessagesCacheGetItemsScriptTest {
@Test
void testCacheGetItemsScript() throws Exception {
final MessagesCacheInsertScript insertScript = new MessagesCacheInsertScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
mock(ScheduledExecutorService.class));
final UUID destinationUuid = UUID.randomUUID();
final byte deviceId = 1;

View File

@@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -16,6 +17,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -32,7 +34,7 @@ class MessagesCacheInsertScriptTest {
@Test
void testCacheInsertScript() throws Exception {
final MessagesCacheInsertScript insertScript =
new MessagesCacheInsertScript(REDIS_CLUSTER_EXTENSION.getRedisCluster());
new MessagesCacheInsertScript(REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
final UUID destinationUuid = UUID.randomUUID();
final byte deviceId = 1;
@@ -41,7 +43,7 @@ class MessagesCacheInsertScriptTest {
.setServerGuid(UUID.randomUUID().toString())
.build();
insertScript.executeAsync(destinationUuid, deviceId, envelope1).join();
insertScript.executeAsync(destinationUuid, deviceId, envelope1).toCompletableFuture().join();
assertEquals(List.of(EnvelopeUtil.compress(envelope1)), getStoredMessages(destinationUuid, deviceId));
@@ -50,12 +52,12 @@ class MessagesCacheInsertScriptTest {
.setServerGuid(UUID.randomUUID().toString())
.build();
insertScript.executeAsync(destinationUuid, deviceId, envelope2).join();
insertScript.executeAsync(destinationUuid, deviceId, envelope2).toCompletableFuture().join();
assertEquals(List.of(EnvelopeUtil.compress(envelope1), EnvelopeUtil.compress(envelope2)),
getStoredMessages(destinationUuid, deviceId));
insertScript.executeAsync(destinationUuid, deviceId, envelope1).join();
insertScript.executeAsync(destinationUuid, deviceId, envelope1).toCompletableFuture().join();
assertEquals(List.of(EnvelopeUtil.compress(envelope1), EnvelopeUtil.compress(envelope2)),
getStoredMessages(destinationUuid, deviceId),
@@ -89,12 +91,13 @@ class MessagesCacheInsertScriptTest {
final byte deviceId = 1;
final MessagesCacheInsertScript insertScript =
new MessagesCacheInsertScript(REDIS_CLUSTER_EXTENSION.getRedisCluster());
new MessagesCacheInsertScript(REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
assertFalse(insertScript.executeAsync(destinationUuid, deviceId, MessageProtos.Envelope.newBuilder()
.setServerTimestamp(Instant.now().getEpochSecond())
.setServerGuid(UUID.randomUUID().toString())
.build())
.toCompletableFuture()
.join());
final FaultTolerantPubSubClusterConnection<byte[], byte[]> pubSubClusterConnection =
@@ -107,6 +110,7 @@ class MessagesCacheInsertScriptTest {
.setServerTimestamp(Instant.now().getEpochSecond())
.setServerGuid(UUID.randomUUID().toString())
.build())
.toCompletableFuture()
.join());
}
}

View File

@@ -9,17 +9,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import io.lettuce.core.RedisCommandExecutionException;
import io.lettuce.core.RedisException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import io.lettuce.core.RedisException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
@@ -40,11 +41,11 @@ class MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScriptTest {
void testInsert(final int count, final Map<ServiceIdentifier, List<Byte>> destinations) throws Exception {
final MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript insertMrmScript = new MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
final byte[] sharedMrmKey = MessagesCache.getSharedMrmKey(UUID.randomUUID());
insertMrmScript.executeAsync(sharedMrmKey,
MessagesCacheTest.generateRandomMrmMessage(destinations)).join();
MessagesCacheTest.generateRandomMrmMessage(destinations)).toCompletableFuture().join();
final int totalDevices = destinations.values().stream().mapToInt(List::size).sum();
final long hashFieldCount = REDIS_CLUSTER_EXTENSION.getRedisCluster()
@@ -83,16 +84,18 @@ class MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScriptTest {
@Test
void testInsertDuplicateKey() throws Exception {
final MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript insertMrmScript = new MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
final byte[] sharedMrmKey = MessagesCache.getSharedMrmKey(UUID.randomUUID());
insertMrmScript.executeAsync(sharedMrmKey,
MessagesCacheTest.generateRandomMrmMessage(new AciServiceIdentifier(UUID.randomUUID()), Device.PRIMARY_ID)).join();
MessagesCacheTest.generateRandomMrmMessage(new AciServiceIdentifier(UUID.randomUUID()), Device.PRIMARY_ID))
.toCompletableFuture()
.join();
final CompletionException completionException = assertThrows(CompletionException.class,
() -> insertMrmScript.executeAsync(sharedMrmKey,
MessagesCacheTest.generateRandomMrmMessage(new AciServiceIdentifier(UUID.randomUUID()),
Device.PRIMARY_ID)).join());
Device.PRIMARY_ID)).toCompletableFuture().join());
assertInstanceOf(RedisException.class, completionException.getCause());
assertTrue(completionException.getCause().getMessage()

View File

@@ -6,10 +6,12 @@
package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -25,7 +27,8 @@ class MessagesCacheRemoveByGuidScriptTest {
@Test
void testCacheRemoveByGuid() throws Exception {
final MessagesCacheInsertScript insertScript = new MessagesCacheInsertScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
mock(ScheduledExecutorService.class));
final UUID destinationUuid = UUID.randomUUID();
final byte deviceId = 1;
@@ -38,10 +41,12 @@ class MessagesCacheRemoveByGuidScriptTest {
insertScript.executeAsync(destinationUuid, deviceId, envelope1);
final MessagesCacheRemoveByGuidScript removeByGuidScript = new MessagesCacheRemoveByGuidScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
final List<byte[]> removedMessages = removeByGuidScript.execute(destinationUuid, deviceId,
List.of(serverGuid)).get(1, TimeUnit.SECONDS);
final List<byte[]> removedMessages =
removeByGuidScript.execute(destinationUuid, deviceId, List.of(serverGuid))
.toCompletableFuture()
.get(1, TimeUnit.SECONDS);
assertEquals(1, removedMessages.size());

View File

@@ -6,12 +6,14 @@
package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.entities.MessageProtos;
@@ -26,7 +28,8 @@ class MessagesCacheRemoveQueueScriptTest {
@Test
void testCacheRemoveQueueScript() throws Exception {
final MessagesCacheInsertScript insertScript = new MessagesCacheInsertScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(),
mock(ScheduledExecutorService.class));
final UUID destinationUuid = UUID.randomUUID();
final byte deviceId = 1;

View File

@@ -6,6 +6,7 @@
package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import io.lettuce.core.cluster.SlotHash;
import java.time.Duration;
@@ -15,6 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -38,10 +40,12 @@ class MessagesCacheRemoveRecipientViewFromMrmDataScriptTest {
void testUpdateSingleKey(final Map<ServiceIdentifier, List<Byte>> destinations) throws Exception {
final MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript insertMrmScript = new MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
final byte[] sharedMrmKey = MessagesCache.getSharedMrmKey(UUID.randomUUID());
insertMrmScript.executeAsync(sharedMrmKey, MessagesCacheTest.generateRandomMrmMessage(destinations)).join();
insertMrmScript.executeAsync(sharedMrmKey, MessagesCacheTest.generateRandomMrmMessage(destinations))
.toCompletableFuture()
.join();
final MessagesCacheRemoveRecipientViewFromMrmDataScript removeRecipientViewFromMrmDataScript = new MessagesCacheRemoveRecipientViewFromMrmDataScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
@@ -99,11 +103,11 @@ class MessagesCacheRemoveRecipientViewFromMrmDataScriptTest {
for (int i = 0; i < keyCount; i++) {
final MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript insertMrmScript = new MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript(
REDIS_CLUSTER_EXTENSION.getRedisCluster());
REDIS_CLUSTER_EXTENSION.getRedisCluster(), mock(ScheduledExecutorService.class));
final byte[] sharedMrmKey = MessagesCache.getSharedMrmKey(UUID.randomUUID());
insertMrmScript.executeAsync(sharedMrmKey,
MessagesCacheTest.generateRandomMrmMessage(serviceIdentifier, deviceId)).join();
MessagesCacheTest.generateRandomMrmMessage(serviceIdentifier, deviceId)).toCompletableFuture().join();
sharedMrmKeys.add(sharedMrmKey);
}

View File

@@ -107,7 +107,7 @@ class MessagesCacheTest {
resubscribeRetryExecutorService = Executors.newSingleThreadScheduledExecutor();
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
messageDeliveryScheduler, sharedExecutorService, mock(ScheduledExecutorService.class), Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
}
@AfterEach
@@ -194,17 +194,6 @@ class MessagesCacheTest {
assertEquals(messagesToPreserve, get(DESTINATION_UUID, DESTINATION_DEVICE_ID, messageCount));
}
@Test
void testHasMessagesAsync() {
assertFalse(messagesCache.hasMessagesAsync(DESTINATION_UUID, DESTINATION_DEVICE_ID).join());
final UUID messageGuid = UUID.randomUUID();
final MessageProtos.Envelope message = generateRandomMessage(messageGuid, true);
messagesCache.insert(messageGuid, DESTINATION_UUID, DESTINATION_DEVICE_ID, message).join();
assertTrue(messagesCache.hasMessagesAsync(DESTINATION_UUID, DESTINATION_DEVICE_ID).join());
}
@Test
void getOldestTimestamp() {
final int messageCount = 100;
@@ -300,7 +289,7 @@ class MessagesCacheTest {
}
final MessagesCache messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
messageDeliveryScheduler, sharedExecutorService, cacheClock, mock(ExperimentEnrollmentManager.class));
messageDeliveryScheduler, sharedExecutorService, mock(ScheduledExecutorService.class), cacheClock, mock(ExperimentEnrollmentManager.class));
final List<MessageProtos.Envelope> actualMessages = Flux.from(
messagesCache.get(DESTINATION_UUID, DESTINATION_DEVICE_ID))
@@ -624,7 +613,7 @@ class MessagesCacheTest {
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
messagesCache = new MessagesCache(mockCluster, messageDeliveryScheduler,
Executors.newSingleThreadExecutor(), Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
Executors.newSingleThreadExecutor(), mock(ScheduledExecutorService.class), Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
}
@AfterEach

View File

@@ -12,7 +12,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -186,31 +185,6 @@ class MessagesManagerTest {
any());
}
@ParameterizedTest
@CsvSource({
"false, false, false",
"false, true, true",
"true, false, true",
"true, true, true"
})
void mayHaveMessages(final boolean hasCachedMessages, final boolean hasPersistedMessages, final boolean expectMayHaveMessages) {
final UUID accountIdentifier = UUID.randomUUID();
final Device device = mock(Device.class);
when(device.getId()).thenReturn(Device.PRIMARY_ID);
when(messagesCache.hasMessagesAsync(accountIdentifier, Device.PRIMARY_ID))
.thenReturn(CompletableFuture.completedFuture(hasCachedMessages));
when(messagesDynamoDb.mayHaveMessages(accountIdentifier, device))
.thenReturn(CompletableFuture.completedFuture(hasPersistedMessages));
if (hasCachedMessages) {
verifyNoInteractions(messagesDynamoDb);
}
assertEquals(expectMayHaveMessages, messagesManager.mayHaveMessages(accountIdentifier, device).join());
}
@ParameterizedTest
@CsvSource({
",,",

View File

@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
@@ -67,7 +68,7 @@ public class ProfilesManagerTest {
profiles = mock(Profiles.class);
s3Client = mock(S3AsyncClient.class);
profilesManager = new ProfilesManager(profiles, cacheCluster, s3Client, BUCKET);
profilesManager = new ProfilesManager(profiles, cacheCluster, mock(ScheduledExecutorService.class), s3Client, BUCKET);
}
@Test

View File

@@ -25,6 +25,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterAll;
@@ -85,7 +86,7 @@ class RedisDynamoDbMessagePublisherTest {
mock(ExperimentEnrollmentManager.class));
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
messageDeliveryScheduler, sharedExecutorService, mock(ScheduledExecutorService.class), Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
redisMessageAvailabilityManager = mock(RedisMessageAvailabilityManager.class);

View File

@@ -31,6 +31,7 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang3.RandomStringUtils;
@@ -101,7 +102,7 @@ class WebSocketConnectionIntegrationTest {
dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
when(dynamicConfigurationManager.getConfiguration()).thenReturn(new DynamicConfiguration());
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
messageDeliveryScheduler, sharedExecutorService, mock(ScheduledExecutorService.class), Clock.systemUTC(), mock(ExperimentEnrollmentManager.class));
messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(7),
sharedExecutorService, mock(ExperimentEnrollmentManager.class));