Skip to content
This repository was archived by the owner on Jan 11, 2026. It is now read-only.

[FEATURE] Feign 재시도 관련 설정 추가#29

Merged
msk226 merged 1 commit intodevelopfrom
SPOT-301/refactor
Aug 8, 2025
Merged

[FEATURE] Feign 재시도 관련 설정 추가#29
msk226 merged 1 commit intodevelopfrom
SPOT-301/refactor

Conversation

@msk226
Copy link
Member

@msk226 msk226 commented Aug 8, 2025

#️⃣ 연관된 이슈


🔎 작업 내용

  • 누락된 Feign 재시도 관련 설정 추가

📷 스크린샷 (선택)

작업한 결과물에 대한 간단한 스크린샷을 올려주세요.


💬리뷰 요구사항 (선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요.

Summary by CodeRabbit

  • New Features

    • Improved reliability for Feign client requests with automatic retry logic on failed requests.
  • Style

    • Minor cleanup of inline comments for improved code readability (no impact on functionality).

@msk226 msk226 self-assigned this Aug 8, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 8, 2025

Walkthrough

A new Spring configuration class, FeignRetryConfig, was added to define retry behavior for Feign clients, specifying retry intervals and attempt limits. Separately, a comment was removed from the SafeFeignExecutor class, with no changes to its logic or functionality.

Changes

Cohort / File(s) Change Summary
Feign Retry Configuration
src/main/java/com/example/spot/common/config/FeignRetryConfig.java
Introduced a new Spring configuration class providing a Feign Retryer.Default bean with custom retry settings.
Minor Comment Cleanup
src/main/java/com/example/spot/common/infrastructure/feign/SafeFeignExecutor.java
Removed an inline comment from a lambda; no logic or functional changes.

Sequence Diagram(s)

sequenceDiagram
    participant ApplicationContext
    participant FeignRetryConfig
    participant FeignClient

    ApplicationContext->>FeignRetryConfig: Initialize configuration
    FeignRetryConfig->>ApplicationContext: Register Retryer bean
    FeignClient->>ApplicationContext: Retrieve Retryer bean
    FeignClient->>FeignClient: Use Retryer for retry logic on failures
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~7 minutes

Poem

In a patch of code, a rabbit hops,
Adds a retry bean with tiny stops.
Feign now waits, then tries again,
While comments vanish, clear as rain.
With every hop, the code grows neat—
Spring’s configuration, oh, what a treat!
🐇✨

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SPOT-301/refactor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@msk226 msk226 merged commit 50d3842 into develop Aug 8, 2025
1 of 2 checks passed
@msk226 msk226 deleted the SPOT-301/refactor branch August 8, 2025 10:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/main/java/com/example/spot/common/config/FeignRetryConfig.java (2)

7-13: Make retry parameters configurable via properties (optional)

Hard-coded periods/attempts hinder environment-specific tuning. Suggest binding via ConfigurationProperties (e.g., feign.retry.period, maxPeriod, maxAttempts) for prod vs. test flexibility.

Example outline:

// @ConfigurationProperties(prefix = "feign.retry")
public record FeignRetryProps(long period, long maxPeriod, int maxAttempts) {}

@Bean
Retryer retryer(FeignRetryProps p) {
    return new Retryer.Default(p.period(), p.maxPeriod(), p.maxAttempts());
}

And set values in application.yml per environment.


10-13: Caution: retries on non-idempotent requests

If any Feign clients perform non-idempotent operations (POST/PUT without idempotency keys), retries can cause duplicate side effects on transient failures. Consider:

  • Retrying only idempotent methods, or
  • Adding an Idempotency-Key header via a request interceptor for those endpoints.
src/main/java/com/example/spot/common/infrastructure/feign/SafeFeignExecutor.java (1)

23-23: Comment removal is fine

The inline comment removal is a no-op. The functional issue above is independent and worth fixing.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed6fe1f and 1ac6297.

📒 Files selected for processing (2)
  • src/main/java/com/example/spot/common/config/FeignRetryConfig.java (1 hunks)
  • src/main/java/com/example/spot/common/infrastructure/feign/SafeFeignExecutor.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_test
🔇 Additional comments (1)
src/main/java/com/example/spot/common/config/FeignRetryConfig.java (1)

10-13: Good addition: global Feign Retryer with exponential backoff

Registers a Retryer.Default(200, 800, 3). This matches the PR goal and will apply to Feign when Spring scans this config.

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

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.

Comment on lines 21 to 24
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());
Copy link

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.

@msk226 msk226 added the 👍 feature New feature or request label Aug 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

👍 feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant