Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update retry configuration for Azure SDKs service builder in Spring tier #25104

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

import com.azure.data.appconfiguration.ConfigurationServiceVersion;
import com.azure.spring.cloud.autoconfigure.properties.AbstractAzureHttpConfigurationProperties;
import com.azure.spring.core.properties.retry.HttpRetryProperties;
import com.azure.spring.service.appconfiguration.AppConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
* Properties for Azure App Configuration.
Expand All @@ -19,6 +21,14 @@ public class AzureAppConfigurationProperties extends AbstractAzureHttpConfigurat
private String connectionString;
private ConfigurationServiceVersion serviceVersion;

@NestedConfigurationProperty
private final HttpRetryProperties retry = new HttpRetryProperties();
saragluna marked this conversation as resolved.
Show resolved Hide resolved

@Override
public HttpRetryProperties getRetry() {
return retry;
}

public String getEndpoint() {
return endpoint;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.azure.cosmos.models.CosmosPermissionProperties;
import com.azure.spring.cloud.autoconfigure.properties.AbstractAzureServiceConfigurationProperties;
import com.azure.spring.core.properties.client.ClientProperties;
import com.azure.spring.core.properties.retry.RetryProperties;
import com.azure.spring.service.cosmos.CosmosProperties;
import com.azure.spring.core.properties.proxy.HttpProxyProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
Expand All @@ -32,6 +33,9 @@ public class AzureCosmosProperties extends AbstractAzureServiceConfigurationProp
@NestedConfigurationProperty
private final HttpProxyProperties proxy = new HttpProxyProperties();

@NestedConfigurationProperty
private final RetryProperties retry = new RetryProperties();
saragluna marked this conversation as resolved.
Show resolved Hide resolved

@NotEmpty
@Pattern(regexp = "http[s]{0,1}://.*.documents.azure.com.*")
private String endpoint;
Expand Down Expand Up @@ -74,6 +78,11 @@ public HttpProxyProperties getProxy() {
return proxy;
}

@Override
public RetryProperties getRetry() {
return retry;
}

@Override
public ClientProperties getClient() {
return new ClientProperties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package com.azure.spring.cloud.autoconfigure.keyvault;

import com.azure.spring.cloud.autoconfigure.properties.AbstractAzureHttpConfigurationProperties;
import com.azure.spring.core.properties.retry.HttpRetryProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
* Common properties for Azure Key Vault
Expand All @@ -13,6 +15,14 @@ public class AzureKeyVaultProperties extends AbstractAzureHttpConfigurationPrope
// TODO (xiada): the default vault url
private String endpoint;

@NestedConfigurationProperty
private final HttpRetryProperties retry = new HttpRetryProperties();

@Override
public HttpRetryProperties getRetry() {
return retry;
}

public String getEndpoint() {
return endpoint;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public void postProcessEnvironment(ConfigurableEnvironment environment, SpringAp
addKeyVaultPropertySource(environment, properties);
}
} else {
logger.debug("Key Vault property source is not enabled");
logger.debug("Key Vault 'propertySourceEnabled' or 'enabled' is not enabled");
}
}

Expand Down Expand Up @@ -171,8 +171,8 @@ private AzureKeyVaultSecretProperties loadProperties(Binder binder) {
* @return true if the key vault is enabled, false otherwise.
*/
private boolean isKeyVaultPropertySourceEnabled(AzureKeyVaultSecretProperties properties) {
return Boolean.TRUE.equals(properties.getPropertySourceEnabled())
|| !properties.getPropertySources().isEmpty();
return (Boolean.TRUE.equals(properties.getPropertySourceEnabled()) || !properties.getPropertySources().isEmpty())
&& Boolean.TRUE.equals(properties.isEnabled());
}

private boolean isKeyVaultClientAvailable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import com.azure.security.keyvault.secrets.SecretServiceVersion;
import com.azure.spring.cloud.autoconfigure.properties.AbstractAzureHttpConfigurationProperties;
import com.azure.spring.core.properties.retry.HttpRetryProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

import java.time.Duration;
import java.util.List;
Expand All @@ -29,6 +31,14 @@ public class AzureKeyVaultPropertySourceProperties extends AbstractAzureHttpConf
private List<String> secretKeys;
private Duration refreshInterval = DEFAULT_REFRESH_INTERVAL;

@NestedConfigurationProperty
private final HttpRetryProperties retry = new HttpRetryProperties();

@Override
public HttpRetryProperties getRetry() {
return retry;
}

public String getName() {
return name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.azure.spring.cloud.autoconfigure.properties;

import com.azure.spring.core.properties.retry.RetryProperties;
import com.azure.spring.core.properties.proxy.ProxyProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

Expand All @@ -14,6 +15,9 @@ public abstract class AbstractAzureAmqpConfigurationProperties extends AbstractA
@NestedConfigurationProperty
protected final AmqpClientConfigurationProperties client = new AmqpClientConfigurationProperties();

@NestedConfigurationProperty
protected final RetryProperties retry = new RetryProperties();

@NestedConfigurationProperty
protected final ProxyProperties proxy = new ProxyProperties();

Expand All @@ -22,6 +26,11 @@ public AmqpClientConfigurationProperties getClient() {
return client;
}

@Override
public RetryProperties getRetry() {
return retry;
}

@Override
public ProxyProperties getProxy() {
return proxy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.azure.spring.core.properties.credential.TokenCredentialProperties;
import com.azure.spring.core.properties.profile.AzureProfile;
import com.azure.spring.core.properties.resource.AzureResourceMetadata;
import com.azure.spring.core.properties.retry.RetryProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
Expand All @@ -17,9 +16,6 @@ public abstract class AbstractAzureServiceConfigurationProperties implements Azu

protected boolean enabled = true;

@NestedConfigurationProperty
protected final RetryProperties retry = new RetryProperties();

@NestedConfigurationProperty
protected final TokenCredentialProperties credential = new TokenCredentialProperties();

Expand All @@ -37,11 +33,6 @@ public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Override
public RetryProperties getRetry() {
return retry;
}

@Override
public TokenCredentialProperties getCredential() {
return credential;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package com.azure.spring.cloud.autoconfigure.storage.common;

import com.azure.spring.cloud.autoconfigure.properties.AbstractAzureHttpConfigurationProperties;
import com.azure.spring.core.properties.retry.StorageRetryProperties;
import com.azure.spring.service.storage.common.StorageProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
* Common properties for all Azure Storage services.
Expand All @@ -21,6 +23,14 @@ public class AzureStorageProperties extends AbstractAzureHttpConfigurationProper

protected String accountName;

@NestedConfigurationProperty
protected final StorageRetryProperties retry = new StorageRetryProperties();

@Override
public StorageRetryProperties getRetry() {
return retry;
}

public String getEndpoint() {
return endpoint;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.core.converter;

import com.azure.spring.core.properties.retry.StorageRetryProperties;
import com.azure.spring.core.properties.retry.RetryProperties;
import com.azure.storage.common.policy.RequestRetryOptions;
import com.azure.storage.common.policy.RetryPolicyType;
import org.springframework.core.convert.converter.Converter;

import java.time.Duration;

/**
* Converts a {@link StorageRetryProperties} to a {@link RequestRetryOptions}.
*/
public final class AzureRequestRetryOptionsConverter implements Converter<StorageRetryProperties, RequestRetryOptions> {
saragluna marked this conversation as resolved.
Show resolved Hide resolved

@Override
public RequestRetryOptions convert(StorageRetryProperties retryProperties) {
if (retryProperties == null) {
return new RequestRetryOptions();
}

RetryPolicyType retryPolicyType = null;
Duration delay = null;
Duration maxDelay = null;
final RetryProperties.BackoffProperties backoffProperties = retryProperties.getBackoff();
if (backoffProperties != null) {
if (backoffProperties.getMultiplier() != null && backoffProperties.getMultiplier() > 0) {
retryPolicyType = RetryPolicyType.EXPONENTIAL;
} else {
retryPolicyType = RetryPolicyType.FIXED;
}
delay = backoffProperties.getDelay();
maxDelay = backoffProperties.getMaxDelay();
}
return new RequestRetryOptions(retryPolicyType, retryProperties.getMaxAttempts(),
retryProperties.getTimeout(), delay, maxDelay, retryProperties.getSecondaryHost());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.core.converter;

import com.azure.core.http.policy.ExponentialBackoff;
import com.azure.core.http.policy.FixedDelay;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.http.policy.RetryStrategy;
import com.azure.spring.core.properties.retry.HttpRetryProperties;
import com.azure.spring.core.properties.retry.RetryProperties;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;

/**
* Converts a {@link HttpRetryProperties} to a {@link RetryPolicy}.
*/
public final class AzureRetryPolicyConverter implements Converter<HttpRetryProperties, RetryPolicy> {

@Override
public RetryPolicy convert(HttpRetryProperties properties) {
if (properties == null || properties.getMaxAttempts() == null) {
return new RetryPolicy();
}

Assert.isTrue(properties.getMaxAttempts().intValue() >= 0, "MaxAttempts can not be less than 0");
RetryStrategy retryStrategy = null;
final RetryProperties.BackoffProperties backoff = properties.getBackoff();
int maxAttempts = properties.getMaxAttempts().intValue();
if (backoff != null) {
Assert.notNull(backoff.getDelay(), "Backoff delay can not be null");
Assert.isTrue(backoff.getDelay().toMillis() > 0, "Backoff delay can not be less than or equal to 0");
if (backoff.getMultiplier() != null && backoff.getMultiplier() > 0) {
Assert.notNull(backoff.getMaxDelay(), "Backoff maxDelay can not be null");
Assert.isTrue(backoff.getMaxDelay().toMillis() > backoff.getDelay().toMillis(),
"Backoff maxDelay can not be less than backoff delay");
retryStrategy = new ExponentialBackoff(maxAttempts, backoff.getDelay(), backoff.getMaxDelay());
} else {
retryStrategy = new FixedDelay(maxAttempts, backoff.getDelay());
}
}
return new RetryPolicy(retryStrategy, properties.getRetryAfterHeader(), properties.getRetryAfterTimeUnit());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
import com.azure.core.http.ProxyOptions;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.util.Header;
import com.azure.core.util.HttpClientOptions;
import com.azure.spring.core.converter.AzureHttpLogOptionsConverter;
import com.azure.spring.core.converter.AzureHttpProxyOptionsConverter;
import com.azure.spring.core.converter.AzureRetryPolicyConverter;
import com.azure.spring.core.http.DefaultHttpProvider;
import com.azure.spring.core.properties.client.ClientProperties;
import com.azure.spring.core.properties.client.HttpClientProperties;
import com.azure.spring.core.properties.proxy.HttpProxyProperties;
import com.azure.spring.core.properties.proxy.ProxyProperties;
import com.azure.spring.core.properties.retry.HttpRetryProperties;
import com.azure.spring.core.properties.retry.RetryProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -41,7 +45,7 @@ public abstract class AbstractAzureHttpClientBuilderFactory<T> extends AbstractA
private HttpPipeline httpPipeline;
private final AzureHttpProxyOptionsConverter proxyOptionsConverter = new AzureHttpProxyOptionsConverter();
private final AzureHttpLogOptionsConverter logOptionsConverter = new AzureHttpLogOptionsConverter();

private final AzureRetryPolicyConverter httpRetryOptionsConverter = new AzureRetryPolicyConverter();
protected abstract BiConsumer<T, HttpClient> consumeHttpClient();

protected abstract BiConsumer<T, HttpPipelinePolicy> consumeHttpPipelinePolicy();
Expand All @@ -50,6 +54,10 @@ public abstract class AbstractAzureHttpClientBuilderFactory<T> extends AbstractA

protected abstract BiConsumer<T, HttpLogOptions> consumeHttpLogOptions();

protected BiConsumer<T, RetryPolicy> consumeRetryPolicy() {
return (a, b) -> { };
}

@Override
protected void configureCore(T builder) {
super.configureCore(builder);
Expand Down Expand Up @@ -119,14 +127,23 @@ protected void configureHttpTransportProperties(T builder) {

@Override
protected void configureRetry(T builder) {
RetryProperties retryProperties = getAzureProperties().getRetry();
if (retryProperties == null) {
return;
}

if (retryProperties instanceof HttpRetryProperties) {
RetryPolicy retryPolicy = httpRetryOptionsConverter.convert((HttpRetryProperties) retryProperties);
consumeRetryPolicy().accept(builder, retryPolicy);
}
saragluna marked this conversation as resolved.
Show resolved Hide resolved
}

protected void configureHttpPipelinePolicies(T builder) {
for (HttpPipelinePolicy policy : this.httpPipelinePolicies) {
consumeHttpPipelinePolicy().accept(builder, policy);
}
}

protected List<Header> getHeaders() {
final ClientProperties client = getAzureProperties().getClient();
if (client == null || client.getHeaders() == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.core.properties.retry;

import java.time.temporal.ChronoUnit;

/**
* Unified http retry properties for all Azure SDKs based on HTTP.
*/
public class HttpRetryProperties extends RetryProperties {

private String retryAfterHeader;
private ChronoUnit retryAfterTimeUnit;
saragluna marked this conversation as resolved.
Show resolved Hide resolved

public String getRetryAfterHeader() {
return retryAfterHeader;
}

public void setRetryAfterHeader(String retryAfterHeader) {
this.retryAfterHeader = retryAfterHeader;
}

public ChronoUnit getRetryAfterTimeUnit() {
return retryAfterTimeUnit;
}

public void setRetryAfterTimeUnit(ChronoUnit retryAfterTimeUnit) {
this.retryAfterTimeUnit = retryAfterTimeUnit;
}
}
Loading