Skip to content
This repository was archived by the owner on Jan 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/java/com/example/spot/common/config/FeignRetryConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.spot.common.config;

import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignRetryConfig {

@Bean
Retryer retryer() {
return new Retryer.Default(200, 800, 3);
}
Comment on lines +10 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Retry may not trigger on 5xx/429 without a RetryableException source

Feign retries only when a RetryableException is thrown (e.g., I/O errors by the client or via a custom ErrorDecoder). If you intend to retry on 5xx/429, also provide an ErrorDecoder that maps those statuses to RetryableException or use Resilience4j/Spring Retry.

Run this to check if such wiring already exists:


🏁 Script executed:

#!/bin/bash
# Look for custom ErrorDecoder implementations
rg -n "class .* implements ErrorDecoder" -A 3

# Calls that throw RetryableException explicitly
rg -n "RetryableException" -A 2

# Per-client Feign configs that might override global beans
rg -n '@FeignClient\(' -A 5

# Other Retryer beans (potential conflicts)
rg -n "Retryer\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*\\(" -n -S

Length of output: 4916


Add a custom ErrorDecoder to trigger retries on HTTP 5xx/429

Currently you’ve defined a global Retryer in FeignRetryConfig (src/main/java/com/example/spot/common/config/FeignRetryConfig.java:11), but there are no custom ErrorDecoder implementations in the codebase. By default Feign only retries when a RetryableException is thrown (typically on I/O errors), not on HTTP status codes.

What to do:

  • Implement and register an ErrorDecoder bean that maps 429 and 5xx responses to RetryableException.
  • You can register it globally in your config or reference it in individual @FeignClient(configuration = …) definitions.

Example snippet:

@Bean
public ErrorDecoder feignErrorDecoder() {
    return (methodKey, response) -> {
        int status = response.status();
        if (status == 429 || (status >= 500 && status <= 599)) {
            return new RetryableException(
                status,
                "Retry on HTTP " + status,
                response.request().httpMethod(),
                null,
                response.request()
            );
        }
        return new Default().decode(methodKey, response);
    };
}
🤖 Prompt for AI Agents
In src/main/java/com/example/spot/common/config/FeignRetryConfig.java around
lines 10 to 13, you need to add a custom ErrorDecoder bean that triggers retries
on HTTP 429 and 5xx status codes. Implement an ErrorDecoder that checks the
response status, and if it is 429 or between 500 and 599, return a
RetryableException to enable retry logic. Register this ErrorDecoder as a bean
in the same configuration class so Feign uses it globally alongside the existing
Retryer bean.

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static <T> T run(Supplier<T> call) {

private static String extractMessage(FeignException e) {
return e.responseBody()
.map(body -> new String(body.array())) // byte[] → String
.map(body -> new String(body.array()))
.orElse(e.getMessage());
Comment on lines 21 to 24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix ByteBuffer decoding: array() is unsafe and ignores position/limit; specify charset

Using new String(body.array()) can throw UnsupportedOperationException for direct/read-only buffers and may include extra bytes beyond the current view. Also relies on platform default charset. Decode using position/remaining and a defined charset.

Apply this diff within extractMessage:

-    private static String extractMessage(FeignException e) {
-        return e.responseBody()
-                .map(body -> new String(body.array()))
-                .orElse(e.getMessage());
-    }
+    private static String extractMessage(FeignException e) {
+        return e.responseBody()
+                .map(buf -> {
+                    var readOnly = buf.asReadOnlyBuffer();
+                    byte[] bytes;
+                    if (readOnly.hasArray()) {
+                        int offset = readOnly.arrayOffset() + readOnly.position();
+                        int length = readOnly.remaining();
+                        bytes = java.util.Arrays.copyOfRange(readOnly.array(), offset, offset + length);
+                    } else {
+                        bytes = new byte[readOnly.remaining()];
+                        readOnly.get(bytes);
+                    }
+                    return new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
+                })
+                .orElse(e.getMessage());
+    }

Note: This uses java.util.Arrays and java.nio.charset.StandardCharsets; import if desired or keep FQCNs as above.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static String extractMessage(FeignException e) {
return e.responseBody()
.map(body -> new String(body.array())) // byte[] → String
.map(body -> new String(body.array()))
.orElse(e.getMessage());
private static String extractMessage(FeignException e) {
return e.responseBody()
.map(buf -> {
var readOnly = buf.asReadOnlyBuffer();
byte[] bytes;
if (readOnly.hasArray()) {
int offset = readOnly.arrayOffset() + readOnly.position();
int length = readOnly.remaining();
bytes = java.util.Arrays.copyOfRange(
readOnly.array(),
offset,
offset + length
);
} else {
bytes = new byte[readOnly.remaining()];
readOnly.get(bytes);
}
return new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
})
.orElse(e.getMessage());
}
🤖 Prompt for AI Agents
In
src/main/java/com/example/spot/common/infrastructure/feign/SafeFeignExecutor.java
lines 21 to 24, the method extractMessage uses new String(body.array()) which is
unsafe for ByteBuffer as it ignores position and limit and may throw
UnsupportedOperationException for direct or read-only buffers, also it uses the
platform default charset. To fix this, decode the ByteBuffer using its position
and remaining bytes with a specified charset like StandardCharsets.UTF_8, for
example by creating a byte array of size body.remaining(), copying the bytes
from the buffer starting at its current position, and then constructing the
String with the specified charset.

}
}