Skip to content

Commit

Permalink
Merge branch 'main' into csm/daikon
Browse files Browse the repository at this point in the history
  • Loading branch information
cmeiklejohn authored Sep 18, 2023
2 parents ece4c7a + d0005fa commit a6382db
Show file tree
Hide file tree
Showing 10 changed files with 401 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
2.2.2:
- (MichaelA-ops) Implement transformers wrapping DB exceptions.
2.2.1:
- (cmeiklejohn) Add default injection of ResponseTimeoutException.
2.2.0:
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ javadoc {

group = "cloud.filibuster"
archivesBaseName = "instrumentation"
version = "2.2.1"
version = "2.2.2"

java {
withJavadocJar()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import cloud.filibuster.instrumentation.storage.ContextStorage;
import cloud.filibuster.instrumentation.storage.ThreadLocalContextStorage;
import cloud.filibuster.junit.server.core.transformers.Accumulator;
import cloud.filibuster.junit.server.core.transformers.DBException;
import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
import com.datastax.oss.driver.api.core.servererrors.OverloadedException;
import com.datastax.oss.driver.api.core.servererrors.ReadFailureException;
Expand Down Expand Up @@ -169,6 +170,16 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
String futureInvokeExceptionOnMethod = null;
JSONObject futureExceptionMetadata = null;

if (transformerFault != null && filibusterClientInstrumentor.shouldAbort()) {
Object transformerFaultValue = getTransformerFaultValue(filibusterClientInstrumentor, transformerFault, method.getReturnType());

if (transformerFaultValue instanceof DBException) {
forcedException = getJsonExceptionFromTransformerFault((DBException) transformerFaultValue);
} else {
return transformerFaultValue;
}
}

if (forcedException != null && filibusterClientInstrumentor.shouldAbort()) {
boolean shouldInjectExceptionOnCurrentMethod = shouldInjectExceptionOnCurrentMethod(forcedException, fullMethodName);

Expand All @@ -192,10 +203,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
}
}

if (transformerFault != null && filibusterClientInstrumentor.shouldAbort()) {
return injectTransformerFault(filibusterClientInstrumentor, transformerFault, method.getReturnType());
}

// ******************************************************************************************
// Invoke.
// ******************************************************************************************
Expand Down Expand Up @@ -240,6 +247,14 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
return invocationResult;
}

private static JSONObject getJsonExceptionFromTransformerFault(DBException transformerFaultValue) {
JSONObject forcedException = new JSONObject();
forcedException.put("name", transformerFaultValue.getName());
forcedException.put("metadata", transformerFaultValue.getMetadata());
forcedException.put("isTransformerFault", true);
return forcedException;
}

private Object invokeOnInterceptedObject(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
try {
return method.invoke(targetObject, args);
Expand All @@ -253,12 +268,13 @@ private Object invokeOnInterceptedObject(Method method, Object[] args) throws In
}

@Nullable
private static Object injectTransformerFault(FilibusterClientInstrumentor filibusterClientInstrumentor, JSONObject transformerFault, Class<?> returnType) {
private static Object getTransformerFaultValue(FilibusterClientInstrumentor filibusterClientInstrumentor, JSONObject transformerFault, Class<?> returnType) {
try {
if (transformerFault.has("value") && transformerFault.has("accumulator")) {

// Extract the transformer fault value from the transformerFault JSONObject.
// Extract the transformer fault value from the transformerFault JSONObject and return it.
Object transformerFaultValue = transformerFault.get("value");
// Extract the transformer fault value from the transformerFault JSONObject.
String sTransformerValue = String.valueOf(transformerFaultValue);

logger.log(Level.INFO, logPrefix + "Injecting the transformed fault value: " + sTransformerValue);
Expand All @@ -267,8 +283,13 @@ private static Object injectTransformerFault(FilibusterClientInstrumentor filibu
Accumulator<?, ?> accumulator = new Gson().fromJson(transformerFault.get("accumulator").toString(), Accumulator.class);

// Notify Filibuster.
filibusterClientInstrumentor.afterInvocationWithTransformerFault(sTransformerValue,
returnType.toString(), accumulator);
if (transformerFaultValue instanceof DBException) {
filibusterClientInstrumentor.afterInvocationWithTransformerFault(sTransformerValue,
((DBException) transformerFaultValue).getName(), accumulator);
} else {
filibusterClientInstrumentor.afterInvocationWithTransformerFault(sTransformerValue,
returnType.toString(), accumulator);
}

// Return the transformer fault value.
return transformerFaultValue == JSONObject.NULL ? null : transformerFaultValue;
Expand Down Expand Up @@ -323,11 +344,12 @@ private static void generateAndThrowException(FilibusterClientInstrumentor filib
JSONObject forcedExceptionMetadata = forcedException.getJSONObject("metadata");
String sCause = forcedExceptionMetadata.getString("cause");
String sCode = forcedExceptionMetadata.getString("code");
boolean isTransformerFault = forcedException.has("isTransformerFault") && forcedException.getBoolean("isTransformerFault");

throwExceptionAndNotifyFilibuster(filibusterClientInstrumentor, sExceptionName, sCause, sCode);
throwExceptionAndNotifyFilibuster(filibusterClientInstrumentor, sExceptionName, sCause, sCode, isTransformerFault);
}

private static void throwExceptionAndNotifyFilibuster(FilibusterClientInstrumentor filibusterClientInstrumentor, String exceptionName, String cause, String code) throws Exception {
private static void throwExceptionAndNotifyFilibuster(FilibusterClientInstrumentor filibusterClientInstrumentor, String exceptionName, String cause, String code, boolean isTransformerFault) throws Exception {
Exception exceptionToThrow;
Random rand = new Random(0);

Expand Down Expand Up @@ -403,7 +425,9 @@ private static void throwExceptionAndNotifyFilibuster(FilibusterClientInstrument
}

// Notify Filibuster.
filibusterClientInstrumentor.afterInvocationWithException(exceptionToThrow);
if (!isTransformerFault) {
filibusterClientInstrumentor.afterInvocationWithException(exceptionToThrow);
}

// Throw callsite exception.
throw exceptionToThrow;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cloud.filibuster.junit.configuration.examples.db.redis;

import cloud.filibuster.junit.configuration.FilibusterAnalysisConfiguration;
import cloud.filibuster.junit.configuration.FilibusterAnalysisConfigurationFile;
import cloud.filibuster.junit.configuration.FilibusterCustomAnalysisConfigurationFile;
import cloud.filibuster.junit.server.core.transformers.RedisTimeoutExceptionTransformer;

public class RedisTransformerTimeoutExceptionsAnalysisConfigurationFile implements FilibusterAnalysisConfigurationFile {
private static final FilibusterCustomAnalysisConfigurationFile filibusterCustomAnalysisConfigurationFile;

static {
FilibusterCustomAnalysisConfigurationFile.Builder filibusterCustomAnalysisConfigurationFileBuilder = new FilibusterCustomAnalysisConfigurationFile.Builder();

FilibusterAnalysisConfiguration.Builder filibusterAnalysisConfigurationBuilderRedisExceptions = new FilibusterAnalysisConfiguration.Builder()
.name("java.lettuce.core.RedisCommandTimeoutException")
.pattern("io.lettuce.core.api.sync.RedisStringCommands/(get|set)\\b");

filibusterAnalysisConfigurationBuilderRedisExceptions.transformer(RedisTimeoutExceptionTransformer.class);

filibusterCustomAnalysisConfigurationFileBuilder.analysisConfiguration(filibusterAnalysisConfigurationBuilderRedisExceptions.build());

filibusterCustomAnalysisConfigurationFile = filibusterCustomAnalysisConfigurationFileBuilder.build();
}

@Override
public FilibusterCustomAnalysisConfigurationFile toFilibusterCustomAnalysisConfigurationFile() {
return filibusterCustomAnalysisConfigurationFile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ private void generateTransformerFaults(JSONObject payload, DistributedExecutionI
createAndScheduleAbstractTestExecution(filibusterConfiguration, distributedExecutionIndex, new JSONObject(handledTransformer.toMap()));
} catch (Throwable e) {
logger.warning("[FILIBUSTER-CORE]: generateTransformerFaults, an exception occurred in generateTransformerFaults: " + e);
throw new FilibusterFaultInjectionException("[FILIBUSTER-CORE]: generateTransformerFaults: ", e);
throw new FilibusterFaultInjectionException("[FILIBUSTER-CORE]: generateTransformerFaults: " + e.getMessage(), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cloud.filibuster.junit.server.core.transformers;

import com.google.errorprone.annotations.CanIgnoreReturnValue;

import java.util.Map;

public class DBException {

private final String name;
private final Map<String, String> metadata;

public DBException(String name, Map<String, String> metadata) {
this.name = name;
this.metadata = metadata;
}

public String getName() {
return name;
}

public Map<String, String> getMetadata() {
return metadata;
}

@Override
public String toString() {
return "DBException{" +
"name='" + name + '\'' +
", metadata=" + metadata +
'}';
}

public static class Builder {
private String name;
private Map<String, String> metadata;

@CanIgnoreReturnValue
public Builder name(String name) {
this.name = name;
return this;
}

@CanIgnoreReturnValue
public Builder metadata(Map<String, String> metadata) {
this.metadata = metadata;
return this;
}

@CanIgnoreReturnValue
public DBException build() {
return new DBException(name, metadata);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package cloud.filibuster.junit.server.core.transformers;

import cloud.filibuster.exceptions.transformer.TransformerNullResultException;
import cloud.filibuster.exceptions.transformer.TransformerRuntimeException;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public final class RedisTimeoutExceptionTransformer implements Transformer<Object, Integer> {
private boolean hasNext = true;
private Object result;
private Accumulator<Object, Integer> accumulator;
private static final ArrayList<DBException> dbExceptions = new ArrayList<>();

static {
DBException.Builder commandTimeoutBuilder = new DBException.Builder();
commandTimeoutBuilder.name("io.lettuce.core.RedisCommandTimeoutException");

Map<String, String> exceptionMap = new HashMap<>();
exceptionMap.put("cause", "Command timed out after 100 millisecond(s)");
exceptionMap.put("code", "");
commandTimeoutBuilder.metadata(exceptionMap);

DBException commandTimeoutException = commandTimeoutBuilder.build();
dbExceptions.add(commandTimeoutException);
}

@Override
@CanIgnoreReturnValue
public RedisTimeoutExceptionTransformer transform(Object payload, Accumulator<Object, Integer> accumulator) {

// Get exception corresponding to idx in context
this.result = dbExceptions.get(accumulator.getContext());
this.accumulator = accumulator;

// Increment context
if (accumulator.getContext() < dbExceptions.size() - 1) {
accumulator.setContext(accumulator.getContext() + 1);
} else {
this.hasNext = false;
}

return this;
}

@Override
public boolean hasNext() {
return hasNext;
}

@Override
public Type getPayloadType() {
return Object.class;
}

@Override
public Object getResult() {
if (this.result == null) {
throw new TransformerNullResultException("Result is null. getResult() was probably called before transform()!");
}
return this.result;
}

@Override
public Type getAccumulatorType() {
return TypeToken.getParameterized(
Accumulator.class,
Object.class,
Integer.class).getType();
}

@Override
public Accumulator<Object, Integer> getInitialAccumulator(Object referenceValue) {
if (dbExceptions.size() == 0) {
throw new TransformerRuntimeException("No DBExceptions were added to the DBExceptionTransformer!");
}

this.result = referenceValue;

Accumulator<Object, Integer> accumulator = new Accumulator<>();
accumulator.setReferenceValue(referenceValue);
accumulator.setContext(0);

return accumulator;
}

@Override
public Accumulator<Object, Integer> getNextAccumulator() {
if (this.accumulator == null) {
return getInitialAccumulator(getResult());
} else {
return accumulator;
}
}

}
4 changes: 4 additions & 0 deletions src/main/resources/fac/redisTimeoutExceptionTransformer.fac
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"java.lettuce.core.RedisCommandTimeoutException": {
"transformers": [{"transformerClassName": "cloud.filibuster.junit.server.core.transformers.RedisTimeoutExceptionTransformer"}],
"pattern": "io.lettuce.core.api.sync.RedisStringCommands/(get|set)\\b"
}}
Loading

0 comments on commit a6382db

Please sign in to comment.