Build fault-tolerant network services
—
Programmatic fault tolerance configuration using TypedGuard and Guard interfaces for scenarios where annotation-based configuration is insufficient or dynamic configuration is required.
Create typed guards for specific return types with fluent configuration API.
TypedGuard<ReturnType> guard = TypedGuard.create(ReturnType.class)
.withRetry().maxRetries(3).delay(1000, ChronoUnit.MILLISECONDS).done()
.withCircuitBreaker().requestVolumeThreshold(10).failureRatio(0.5).done()
.withTimeout().timeout(5000, ChronoUnit.MILLISECONDS).done()
.build();
// Guard usage
ReturnType result = guard.call(() -> riskyOperation());@ApplicationScoped
public class ProgrammaticFaultToleranceService {
private final Supplier<String> weatherSupplier;
private final Function<String, WeatherData> weatherFunction;
@PostConstruct
public void initializeGuards() {
// Create guard for weather API calls
TypedGuard<String> weatherGuard = TypedGuard.create(String.class)
.withRetry()
.maxRetries(3)
.delay(1000, ChronoUnit.MILLISECONDS)
.retryOn(IOException.class, TimeoutException.class)
.done()
.withCircuitBreaker()
.requestVolumeThreshold(10)
.failureRatio(0.4)
.delay(30000, ChronoUnit.MILLISECONDS)
.done()
.withTimeout()
.timeout(8000, ChronoUnit.MILLISECONDS)
.done()
.build();
// Adapt supplier for easy usage
weatherSupplier = weatherGuard.adaptSupplier(() -> {
return weatherApiClient.getCurrentWeather();
});
// Create function guard for city-specific weather
TypedGuard<WeatherData> cityWeatherGuard = TypedGuard.create(WeatherData.class)
.withRetry()
.maxRetries(2)
.delay(500, ChronoUnit.MILLISECONDS)
.done()
.withCircuitBreaker()
.requestVolumeThreshold(5)
.failureRatio(0.3)
.name("city-weather-circuit")
.done()
.build();
weatherFunction = cityWeatherGuard.adaptFunction(city -> {
return weatherApiClient.getWeatherForCity(city);
});
}
public String getCurrentWeather() {
return weatherSupplier.get();
}
public WeatherData getWeatherForCity(String city) {
return weatherFunction.apply(city);
}
}Comprehensive configuration options for all fault tolerance strategies.
TypedGuard<T> guard = TypedGuard.create(ReturnType.class)
.withRetry()
.maxRetries(5)
.delay(1000, ChronoUnit.MILLISECONDS)
.maxDuration(30000, ChronoUnit.MILLISECONDS)
.jitter(200, ChronoUnit.MILLISECONDS)
.retryOn(IOException.class, TimeoutException.class)
.abortOn(SecurityException.class)
.done()
.build();TypedGuard<T> guard = TypedGuard.create(ReturnType.class)
.withCircuitBreaker()
.requestVolumeThreshold(20)
.failureRatio(0.5)
.delay(10000, ChronoUnit.MILLISECONDS)
.successThreshold(3)
.name("my-circuit-breaker")
.failOn(RuntimeException.class)
.skipOn(IllegalArgumentException.class)
.done()
.build();TypedGuard<T> guard = TypedGuard.create(ReturnType.class)
.withTimeout()
.timeout(5000, ChronoUnit.MILLISECONDS)
.done()
.build();TypedGuard<T> guard = TypedGuard.create(ReturnType.class)
.withBulkhead()
.limit(10)
.queueSize(20)
.done()
.build();TypedGuard<T> guard = TypedGuard.create(ReturnType.class)
.withRateLimit()
.limit(100)
.window(1, ChronoUnit.MINUTES)
.minSpacing(100, ChronoUnit.MILLISECONDS)
.type(RateLimitType.ROLLING)
.done()
.build();Different ways to use guards with various functional interfaces.
// Supplier adaptation (no parameters)
Supplier<T> guardedSupplier = guard.adaptSupplier(originalSupplier);
// Function adaptation (single parameter)
Function<P, T> guardedFunction = guard.adaptFunction(originalFunction);
// Callable adaptation (throws exceptions)
T result = guard.call(callableOperation);
// Runnable adaptation (void return)
guard.run(runnableOperation);@ApplicationScoped
public class AdaptedGuardService {
private final Supplier<List<User>> userListSupplier;
private final Function<Long, User> userByIdFunction;
private final Function<UserQuery, List<User>> userQueryFunction;
@PostConstruct
public void setupGuards() {
// Guard for listing all users
TypedGuard<List<User>> listGuard = TypedGuard.create(new TypeToken<List<User>>() {})
.withRetry().maxRetries(2).delay(500, ChronoUnit.MILLISECONDS).done()
.withTimeout().timeout(3000, ChronoUnit.MILLISECONDS).done()
.build();
userListSupplier = listGuard.adaptSupplier(() -> {
return userRepository.findAll();
});
// Guard for finding user by ID
TypedGuard<User> userGuard = TypedGuard.create(User.class)
.withRetry().maxRetries(3).done()
.withCircuitBreaker().requestVolumeThreshold(10).done()
.build();
userByIdFunction = userGuard.adaptFunction(userId -> {
return userRepository.findById(userId);
});
// Guard for complex queries
TypedGuard<List<User>> queryGuard = TypedGuard.create(new TypeToken<List<User>>() {})
.withBulkhead().limit(5).done()
.withTimeout().timeout(10000, ChronoUnit.MILLISECONDS).done()
.build();
userQueryFunction = queryGuard.adaptFunction(query -> {
return userRepository.find(query);
});
}
public List<User> getAllUsers() {
return userListSupplier.get();
}
public User findUserById(Long userId) {
return userByIdFunction.apply(userId);
}
public List<User> findUsers(UserQuery query) {
return userQueryFunction.apply(query);
}
}Runtime configuration of guards based on external parameters or conditions.
public TypedGuard<T> createDynamicGuard(GuardConfiguration config) {
TypedGuardBuilder<T> builder = TypedGuard.create(ReturnType.class);
if (config.isRetryEnabled()) {
builder = builder.withRetry()
.maxRetries(config.getMaxRetries())
.delay(config.getRetryDelay(), ChronoUnit.MILLISECONDS)
.done();
}
if (config.isCircuitBreakerEnabled()) {
builder = builder.withCircuitBreaker()
.requestVolumeThreshold(config.getCircuitBreakerThreshold())
.failureRatio(config.getFailureRatio())
.done();
}
return builder.build();
}@ApplicationScoped
public class DynamicGuardService {
@Inject
ConfigurationService configService;
private Map<String, TypedGuard<ApiResponse>> apiGuards = new ConcurrentHashMap<>();
public ApiResponse callApi(String apiName, ApiRequest request) {
TypedGuard<ApiResponse> guard = getOrCreateGuard(apiName);
return guard.call(() -> apiClient.call(apiName, request));
}
private TypedGuard<ApiResponse> getOrCreateGuard(String apiName) {
return apiGuards.computeIfAbsent(apiName, name -> {
ApiConfiguration config = configService.getApiConfiguration(name);
TypedGuardBuilder<ApiResponse> builder = TypedGuard.create(ApiResponse.class);
// Configure retry based on API characteristics
builder = builder.withRetry()
.maxRetries(config.getMaxRetries())
.delay(config.getRetryDelay(), ChronoUnit.MILLISECONDS);
if (config.hasExponentialBackoff()) {
builder = builder.withExponentialBackoff()
.factor(config.getBackoffFactor())
.maxDelay(config.getMaxBackoffDelay(), ChronoUnit.MILLISECONDS);
}
builder = builder.done();
// Configure circuit breaker for unreliable APIs
if (config.isUnreliable()) {
builder = builder.withCircuitBreaker()
.requestVolumeThreshold(config.getCircuitThreshold())
.failureRatio(config.getFailureRatio())
.delay(config.getCircuitDelay(), ChronoUnit.MILLISECONDS)
.name(name + "-circuit")
.done();
}
// Configure rate limiting for rate-limited APIs
if (config.hasRateLimit()) {
builder = builder.withRateLimit()
.limit(config.getRateLimit())
.window(config.getRateWindow(), ChronoUnit.MINUTES)
.done();
}
return builder.build();
});
}
}Combining multiple guards and reusing guard configurations.
// Base guard configuration
TypedGuard<T> baseGuard = TypedGuard.create(ReturnType.class)
.withRetry().maxRetries(3).done()
.withTimeout().timeout(5000, ChronoUnit.MILLISECONDS).done()
.build();
// Extended guard with additional strategies
TypedGuard<T> extendedGuard = TypedGuard.create(ReturnType.class)
.from(baseGuard) // Copy configuration from base guard
.withCircuitBreaker().requestVolumeThreshold(10).done()
.build();@ApplicationScoped
public class GuardCompositionService {
// Base guard for all database operations
private final TypedGuard<Object> databaseBaseGuard;
// Specialized guards for different operation types
private final TypedGuard<List<Entity>> queryGuard;
private final TypedGuard<Entity> crudGuard;
private final TypedGuard<Void> batchOperationGuard;
@PostConstruct
public void initializeGuards() {
// Base configuration for all database operations
databaseBaseGuard = TypedGuard.create(Object.class)
.withRetry()
.maxRetries(3)
.delay(500, ChronoUnit.MILLISECONDS)
.retryOn(SQLException.class, TransientDataAccessException.class)
.done()
.withTimeout()
.timeout(10000, ChronoUnit.MILLISECONDS)
.done()
.build();
// Query operations with additional circuit breaker
queryGuard = TypedGuard.create(new TypeToken<List<Entity>>() {})
.from(databaseBaseGuard)
.withCircuitBreaker()
.requestVolumeThreshold(15)
.failureRatio(0.4)
.name("database-query-circuit")
.done()
.build();
// CRUD operations with bulkhead
crudGuard = TypedGuard.create(Entity.class)
.from(databaseBaseGuard)
.withBulkhead()
.limit(5)
.queueSize(10)
.done()
.build();
// Batch operations with extended timeout and rate limiting
batchOperationGuard = TypedGuard.create(Void.class)
.from(databaseBaseGuard)
.withTimeout()
.timeout(60000, ChronoUnit.MILLISECONDS) // Override base timeout
.done()
.withRateLimit()
.limit(5)
.window(1, ChronoUnit.MINUTES)
.done()
.build();
}
public List<Entity> findEntities(EntityQuery query) {
return queryGuard.call(() -> entityRepository.find(query));
}
public Entity saveEntity(Entity entity) {
return crudGuard.call(() -> entityRepository.save(entity));
}
public void processBatch(List<Entity> entities) {
batchOperationGuard.run(() -> batchProcessor.process(entities));
}
}// Main typed guard interface
interface TypedGuard<T> {
T call(Callable<T> callable) throws Exception;
void run(Runnable runnable);
Supplier<T> adaptSupplier(Supplier<T> supplier);
<P> Function<P, T> adaptFunction(Function<P, T> function);
<P1, P2> BiFunction<P1, P2, T> adaptBiFunction(BiFunction<P1, P2, T> function);
}
// Builder for typed guards
interface TypedGuardBuilder<T> {
RetryBuilder<T> withRetry();
CircuitBreakerBuilder<T> withCircuitBreaker();
TimeoutBuilder<T> withTimeout();
BulkheadBuilder<T> withBulkhead();
RateLimitBuilder<T> withRateLimit();
TypedGuardBuilder<T> from(TypedGuard<?> baseGuard);
TypedGuard<T> build();
}
// Guard creation factory
class TypedGuard {
static <T> TypedGuardBuilder<T> create(Class<T> type);
static <T> TypedGuardBuilder<T> create(TypeToken<T> typeToken);
}// Retry configuration builder
interface RetryBuilder<T> {
RetryBuilder<T> maxRetries(int maxRetries);
RetryBuilder<T> delay(long delay, ChronoUnit unit);
RetryBuilder<T> maxDuration(long maxDuration, ChronoUnit unit);
RetryBuilder<T> jitter(long jitter, ChronoUnit unit);
RetryBuilder<T> retryOn(Class<? extends Throwable>... exceptions);
RetryBuilder<T> abortOn(Class<? extends Throwable>... exceptions);
RetryBuilder<T> withExponentialBackoff();
RetryBuilder<T> withFibonacciBackoff();
RetryBuilder<T> withCustomBackoff(Class<? extends CustomBackoffStrategy> strategy);
TypedGuardBuilder<T> done();
}
// Circuit breaker configuration builder
interface CircuitBreakerBuilder<T> {
CircuitBreakerBuilder<T> requestVolumeThreshold(int threshold);
CircuitBreakerBuilder<T> failureRatio(double ratio);
CircuitBreakerBuilder<T> delay(long delay, ChronoUnit unit);
CircuitBreakerBuilder<T> successThreshold(int threshold);
CircuitBreakerBuilder<T> name(String name);
CircuitBreakerBuilder<T> failOn(Class<? extends Throwable>... exceptions);
CircuitBreakerBuilder<T> skipOn(Class<? extends Throwable>... exceptions);
TypedGuardBuilder<T> done();
}
// Additional builder interfaces for other strategies...// Type token for generic type support
abstract class TypeToken<T> {
protected TypeToken() {}
public static <T> TypeToken<T> of(Class<T> type);
public Type getType();
}
// Usage for generic types
TypedGuard<List<String>> listGuard = TypedGuard.create(new TypeToken<List<String>>() {});
TypedGuard<Map<String, User>> mapGuard = TypedGuard.create(new TypeToken<Map<String, User>>() {});Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-smallrye-fault-tolerance