A 503 response is a remote status, and the checked RestClient call makes one request before surfacing that failure.
Spring RestClient 503 response: count attempts before adding a retry
Count the local exchange
The loopback server increments a counter and returns 503 for the receipt-risk GET. RestClientBoundaryTest expects HttpServerErrorException from retrieve().body(String.class), then asserts that the server saw one request. This proves there is no implicit second attempt in this configured fixture. It does not prove every interceptor or future client configuration is retry-free.
A retry policy is an application decision. For an idempotent GET, another attempt may be allowed only if the total request deadline has room and the upstream contract permits it. The budget worksheet keeps the attempts inside the public request deadline.
Do not flatten all failures
A 503 means a server sent an HTTP response. The stalled-response case is different: it fails while waiting for bytes. Authentication errors, malformed responses and validation failures also need different policies. Retrying every exception can multiply load during an outage.
Record status class, attempt count and elapsed time using bounded labels. Do not put a receipt ID into a metric tag. The meter lesson explains the memory and backend cost of unbounded identities.
Checked source
assertThrows(HttpServerErrorException.class, () -> riskClient.get()
.uri("/risk/receipt-2048").retrieve().body(String.class));
assertEquals(1, upstreamRequestCount.get());Verification boundary
RestClientBoundaryTest.errorStatusDoesNotTriggerAnImplicitRetry.
Costs and limits
A local 503 with one explicit request factory is checked. No retry interceptor, production upstream or shared outage is included.
Common Mistakes
- Do not call a 503 a connection timeout.
- Do not assume a framework call will retry safely by itself.
- Do not add unbounded retries to an overloaded upstream.
Read next
Spring RestClient read timeout: reject a server that accepts but stalls, Spring HTTP client timeouts: bound the call inside the request deadline, Spring HTTP retries: only replay a command with a defined identity, Spring Boot metrics: bound tag values instead of tracking each receipt.
