Skip to content

Feat/414 : Ai피드백 Resilence4j를 활용한 CB,Retry 적용#415

Merged
HeeMang-Lee merged 5 commits intodevfrom
feat/414
Aug 18, 2025
Merged

Feat/414 : Ai피드백 Resilence4j를 활용한 CB,Retry 적용#415
HeeMang-Lee merged 5 commits intodevfrom
feat/414

Conversation

@HeeMang-Lee
Copy link
Member

@HeeMang-Lee HeeMang-Lee commented Aug 18, 2025

🔎 작업 내용

  • Resilence4j 데코레이터 클래스 생성(Retry 먼저 진행 후 CB 진행)
  • properties에 서킷브레이커 리트라이 키 설정
  • Fallback구조에 데코레이터 패턴 적용

Summary by CodeRabbit

  • New Features
    • OpenAI·Claude 대화(동기) 및 스트리밍(비동기) 경로에 회로차단기·재시도 기반 탄력성 적용으로 일시적 오류 자동 재시도, 느린 호출 감지 및 장애 격리로 안정성과 복원성 향상.
  • Chores
    • 탄력성 관련 라이브러리 종속성 추가 및 중앙화된 탄력성 구성 컴포넌트 도입.
    • 회로차단기·재시도 기본·인스턴스 설정을 애플리케이션 설정에 추가하고 주석 정리.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 18, 2025

Warning

Rate limit exceeded

@HeeMang-Lee has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 22 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 000b232 and 0e236b0.

📒 Files selected for processing (3)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/ClaudeChatClient.java (3 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/OpenAiChatClient.java (3 hunks)
  • cs25-service/src/main/resources/application.properties (1 hunks)

Walkthrough

resilience4j 의존성과 구성(properties)이 추가되고 AiResilience 컴포넌트가 새로 도입됨. OpenAi/Claude 클라이언트들이 생성자로 AiResilience를 주입받아 동기/스트림 호출을 각각 executeSync/executeStream로 래핑하도록 호출 흐름이 변경됨.

Changes

Cohort / File(s) Summary
빌드 의존성 추가
cs25-service/build.gradle
resilience4j 관련 의존성 4개(resilience4j-spring-boot3, resilience4j-circuitbreaker, resilience4j-retry, resilience4j-reactor v2.3.0) 추가 및 블록 주석 추가.
Resilience 컴포넌트 추가
cs25-service/src/main/java/.../resilience/AiResilience.java
CircuitBreakerRegistry, RetryRegistry를 주입받아 동기용 executeSync와 Flux용 executeStream을 제공하는 Spring @component 추가. Retry → CircuitBreaker 순으로 적용.
AI 클라이언트 변경
cs25-service/src/main/java/.../client/ClaudeChatClient.java, cs25-service/src/main/java/.../client/OpenAiChatClient.java
생성자에 AiResilience 주입 인자 추가 및 내부 호출(call/stream)을 각각 resilience.executeSync("...", ...) / resilience.executeStream("...", ...)로 래핑하도록 제어 흐름 변경(외부 시그니처는 유지, 생성자 변경). 오류 매핑 처리 보강.
구성 프로퍼티 추가/수정
cs25-service/src/main/resources/application.properties
resilience4j circuitbreaker 및 retry 기본 설정과 인스턴스(openai, claude) 매핑 추가. 일부 주석 텍스트 정리 및 불필요 헤더 삭제.

Sequence Diagram(s)

sequenceDiagram
  participant Service as 호출자 (서비스 레이어)
  participant Client as OpenAi/Claude Client
  participant Res as AiResilience
  participant CB as CircuitBreaker
  participant Retry as Retry
  participant LLM as Underlying ChatClient

  Service->>Client: call(prompt)
  Client->>Res: executeSync("openai|claude", supplier)
  Res->>Retry: decorate supplier with Retry
  Res->>CB: decorate with CircuitBreaker
  Res->>LLM: invoke decorated supplier
  LLM-->>Res: response
  Res-->>Client: result
  Client-->>Service: return
Loading
sequenceDiagram
  participant Service as 호출자
  participant Client as OpenAi/Claude Client
  participant Res as AiResilience
  participant LLM as Underlying ChatClient (Flux)
  participant RetryOp as RetryOperator
  participant CBOp as CircuitBreakerOperator

  Service->>Client: stream(prompt)
  Client->>Res: executeStream("openai|claude", supplier)
  Res->>LLM: supplier.get() -> Flux
  Res->>RetryOp: apply RetryOperator
  Res->>CBOp: apply CircuitBreakerOperator
  LLM-->>Service: Flux events (onNext/onError)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • crocusia
  • Kimyoonbeom
  • Ksr-ccb
  • wannabeing

Poem

깡충! 토끼가 말하네, "코드에 방패 달았네" 🐇
회로는 닫고, 재시도는 살짝 다시—
스트림은 흐르고, 응답은 안전하게 도착.
작은 의존성, 큰 안전망으로 폴짝!

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/414

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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

Copy link
Contributor

@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: 3

🧹 Nitpick comments (4)
cs25-service/build.gradle (1)

36-41: Resilience4j 의존성 추가는 적절합니다. Starter 중복 여부와 Micrometer 바인더 포함 여부만 확인 부탁드립니다.

  • resilience4j-spring-boot3가 다수 모듈을 transitive로 끌어오므로, 개별 모듈(circuitbreaker, retry, reactor) 선언이 불필요할 수 있습니다. 실제 중복/중복제거 가능 여부 확인을 권장합니다.
  • Actuator + Micrometer(프로메테우스 레지스트리) 사용 중이므로 Resilience4j 메트릭 노출이 자동으로 되는지 확인해 주세요. 필요 시 resilience4j-micrometer 의존성이 추가로 요구될 수 있습니다.

검증 팁:

  • Gradle 의존성 트리 확인: ./gradlew :cs25-service:dependencies --configuration runtimeClasspath | rg resilience4j -n -C2
  • 메트릭 노출 확인: 애플리케이션 구동 후 /actuator/metrics에서 resilience4j.circuitbreaker.calls 등 지표가 보이는지 점검
cs25-service/src/main/java/com/example/cs25service/domain/ai/resilience/AiResilience.java (2)

21-32: 동기 데코레이터 순서 주석의 의미를 더 명확히 해주세요.

현재 구현은 Retry로 감싼 Supplier를 다시 CircuitBreaker로 감싸는 형태입니다(결과적으로 CB가 바깥, Retry가 안쪽). 주석의 "Retry → CircuitBreaker"가 의도(= CB가 재시도들의 외피가 아닌, 재시도 단위 내에서만 동작해야 한다/말아야 한다)를 혼동 없이 전달하도록 보완하면 유지보수에 도움이 됩니다.

예: "Retry로 supplier를 먼저 감싸고, 그 뒤에 CircuitBreaker로 최종 래핑(= CB가 Retry 전체를 외부에서 감시)" 같은 식으로 설명을 추가.


37-44: Flux.defer로 supplier 조립 시 발생 가능한 예외까지 보호하세요.

지금은 supplier.get()을 즉시 호출한 뒤 오퍼레이터를 적용합니다. 만약 조립 시점(assembly)에서 예외가 던져지면 Retry/CB에 포착되지 않을 수 있습니다. Flux.defer(supplier)를 사용해 구독 시점(subscription)까지 지연하면, Resilience4j 오퍼레이터가 모든 예외를 안정적으로 처리합니다.

아래와 같이 수정 제안드립니다.

-        return supplier.get()
-            .transformDeferred(RetryOperator.of(retry))
-            .transformDeferred(CircuitBreakerOperator.of(cb));
+        return Flux.defer(supplier)
+            .transformDeferred(RetryOperator.of(retry))
+            .transformDeferred(CircuitBreakerOperator.of(cb));
cs25-service/src/main/resources/application.properties (1)

76-87: CircuitBreaker 기본값 재검토 제안: Open 상태 30분은 과도할 수 있습니다.

  • waitDurationInOpenState=30m은 사용자 대화형 시나리오에서 복구 지연이 큽니다. 1~5분 수준부터 시작해 트래픽/장애 패턴에 맞춰 조정하는 것을 권장합니다.
  • permittedNumberOfCallsInHalfOpenState=1 역시 복구 판단에 노이즈가 있을 수 있으므로 3~10 범위에서 검토를 권장합니다.

원하시면 워크로드 가정(요청량/에러율) 기반으로 권장 파라미터 계산식/시뮬레이션 스크립트를 제공하겠습니다.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a34fbb6 and 000b232.

📒 Files selected for processing (5)
  • cs25-service/build.gradle (1 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/ClaudeChatClient.java (3 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/client/OpenAiChatClient.java (3 hunks)
  • cs25-service/src/main/java/com/example/cs25service/domain/ai/resilience/AiResilience.java (1 hunks)
  • cs25-service/src/main/resources/application.properties (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
cs25-service/src/main/java/com/example/cs25service/domain/ai/resilience/AiResilience.java (2)
cs25-service/src/main/java/com/example/cs25service/domain/ai/client/ClaudeChatClient.java (1)
  • Component (11-52)
cs25-service/src/main/java/com/example/cs25service/domain/ai/client/OpenAiChatClient.java (1)
  • Component (11-54)
🔇 Additional comments (4)
cs25-service/src/main/java/com/example/cs25service/domain/ai/client/OpenAiChatClient.java (2)

17-22: Resilience 주입 및 필드 추가 구조 좋습니다.

생성자 주입과 불변 필드로 컴포넌트 설계가 명확합니다.


26-33: 동기 호출 경로에 Resilience 적용 LGTM.

.call().content().trim()executeSync로 감싼 흐름이 자연스럽고, 구성과 이름 매핑도 openai 인스턴스와 일치합니다.

cs25-service/src/main/java/com/example/cs25service/domain/ai/client/ClaudeChatClient.java (2)

17-21: Resilience 주입 및 필드 추가 구조 좋습니다.

OpenAI 클라이언트와 대칭적인 설계로 일관성이 좋습니다.


25-31: 동기 호출 경로에 Resilience 적용 LGTM.

claude 인스턴스명과 구성의 매핑이 명확합니다.

Comment on lines +89 to +94
resilience4j.retry.configs.default.maxAttempts=2
resilience4j.retry.configs.default.waitDuration=200ms
resilience4j.retry.configs.default.enableExponentialBackoff=true
resilience4j.retry.configs.default.exponentialBackoffMultiplier=2
resilience4j.retry.instances.openai.baseConfig=default
resilience4j.retry.instances.claude.baseConfig=default
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

4xx 클라이언트 오류는 Retry/CB 집계에서 제외하는 것을 강력 권장합니다.

현재 스트림 경로에서 오류가 AiException으로 래핑되어 올라오면 예외 타입 기반 필터링이 어려워집니다(아래 클라이언트 파일 코멘트의 리팩터를 먼저 적용 권장). 그 후 아래와 같이 4xx를 무시하도록 설정하면 불필요한 재시도/CB 오픈을 줄일 수 있습니다. 429(레이트리밋)는 유지해 재시도 대상으로 남기는 편이 좋습니다.

아래 설정 추가를 제안드립니다(예: default config에 4xx 무시):

 resilience4j.retry.configs.default.maxAttempts=2
 resilience4j.retry.configs.default.waitDuration=200ms
 resilience4j.retry.configs.default.enableExponentialBackoff=true
 resilience4j.retry.configs.default.exponentialBackoffMultiplier=2
+resilience4j.retry.configs.default.ignoreExceptions[0]=org.springframework.web.reactive.function.client.WebClientResponseException$BadRequest
+resilience4j.retry.configs.default.ignoreExceptions[1]=org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized
+resilience4j.retry.configs.default.ignoreExceptions[2]=org.springframework.web.reactive.function.client.WebClientResponseException$Forbidden
+resilience4j.retry.configs.default.ignoreExceptions[3]=org.springframework.web.reactive.function.client.WebClientResponseException$NotFound
 resilience4j.retry.instances.openai.baseConfig=default
 resilience4j.retry.instances.claude.baseConfig=default

CircuitBreaker 쪽도 동일하게 4xx를 무시하도록 설정하면 더 안정적입니다.

 resilience4j.circuitbreaker.configs.default.minimumNumberOfCalls=6
 resilience4j.circuitbreaker.configs.default.waitDurationInOpenState=30m
 resilience4j.circuitbreaker.configs.default.permittedNumberOfCallsInHalfOpenState=1
 resilience4j.circuitbreaker.configs.default.automaticTransitionFromOpenToHalfOpenEnabled=true
+resilience4j.circuitbreaker.configs.default.ignoreExceptions[0]=org.springframework.web.reactive.function.client.WebClientResponseException$BadRequest
+resilience4j.circuitbreaker.configs.default.ignoreExceptions[1]=org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized
+resilience4j.circuitbreaker.configs.default.ignoreExceptions[2]=org.springframework.web.reactive.function.client.WebClientResponseException$Forbidden
+resilience4j.circuitbreaker.configs.default.ignoreExceptions[3]=org.springframework.web.reactive.function.client.WebClientResponseException$NotFound

참고: 이 설정이 효과를 내기 위해서는 아래 클라이언트 코드에서 onErrorResume로 즉시 AiException을 던지던 부분을 Resilience4j 적용 “이후”로 이동해야 합니다(원인 유지).

🤖 Prompt for AI Agents
In cs25-service/src/main/resources/application.properties around lines 89 to 94,
the Retry (and CircuitBreaker) configuration currently retries on all errors;
add configuration to exclude 4xx client errors from retry and CB evaluation
(keep 429 as retryable) by updating the default retry/circuit breaker configs to
ignore HTTP 4xx status codes (or exceptions that wrap 4xx) and add a specific
rule to treat 429 as retryable; also ensure the client code change (move the
onErrorResume that wraps/throws AiException) is applied so that the original
HTTP status-based exception is visible to Resilience4j (i.e., stop wrapping into
AiException before resilience filters run), allowing the new ignore-4xx rules to
take effect.

@HeeMang-Lee HeeMang-Lee merged commit 62dde4e into dev Aug 18, 2025
1 check passed
@HeeMang-Lee HeeMang-Lee deleted the feat/414 branch August 18, 2025 06:47
This was referenced Aug 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI Fallback구조 보완) Spring Retry 및 서킷브레이커 도입

3 participants