Skip to content

feat: AI 이미지 생성 DLQ 및 재시도 메커니즘 구현#132

Merged
yooooonshine merged 4 commits intodevelopfrom
modic_backend_131
Aug 25, 2025
Merged

feat: AI 이미지 생성 DLQ 및 재시도 메커니즘 구현#132
yooooonshine merged 4 commits intodevelopfrom
modic_backend_131

Conversation

@goalSetter09
Copy link
Collaborator

@goalSetter09 goalSetter09 commented Aug 9, 2025

Summary

resolved #131

  • RabbitMQ DLQ (Dead Letter Queue) 및 재시도 메커니즘을 구현하여 AI 이미지 생성 요청의 안정성 개선
  • 실패한 메시지에 대한 자동 재시도 및 최종 실패 처리 로직 추가
  • DlqListener를 통한 최종 실패 메시지 처리 및 상태 업데이트

주요 변경사항

  • RabbitMQ DLQ 관련 상수 추가: DLQ, 재시도 Exchange/Queue 관련 상수 정의
  • DLQ 및 재시도 메커니즘 구현: RabbitMqConfig에 DLX, DLQ, 재시도 Queue 설정 추가
  • DlqListener 기본 구조 생성: 실패 메시지 처리를 위한 리스너 클래스 구현
  • 최종 실패 메시지 처리: DLQ의 메시지를 처리하여 AI 요청 상태를 FAILED로 업데이트

Test plan

  • AI 이미지 생성 요청이 실패했을 때 DLQ로 메시지가 전송되는지 확인
  • 재시도 메커니즘이 정상적으로 동작하는지 확인 (60초 후 재시도)
  • 최종 실패 시 DlqListener가 메시지를 처리하고 상태를 FAILED로 업데이트하는지 확인
  • RabbitMQ 연결 및 Queue/Exchange 설정이 정상적으로 생성되는지 확인

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 신규 기능
    • AI 이미지 요청 메시지 처리에 대해 실패 시 자동 재시도 및 최종 실패 메시지 관리를 위한 큐 및 교환 설정이 추가되었습니다.
    • 최종 실패한 AI 이미지 요청 메시지를 감지하고 해당 요청의 상태를 실패로 기록하는 기능이 도입되었습니다.

goalSetter09 and others added 4 commits August 9, 2025 17:43
AI 이미지 생성 요청의 실패 처리 및 재시도 메커니즘을 위한
DLX, DLQ, Retry 관련 상수들을 RabbitMqConfig에 추가

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- DLX, DLQ, 재시도 Exchange 및 Queue Bean 추가
- aiImageRequestQueue에 DLX 설정 적용
- TTL 60초 재시도 대기 큐 구성
- 실패한 메시지의 자동 재시도 및 최종 처리 인프라 완성

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 실패한 AI 이미지 생성 요청 처리용 리스너 클래스 추가
- 최대 재시도 횟수 3회, 재시도 헤더 관리 설정
- AiRequestRepository, RabbitTemplate 의존성 주입
- 후속 메서드 구현을 위한 기본 프레임워크 완성

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 최종 실패한 AI 이미지 생성 요청을 위한 핸들러 추가
- AI_IMAGE_REQUEST_DLQ 큐 리스너 구현
- 요청 상태를 FAILED로 업데이트하는 트랜잭션 처리
- 요청이 존재하지 않는 경우 오류 로깅 추가
- 단순화된 DLQ 처리 로직으로 안정성 향상

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Aug 9, 2025

Walkthrough

AI 이미지 요청 큐에 대한 RabbitMQ dead-letter(사망 편지) 및 재시도 메커니즘이 추가되었습니다. 큐, 익스체인지, 라우팅 키 관련 상수와 빈이 신설 및 확장되었고, DLQ 메시지를 처리하여 요청 엔티티 상태를 갱신하는 새로운 리스너 클래스가 도입되었습니다.

Changes

Cohort / File(s) Change Summary
RabbitMQ Dead-letter 및 Retry 구성
src/main/java/hanium/modic/backend/common/amqp/config/RabbitMqConfig.java
AI 이미지 요청 큐에 dead-letter exchange, dead-letter queue, retry exchange, retry queue, TTL 및 라우팅 바인딩 등 재시도 흐름 추가. 관련 상수 및 빈 메서드 신설. 기존 큐 선언에 DLX, 라우팅 키 인자 추가.
DLQ 메시지 최종 처리 리스너
src/main/java/hanium/modic/backend/domain/ai/listener/DlqListener.java
DLQ에서 메시지를 수신하여 실패한 AI 요청의 상태를 DB에서 FAILED로 갱신하는 새 리스너 클래스 추가. 트랜잭션 처리 및 로깅 포함.

Sequence Diagram(s)

sequenceDiagram
    participant Producer
    participant AIImageRequestQueue
    participant Consumer
    participant DLX (Dead-letter Exchange)
    participant RetryExchange
    participant RetryQueue
    participant DLQ
    participant DlqListener
    participant DB

    Producer->>AIImageRequestQueue: 메시지 전송
    AIImageRequestQueue->>Consumer: 메시지 전달
    Consumer-->>AIImageRequestQueue: 처리 실패 (예외 발생)
    AIImageRequestQueue->>DLX: 메시지 dead-lettering
    DLX->>RetryExchange: dead-letter routing
    RetryExchange->>RetryQueue: 메시지 저장 (TTL 60초)
    RetryQueue->>AIImageRequestQueue: TTL 만료 후 재전송
    AIImageRequestQueue->>Consumer: 재시도
    alt 재시도 실패 반복 후 최대 횟수 초과
        AIImageRequestQueue->>DLX: 메시지 dead-lettering
        DLX->>DLQ: 최종 실패 메시지 전달
        DLQ->>DlqListener: 메시지 수신
        DlqListener->>DB: 요청 상태 FAILED로 변경
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15–20 minutes

Possibly related issues

  • AI 이미지 실패 처리 #131: AI 이미지 요청에 대한 DLQ 및 재시도 처리, DLQ 메시지 최종 처리 리스너 구현 요구사항을 직접적으로 반영합니다.

Poem

🐰
큐를 돌고 도는 메시지의 춤,
실패하면 DLQ로 점프!
재시도 끝에 또 실패하면
리스너 토끼가 살포시 확인,
"이제 그만, 상태는 FAILED!"
토끼는 오늘도 로그를 남기며
메시지 여정에 마침표를 찍어요.

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 modic_backend_131

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.

@goalSetter09 goalSetter09 requested a review from Copilot August 9, 2025 14:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements a Dead Letter Queue (DLQ) and retry mechanism for AI image generation requests to improve system reliability. The implementation includes automatic retry logic for failed messages and final failure handling through a dedicated DLQ listener.

  • Adds RabbitMQ DLQ and retry infrastructure with proper exchange and queue configurations
  • Implements DlqListener to handle final failed messages and update AI request status to FAILED
  • Configures 60-second retry delay mechanism with TTL-based message reprocessing

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
RabbitMqConfig.java Adds DLQ/retry exchanges, queues, and bindings with TTL configuration
DlqListener.java Creates listener to process final failed messages and update request status

// DLX에서 재시도 Exchange로의 바인딩
@Bean
public Binding aiImageRequestDlxToRetryBinding(TopicExchange aiImageRequestRetryExchange, TopicExchange aiImageRequestDlx) {
return BindingBuilder.bind(aiImageRequestRetryExchange)
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

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

The binding direction is incorrect. This method attempts to bind a TopicExchange to another TopicExchange, but the bind() method expects a Queue as the first parameter. This should bind the retry exchange to the DLX, not the other way around.

Suggested change
return BindingBuilder.bind(aiImageRequestRetryExchange)
public Binding aiImageRequestDlxToRetryBinding(Queue aiImageRequestRetryQueue, TopicExchange aiImageRequestDlx) {
return BindingBuilder.bind(aiImageRequestRetryQueue)

Copilot uses AI. Check for mistakes.
return new Queue(AI_IMAGE_REQUEST_QUEUE, true);
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", AI_IMAGE_REQUEST_DLX);
args.put("x-dead-letter-routing-key", AI_IMAGE_REQUEST_RETRY_ROUTING_KEY); // 기본적으로 재시도로 라우팅
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

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

The dead letter routing key configuration may cause infinite retry loops. When a message fails, it goes to DLX with retry routing key, but there's no mechanism to limit retry attempts or eventually route to the final DLQ. Consider adding retry count tracking or max retry limit.

Copilot uses AI. Check for mistakes.
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 (2)
src/main/java/hanium/modic/backend/common/amqp/config/RabbitMqConfig.java (1)

108-114: TTL 하드코딩 대신 설정값으로 분리 권장

x-message-ttl 을 60 000 ms로 직접 넣어 두었습니다.
운영 환경에서 재시도 지연 시간이 바뀔 때마다 코드를 다시 배포해야 하므로,
application.yml (RabbitMqProperties 등)로 이동해 동적으로 주입하는 편이 유지보수에 유리합니다.

src/main/java/hanium/modic/backend/domain/ai/listener/DlqListener.java (1)

32-40: 엔티티 미존재 시 idempotent 처리가 필요합니다

DB에 해당 requestId 가 없을 때 단순 로그만 남기고 종료하고 있습니다.
동일 메시지가 중복 수신될 경우에도 안전하게 무시되도록

if(aiRequestOpt.isEmpty()) {
    // 이미 DLQ 처리된 건인지 확인 후 ACK
    return;
}

처럼 idempotent 로직을 명시하거나,
운영 상 문제라면 별도 알림/모니터링으로 연결해 주세요.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b7817d8 and ef925f7.

📒 Files selected for processing (2)
  • src/main/java/hanium/modic/backend/common/amqp/config/RabbitMqConfig.java (3 hunks)
  • src/main/java/hanium/modic/backend/domain/ai/listener/DlqListener.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
🔇 Additional comments (1)
src/main/java/hanium/modic/backend/common/amqp/config/RabbitMqConfig.java (1)

125-130: Exchange-to-Exchange 바인딩 호환성 확인 필요

BindingBuilder.bind(aiImageRequestRetryExchange).to(aiImageRequestDlx)… 형태의 E2E 바인딩은
Spring AMQP 2.1+ / RabbitMQ 3.6+ 에서만 지원됩니다.
운영 브로커·라이브러리 버전이 해당 요구 사항을 충족하는지 확인해 주세요.

Comment on lines +45 to +48
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", AI_IMAGE_REQUEST_DLX);
args.put("x-dead-letter-routing-key", AI_IMAGE_REQUEST_RETRY_ROUTING_KEY); // 기본적으로 재시도로 라우팅
return new Queue(AI_IMAGE_REQUEST_QUEUE, true, false, false, args);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

재시도 루프가 무한 반복될 수 있습니다 – 최종 DLQ 라우팅 로직을 명확히 해주세요.

현재 x-dead-letter-exchange 를 DLX로, x-dead-letter-routing-key 를 재시도용 라우팅 키로만 지정해 두어 최초 실패 시에는 무조건 재시도 큐로만 흐르게 됩니다.
메시지가 재시도 큐에서 다시 만료-실패를 반복하면 동일 경로로 계속 순환하여 실제 DLQ(ai.image.request.dlq)에 도달하지 못할 가능성이 있습니다.

  • x-death 헤더를 검사해 재시도 횟수를 초과하면 DLQ 라우팅 키로 재게시하거나,
  • 별도의 정책(예: DLX-policy)으로 max-length, max-delivery-count 등을 설정해 DLQ로 강제 전환

과 같은 명시적 종료 지점을 추가해 주세요.
무한 재시도는 메시지 폭주 및 비용 증가로 이어질 수 있습니다.

🤖 Prompt for AI Agents
In src/main/java/hanium/modic/backend/common/amqp/config/RabbitMqConfig.java
around lines 45 to 48, the current dead-letter exchange and routing key setup
causes infinite retry loops because messages always route back to the retry
queue. To fix this, implement logic to check the 'x-death' header for retry
count and once a maximum retry threshold is exceeded, route the message to the
final dead-letter queue using its routing key. Alternatively, configure queue
policies like max-length or max-delivery-count to automatically move messages to
the DLQ after retries. This ensures messages do not endlessly cycle and properly
reach the DLQ after retry limits.

Comment on lines +26 to +29
@Transactional
@RabbitListener(queues = AI_IMAGE_REQUEST_DLQ)
public void handleFinalFailedMessage(AiImageRequestMessageDto messageDto, Message message) {
log.error("[최종 실패] AI 이미지 생성 요청 최종 실패: requestId={}", messageDto.requestId());
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

메시지 소비 실패 시 재큐잉/ACK 전략을 명시하세요

@Transactional 내부에서 DB 예외가 발생하면 런타임 예외가 throw 되어 컨테이너가 NACK + requeue false 로 처리할 수 있습니다.
현재 리스너는 수동 ACK 설정이 없으므로, 예외 발생 시 메시지가 즉시 DLX 로 이동해 재시도 없이 종료될 수 있으니

  • ackMode=MANUAL + 성공 시 수동 ACK, 실패 시 channel.basicNack(..., requeue=true)
  • 또는 SimpleRabbitListenerContainerFactorydefaultRequeueRejected=true

등 원하는 재시도·폐기 정책을 명확히 지정해 주세요.

🤖 Prompt for AI Agents
In src/main/java/hanium/modic/backend/domain/ai/listener/DlqListener.java around
lines 26 to 29, the message listener lacks explicit acknowledgment and requeue
strategy, which can cause messages to be lost or prematurely dead-lettered on
exceptions. To fix this, configure the listener with manual acknowledgment mode
by setting ackMode=MANUAL, then in the method, acknowledge the message manually
on success and on failure call channel.basicNack with requeue=true to enable
retries. Alternatively, set defaultRequeueRejected=true in the
SimpleRabbitListenerContainerFactory configuration to control requeue behavior
globally.

@yooooonshine yooooonshine merged commit 22e9c94 into develop Aug 25, 2025
2 checks passed
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 이미지 실패 처리

3 participants