Search in sources :

Example 1 with FaultToleranceService

use of fish.payara.microprofile.faulttolerance.FaultToleranceService in project Payara by payara.

the class AsynchronousInterceptor method intercept.

@AroundInvoke
public Object intercept(InvocationContext invocationContext) throws Exception {
    Object proceededInvocationContext = null;
    // Get the configured ManagedExecutorService from the Fault Tolerance Service
    FaultToleranceService faultToleranceService = Globals.getDefaultBaseServiceLocator().getService(FaultToleranceService.class);
    ManagedExecutorService managedExecutorService = faultToleranceService.getManagedExecutorService();
    InvocationManager invocationManager = Globals.getDefaultBaseServiceLocator().getService(InvocationManager.class);
    Config config = null;
    try {
        config = ConfigProvider.getConfig();
    } catch (IllegalArgumentException ex) {
        logger.log(Level.INFO, "No config could be found", ex);
    }
    try {
        // Attempt to proceed the InvocationContext with Asynchronous semantics if Fault Tolerance is enabled
        if (faultToleranceService.isFaultToleranceEnabled(faultToleranceService.getApplicationName(invocationManager, invocationContext), config)) {
            Callable callable = () -> {
                return invocationContext.proceed();
            };
            logger.log(Level.FINER, "Proceeding invocation asynchronously");
            proceededInvocationContext = new FutureDelegator(managedExecutorService.submit(callable));
        } else {
            // If fault tolerance isn't enabled, just proceed as normal
            logger.log(Level.FINE, "Fault Tolerance not enabled for {0}, proceeding normally without asynchronous.", faultToleranceService.getApplicationName(invocationManager, invocationContext));
            proceededInvocationContext = invocationContext.proceed();
        }
    } catch (Exception ex) {
        // If an exception was thrown, check if the method is annotated with @Fallback
        // We should only get here if executing synchronously, as the exception wouldn't get thrown in this thread
        Fallback fallback = FaultToleranceCdiUtils.getAnnotation(beanManager, Fallback.class, invocationContext);
        // If the method was annotated with Fallback, attempt it, otherwise just propagate the exception upwards
        if (fallback != null) {
            logger.log(Level.FINE, "Fallback annotation found on method - falling back from Asynchronous");
            FallbackPolicy fallbackPolicy = new FallbackPolicy(fallback, config, invocationContext);
            proceededInvocationContext = fallbackPolicy.fallback(invocationContext);
        } else {
            throw ex;
        }
    }
    return proceededInvocationContext;
}
Also used : FallbackPolicy(fish.payara.microprofile.faulttolerance.interceptors.fallback.FallbackPolicy) ManagedExecutorService(javax.enterprise.concurrent.ManagedExecutorService) Config(org.eclipse.microprofile.config.Config) InvocationManager(org.glassfish.api.invocation.InvocationManager) Fallback(org.eclipse.microprofile.faulttolerance.Fallback) FaultToleranceService(fish.payara.microprofile.faulttolerance.FaultToleranceService) Callable(java.util.concurrent.Callable) FaultToleranceException(org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceException) TimeoutException(java.util.concurrent.TimeoutException) ExecutionException(java.util.concurrent.ExecutionException) AroundInvoke(javax.interceptor.AroundInvoke)

Example 2 with FaultToleranceService

use of fish.payara.microprofile.faulttolerance.FaultToleranceService in project Payara by payara.

the class BulkheadInterceptor method bulkhead.

/**
 * Proceeds the context under Bulkhead semantics.
 * @param invocationContext The context to proceed.
 * @return The outcome of the invocationContext
 * @throws Exception
 */
private Object bulkhead(InvocationContext invocationContext) throws Exception {
    Object proceededInvocationContext = null;
    FaultToleranceService faultToleranceService = Globals.getDefaultBaseServiceLocator().getService(FaultToleranceService.class);
    Bulkhead bulkhead = FaultToleranceCdiUtils.getAnnotation(beanManager, Bulkhead.class, invocationContext);
    Config config = null;
    try {
        config = ConfigProvider.getConfig();
    } catch (IllegalArgumentException ex) {
        logger.log(Level.INFO, "No config could be found", ex);
    }
    int value = (Integer) FaultToleranceCdiUtils.getOverrideValue(config, Bulkhead.class, "value", invocationContext, Integer.class).orElse(bulkhead.value());
    int waitingTaskQueue = (Integer) FaultToleranceCdiUtils.getOverrideValue(config, Bulkhead.class, "waitingTaskQueue", invocationContext, Integer.class).orElse(bulkhead.waitingTaskQueue());
    InvocationManager invocationManager = Globals.getDefaultBaseServiceLocator().getService(InvocationManager.class);
    String appName = faultToleranceService.getApplicationName(invocationManager, invocationContext);
    Semaphore bulkheadExecutionSemaphore = faultToleranceService.getBulkheadExecutionSemaphore(appName, invocationContext.getMethod(), value);
    // If the Asynchronous annotation is present, use threadpool style, otherwise use semaphore style
    if (FaultToleranceCdiUtils.getAnnotation(beanManager, Asynchronous.class, invocationContext) != null) {
        Semaphore bulkheadExecutionQueueSemaphore = faultToleranceService.getBulkheadExecutionQueueSemaphore(appName, invocationContext.getMethod(), waitingTaskQueue);
        // Check if there are any free permits for concurrent execution
        if (!bulkheadExecutionSemaphore.tryAcquire(0, TimeUnit.SECONDS)) {
            logger.log(Level.FINER, "Attempting to acquire bulkhead queue semaphore.");
            // If there aren't any free permits, see if there are any free queue permits
            if (bulkheadExecutionQueueSemaphore.tryAcquire(0, TimeUnit.SECONDS)) {
                logger.log(Level.FINER, "Acquired bulkhead queue semaphore.");
                // If there is a free queue permit, queue for an executor permit
                try {
                    logger.log(Level.FINER, "Attempting to acquire bulkhead execution semaphore.");
                    faultToleranceService.startFaultToleranceSpan(new RequestTraceSpan("obtainBulkheadSemaphore"), invocationManager, invocationContext);
                    try {
                        bulkheadExecutionSemaphore.acquire();
                    } finally {
                        // Make sure we end the trace right here
                        faultToleranceService.endFaultToleranceSpan();
                    }
                    logger.log(Level.FINER, "Acquired bulkhead queue semaphore.");
                    // Release the queue permit
                    bulkheadExecutionQueueSemaphore.release();
                    // Proceed the invocation and wait for the response
                    try {
                        logger.log(Level.FINER, "Proceeding bulkhead context");
                        proceededInvocationContext = invocationContext.proceed();
                    } catch (Exception ex) {
                        logger.log(Level.FINE, "Exception proceeding Bulkhead context", ex);
                        // Generic catch, as we need to release the semaphore permits
                        bulkheadExecutionSemaphore.release();
                        bulkheadExecutionQueueSemaphore.release();
                        // Let the exception propagate further up - we just want to release the semaphores
                        throw ex;
                    }
                    // Release the execution permit
                    bulkheadExecutionSemaphore.release();
                } catch (InterruptedException ex) {
                    logger.log(Level.INFO, "Interrupted acquiring bulkhead semaphore", ex);
                    throw new BulkheadException(ex);
                }
            } else {
                throw new BulkheadException("No free work or queue permits.");
            }
        } else {
            // Proceed the invocation and wait for the response
            try {
                logger.log(Level.FINER, "Proceeding bulkhead context");
                proceededInvocationContext = invocationContext.proceed();
            } catch (Exception ex) {
                logger.log(Level.FINE, "Exception proceeding Bulkhead context", ex);
                // Generic catch, as we need to release the semaphore permits
                bulkheadExecutionSemaphore.release();
                // Let the exception propagate further up - we just want to release the semaphores
                throw ex;
            }
            // Release the permit
            bulkheadExecutionSemaphore.release();
        }
    } else {
        // Try to get an execution permit
        if (bulkheadExecutionSemaphore.tryAcquire(0, TimeUnit.SECONDS)) {
            // Proceed the invocation and wait for the response
            try {
                logger.log(Level.FINER, "Proceeding bulkhead context");
                proceededInvocationContext = invocationContext.proceed();
            } catch (Exception ex) {
                logger.log(Level.FINE, "Exception proceeding Bulkhead context", ex);
                // Generic catch, as we need to release the semaphore permits
                bulkheadExecutionSemaphore.release();
                // Let the exception propagate further up - we just want to release the semaphores
                throw ex;
            }
            // Release the permit
            bulkheadExecutionSemaphore.release();
        } else {
            throw new BulkheadException("No free work permits.");
        }
    }
    return proceededInvocationContext;
}
Also used : Asynchronous(org.eclipse.microprofile.faulttolerance.Asynchronous) Config(org.eclipse.microprofile.config.Config) InvocationManager(org.glassfish.api.invocation.InvocationManager) RequestTraceSpan(fish.payara.notification.requesttracing.RequestTraceSpan) Semaphore(java.util.concurrent.Semaphore) FaultToleranceService(fish.payara.microprofile.faulttolerance.FaultToleranceService) BulkheadException(org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException) Bulkhead(org.eclipse.microprofile.faulttolerance.Bulkhead) BulkheadException(org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException)

Example 3 with FaultToleranceService

use of fish.payara.microprofile.faulttolerance.FaultToleranceService in project Payara by payara.

the class CircuitBreakerInterceptor method intercept.

@AroundInvoke
public Object intercept(InvocationContext invocationContext) throws Exception {
    Object proceededInvocationContext = null;
    FaultToleranceService faultToleranceService = Globals.getDefaultBaseServiceLocator().getService(FaultToleranceService.class);
    InvocationManager invocationManager = Globals.getDefaultBaseServiceLocator().getService(InvocationManager.class);
    Config config = null;
    try {
        config = ConfigProvider.getConfig();
    } catch (IllegalArgumentException ex) {
        logger.log(Level.INFO, "No config could be found", ex);
    }
    // Attempt to proceed the invocation with CircuitBreaker semantics if Fault Tolerance is enabled
    try {
        if (faultToleranceService.isFaultToleranceEnabled(faultToleranceService.getApplicationName(invocationManager, invocationContext), config)) {
            logger.log(Level.FINER, "Proceeding invocation with circuitbreaker semantics");
            proceededInvocationContext = circuitBreak(invocationContext);
        } else {
            // If fault tolerance isn't enabled, just proceed as normal
            logger.log(Level.FINE, "Fault Tolerance not enabled for {0}, proceeding normally without " + "circuitbreaker.", faultToleranceService.getApplicationName(invocationManager, invocationContext));
            proceededInvocationContext = invocationContext.proceed();
        }
    } catch (Exception ex) {
        Retry retry = FaultToleranceCdiUtils.getAnnotation(beanManager, Retry.class, invocationContext);
        if (retry != null) {
            logger.log(Level.FINE, "Retry annotation found on method, propagating error upwards.");
            throw ex;
        } else {
            Fallback fallback = FaultToleranceCdiUtils.getAnnotation(beanManager, Fallback.class, invocationContext);
            if (fallback != null) {
                logger.log(Level.FINE, "Fallback annotation found on method, and no Retry annotation - " + "falling back from CircuitBreaker");
                FallbackPolicy fallbackPolicy = new FallbackPolicy(fallback, config, invocationContext);
                proceededInvocationContext = fallbackPolicy.fallback(invocationContext);
            } else {
                throw ex;
            }
        }
    }
    return proceededInvocationContext;
}
Also used : FallbackPolicy(fish.payara.microprofile.faulttolerance.interceptors.fallback.FallbackPolicy) Config(org.eclipse.microprofile.config.Config) InvocationManager(org.glassfish.api.invocation.InvocationManager) Fallback(org.eclipse.microprofile.faulttolerance.Fallback) Retry(org.eclipse.microprofile.faulttolerance.Retry) FaultToleranceService(fish.payara.microprofile.faulttolerance.FaultToleranceService) NamingException(javax.naming.NamingException) NoSuchElementException(java.util.NoSuchElementException) CircuitBreakerOpenException(org.eclipse.microprofile.faulttolerance.exceptions.CircuitBreakerOpenException) AroundInvoke(javax.interceptor.AroundInvoke)

Example 4 with FaultToleranceService

use of fish.payara.microprofile.faulttolerance.FaultToleranceService in project Payara by payara.

the class CircuitBreakerInterceptor method scheduleHalfOpen.

/**
 * Helper method that schedules the CircuitBreaker state to be set to HalfOpen after the configured delay
 * @param delayMillis The number of milliseconds to wait before setting the state
 * @param circuitBreakerState The CircuitBreakerState to set the state of
 * @throws NamingException If the ManagedScheduledExecutor couldn't be found
 */
private void scheduleHalfOpen(long delayMillis, CircuitBreakerState circuitBreakerState) throws NamingException {
    Runnable halfOpen = () -> {
        circuitBreakerState.setCircuitState(CircuitBreakerState.CircuitState.HALF_OPEN);
        logger.log(Level.FINE, "Setting CircuitBreaker state to half open");
    };
    FaultToleranceService faultToleranceService = Globals.getDefaultBaseServiceLocator().getService(FaultToleranceService.class);
    ManagedScheduledExecutorService managedScheduledExecutorService = faultToleranceService.getManagedScheduledExecutorService();
    managedScheduledExecutorService.schedule(halfOpen, delayMillis, TimeUnit.MILLISECONDS);
    logger.log(Level.FINER, "CircuitBreaker half open state scheduled in {0} milliseconds", delayMillis);
}
Also used : FaultToleranceService(fish.payara.microprofile.faulttolerance.FaultToleranceService) ManagedScheduledExecutorService(javax.enterprise.concurrent.ManagedScheduledExecutorService)

Example 5 with FaultToleranceService

use of fish.payara.microprofile.faulttolerance.FaultToleranceService in project Payara by payara.

the class RetryInterceptor method retry.

/**
 * Proceeds the given invocation context with Retry semantics.
 * @param invocationContext The invocation context to proceed
 * @return The proceeded invocation context
 * @throws Exception If the invocation throws an exception that shouldn't be retried, or if all retry attempts are
 * expended
 */
private Object retry(InvocationContext invocationContext) throws Exception {
    Object proceededInvocationContext = null;
    Retry retry = FaultToleranceCdiUtils.getAnnotation(beanManager, Retry.class, invocationContext);
    FaultToleranceService faultToleranceService = Globals.getDefaultBaseServiceLocator().getService(FaultToleranceService.class);
    InvocationManager invocationManager = Globals.getDefaultBaseServiceLocator().getService(InvocationManager.class);
    try {
        proceededInvocationContext = invocationContext.proceed();
    } catch (Exception ex) {
        Config config = null;
        try {
            config = ConfigProvider.getConfig();
        } catch (IllegalArgumentException iae) {
            logger.log(Level.INFO, "No config could be found", ex);
        }
        Class<? extends Throwable>[] retryOn = retry.retryOn();
        try {
            String retryOnString = ((String) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "retryOn", invocationContext, String.class).get());
            List<Class> classList = new ArrayList<>();
            // Remove any curly or square brackets from the string, as well as any spaces and ".class"es
            for (String className : retryOnString.replaceAll("[\\{\\[ \\]\\}]", "").replaceAll("\\.class", "").split(",")) {
                classList.add(Class.forName(className));
            }
            retryOn = classList.toArray(retryOn);
        } catch (NoSuchElementException nsee) {
            logger.log(Level.FINER, "Could not find element in config", nsee);
        } catch (ClassNotFoundException cnfe) {
            logger.log(Level.INFO, "Could not find class from retryOn config, defaulting to annotation. " + "Make sure you give the full canonical class name.", cnfe);
        }
        Class<? extends Throwable>[] abortOn = retry.abortOn();
        try {
            String abortOnString = (String) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "abortOn", invocationContext, String.class).get();
            List<Class> classList = new ArrayList<>();
            // Remove any curly or square brackets from the string, as well as any spaces and ".class"es
            for (String className : abortOnString.replaceAll("[\\{\\[ \\]\\}]", "").replaceAll("\\.class", "").split(",")) {
                classList.add(Class.forName(className));
            }
            abortOn = classList.toArray(abortOn);
        } catch (NoSuchElementException nsee) {
            logger.log(Level.FINER, "Could not find element in config", nsee);
        } catch (ClassNotFoundException cnfe) {
            logger.log(Level.INFO, "Could not find class from abortOn config, defaulting to annotation. " + "Make sure you give the full canonical class name.", cnfe);
        }
        if (!shouldRetry(retryOn, abortOn, ex)) {
            logger.log(Level.FINE, "Exception is contained in retryOn or abortOn, not retrying.", ex);
            throw ex;
        }
        int maxRetries = (Integer) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "maxRetries", invocationContext, Integer.class).orElse(retry.maxRetries());
        long delay = (Long) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "delay", invocationContext, Long.class).orElse(retry.delay());
        ChronoUnit delayUnit = (ChronoUnit) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "delayUnit", invocationContext, ChronoUnit.class).orElse(retry.delayUnit());
        long maxDuration = (Long) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "maxDuration", invocationContext, Long.class).orElse(retry.maxDuration());
        ChronoUnit durationUnit = (ChronoUnit) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "durationUnit", invocationContext, ChronoUnit.class).orElse(retry.durationUnit());
        long jitter = (Long) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "jitter", invocationContext, Long.class).orElse(retry.jitter());
        ChronoUnit jitterDelayUnit = (ChronoUnit) FaultToleranceCdiUtils.getOverrideValue(config, Retry.class, "jitterDelayUnit", invocationContext, ChronoUnit.class).orElse(retry.jitterDelayUnit());
        long delayMillis = Duration.of(delay, delayUnit).toMillis();
        long jitterMillis = Duration.of(jitter, jitterDelayUnit).toMillis();
        long timeoutTime = System.currentTimeMillis() + Duration.of(maxDuration, durationUnit).toMillis();
        Exception retryException = ex;
        faultToleranceService.startFaultToleranceSpan(new RequestTraceSpan("retryMethod"), invocationManager, invocationContext);
        try {
            if (maxRetries == -1 && maxDuration > 0) {
                logger.log(Level.FINER, "Retrying until maxDuration is breached.");
                while (System.currentTimeMillis() < timeoutTime) {
                    try {
                        proceededInvocationContext = invocationContext.proceed();
                        break;
                    } catch (Exception caughtException) {
                        retryException = caughtException;
                        if (!shouldRetry(retryOn, abortOn, caughtException)) {
                            break;
                        }
                        if (delayMillis > 0 || jitterMillis > 0) {
                            faultToleranceService.startFaultToleranceSpan(new RequestTraceSpan("delayRetry"), invocationManager, invocationContext);
                            try {
                                Thread.sleep(delayMillis + ThreadLocalRandom.current().nextLong(0, jitterMillis));
                            } finally {
                                faultToleranceService.endFaultToleranceSpan();
                            }
                        }
                    }
                }
            } else if (maxRetries == -1 && maxDuration == 0) {
                logger.log(Level.INFO, "Retrying potentially forever!");
                while (true) {
                    try {
                        proceededInvocationContext = invocationContext.proceed();
                        break;
                    } catch (Exception caughtException) {
                        retryException = caughtException;
                        if (!shouldRetry(retryOn, abortOn, caughtException)) {
                            break;
                        }
                        if (delayMillis > 0 || jitterMillis > 0) {
                            faultToleranceService.startFaultToleranceSpan(new RequestTraceSpan("delayRetry"), invocationManager, invocationContext);
                            try {
                                Thread.sleep(delayMillis + ThreadLocalRandom.current().nextLong(0, jitterMillis));
                            } finally {
                                faultToleranceService.endFaultToleranceSpan();
                            }
                        }
                    }
                }
            } else if (maxRetries != -1 && maxDuration > 0) {
                logger.log(Level.INFO, "Retrying as long as maxDuration ({0}ms) isn''t breached, and no more than {1} times", new Object[] { Duration.of(maxDuration, durationUnit).toMillis(), maxRetries });
                while (maxRetries > 0 && System.currentTimeMillis() < timeoutTime) {
                    try {
                        proceededInvocationContext = invocationContext.proceed();
                        break;
                    } catch (Exception caughtException) {
                        retryException = caughtException;
                        if (!shouldRetry(retryOn, abortOn, caughtException)) {
                            break;
                        }
                        if (delayMillis > 0 || jitterMillis > 0) {
                            faultToleranceService.startFaultToleranceSpan(new RequestTraceSpan("delayRetry"), invocationManager, invocationContext);
                            try {
                                Thread.sleep(delayMillis + ThreadLocalRandom.current().nextLong(0, jitterMillis));
                            } finally {
                                faultToleranceService.endFaultToleranceSpan();
                            }
                        }
                        maxRetries--;
                    }
                }
            } else {
                logger.log(Level.INFO, "Retrying no more than {0} times", maxRetries);
                while (maxRetries > 0) {
                    try {
                        proceededInvocationContext = invocationContext.proceed();
                        break;
                    } catch (Exception caughtException) {
                        retryException = caughtException;
                        if (!shouldRetry(retryOn, abortOn, caughtException)) {
                            break;
                        }
                        if (delayMillis > 0 || jitterMillis > 0) {
                            faultToleranceService.startFaultToleranceSpan(new RequestTraceSpan("delayRetry"), invocationManager, invocationContext);
                            try {
                                Thread.sleep(delayMillis + ThreadLocalRandom.current().nextLong(0, jitterMillis));
                            } finally {
                                faultToleranceService.endFaultToleranceSpan();
                            }
                        }
                        maxRetries--;
                    }
                }
            }
        } finally {
            faultToleranceService.endFaultToleranceSpan();
        }
        if (proceededInvocationContext == null) {
            throw retryException;
        }
    }
    return proceededInvocationContext;
}
Also used : Config(org.eclipse.microprofile.config.Config) InvocationManager(org.glassfish.api.invocation.InvocationManager) RequestTraceSpan(fish.payara.notification.requesttracing.RequestTraceSpan) FaultToleranceService(fish.payara.microprofile.faulttolerance.FaultToleranceService) NoSuchElementException(java.util.NoSuchElementException) ArrayList(java.util.ArrayList) List(java.util.List) Retry(org.eclipse.microprofile.faulttolerance.Retry) NoSuchElementException(java.util.NoSuchElementException) ChronoUnit(java.time.temporal.ChronoUnit)

Aggregations

FaultToleranceService (fish.payara.microprofile.faulttolerance.FaultToleranceService)11 InvocationManager (org.glassfish.api.invocation.InvocationManager)9 Config (org.eclipse.microprofile.config.Config)8 FallbackPolicy (fish.payara.microprofile.faulttolerance.interceptors.fallback.FallbackPolicy)5 AroundInvoke (javax.interceptor.AroundInvoke)5 Fallback (org.eclipse.microprofile.faulttolerance.Fallback)5 Retry (org.eclipse.microprofile.faulttolerance.Retry)5 NoSuchElementException (java.util.NoSuchElementException)4 RequestTraceSpan (fish.payara.notification.requesttracing.RequestTraceSpan)3 NamingException (javax.naming.NamingException)3 ChronoUnit (java.time.temporal.ChronoUnit)2 ArrayList (java.util.ArrayList)2 ManagedScheduledExecutorService (javax.enterprise.concurrent.ManagedScheduledExecutorService)2 BulkheadException (org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException)2 CircuitBreakerOpenException (org.eclipse.microprofile.faulttolerance.exceptions.CircuitBreakerOpenException)2 CircuitBreakerState (fish.payara.microprofile.faulttolerance.state.CircuitBreakerState)1 List (java.util.List)1 Callable (java.util.concurrent.Callable)1 ExecutionException (java.util.concurrent.ExecutionException)1 Semaphore (java.util.concurrent.Semaphore)1