DEV Community

Luis Fabrício De Llamas
Luis Fabrício De Llamas

Posted on

SmallRye Fault Tolerance na prática: retry, fallback e circuit breaker em um gateway Pix com Quarkus

Neste artigo mostro como usar SmallRye Fault Tolerance com Quarkus para proteger um gateway Pix com @Retry, @Timeout, @CircuitBreaker e @Fallback. O cenário simula uma fintech que precisa de resiliência real quando o provedor de autorização fica instável.

O cenário que eu montei

A aplicação simula um gateway Pix de fintech recebendo pedidos de cobrança e chamando um provedor externo de autorização. Quando esse provedor fica instável, a API não pode simplesmente responder erro para tudo e deixar o time operacional apagar incêndio no braço.

Para esse lab, o fluxo ficou assim:

  • cobrança comum tenta novamente e segue o jogo
  • cobrança mais alta não insiste para sempre
  • quando o risco operacional aumenta, a resposta volta como análise manual

Foi aí que quarkus-smallrye-fault-tolerance fez sentido de verdade.

Estrutura do projeto

.
├── README.md
├── ARTICLE.md
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── dev
│   │   │       └── dailylab
│   │   │           └── payments
│   │   │               ├── PixChargeRequest.java
│   │   │               ├── PixChargeResource.java
│   │   │               ├── PixChargeResult.java
│   │   │               ├── PixChargeService.java
│   │   │               └── PixGatewayClient.java
│   │   └── resources
│   │       └── application.properties
│   └── test
│       └── java
│           └── dev
│               └── dailylab
│                   └── payments
│                       └── PixChargeResourceTest.java
└── mvnw
Enter fullscreen mode Exit fullscreen mode

Onde a extensão entrou de verdade

O coração do lab está no serviço. A chamada externa foi protegida com retry, timeout, circuit breaker e fallback no mesmo método.

@Retry(maxRetries = 2, delay = 200, delayUnit = ChronoUnit.MILLIS)
@Timeout(value = 400, unit = ChronoUnit.MILLIS)
@CircuitBreaker(requestVolumeThreshold = 4, failureRatio = 0.75, delay = 2, delayUnit = ChronoUnit.SECONDS)
@Fallback(fallbackMethod = "reserveForManualReview")
public PixChargeResult process(PixChargeRequest request) {
    int attempt = gatewayClient.nextAttempt();
    if (request.amount().compareTo(new BigDecimal("1500.00")) > 0) {
        throw new IllegalStateException("provider timeout on high-value pix charge");
    }
    if (attempt < 3) {
        throw new IllegalStateException("temporary provider instability on attempt " + attempt);
    }
    return new PixChargeResult(
            "APPROVED",
            "primary-pix-provider",
            request.amount(),
            "Charge approved after retry strategy stabilized the provider call",
            request.correlationId());
}
Enter fullscreen mode Exit fullscreen mode

O que eu gosto aqui é que a regra fica visível. Não tem controller fazendo malabarismo, nem exception espalhada em três camadas diferentes. A decisão operacional está no ponto em que a falha realmente importa.

O endpoint HTTP ficou enxuto:

@Path("/pix-charges")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class PixChargeResource {

    private final PixChargeService service;

    public PixChargeResource(PixChargeService service) {
        this.service = service;
    }

    @POST
    public PixChargeResult create(PixChargeRequest request) {
        return service.process(request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Configuração usada

A configuração ficou pequena porque o objetivo era mostrar a extensão resolvendo um caso concreto.

quarkus.swagger-ui.always-include=true
quarkus.smallrye-openapi.info-title=Lab SmallRye Fault Tolerance
quarkus.smallrye-openapi.info-version=1.0.0
quarkus.smallrye-openapi.info-description=API de fallback para cobrancas Pix em um contexto de antifraude e estabilidade de gateway.
Enter fullscreen mode Exit fullscreen mode

Como rodar e testar

Subindo localmente:

./mvnw quarkus:dev
Enter fullscreen mode Exit fullscreen mode

Cobrança que passa depois do retry:

curl -X POST http://localhost:8080/pix-charges \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "cust-01",
    "amount": 1200.00,
    "correlationId": "pix-001"
  }'
Enter fullscreen mode Exit fullscreen mode

Cobrança que desvia para análise manual:

curl -X POST http://localhost:8080/pix-charges \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "cust-02",
    "amount": 2500.00,
    "correlationId": "pix-002"
  }'
Enter fullscreen mode Exit fullscreen mode

Swagger:

  • http://localhost:8080/q/swagger-ui

O que eu tirei desse lab

Essa extensão ganha muito quando o exemplo deixa de ser acadêmico. No caso da cobrança Pix, retry e fallback não são enfeite: eles viram uma decisão de operação. Cobrança pequena pode insistir um pouco. Cobrança maior precisa preservar o sistema e cair para outro fluxo.

Também ficou claro que esse tipo de lab funciona melhor quando o código é curto, o cenário é específico e o teste cobre a resposta final. A leitura fica muito mais honesta.

Link pro GitHub

https://github.com/dellamas/quarkus-smallrye-fault-tolerance

Gostou e quer ver mais conteúdos como este? Então deixa uma ⭐ no repositório.

No LinkedIn eu sempre posto sobre eventos, comunidade Java e Quarkus:

💼 LinkedIn

E o artigo deste projeto está no dev.to:

✍️ dev.to

Top comments (0)