Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype "before retry action" support #485

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Initial implementation of BeforeRetry prototype.
  • Loading branch information
hbelmiro committed Aug 26, 2021
commit 095d07edb81749208d6ca581f2d6faf08201b84d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.smallrye.faulttolerance.api;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface BeforeRetryAnnotation {
String beforeRetryMethod() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import io.smallrye.common.annotation.Blocking;
import io.smallrye.common.annotation.NonBlocking;
import io.smallrye.faulttolerance.api.BeforeRetryAnnotation;
import io.smallrye.faulttolerance.api.CircuitBreakerName;
import io.smallrye.faulttolerance.api.CustomBackoff;
import io.smallrye.faulttolerance.api.ExponentialBackoff;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class FaultToleranceMethod {
public Bulkhead bulkhead;
public CircuitBreaker circuitBreaker;
public Fallback fallback;
public BeforeRetryAnnotation beforeRetryAnnotation;
public Retry retry;
public Timeout timeout;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.smallrye.faulttolerance.core.before.retry;

import static io.smallrye.faulttolerance.core.before.retry.BeforeRetryLogger.LOG;

import io.smallrye.faulttolerance.core.FaultToleranceStrategy;
import io.smallrye.faulttolerance.core.InvocationContext;

public class BeforeRetry<V> implements FaultToleranceStrategy<V> {

private final BeforeRetryFunction<V> beforeRetry;

private final FaultToleranceStrategy<V> delegate;

public BeforeRetry(FaultToleranceStrategy<V> delegate, BeforeRetryFunction<V> beforeRetry) {
this.delegate = delegate;
this.beforeRetry = beforeRetry;
}

@Override
public V apply(InvocationContext<V> ctx) throws Exception {
LOG.trace("BeforeRetry started");
try {
return doApply(ctx);
} finally {
LOG.trace("BeforeRetry finished");
}
}

private V doApply(InvocationContext<V> ctx) throws Exception {
beforeRetry.call(ctx);
return delegate.apply(ctx);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.smallrye.faulttolerance.core.before.retry;

import io.smallrye.faulttolerance.core.InvocationContext;

@FunctionalInterface
public interface BeforeRetryFunction<T> {
void call(InvocationContext<T> invocationContext) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.smallrye.faulttolerance.core.before.retry;

import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.MessageLogger;

@MessageLogger(projectCode = "SRFTL", length = 5)
interface BeforeRetryLogger extends BasicLogger {
BeforeRetryLogger LOG = Logger.getMessageLogger(BeforeRetryLogger.class, BeforeRetryLogger.class.getPackage().getName());
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
import io.smallrye.faulttolerance.core.async.CompletionStageExecution;
import io.smallrye.faulttolerance.core.async.FutureExecution;
import io.smallrye.faulttolerance.core.async.RememberEventLoop;
import io.smallrye.faulttolerance.core.before.retry.BeforeRetry;
import io.smallrye.faulttolerance.core.before.retry.BeforeRetryFunction;
import io.smallrye.faulttolerance.core.bulkhead.CompletionStageThreadPoolBulkhead;
import io.smallrye.faulttolerance.core.bulkhead.FutureThreadPoolBulkhead;
import io.smallrye.faulttolerance.core.bulkhead.SemaphoreBulkhead;
Expand Down Expand Up @@ -332,6 +334,10 @@ private <T> FaultToleranceStrategy<T> prepareSyncStrategy(FaultToleranceOperatio
}

if (operation.hasRetry()) {
if (operation.hasBeforeRetry()) {
result = new BeforeRetry<>(result, prepareBeforeRetryFunction(point, operation));
}

long maxDurationMs = getTimeInMs(operation.getRetry().maxDuration(), operation.getRetry().durationUnit());

Supplier<BackOff> backoff = prepareRetryBackoff(operation);
Expand Down Expand Up @@ -362,6 +368,47 @@ private <T> FaultToleranceStrategy<T> prepareSyncStrategy(FaultToleranceOperatio
return result;
}

private static <V> BeforeRetryFunction<V> prepareBeforeRetryFunction(InterceptionPoint point,
FaultToleranceOperation operation) {
Method beforeRetryMethod;

String beforeRetryMethodName = operation.getBeforeRetry().beforeRetryMethod();

if (!"".equals(beforeRetryMethodName)) {
try {
Method method = point.method();
beforeRetryMethod = SecurityActions.getDeclaredMethod(point.beanClass(), method.getDeclaringClass(),
beforeRetryMethodName, method.getGenericParameterTypes());
if (beforeRetryMethod == null) {
throw new FaultToleranceException("Could not obtain BeforeRetry method " + beforeRetryMethodName);
}
SecurityActions.setAccessible(beforeRetryMethod);
} catch (PrivilegedActionException e) {
throw new FaultToleranceException("Could not obtain BeforeRetry method", e);
}
} else {
throw new FaultToleranceException("Could not obtain BeforeRetry method");
}

return invocationContext -> {
InvocationContext interceptionContext = invocationContext.get(InvocationContext.class);
try {
beforeRetryMethod.invoke(interceptionContext.getTarget(),
interceptionContext.getParameters());
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Exception) {
throw (Exception) cause;
}
throw new FaultToleranceException("Error during BeforeRetry method invocation", cause);
} catch (Exception e) {
throw e;
} catch (Throwable e) {
throw new FaultToleranceException("Error during BeforeRetry method invocation", e);
}
};
}

private <T> FaultToleranceStrategy<Future<T>> prepareFutureStrategy(FaultToleranceOperation operation,
InterceptionPoint point) {
FaultToleranceStrategy<Future<T>> result = invocation();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.smallrye.faulttolerance.config;

import io.smallrye.faulttolerance.api.BeforeRetryAnnotation;
import io.smallrye.faulttolerance.autoconfig.AutoConfig;
import io.smallrye.faulttolerance.autoconfig.Config;

@AutoConfig
public interface BeforeRetryConfig extends BeforeRetryAnnotation, Config {

@Override
default void validate() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import io.smallrye.common.annotation.Blocking;
import io.smallrye.common.annotation.NonBlocking;
import io.smallrye.faulttolerance.api.BeforeRetryAnnotation;
import io.smallrye.faulttolerance.api.CircuitBreakerName;
import io.smallrye.faulttolerance.api.CustomBackoff;
import io.smallrye.faulttolerance.api.ExponentialBackoff;
Expand All @@ -36,6 +37,7 @@ public static FaultToleranceMethod create(AnnotatedMethod<?> method) {
result.bulkhead = getAnnotation(Bulkhead.class, method, annotationsPresentDirectly);
result.circuitBreaker = getAnnotation(CircuitBreaker.class, method, annotationsPresentDirectly);
result.fallback = getAnnotation(Fallback.class, method, annotationsPresentDirectly);
result.beforeRetryAnnotation = getAnnotation(BeforeRetryAnnotation.class, method, annotationsPresentDirectly);
result.retry = getAnnotation(Retry.class, method, annotationsPresentDirectly);
result.timeout = getAnnotation(Timeout.class, method, annotationsPresentDirectly);

Expand Down Expand Up @@ -82,6 +84,8 @@ public static FaultToleranceMethod create(Class<?> beanClass, Method method) {
result.bulkhead = getAnnotation(Bulkhead.class, method, beanClass, annotationsPresentDirectly);
result.circuitBreaker = getAnnotation(CircuitBreaker.class, method, beanClass, annotationsPresentDirectly);
result.fallback = getAnnotation(Fallback.class, method, beanClass, annotationsPresentDirectly);
result.beforeRetryAnnotation = getAnnotation(BeforeRetryAnnotation.class, method, beanClass,
annotationsPresentDirectly);
result.retry = getAnnotation(Retry.class, method, beanClass, annotationsPresentDirectly);
result.timeout = getAnnotation(Timeout.class, method, beanClass, annotationsPresentDirectly);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;

import io.smallrye.faulttolerance.api.BeforeRetryAnnotation;
import io.smallrye.faulttolerance.api.CircuitBreakerName;
import io.smallrye.faulttolerance.api.CustomBackoff;
import io.smallrye.faulttolerance.api.ExponentialBackoff;
Expand All @@ -53,6 +54,7 @@ public static FaultToleranceOperation create(FaultToleranceMethod method) {
CircuitBreakerNameConfigImpl.create(method),
FallbackConfigImpl.create(method),
RetryConfigImpl.create(method),
BeforeRetryConfigImpl.create(method),
TimeoutConfigImpl.create(method),
ExponentialBackoffConfigImpl.create(method),
FibonacciBackoffConfigImpl.create(method),
Expand All @@ -79,6 +81,8 @@ public static FaultToleranceOperation create(FaultToleranceMethod method) {

private final RetryConfig retry;

private final BeforeRetryConfig beforeRetry;

private final TimeoutConfig timeout;

private final ExponentialBackoffConfig exponentialBackoff;
Expand All @@ -97,6 +101,7 @@ private FaultToleranceOperation(Class<?> beanClass,
CircuitBreakerNameConfig circuitBreakerName,
FallbackConfig fallback,
RetryConfig retry,
BeforeRetryConfig beforeRetry,
TimeoutConfig timeout,
ExponentialBackoffConfig exponentialBackoff,
FibonacciBackoffConfig fibonacciBackoff,
Expand All @@ -113,6 +118,7 @@ private FaultToleranceOperation(Class<?> beanClass,
this.circuitBreakerName = circuitBreakerName;
this.fallback = fallback;
this.retry = retry;
this.beforeRetry = beforeRetry;
this.timeout = timeout;

this.exponentialBackoff = exponentialBackoff;
Expand Down Expand Up @@ -195,6 +201,14 @@ public Fallback getFallback() {
return fallback;
}

public boolean hasBeforeRetry() {
return beforeRetry != null;
}

public BeforeRetryAnnotation getBeforeRetry() {
return beforeRetry;
}

public boolean hasRetry() {
return retry != null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.smallrye.faulttolerance.before.retry;

import static org.assertj.core.api.Assertions.assertThat;

import javax.inject.Inject;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import io.smallrye.faulttolerance.util.FaultToleranceBasicTest;

@FaultToleranceBasicTest
public class BeforeRetryTest {

@Inject
private BeforeRetryTestBean beforeRetryTestBean;

@AfterEach
public void cleanUp() {
beforeRetryTestBean.reset();
}

@Test
public void call() {
String value = beforeRetryTestBean.call();
assertThat(value).isEqualTo("call 2");
assertThat(beforeRetryTestBean.getBeforeRetryRuns()).isEqualTo(2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.smallrye.faulttolerance.before.retry;

import java.util.concurrent.atomic.AtomicInteger;

import javax.enterprise.context.Dependent;

import org.eclipse.microprofile.faulttolerance.Retry;

import io.smallrye.faulttolerance.api.BeforeRetryAnnotation;

@Dependent
public class BeforeRetryTestBean {

private final AtomicInteger attempt = new AtomicInteger();

private final AtomicInteger beforeRetryRuns = new AtomicInteger();

@Retry(maxRetries = 2)
@BeforeRetryAnnotation(beforeRetryMethod = "beforeRetry")
public String call() {
if (attempt.getAndIncrement() < 1) {
throw new IllegalStateException();
}
return "call " + attempt.get();
}

public void beforeRetry() {
beforeRetryRuns.incrementAndGet();
}

public void reset() {
attempt.set(0);
beforeRetryRuns.set(0);
}

int getBeforeRetryRuns() {
return beforeRetryRuns.get();
}
}