mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-22 04:18:04 +01:00
Use reactive streams for WebSocket message queue
Initially, uses `ExperimentEnrollmentManager` to do a safe rollout.
This commit is contained in:
@@ -13,18 +13,29 @@ import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.lettuce.core.FlushMode;
|
||||
import io.lettuce.core.RedisFuture;
|
||||
import io.lettuce.core.RedisNoScriptException;
|
||||
import io.lettuce.core.ScriptOutputType;
|
||||
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;
|
||||
import io.lettuce.core.cluster.api.reactive.RedisAdvancedClusterReactiveCommands;
|
||||
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||
import io.lettuce.core.protocol.AsyncCommand;
|
||||
import io.lettuce.core.protocol.RedisCommand;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class ClusterLuaScriptTest {
|
||||
class ClusterLuaScriptTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||
@@ -32,7 +43,7 @@ public class ClusterLuaScriptTest {
|
||||
@Test
|
||||
void testExecute() {
|
||||
final RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
||||
final FaultTolerantRedisCluster mockCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
||||
final FaultTolerantRedisCluster mockCluster = RedisClusterHelper.builder().stringCommands(commands).build();
|
||||
|
||||
final String script = "return redis.call(\"SET\", KEYS[1], ARGV[1])";
|
||||
final ScriptOutputType scriptOutputType = ScriptOutputType.VALUE;
|
||||
@@ -51,7 +62,7 @@ public class ClusterLuaScriptTest {
|
||||
@Test
|
||||
void testExecuteScriptNotLoaded() {
|
||||
final RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
||||
final FaultTolerantRedisCluster mockCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
||||
final FaultTolerantRedisCluster mockCluster = RedisClusterHelper.builder().stringCommands(commands).build();
|
||||
|
||||
final String script = "return redis.call(\"SET\", KEYS[1], ARGV[1])";
|
||||
final ScriptOutputType scriptOutputType = ScriptOutputType.VALUE;
|
||||
@@ -71,8 +82,10 @@ public class ClusterLuaScriptTest {
|
||||
void testExecuteBinaryScriptNotLoaded() {
|
||||
final RedisAdvancedClusterCommands<String, String> stringCommands = mock(RedisAdvancedClusterCommands.class);
|
||||
final RedisAdvancedClusterCommands<byte[], byte[]> binaryCommands = mock(RedisAdvancedClusterCommands.class);
|
||||
final FaultTolerantRedisCluster mockCluster =
|
||||
RedisClusterHelper.buildMockRedisCluster(stringCommands, binaryCommands);
|
||||
final FaultTolerantRedisCluster mockCluster = RedisClusterHelper.builder()
|
||||
.stringCommands(stringCommands)
|
||||
.binaryCommands(binaryCommands)
|
||||
.build();
|
||||
|
||||
final String script = "return redis.call(\"SET\", KEYS[1], ARGV[1])";
|
||||
final ScriptOutputType scriptOutputType = ScriptOutputType.VALUE;
|
||||
@@ -85,17 +98,85 @@ public class ClusterLuaScriptTest {
|
||||
luaScript.executeBinary(keys, values);
|
||||
|
||||
verify(binaryCommands).eval(script, scriptOutputType, keys.toArray(new byte[0][]), values.toArray(new byte[0][]));
|
||||
verify(binaryCommands).evalsha(luaScript.getSha(), scriptOutputType, keys.toArray(new byte[0][]), values.toArray(new byte[0][]));
|
||||
verify(binaryCommands).evalsha(luaScript.getSha(), scriptOutputType, keys.toArray(new byte[0][]),
|
||||
values.toArray(new byte[0][]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteRealCluster() {
|
||||
void testExecuteBinaryAsyncScriptNotLoaded() throws Exception {
|
||||
final RedisAdvancedClusterAsyncCommands<byte[], byte[]> binaryAsyncCommands =
|
||||
mock(RedisAdvancedClusterAsyncCommands.class);
|
||||
final FaultTolerantRedisCluster mockCluster =
|
||||
RedisClusterHelper.builder().binaryAsyncCommands(binaryAsyncCommands).build();
|
||||
|
||||
final String script = "return redis.call(\"SET\", KEYS[1], ARGV[1])";
|
||||
final ScriptOutputType scriptOutputType = ScriptOutputType.VALUE;
|
||||
final List<byte[]> keys = List.of("key".getBytes(StandardCharsets.UTF_8));
|
||||
final List<byte[]> values = List.of("value".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
final AsyncCommand<?, ?, ?> evalShaFailure = new AsyncCommand<>(mock(RedisCommand.class));
|
||||
evalShaFailure.completeExceptionally(new RedisNoScriptException("OH NO"));
|
||||
|
||||
final AsyncCommand<?, ?, ?> evalSuccess = new AsyncCommand<>(mock(RedisCommand.class));
|
||||
evalSuccess.complete();
|
||||
|
||||
when(binaryAsyncCommands.evalsha(any(), any(), any(), any())).thenReturn((RedisFuture<Object>) evalShaFailure);
|
||||
when(binaryAsyncCommands.eval(anyString(), any(), any(), any())).thenReturn((RedisFuture<Object>) evalSuccess);
|
||||
|
||||
final ClusterLuaScript luaScript = new ClusterLuaScript(mockCluster, script, scriptOutputType);
|
||||
luaScript.executeBinaryAsync(keys, values).get(5, TimeUnit.SECONDS);
|
||||
|
||||
verify(binaryAsyncCommands).eval(script, scriptOutputType, keys.toArray(new byte[0][]),
|
||||
values.toArray(new byte[0][]));
|
||||
verify(binaryAsyncCommands).evalsha(luaScript.getSha(), scriptOutputType, keys.toArray(new byte[0][]),
|
||||
values.toArray(new byte[0][]));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExecuteBinaryReactiveScriptNotLoaded() {
|
||||
final RedisAdvancedClusterReactiveCommands<byte[], byte[]> binaryReactiveCommands =
|
||||
mock(RedisAdvancedClusterReactiveCommands.class);
|
||||
final FaultTolerantRedisCluster mockCluster = RedisClusterHelper.builder()
|
||||
.binaryReactiveCommands(binaryReactiveCommands).build();
|
||||
|
||||
final String script = "return redis.call(\"SET\", KEYS[1], ARGV[1])";
|
||||
final ScriptOutputType scriptOutputType = ScriptOutputType.VALUE;
|
||||
final List<byte[]> keys = List.of("key".getBytes(StandardCharsets.UTF_8));
|
||||
final List<byte[]> values = List.of("value".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
when(binaryReactiveCommands.evalsha(any(), any(), any(), any()))
|
||||
.thenReturn(Flux.error(new RedisNoScriptException("OH NO")));
|
||||
when(binaryReactiveCommands.eval(anyString(), any(), any(), any())).thenReturn(Flux.just("ok"));
|
||||
|
||||
final ClusterLuaScript luaScript = new ClusterLuaScript(mockCluster, script, scriptOutputType);
|
||||
luaScript.executeBinaryReactive(keys, values).blockLast(Duration.ofSeconds(5));
|
||||
|
||||
verify(binaryReactiveCommands).eval(script, scriptOutputType, keys.toArray(new byte[0][]),
|
||||
values.toArray(new byte[0][]));
|
||||
verify(binaryReactiveCommands).evalsha(luaScript.getSha(), scriptOutputType, keys.toArray(new byte[0][]),
|
||||
values.toArray(new byte[0][]));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(ExecuteMode.class)
|
||||
void testExecuteRealCluster(final ExecuteMode mode) throws Exception {
|
||||
REDIS_CLUSTER_EXTENSION.getRedisCluster().withCluster(c -> c.sync().scriptFlush(FlushMode.SYNC));
|
||||
REDIS_CLUSTER_EXTENSION.getRedisCluster().withCluster(c -> c.sync().configResetstat());
|
||||
|
||||
final ClusterLuaScript script = new ClusterLuaScript(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
"return 2;",
|
||||
ScriptOutputType.INTEGER);
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
assertEquals(2L, script.execute(Collections.emptyList(), Collections.emptyList()));
|
||||
final long actual = switch (mode) {
|
||||
case SYNC -> (long) script.execute(Collections.emptyList(), Collections.emptyList());
|
||||
case ASYNC ->
|
||||
(long) script.executeAsync(Collections.emptyList(), Collections.emptyList()).get(5, TimeUnit.SECONDS);
|
||||
case REACTIVE -> (long) script.executeReactive(Collections.emptyList(), Collections.emptyList())
|
||||
.blockLast(Duration.ofSeconds(5));
|
||||
};
|
||||
|
||||
assertEquals(2L, actual);
|
||||
}
|
||||
|
||||
final int evalCount = REDIS_CLUSTER_EXTENSION.getRedisCluster().withCluster(connection -> {
|
||||
@@ -120,4 +201,11 @@ public class ClusterLuaScriptTest {
|
||||
|
||||
assertEquals(1, evalCount);
|
||||
}
|
||||
|
||||
private enum ExecuteMode {
|
||||
SYNC,
|
||||
ASYNC,
|
||||
REACTIVE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user