mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 01:01:15 +01:00
Break out into a multi-module project
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package org.whispersystems.textsecuregcm.redis;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.exceptions.JedisDataException;
|
||||
|
||||
public class LuaScript {
|
||||
|
||||
private final ReplicatedJedisPool jedisPool;
|
||||
private final String script;
|
||||
private final byte[] sha;
|
||||
|
||||
public static LuaScript fromResource(ReplicatedJedisPool jedisPool, String resource) throws IOException {
|
||||
InputStream inputStream = LuaScript.class.getClassLoader().getResourceAsStream(resource);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int read;
|
||||
|
||||
while ((read = inputStream.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
baos.close();
|
||||
|
||||
return new LuaScript(jedisPool, new String(baos.toByteArray()));
|
||||
}
|
||||
|
||||
private LuaScript(ReplicatedJedisPool jedisPool, String script) {
|
||||
this.jedisPool = jedisPool;
|
||||
this.script = script;
|
||||
this.sha = storeScript(jedisPool, script).getBytes();
|
||||
}
|
||||
|
||||
public Object execute(List<byte[]> keys, List<byte[]> args) {
|
||||
try (Jedis jedis = jedisPool.getWriteResource()) {
|
||||
try {
|
||||
return jedis.evalsha(sha, keys, args);
|
||||
} catch (JedisDataException e) {
|
||||
storeScript(jedisPool, script);
|
||||
return jedis.evalsha(sha, keys, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String storeScript(ReplicatedJedisPool jedisPool, String script) {
|
||||
try (Jedis jedis = jedisPool.getWriteResource()) {
|
||||
return jedis.scriptLoad(script);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.whispersystems.textsecuregcm.redis;
|
||||
|
||||
public class RedisException extends Exception {
|
||||
|
||||
public RedisException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.whispersystems.textsecuregcm.redis;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||
|
||||
public class RedisOperation {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RedisOperation.class);
|
||||
|
||||
public static void unchecked(Operation operation) {
|
||||
try {
|
||||
operation.run();
|
||||
} catch (RedisException e) {
|
||||
logger.warn("Jedis failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean unchecked(BooleanOperation operation) {
|
||||
try {
|
||||
return operation.run();
|
||||
} catch (RedisException e) {
|
||||
logger.warn("Jedis failure", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Operation {
|
||||
public void run() throws RedisException;
|
||||
}
|
||||
|
||||
public interface BooleanOperation {
|
||||
public boolean run() throws RedisException;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.whispersystems.textsecuregcm.redis;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.SharedMetricRegistries;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.util.CircuitBreakerUtil;
|
||||
import org.whispersystems.textsecuregcm.util.Constants;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
|
||||
public class ReplicatedJedisPool {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ReplicatedJedisPool.class);
|
||||
private final AtomicInteger replicaIndex = new AtomicInteger(0);
|
||||
|
||||
private final Supplier<Jedis> master;
|
||||
private final ArrayList<Supplier<Jedis>> replicas;
|
||||
|
||||
public ReplicatedJedisPool(String name,
|
||||
JedisPool master,
|
||||
List<JedisPool> replicas,
|
||||
CircuitBreakerConfiguration circuitBreakerConfiguration)
|
||||
{
|
||||
if (replicas.size() < 1) throw new IllegalArgumentException("There must be at least one replica");
|
||||
|
||||
MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||
CircuitBreakerConfig config = circuitBreakerConfiguration.toCircuitBreakerConfig();
|
||||
CircuitBreaker masterBreaker = CircuitBreaker.of(String.format("%s-master", name), config);
|
||||
|
||||
CircuitBreakerUtil.registerMetrics(metricRegistry, masterBreaker, ReplicatedJedisPool.class);
|
||||
|
||||
this.master = CircuitBreaker.decorateSupplier(masterBreaker, master::getResource);
|
||||
this.replicas = new ArrayList<>(replicas.size());
|
||||
|
||||
for (int i=0;i<replicas.size();i++) {
|
||||
JedisPool replica = replicas.get(i);
|
||||
CircuitBreaker slaveBreaker = CircuitBreaker.of(String.format("%s-slave-%d", name, i), config);
|
||||
|
||||
CircuitBreakerUtil.registerMetrics(metricRegistry, slaveBreaker, ReplicatedJedisPool.class);
|
||||
this.replicas.add(CircuitBreaker.decorateSupplier(slaveBreaker, replica::getResource));
|
||||
}
|
||||
}
|
||||
|
||||
public Jedis getWriteResource() {
|
||||
return master.get();
|
||||
}
|
||||
|
||||
public Jedis getReadResource() {
|
||||
int failureCount = 0;
|
||||
|
||||
while (failureCount < replicas.size()) {
|
||||
try {
|
||||
return replicas.get(replicaIndex.getAndIncrement() % replicas.size()).get();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("Failure obtaining read replica pool", e);
|
||||
}
|
||||
|
||||
failureCount++;
|
||||
}
|
||||
|
||||
throw new JedisException("All read replica pools failed!");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user