or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

annotation-config.mdaot.mdcaching.mdcontext-lifecycle.mdevents.mdformatting.mdi18n.mdindex.mdjmx.mdresilience.mdscheduling.mdstereotypes.mdvalidation.md
tile.json

resilience.mddocs/

Resilience Patterns (Spring 7.0+)

@EnableResilientMethods

@Configuration
@EnableResilientMethods
public class ResilienceConfig {}

@Retryable

@Service
public class ApiService {

    // Basic retry
    @Retryable(
        value = IOException.class,
        maxRetries = 3,
        delay = 1000
    )
    public String callApi(String url) throws IOException {
        return httpClient.get(url);
    }

    // Exponential backoff
    @Retryable(
        includes = {SocketTimeoutException.class, ConnectException.class},
        maxRetries = 5,
        delay = 1000,        // 1s initial
        multiplier = 2.0,    // Double each time
        maxDelay = 10000,    // Cap at 10s
        jitter = 500         // Random ±500ms
    )
    public Response retryWithBackoff(Request req) {
        return apiClient.execute(req);
    }

    // Property-based configuration
    @Retryable(
        maxRetriesString = "${api.retry.max:3}",
        delayString = "${api.retry.delay:1s}",
        multiplierString = "${api.retry.multiplier:1.5}"
    )
    public Data configurable() {
        return service.fetch();
    }

    // Exclude exceptions
    @Retryable(
        includes = Exception.class,
        excludes = {IllegalArgumentException.class, NullPointerException.class},
        maxRetries = 3
    )
    public void selectiveRetry() {
        // Retries any exception except IllegalArgumentException and NPE
    }

    // Custom predicate
    @Retryable(
        predicate = TransientErrorPredicate.class,
        maxRetries = 3,
        delay = 1000
    )
    public Result customPredicate() {
        return compute();
    }
}

public class TransientErrorPredicate implements MethodRetryPredicate {
    @Override
    public boolean shouldRetry(Method method, Throwable throwable) {
        return throwable instanceof TransientException
            && ((TransientException) throwable).isRetryable();
    }
}

@ConcurrencyLimit

@Service
@ConcurrencyLimit(10)  // Shared limit for all methods
public class DatabaseService {

    public User findUser(Long id) {
        return userRepository.findById(id);
    }

    // Method-specific limit
    @ConcurrencyLimit(3)
    public void expensiveOperation(String data) {
        // Only 3 concurrent executions
    }

    // Property-based limit
    @ConcurrencyLimit(limitString = "${db.concurrency:5}")
    public List<Order> findOrders(Long userId) {
        return orderRepository.findByUserId(userId);
    }
}

// Serial execution (mutex)
@Service
public class SingleThreadService {

    @ConcurrencyLimit(1)
    public void synchronizedOperation() {
        // Only one concurrent execution at a time
    }
}

Reactive Support

@Service
public class ReactiveApiService {

    // Works with Mono/Flux
    @Retryable(
        value = WebClientException.class,
        maxRetries = 5,
        delay = 1000,
        multiplier = 2.0
    )
    public Mono<String> fetchDataReactive(String url) {
        return webClient.get()
            .uri(url)
            .retrieve()
            .bodyToMono(String.class);
    }

    @Retryable(maxRetries = 3)
    public Flux<Item> streamItems() {
        return webClient.get()
            .uri("/items")
            .retrieve()
            .bodyToFlux(Item.class);
    }
}

Programmatic Retry

@Configuration
public class RetryConfig {

    @Bean
    public RetryInterceptor retryInterceptor() {
        MethodRetrySpec spec = new MethodRetrySpec(
            (method, throwable) -> throwable instanceof IOException,
            3,  // maxRetries
            Duration.ofSeconds(1),  // delay
            Duration.ofMillis(500), // jitter
            2.0,  // multiplier
            Duration.ofSeconds(10)  // maxDelay
        );
        return new SimpleRetryInterceptor(spec);
    }
}

Individual Feature Enablement

// Only enable retry
@Configuration
public class RetryOnlyConfig {

    @Bean
    public RetryAnnotationBeanPostProcessor retryProcessor() {
        return new RetryAnnotationBeanPostProcessor();
    }
}

// Only enable concurrency limiting
@Configuration
public class ConcurrencyOnlyConfig {

    @Bean
    public ConcurrencyLimitBeanPostProcessor concurrencyProcessor() {
        return new ConcurrencyLimitBeanPostProcessor();
    }
}