/*
*
* Copyright 2017: Robert Winkler, Lucas Lech
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
*/
package io.github.resilience4j.bulkhead;
import io.github.resilience4j.bulkhead.event.BulkheadEvent;
import io.github.resilience4j.bulkhead.internal.SemaphoreBulkhead;
import io.github.resilience4j.bulkhead.utils.BulkheadUtils;
import io.reactivex.Flowable;
import io.vavr.CheckedConsumer;
import io.vavr.CheckedFunction0;
import io.vavr.CheckedFunction1;
import io.vavr.CheckedRunnable;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* A {@link Bulkhead} represent an entity limiting the amount of parallel operations. It does not assume nor does it mandate usage
* of any particular concurrency and/or io model. These details are left for the client to manage. This bulkhead, depending on the
* underlying concurrency/io model can be used to shed load, and, where it makes sense, limit resource use (i.e. limit amount of
* threads/actors involved in a particular flow, etc).
*
* In order to execute an operation protected by this bulkhead, a permission must be obtained by calling {@link Bulkhead#isCallPermitted()}
* If the bulkhead is full, no additional operations will be permitted to execute until space is available.
*
* Once the operation is complete, regardless of the result, client needs to call {@link Bulkhead#onComplete()} in order to maintain
* integrity of internal bulkhead state.
*
*/
public interface Bulkhead {
/**
* Attempts to acquire a permit, which allows an call to be executed.
*
* @return boolean whether a call should be executed
*/
boolean isCallPermitted();
/**
* Records a completed call.
*/
void onComplete();
/**
* Returns the name of this bulkhead.
*
* @return the name of this bulkhead
*/
String getName();
/**
* Returns the BulkheadConfig of this Bulkhead.
*
* @return bulkhead config
*/
BulkheadConfig getBulkheadConfig();
/**
* Get the Metrics of this Bulkhead.
*
* @return the Metrics of this Bulkhead
*/
Metrics getMetrics();
/**
* Returns a reactive stream of BulkheadEvent events.
*
* @return a stream of events
*/
Flowable<BulkheadEvent> getEventStream();
/**
* Decorates and executes the decorated Supplier.
*
* @param supplier the original Supplier
* @param <T> the type of results supplied by this supplier
* @return the result of the decorated Supplier.
*/
default <T> T executeSupplier(Supplier<T> supplier){
return decorateSupplier(this, supplier).get();
}
/**
* Decorates and executes the decorated Callable.
*
* @param callable the original Callable
*
* @return the result of the decorated Callable.
* @param <T> the result type of callable
* @throws Exception if unable to compute a result
*/
default <T> T executeCallable(Callable<T> callable) throws Exception{
return decorateCallable(this, callable).call();
}
/**
* Decorates and executes the decorated Runnable.
*
* @param runnable the original Runnable
*/
default void executeRunnable(Runnable runnable){
decorateRunnable(this, runnable).run();
}
/**
* Decorates and executes the decorated CompletionStage.
*
* @param supplier the original CompletionStage
* @param <T> the type of results supplied by this supplier
* @return the decorated CompletionStage.
*/
default <T> CompletionStage<T> executeCompletionStage(Supplier<CompletionStage<T>> supplier){
return decorateCompletionStage(this, supplier).get();
}
/**
* Returns a supplier which is decorated by a bulkhead.
*
* @param bulkhead the Bulkhead
* @param supplier the original supplier
* @param <T> the type of results supplied by this supplier
* @return a supplier which is decorated by a Bulkhead.
*/
static <T> CheckedFunction0<T> decorateCheckedSupplier(Bulkhead bulkhead, CheckedFunction0<T> supplier){
return () -> {
BulkheadUtils.isCallPermitted(bulkhead);
try {
return supplier.apply();
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a supplier which is decorated by a bulkhead.
*
* @param bulkhead the bulkhead
* @param supplier the original supplier
* @param <T> the type of the returned CompletionStage's result
* @return a supplier which is decorated by a Bulkhead.
*/
static <T> Supplier<CompletionStage<T>> decorateCompletionStage(Bulkhead bulkhead, Supplier<CompletionStage<T>> supplier) {
return () -> {
final CompletableFuture<T> promise = new CompletableFuture<>();
if (!bulkhead.isCallPermitted()) {
promise.completeExceptionally(new BulkheadFullException(String.format("Bulkhead '%s' is open", bulkhead.getName())));
}
else {
try {
supplier.get()
.whenComplete(
(result, throwable) -> {
bulkhead.onComplete();
if (throwable != null) {
promise.completeExceptionally(throwable);
}
else {
promise.complete(result);
}
}
);
}
catch (Throwable throwable) {
bulkhead.onComplete();
promise.completeExceptionally(throwable);
}
}
return promise;
};
}
/**
* Returns a runnable which is decorated by a bulkhead.
*
* @param bulkhead the bulkhead
* @param runnable the original runnable
*
* @return a runnable which is decorated by a Bulkhead.
*/
static CheckedRunnable decorateCheckedRunnable(Bulkhead bulkhead, CheckedRunnable runnable){
return () -> {
BulkheadUtils.isCallPermitted(bulkhead);
try{
runnable.run();
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a callable which is decorated by a bulkhead.
*
* @param bulkhead the bulkhead
* @param callable the original Callable
* @param <T> the result type of callable
*
* @return a supplier which is decorated by a Bulkhead.
*/
static <T> Callable<T> decorateCallable(Bulkhead bulkhead, Callable<T> callable){
return () -> {
BulkheadUtils.isCallPermitted(bulkhead);
try {
return callable.call();
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a supplier which is decorated by a bulkhead.
*
* @param bulkhead the bulkhead
* @param supplier the original supplier
* @param <T> the type of results supplied by this supplier
*
* @return a supplier which is decorated by a Bulkhead.
*/
static <T> Supplier<T> decorateSupplier(Bulkhead bulkhead, Supplier<T> supplier){
return () -> {
BulkheadUtils.isCallPermitted(bulkhead);
try {
return supplier.get();
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a consumer which is decorated by a bulkhead.
* @param bulkhead the bulkhead
* @param consumer the original consumer
* @param <T> the type of the input to the consumer
*
* @return a consumer which is decorated by a Bulkhead.
*/
static <T> Consumer<T> decorateConsumer(Bulkhead bulkhead, Consumer<T> consumer){
return (t) -> {
BulkheadUtils.isCallPermitted(bulkhead);
try {
consumer.accept(t);
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a consumer which is decorated by a bulkhead.
* @param bulkhead the bulkhead
* @param consumer the original consumer
* @param <T> the type of the input to the consumer
*
* @return a consumer which is decorated by a Bulkhead.
*/
static <T> CheckedConsumer<T> decorateCheckedConsumer(Bulkhead bulkhead, CheckedConsumer<T> consumer){
return (t) -> {
BulkheadUtils.isCallPermitted(bulkhead);
try {
consumer.accept(t);
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a runnable which is decorated by a bulkhead.
*
* @param bulkhead the bulkhead
* @param runnable the original runnable
*
* @return a runnable which is decorated by a bulkhead.
*/
static Runnable decorateRunnable(Bulkhead bulkhead, Runnable runnable){
return () -> {
BulkheadUtils.isCallPermitted(bulkhead);
try{
runnable.run();
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a function which is decorated by a bulkhead.
* @param bulkhead the bulkhead
* @param function the original function
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
* @return a function which is decorated by a bulkhead.
*/
static <T, R> Function<T, R> decorateFunction(Bulkhead bulkhead, Function<T, R> function){
return (T t) -> {
BulkheadUtils.isCallPermitted(bulkhead);
try{
return function.apply(t);
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Returns a function which is decorated by a bulkhead.
*
* @param bulkhead the bulkhead
* @param function the original function
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
* @return a function which is decorated by a bulkhead.
*/
static <T, R> CheckedFunction1<T, R> decorateCheckedFunction(Bulkhead bulkhead, CheckedFunction1<T, R> function){
return (T t) -> {
BulkheadUtils.isCallPermitted(bulkhead);
try{
return function.apply(t);
}
finally {
bulkhead.onComplete();
}
};
}
/**
* Create a Bulkhead with a default configuration.
*
* @param name the name of the bulkhead
* @return a Bulkhead instance
*/
static Bulkhead ofDefaults(String name) {
return new SemaphoreBulkhead(name);
}
/**
* Creates a bulkhead with a custom configuration
*
* @param name the name of the bulkhead
* @param config a custom BulkheadConfig configuration
* @return a Bulkhead instance
*/
static Bulkhead of(String name, BulkheadConfig config) {
return new SemaphoreBulkhead(name, config);
}
/**
* Creates a bulkhead with a custom configuration
*
* @param name the name of the bulkhead
* @param bulkheadConfigSupplier custom configuration supplier
* @return a Bulkhead instance
*/
static Bulkhead of(String name, Supplier<BulkheadConfig> bulkheadConfigSupplier) {
return new SemaphoreBulkhead(name, bulkheadConfigSupplier);
}
interface Metrics {
/**
* Returns the number of parallel executions this bulkhead can support at this point in time.
*
* @return remaining bulkhead depth
*/
int getAvailableConcurrentCalls();
}
}