Skip to content

Commit

Permalink
feat: improve store-redis exceptions
Browse files Browse the repository at this point in the history
Utilize Path Exceptions in redis store
  • Loading branch information
mattnichols committed Oct 14, 2022
1 parent 5cb53d6 commit 829b29c
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 624 deletions.
2 changes: 1 addition & 1 deletion store-redis/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
coppuccino {
coverage {
minimumCoverage = 0.69
minimumCoverage = 0.78
}
}

Expand Down
99 changes: 68 additions & 31 deletions store-redis/src/main/java/com/mx/redis/RedisStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.time.Duration;
import java.util.Set;
import java.util.function.Function;

import lombok.Getter;

Expand All @@ -10,6 +11,7 @@

import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisException;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.protocol.ProtocolVersion;
Expand Down Expand Up @@ -42,83 +44,119 @@ public RedisStore(ObjectMap configurations) {

@Override
public final void delete(String key) {
connection().sync().del(key);
safeCall("delete", (conn) -> {
conn.sync().del(key);
return Void.TYPE;
});
}

@Override
public final void deleteSet(String key, String value) {
connection().sync().srem(key, value);
safeCall("deleteSet", (conn) -> {
conn.sync().srem(key, value);
return Void.TYPE;
});
}

@Override
public final String get(String key) {
return connection().sync().get(key);
return safeCall("get", (conn) -> {
return conn.sync().get(key);
});
}

@Override
public final Set<String> getSet(String key) {
return connection().sync().smembers(key);
return safeCall("getSet", (conn) -> {
return conn.sync().smembers(key);
});
}

@Override
public final boolean inSet(String key, String value) {
return connection().sync().sismember(key, value);
return safeCall("inSet", (conn) -> {
return conn.sync().sismember(key, value);
});
}

@Override
public final void put(String key, String value, long expirySeconds) {
connection().sync().set(key, value);
connection().sync().expire(key, expirySeconds);
safeCall("put", (conn) -> {
conn.sync().set(key, value);
conn.sync().expire(key, expirySeconds);
return Void.TYPE;
});
}

@Override
public final void put(String key, String value) {
throw new UnsupportedOperationException(PUT_UNSUPPORTED_OPERATION);
throw new RedisStoreUnsupportedException(PUT_UNSUPPORTED_OPERATION);
}

@Override
public final void putSet(String key, String value, long expirySeconds) {
connection().sync().sadd(key, value);
connection().sync().expire(key, expirySeconds);
safeCall("putSet", (conn) -> {
conn.sync().sadd(key, value);
conn.sync().expire(key, expirySeconds);
return Void.TYPE;
});
}

@Override
public final void putSet(String key, String value) {
throw new UnsupportedOperationException(PUT_UNSUPPORTED_OPERATION);
throw new RedisStoreUnsupportedException(PUT_UNSUPPORTED_OPERATION);
}

@Override
public final boolean putIfNotExist(String key, String value, long expirySeconds) {
boolean result = connection().sync().setnx(key, value);
if (result) {
connection().sync().expire(key, expirySeconds);
}

return result;
return safeCall("putIfNotExist", (conn) -> {
boolean result = conn.sync().setnx(key, value);
if (result) {
conn.sync().expire(key, expirySeconds);
}

return result;
});
}

@Override
public final boolean putIfNotExist(String key, String value) {
throw new UnsupportedOperationException(PUT_UNSUPPORTED_OPERATION);
throw new RedisStoreUnsupportedException(PUT_UNSUPPORTED_OPERATION);
}

// Private

private synchronized StatefulRedisConnection<String, String> buildConnection() {
ClientResources resources = ClientResources.builder()
.ioThreadPoolSize(configurations.getAsInteger("ioThreadPoolSize", DEFAULT_THREAD_POOL_SIZE))
.computationThreadPoolSize(configurations.getAsInteger("computationThreadPoolSize", DEFAULT_COMPUTATION_THREAD_POOL_SIZE))
.build();
final synchronized StatefulRedisConnection<String, String> buildConnection() {
try {
ClientResources resources = ClientResources.builder()
.ioThreadPoolSize(configurations.getAsInteger("ioThreadPoolSize", DEFAULT_THREAD_POOL_SIZE))
.computationThreadPoolSize(configurations.getAsInteger("computationThreadPoolSize", DEFAULT_COMPUTATION_THREAD_POOL_SIZE))
.build();

RedisClient redisClient = RedisClient.create(resources, new RedisURI(configurations.getAsString("host", DEFAULT_HOST), configurations.getAsInteger("port", DEFAULT_PORT), Duration.ofSeconds(configurations.getAsInteger("connectionTimeoutSeconds", DEFAULT_CONNECTION_TIMEOUT_IN_SECONDS))));
RedisClient redisClient = RedisClient.create(resources, new RedisURI(configurations.getAsString("host", DEFAULT_HOST), configurations.getAsInteger("port", DEFAULT_PORT), Duration.ofSeconds(configurations.getAsInteger("connectionTimeoutSeconds", DEFAULT_CONNECTION_TIMEOUT_IN_SECONDS))));

// RESP3 executes a HELLO command to discover the protocol before executing any commands made by the client.
// This can cause issues if we are communicating over a proxy and the proxy doesn't speak RESP3. For now, it is safer
// to default to RESP2 until RESP3 becomes more normalized.
ClientOptions options = ClientOptions.builder().protocolVersion(ProtocolVersion.RESP2).build();
redisClient.setOptions(options);
// RESP3 executes a HELLO command to discover the protocol before executing any commands made by the client.
// This can cause issues if we are communicating over a proxy and the proxy doesn't speak RESP3. For now, it is safer
// to default to RESP2 until RESP3 becomes more normalized.
ClientOptions options = ClientOptions.builder().protocolVersion(ProtocolVersion.RESP2).build();
redisClient.setOptions(options);

return redisClient.connect();
return redisClient.connect();
} catch (RedisException e) {
throw new RedisStoreConnectionException("An error occurred connecting to redis", e);
}
}

private <T> T safeCall(String operation, Function<StatefulRedisConnection<String, String>, T> runnable) {
try {
return runnable.apply(connection());
} catch (RedisStoreConnectionException e) {
throw e;
} catch (RedisException e) {
throw new RedisStoreOperationException("Redis error occurred on " + operation, e);
} catch (RuntimeException e) {
throw new RedisStoreOperationException("Unknown exception thrown by redis on " + operation, e);
}
}

private StatefulRedisConnection<String, String> connection() {
Expand All @@ -128,5 +166,4 @@ private StatefulRedisConnection<String, String> connection() {

return connection;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mx.redis;

import com.mx.common.facility.FacilityException;

public class RedisStoreConnectionException extends FacilityException {
public RedisStoreConnectionException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mx.redis;

import com.mx.common.facility.FacilityException;

public class RedisStoreOperationException extends FacilityException {
public RedisStoreOperationException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mx.redis;

import com.mx.common.facility.FacilityException;

public class RedisStoreUnsupportedException extends FacilityException {
public RedisStoreUnsupportedException(String message) {
super(message);
}
}
Loading

0 comments on commit 829b29c

Please sign in to comment.