Develop - #181
Conversation
- DocumentRepository: findByStatusAndCreatedAtBefore 쿼리 추가 - DocumentService: getPendingDocumentsOlderThan(int minutes) 메서드 추가
- GuideonCoreApplication: @EnableScheduling 추가 - DocumentGlobalInternalController: GET /internal/v1/documents/pending 엔드포인트 추가 - DocumentTimeoutScheduler: PENDING 30분 초과 문서 FAILED 자동 전환 (5분 주기)
- GuideonBackendApplication: @EnableScheduling 추가 - CoreDocumentClient: getPendingDocuments Feign 메서드 추가 - DocumentRetryScheduler: PENDING 5분 초과 문서 FastAPI 자동 재처리 요청 (5분 주기)
- reprocess() 상태를 PENDING → PROCESSING으로 변경하여 재처리 스케줄러의 중복 요청 방지 - DocumentTimeoutScheduler에 @ConditionalOnProperty 추가로 admin-bff 중복 실행 방지 - admin-bff application.yml에 timeout-scheduler 비활성화 설정 추가 - DocumentGlobalInternalController olderThanMinutes 파라미터에 @positive 검증 추가
Refactor/file delete
|
Warning Review limit reached
More reviews will be available in 25 minutes and 43 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
Walkthrough이 PR은 PENDING 상태 문서를 Core 서비스에서 조회하고, 타임아웃 시 자동 실패 처리하며, Admin-BFF에서 주기적으로 재처리를 요청하는 스케줄링 기능을 추가합니다. 저장소 쿼리, 서비스 로직, 내부 REST 엔드포인트, 두 개의 스케줄러(타임아웃/재처리), 그리고 구성 설정이 포함됩니다. ChangesPENDING 문서 타임아웃 및 재처리 스케줄링
Sequence DiagramsequenceDiagram
participant Core as Core Service
participant AdminBFF as Admin-BFF
participant Database as Database
Note over Core,AdminBFF: 1. 타임아웃 스케줄러 실행
Core->>Core: DocumentTimeoutScheduler triggers (scheduled)
Core->>Database: SELECT PENDING docs older than timeout threshold
Database-->>Core: List<Document>
Core->>Database: UPDATE status to FAILED with reason
Core-->>Core: Log warn
Note over AdminBFF,Core: 2. 재처리 스케줄러 실행
AdminBFF->>AdminBFF: DocumentRetryScheduler triggers (scheduled)
AdminBFF->>Core: GET /internal/v1/documents/pending?olderThanMinutes=5
Core->>Database: SELECT PENDING docs older than 5 minutes
Database-->>Core: List<DocumentDto>
Core-->>AdminBFF: Response
loop For each pending document
AdminBFF->>Core: POST reprocessDocument(siteId, docId)
Core->>Core: Document.reprocess() - status = PROCESSING
Core->>Database: UPDATE document status to PROCESSING
Core-->>AdminBFF: Success response
AdminBFF-->>AdminBFF: Log completion
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java (1)
41-49: ⚡ Quick win모니터링 메트릭 추가 권장
재처리 요청의 성공/실패를 추적할 수 있는 메트릭을 추가하면 운영 시 가시성이 향상됩니다. 예를 들어 Micrometer를 사용하여 재처리 시도 횟수, 성공률, 실패 원인 등을 추적할 수 있습니다.
📊 메트릭 추가 예시
+ import io.micrometer.core.instrument.Counter; + import io.micrometer.core.instrument.MeterRegistry; + `@Slf4j` `@Component` `@RequiredArgsConstructor` public class DocumentRetryScheduler { private final CoreDocumentClient coreDocumentClient; + private final MeterRegistry meterRegistry; `@Value`("${document.retry-pending-after-minutes:5}") private int retryAfterMinutes; `@Scheduled`(fixedDelayString = "${document.retry-interval-ms:300000}") public void retryPendingDocuments() { // ... 조회 로직 ... for (DocumentDto doc : pendingDocs) { try { coreDocumentClient.reprocessDocument(doc.getSiteId(), doc.getDocId()); + meterRegistry.counter("document.retry.success").increment(); log.info("[DocumentRetry] doc_id={}, site_id={}, name={} → 재처리 요청 완료", doc.getDocId(), doc.getSiteId(), doc.getOriginalName()); } catch (Exception e) { + meterRegistry.counter("document.retry.failure", "error", e.getClass().getSimpleName()).increment(); log.error("[DocumentRetry] doc_id={} 재처리 요청 실패: {}", doc.getDocId(), e.getMessage()); } } } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java` around lines 41 - 49, Add Micrometer metrics to DocumentRetryScheduler to track reprocess attempts, successes, and failures: create and inject a MeterRegistry (or use an existing one) and define Counters (e.g., documentReprocessAttempts, documentReprocessSuccesses, documentReprocessFailures) and optionally a Timer; in the retry loop that iterates pendingDocs, increment documentReprocessAttempts before calling coreDocumentClient.reprocessDocument(doc.getSiteId(), doc.getDocId()), increment documentReprocessSuccesses on successful completion (alongside the existing log.info) and increment documentReprocessFailures in the catch block (alongside log.error), including a tag for failure reason (e.getClass().getSimpleName()) and tags for site_id/doc_id as appropriate to help filtering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java`:
- Around line 27-50: The scheduled retryPendingDocuments method in
DocumentRetryScheduler can run concurrently across scaled instances causing
duplicate reprocessing; fix by introducing a distributed lock (e.g., ShedLock):
add and configure a LockProvider bean (JdbcTemplateLockProvider or other) and
enable scheduling locks (EnableSchedulerLock) in a SchedulerConfig, then protect
DocumentRetryScheduler.retryPendingDocuments with the scheduler lock annotation
(e.g., `@SchedulerLock` with an appropriate name and lockAtMostFor/lockAtLeastFor)
so only one instance executes the retry loop against coreDocumentClient at a
time.
- Around line 30-35: The catch is too broad in DocumentRetryScheduler around
coreDocumentClient.getPendingDocuments(retryAfterMinutes); update it to handle
expected transient vs permanent errors: catch specific client/network exceptions
first (e.g., FeignException, HttpClientErrorException/HttpServerErrorException
or your HTTP client’s specific types) and handle them with a warn and retry
behavior, and add a separate catch (Exception e) that logs at error level
(including stacktrace) so unexpected configuration/auth errors are visible;
reference the pendingDocs assignment and
coreDocumentClient.getPendingDocuments(...) when making these changes.
---
Nitpick comments:
In
`@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java`:
- Around line 41-49: Add Micrometer metrics to DocumentRetryScheduler to track
reprocess attempts, successes, and failures: create and inject a MeterRegistry
(or use an existing one) and define Counters (e.g., documentReprocessAttempts,
documentReprocessSuccesses, documentReprocessFailures) and optionally a Timer;
in the retry loop that iterates pendingDocs, increment documentReprocessAttempts
before calling coreDocumentClient.reprocessDocument(doc.getSiteId(),
doc.getDocId()), increment documentReprocessSuccesses on successful completion
(alongside the existing log.info) and increment documentReprocessFailures in the
catch block (alongside log.error), including a tag for failure reason
(e.getClass().getSimpleName()) and tags for site_id/doc_id as appropriate to
help filtering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4999d0a0-3463-414d-b005-a1b22ccdc266
📒 Files selected for processing (12)
docker-compose.ymlguideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.javaguideon-admin-bff/src/main/java/com/guideon/guideonbackend/client/CoreDocumentClient.javaguideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.javaguideon-admin-bff/src/main/resources/application.ymlguideon-core/src/main/java/com/guideon/core/GuideonCoreApplication.javaguideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.javaguideon-core/src/main/java/com/guideon/core/api/DocumentInternalController.javaguideon-core/src/main/java/com/guideon/core/domain/document/entity/Document.javaguideon-core/src/main/java/com/guideon/core/domain/document/repository/DocumentRepository.javaguideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.javaguideon-core/src/main/java/com/guideon/core/service/DocumentService.java
| @Scheduled(fixedDelayString = "${document.retry-interval-ms:300000}") // 기본 5분마다 | ||
| public void retryPendingDocuments() { | ||
| List<DocumentDto> pendingDocs; | ||
| try { | ||
| pendingDocs = coreDocumentClient.getPendingDocuments(retryAfterMinutes); | ||
| } catch (Exception e) { | ||
| log.warn("[DocumentRetry] Core 조회 실패 (다음 주기에 재시도): {}", e.getMessage()); | ||
| return; | ||
| } | ||
|
|
||
| if (pendingDocs.isEmpty()) return; | ||
|
|
||
| log.info("[DocumentRetry] PENDING 문서 {}개 재처리 요청", pendingDocs.size()); | ||
|
|
||
| for (DocumentDto doc : pendingDocs) { | ||
| try { | ||
| coreDocumentClient.reprocessDocument(doc.getSiteId(), doc.getDocId()); | ||
| log.info("[DocumentRetry] doc_id={}, site_id={}, name={} → 재처리 요청 완료", | ||
| doc.getDocId(), doc.getSiteId(), doc.getOriginalName()); | ||
| } catch (Exception e) { | ||
| log.error("[DocumentRetry] doc_id={} 재처리 요청 실패: {}", doc.getDocId(), e.getMessage()); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
다중 인스턴스 환경에서 중복 재처리 요청 발생 가능
Admin-BFF가 여러 인스턴스로 스케일 아웃되면 모든 인스턴스가 동시에 이 스케줄러를 실행하여 동일한 PENDING 문서에 대해 중복으로 재처리를 요청합니다. 이는 불필요한 부하를 유발하고 FastAPI에 대한 중복 호출을 발생시킵니다.
분산 환경에서 단일 인스턴스만 스케줄러를 실행하도록 ShedLock 등의 분산 락 메커니즘을 추가하는 것을 권장합니다.
🔒 ShedLock을 사용한 분산 락 적용 예시
의존성 추가 (build.gradle 또는 pom.xml):
implementation 'net.javacrumbs.shedlock:shedlock-spring:5.x.x'
implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.x.x'설정 클래스에 ShedLock 활성화:
`@Configuration`
`@EnableSchedulerLock`(defaultLockAtMostFor = "10m")
public class SchedulerConfig {
`@Bean`
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}스케줄러 메서드에 락 적용:
- `@Scheduled`(fixedDelayString = "${document.retry-interval-ms:300000}")
+ `@Scheduled`(fixedDelayString = "${document.retry-interval-ms:300000}")
+ `@SchedulerLock`(name = "DocumentRetryScheduler_retryPendingDocuments",
+ lockAtMostFor = "10m", lockAtLeastFor = "1m")
public void retryPendingDocuments() {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java`
around lines 27 - 50, The scheduled retryPendingDocuments method in
DocumentRetryScheduler can run concurrently across scaled instances causing
duplicate reprocessing; fix by introducing a distributed lock (e.g., ShedLock):
add and configure a LockProvider bean (JdbcTemplateLockProvider or other) and
enable scheduling locks (EnableSchedulerLock) in a SchedulerConfig, then protect
DocumentRetryScheduler.retryPendingDocuments with the scheduler lock annotation
(e.g., `@SchedulerLock` with an appropriate name and lockAtMostFor/lockAtLeastFor)
so only one instance executes the retry loop against coreDocumentClient at a
time.
There was a problem hiding this comment.
현재 단일 인스턴스 운영이라 실질적 문제가 없어 @SchedulerLock 및 관련 import를 제거했습니다. 스케일 아웃 시점에 ShedLock 적용해보겠습니다.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
@coderabbitai
admin-bff는 관리자용이라 트래픽이 적을 것 같습니다.
There was a problem hiding this comment.
@hwangtae123 납득이 됩니다. 단일 인스턴스 운영 + 관리자 전용 서비스라면 현재 구성으로 충분합니다. 다만, 향후 가용성(HA) 목적으로 인스턴스를 2개 이상 띄우는 경우에도 동일한 중복 실행 문제가 발생할 수 있으니, 그 시점에 ShedLock 적용을 고려해 두시면 좋겠습니다.
✏️ Learnings added
Learnt from: hwangtae123
Repo: Project-Guideon/guideon-backend PR: 181
File: guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java:27-50
Timestamp: 2026-06-01T14:35:39.648Z
Learning: guideon-admin-bff의 DocumentRetryScheduler는 관리자 전용 서비스(admin-bff)에서 실행되며, 현재 단일 인스턴스로 운영되고 트래픽이 적다. 스케일 아웃 시점에 ShedLock 분산 락을 적용할 계획이다. 분산 락 관련 리뷰 코멘트는 현재 배포 구조상 실질적 영향이 없음.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Summary
Tasks
To Reviewer
Screenshot
Summary by CodeRabbit
새로운 기능
설정