Use central registries for Retry and CircuitBreaker instances

This commit is contained in:
Jon Chambers
2025-08-27 11:33:42 -04:00
committed by GitHub
parent a8c6fa93e0
commit f616612104
33 changed files with 326 additions and 349 deletions

View File

@@ -12,6 +12,7 @@ import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
import javax.annotation.Nullable;
/**
* @param env The ios environment to use, typically SANDBOX or PRODUCTION
@@ -26,6 +27,8 @@ import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
* subscription levels
* @param appleRootCerts Apple root certificates to verify signed API responses, encoded as base64 strings:
* https://www.apple.com/certificateauthority/
* @param retryConfigurationName The name of the retry configuration to use in the App Store client; if `null`, uses the
* global default configuration.
*/
public record AppleAppStoreConfiguration(
@NotNull Environment env,
@@ -37,11 +40,5 @@ public record AppleAppStoreConfiguration(
@NotBlank String subscriptionGroupId,
@NotNull Map<String, Long> productIdToLevel,
@NotNull List<@NotBlank String> appleRootCerts,
@NotNull @Valid RetryConfiguration retry) {
public AppleAppStoreConfiguration {
if (retry == null) {
retry = new RetryConfiguration();
}
}
@Nullable String retryConfigurationName) {
}

View File

@@ -11,6 +11,7 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
import org.whispersystems.textsecuregcm.subscriptions.PaymentMethod;
@@ -22,7 +23,8 @@ import org.whispersystems.textsecuregcm.subscriptions.PaymentMethod;
* @param supportedCurrenciesByPaymentMethod the set of supported currencies
* @param graphqlUrl the Braintree GraphQL URl to use (this must match the environment)
* @param merchantAccounts merchant account within the merchant for processing individual currencies
* @param circuitBreaker configuration for the circuit breaker used by the GraphQL HTTP client
* @param circuitBreakerConfigurationName the name of the circuit breaker configuration for the breaker used by the
* GraphQL HTTP client; if `null`, uses the global default configuration
*/
public record BraintreeConfiguration(@NotBlank String merchantId,
@NotBlank String publicKey,
@@ -31,15 +33,6 @@ public record BraintreeConfiguration(@NotBlank String merchantId,
@Valid @NotEmpty Map<PaymentMethod, Set<@NotBlank String>> supportedCurrenciesByPaymentMethod,
@NotBlank String graphqlUrl,
@NotEmpty Map<String, String> merchantAccounts,
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
@Nullable String circuitBreakerConfigurationName,
@Valid @NotNull PubSubPublisherFactory pubSubPublisher) {
public BraintreeConfiguration {
if (circuitBreaker == null) {
// Its a little counter-intuitive, but this compact constructor allows a default value
// to be used when one isnt specified (e.g. in YAML), allowing the field to still be
// validated as @NotNull
circuitBreaker = new CircuitBreakerConfiguration();
}
}
}

View File

@@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotNull;
import java.util.Collections;
import java.util.Map;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
import javax.annotation.Nullable;
/**
* Configuration for the cdn3 storage manager
@@ -16,8 +17,10 @@ import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
* storage-manager when copying to determine how to read a source object. Current schemes are
* 'gcs' and 'r2'
* @param numHttpClients The number http clients to use with the storage-manager to support request striping
* @param circuitBreaker A circuit breaker configuration for the storage-manager http client
* @param retry A retry configuration for the storage-manager http client
* @param circuitBreakerConfigurationName The name of a circuit breaker configuration for the storage-manager http
* client; if `null`, uses the global default configuration
* @param retryConfigurationName The name of a retry configuration for the storage-manager http client; if
* `null`, uses the global default configuration
*/
public record Cdn3StorageManagerConfiguration(
@NotNull String baseUri,
@@ -25,8 +28,8 @@ public record Cdn3StorageManagerConfiguration(
@NotNull SecretString clientSecret,
@NotNull Map<Integer, String> sourceSchemes,
@NotNull Integer numHttpClients,
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
@NotNull @Valid RetryConfiguration retry) {
@Nullable String circuitBreakerConfigurationName,
@Nullable String retryConfigurationName) {
public Cdn3StorageManagerConfiguration {
if (numHttpClients == null) {
@@ -35,11 +38,5 @@ public record Cdn3StorageManagerConfiguration(
if (sourceSchemes == null) {
sourceSchemes = Collections.emptyMap();
}
if (circuitBreaker == null) {
circuitBreaker = new CircuitBreakerConfiguration();
}
if (retry == null) {
retry = new RetryConfiguration();
}
}
}

View File

@@ -1,29 +0,0 @@
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
/**
* Configuration used to interact with a cdn via HTTP
*/
public class ClientCdnConfiguration {
@JsonProperty
@NotNull
@Valid
CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
@JsonProperty
@NotNull
@Valid
RetryConfiguration retry = new RetryConfiguration();
public CircuitBreakerConfiguration getCircuitBreaker() {
return circuitBreaker;
}
public RetryConfiguration getRetry() {
return retry;
}
}

View File

@@ -15,6 +15,7 @@ import java.time.Duration;
import java.util.List;
import jakarta.validation.constraints.Positive;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
import javax.annotation.Nullable;
/**
* Configuration properties for Cloudflare TURN integration.
@@ -27,8 +28,10 @@ import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
* @param urlsWithIps a collection of {@link String#format(String, Object...)} patterns to be populated with resolved IP
* addresses for {@link #hostname} in responses to clients; each pattern must include a single
* {@code %s} placeholder for the IP address
* @param circuitBreaker a circuit breaker for requests to Cloudflare
* @param retry a retry policy for requests to Cloudflare
* @param circuitBreakerConfigurationName the name of a circuit breaker configuration for requests to Cloudflare; if
* `null`, uses the global default configuration
* @param retryConfigurationName the name of a retry policy for requests to Cloudflare; if `null`, uses the global
* default configuration
* @param hostname the hostname to resolve to IP addresses for use with {@link #urlsWithIps}; also transmitted to
* clients for use as an SNI when connecting to pre-resolved hosts
* @param numHttpClients the number of parallel HTTP clients to use to communicate with Cloudflare
@@ -39,24 +42,11 @@ public record CloudflareTurnConfiguration(@NotNull SecretString apiToken,
@NotNull Duration clientCredentialTtl,
@NotNull @NotEmpty @Valid List<@NotBlank String> urls,
@NotNull @NotEmpty @Valid List<@NotBlank String> urlsWithIps,
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
@NotNull @Valid RetryConfiguration retry,
@Nullable String circuitBreakerConfigurationName,
@Nullable String retryConfigurationName,
@NotBlank String hostname,
@Positive int numHttpClients) {
public CloudflareTurnConfiguration {
if (circuitBreaker == null) {
// Its a little counter-intuitive, but this compact constructor allows a default value
// to be used when one isnt specified (e.g. in YAML), allowing the field to still be
// validated as @NotNull
circuitBreaker = new CircuitBreakerConfiguration();
}
if (retry == null) {
retry = new RetryConfiguration();
}
}
@AssertTrue
@Schema(hidden = true)
public boolean isClientTtlShorterThanRequestedTtl() {

View File

@@ -9,10 +9,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.annotations.VisibleForTesting;
import io.lettuce.core.resource.ClientResources;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient;
@JsonTypeName("default")
@@ -27,9 +27,8 @@ public class RedisClusterConfiguration implements FaultTolerantRedisClusterFacto
private Duration timeout = Duration.ofSeconds(1);
@JsonProperty
@NotNull
@Valid
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
@Nullable
private String circuitBreakerConfigurationName;
@VisibleForTesting
void setConfigurationUri(final String configurationUri) {
@@ -44,8 +43,8 @@ public class RedisClusterConfiguration implements FaultTolerantRedisClusterFacto
return timeout;
}
public CircuitBreakerConfiguration getCircuitBreakerConfiguration() {
return circuitBreaker;
@Nullable public String getCircuitBreakerConfigurationName() {
return circuitBreakerConfigurationName;
}
@Override

View File

@@ -9,10 +9,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.annotations.VisibleForTesting;
import io.lettuce.core.resource.ClientResources;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
@JsonTypeName("default")
@@ -27,9 +27,8 @@ public class RedisConfiguration implements FaultTolerantRedisClientFactory {
private Duration timeout = Duration.ofSeconds(1);
@JsonProperty
@NotNull
@Valid
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
@Nullable
private String circuitBreakerConfigurationName;
public String getUri() {
return uri;
@@ -44,8 +43,8 @@ public class RedisConfiguration implements FaultTolerantRedisClientFactory {
return timeout;
}
public @NotNull @Valid CircuitBreakerConfiguration getCircuitBreakerConfiguration() {
return circuitBreaker;
@Nullable public String getCircuitBreakerConfigurationName() {
return circuitBreakerConfigurationName;
}
@Override

View File

@@ -5,24 +5,16 @@
package org.whispersystems.textsecuregcm.configuration;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
public record SecureStorageServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
@NotBlank String uri,
@NotEmpty List<@NotBlank String> storageCaCertificates,
@Valid CircuitBreakerConfiguration circuitBreaker,
@Valid RetryConfiguration retry) {
public SecureStorageServiceConfiguration {
if (circuitBreaker == null) {
circuitBreaker = new CircuitBreakerConfiguration();
}
if (retry == null) {
retry = new RetryConfiguration();
}
}
@Nullable String circuitBreakerConfigurationName,
@Nullable String retryConfigurationName) {
}

View File

@@ -11,22 +11,13 @@ import jakarta.validation.constraints.NotNull;
import java.util.List;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.util.ExactlySize;
import javax.annotation.Nullable;
public record SecureValueRecoveryConfiguration(
@NotBlank String uri,
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
@NotEmpty List<@NotBlank String> svrCaCertificates,
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
@NotNull @Valid RetryConfiguration retry) {
public SecureValueRecoveryConfiguration {
if (circuitBreaker == null) {
circuitBreaker = new CircuitBreakerConfiguration();
}
if (retry == null) {
retry = new RetryConfiguration();
}
}
@Nullable String circuitBreakerConfigurationName,
@Nullable String retryConfigurationName) {
}