Break out into a multi-module project

This commit is contained in:
Moxie Marlinspike
2019-04-20 21:56:20 -07:00
parent b41dde777e
commit d0d375aeb7
318 changed files with 255 additions and 215 deletions

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,8 @@
package org.whispersystems.textsecuregcm.redis;
public class RedisException extends Exception {
public RedisException(Exception e) {
super(e);
}
}

View File

@@ -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;
}
}

View File

@@ -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!");
}
}