Skip to content

Develop - #181

Merged
hwangtae123 merged 7 commits into
mainfrom
develop
Jun 2, 2026
Merged

Develop#181
hwangtae123 merged 7 commits into
mainfrom
develop

Conversation

@hwangtae123

@hwangtae123 hwangtae123 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Tasks

  • 문서처리 스케쥴링 추가

To Reviewer

Screenshot

Summary by CodeRabbit

새로운 기능

  • 대기 중인 문서의 자동 재처리 스케줄러 추가
  • 시간 초과된 문서의 자동 실패 처리 기능 추가

설정

  • 파일 기본 URL 환경 변수 설정
  • 문서 스케줄러 타임아웃 기능 설정값 추가

hwangtae123 and others added 6 commits June 1, 2026 15:12
- 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 검증 추가
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hwangtae123, we couldn't start this review because you've reached your PR review rate limit.

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 @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 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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b7c25122-456c-4f80-a646-6bfd29738a1e

📥 Commits

Reviewing files that changed from the base of the PR and between 33a8056 and 9ab145a.

📒 Files selected for processing (1)
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java

Walkthrough

이 PR은 PENDING 상태 문서를 Core 서비스에서 조회하고, 타임아웃 시 자동 실패 처리하며, Admin-BFF에서 주기적으로 재처리를 요청하는 스케줄링 기능을 추가합니다. 저장소 쿼리, 서비스 로직, 내부 REST 엔드포인트, 두 개의 스케줄러(타임아웃/재처리), 그리고 구성 설정이 포함됩니다.

Changes

PENDING 문서 타임아웃 및 재처리 스케줄링

계층 / 파일 요약
Core 서비스 PENDING 문서 조회 계층
guideon-core/src/main/java/com/guideon/core/domain/document/repository/DocumentRepository.java, guideon-core/src/main/java/com/guideon/core/service/DocumentService.java, guideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.java
DocumentRepositoryfindByStatusAndCreatedAtBefore() 쿼리 메서드 추가하고, DocumentService에서 이를 활용하여 getPendingDocumentsOlderThan() 메서드로 PENDING 문서를 시간 기준으로 조회하도록 구현. DocumentGlobalInternalController/internal/v1/documents/pending 내부 REST 엔드포인트 추가.
Core 서비스 타임아웃 스케줄러
guideon-core/src/main/java/com/guideon/core/GuideonCoreApplication.java, guideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.java, guideon-core/src/main/java/com/guideon/core/domain/document/entity/Document.java
GuideonCoreApplication에서 @EnableScheduling 추가하여 스케줄링 활성화. DocumentTimeoutScheduler에서 PENDING 문서를 주기적으로 조회하여 타임아웃된 문서를 FAILED 상태로 전환. Document.reprocess()에서 상태를 PROCESSING으로 설정하도록 수정.
Admin-BFF 재처리 스케줄러
guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java, guideon-admin-bff/src/main/java/com/guideon/guideonbackend/client/CoreDocumentClient.java, guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java
GuideonBackendApplication에서 @EnableScheduling 추가. CoreDocumentClientgetPendingDocuments() Feign 메서드 추가하여 Core 서비스의 PENDING 문서 조회. DocumentRetryScheduler에서 조회한 문서들에 대해 주기적으로 재처리 요청 수행.
인프라 및 설정
docker-compose.yml, guideon-admin-bff/src/main/resources/application.yml, guideon-core/src/main/java/com/guideon/core/api/DocumentInternalController.java
docker-compose.yml에서 admin-bff 환경 변수에 FILE_BASE_URL 추가. application.yml에서 document.timeout-scheduler.enabled: false로 타임아웃 스케줄러 비활성화. DocumentInternalController import 영역에 빈 줄 추가.

Sequence Diagram

sequenceDiagram
  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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • Project-Guideon/guideon-backend#179: PENDING 문서의 타임아웃 및 재처리 스케줄링 기능을 구현하는 코드 변경사항이 해당 이슈의 요구사항과 일치합니다.

Possibly related PRs

  • Project-Guideon/guideon-backend#180: 동일한 PENDING 문서 조회, 타임아웃 처리, 재처리 스케줄링 기능(CoreDocumentClient.getPendingDocuments(), DocumentRetryScheduler, DocumentGlobalInternalController, DocumentService/Repository의 PENDING 조회)의 구현이 일치합니다.

Poem

🐰 스케줄러의 춤

PENDING이 쌓이면 타임아웃이 한숨,
다시 처리할 차례가 되어 춤을 춘다.
Core와 BFF, 손잡고 돌며
문서는 흐르고 상태는 변하고,
자동화의 마법으로 일은 이뤄지는도다. ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목 'Develop'은 실제 변경 사항인 문서 처리 스케줄링 기능과 무관한 매우 모호한 표현입니다. PR 제목을 'Add document retry and timeout scheduling' 또는 '문서 재처리 및 타임아웃 스케줄링 추가' 등 실제 변경 내용을 반영하는 구체적인 문구로 변경하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명이 주요 필수 섹션(Summary, Tasks, To Reviewer, Screenshot)을 모두 포함하고 있으며, 이슈 참조와 완료된 작업이 명시되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (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

📥 Commits

Reviewing files that changed from the base of the PR and between 04b676c and 33a8056.

📒 Files selected for processing (12)
  • docker-compose.yml
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/client/CoreDocumentClient.java
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java
  • guideon-admin-bff/src/main/resources/application.yml
  • guideon-core/src/main/java/com/guideon/core/GuideonCoreApplication.java
  • guideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.java
  • guideon-core/src/main/java/com/guideon/core/api/DocumentInternalController.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/entity/Document.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/repository/DocumentRepository.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.java
  • guideon-core/src/main/java/com/guideon/core/service/DocumentService.java

Comment on lines +27 to +50
@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());
}
}
}

@coderabbitai coderabbitai Bot Jun 1, 2026

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 | 🟠 Major | 🏗️ Heavy lift

다중 인스턴스 환경에서 중복 재처리 요청 발생 가능

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

현재 단일 인스턴스 운영이라 실질적 문제가 없어 @SchedulerLock 및 관련 import를 제거했습니다. 스케일 아웃 시점에 ShedLock 적용해보겠습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai
admin-bff는 관리자용이라 트래픽이 적을 것 같습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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.

@hwangtae123
hwangtae123 merged commit 90e43d2 into main Jun 2, 2026
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.

1 participant