Merge branch 'tenants-client-retry' into 'main'

tenants client retry logic

See merge request fforesight/tenant-commons!18
This commit is contained in:
Maverick Studer 2024-08-29 09:56:52 +02:00
commit 4493aff8ee
4 changed files with 99 additions and 6 deletions

View File

@ -2,7 +2,6 @@ package com.knecon.fforesight.tenantcommons;
import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.HttpStatus;
@ -16,7 +15,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
@FeignClient(name = "TenantsResource", url = "${tenant-user-management-service.url}")
@FeignClient(name = "TenantsResource", url = "${tenant-user-management-service.url}", configuration = TenantsClientConfiguration.class)
@ResponseStatus(value = HttpStatus.OK)
@ConditionalOnProperty(prefix = "fforesight.tenants", value = "remote", havingValue = "true")
public interface TenantsClient extends TenantProvider {

View File

@ -0,0 +1,94 @@
package com.knecon.fforesight.tenantcommons;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.RetryableException;
import feign.Retryer;
import lombok.extern.slf4j.Slf4j;
@Configuration
public class TenantsClientConfiguration {
public static final int INITIAL_BACKOFF_SECONDS = 15;
public static final int MAX_BACKOFF_MINUTES = 8;
public static final int RETRY_ATTEMPTS = 4;
@Bean
public Retryer feignRetryer() {
return new ExponentialRetryer(TimeUnit.SECONDS.toMillis(INITIAL_BACKOFF_SECONDS), TimeUnit.MINUTES.toMillis(MAX_BACKOFF_MINUTES), RETRY_ATTEMPTS);
}
@Slf4j
@SuppressWarnings("PMD.ProperCloneImplementation")
public static class ExponentialRetryer extends Retryer.Default {
private final long initialPeriod;
private final long maxPeriod;
private final int maxAttempts;
private int attempt = 1;
private long currentPeriod;
public ExponentialRetryer(long initialPeriod, long maxPeriod, int maxAttempts) {
this.initialPeriod = initialPeriod;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.currentPeriod = initialPeriod;
}
@Override
public void continueOrPropagate(RetryableException e) {
if (attempt > maxAttempts) {
log.error("Max retry attempts reached. Giving up after {} attempts.", maxAttempts);
throw e;
}
log.info("Attempt {} - Retrying due to exception: {}", attempt, e.getMessage());
long interval;
if (e.retryAfter() != null) {
interval = e.retryAfter().getTime() - System.currentTimeMillis();
if (interval > maxPeriod) {
interval = maxPeriod;
}
if (interval < 0L) {
return;
}
} else {
interval = currentPeriod;
currentPeriod = Math.min(currentPeriod * 2, maxPeriod);
}
try {
Thread.sleep(interval);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
throw e; // Rethrow the exception if interrupted
}
attempt++;
}
@Override
public ExponentialRetryer clone() {
return new ExponentialRetryer(initialPeriod, maxPeriod, maxAttempts);
}
}
}

View File

@ -17,9 +17,9 @@ public abstract class TenantMessagingConfiguration {
@Value("${POD_NAME:}")
private String podName;
private static final String TENANT_EVENTS_DLQ_SUFFIX = "_tenant_events_dlq";
private static final String TENANT_CREATED_QUEUE_SUFFIX = "_tenant_created_queue";
private static final String TENANT_DELETED_QUEUE_SUFFIX = "_tenant_deleted_queue";
private static final String TENANT_EVENTS_DLQ_SUFFIX = "_tenant_events_error";
private static final String TENANT_CREATED_QUEUE_SUFFIX = "_tenant_created";
private static final String TENANT_DELETED_QUEUE_SUFFIX = "_tenant_deleted";
// time in ms after which a deletion will be executed when no consumer is present
// see: https://www.rabbitmq.com/docs/ttl#queue-ttl

View File

@ -17,7 +17,7 @@ public class ApplicationTaskExecutorBeanConfig {
@Lazy
@Bean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME, DEFAULT_TASK_EXECUTOR_BEAN_NAME})
@ConditionalOnMissingBean({ThreadPoolTaskExecutor.class})
@ConditionalOnMissingBean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME, DEFAULT_TASK_EXECUTOR_BEAN_NAME})
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}