Skip to content

[Feat] Worker 자동 Polling 및 인덱싱 실행 오케스트레이션 - #94

Merged
Gimini-3 merged 10 commits into
developfrom
feature/92
Aug 5, 2026
Merged

[Feat] Worker 자동 Polling 및 인덱싱 실행 오케스트레이션#94
Gimini-3 merged 10 commits into
developfrom
feature/92

Conversation

@Gimini-3

@Gimini-3 Gimini-3 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

변경 내용

  • 등록이 완료된 Worker가 실행 슬롯 범위 안에서 대기 중인 인덱싱 Job을 자동 Claim하도록 Polling 흐름을 추가했습니다.
  • Claim → Attempt 시작 → 문서 파싱·청킹 → Embedding 저장 → 인덱싱 완료 단계를 내부 서비스로 연결했습니다.
  • 활성 실행의 Lease 자동 갱신과 소유권 상실 감지, 단계별 실패 분류 및 기존 재시도·최종 실패 정책 연결을 구현했습니다.
  • 신규 Claim 중단, Grace Period 대기, Lease 갱신 및 실행 슬롯 정리를 포함한 종료 흐름을 추가했습니다.
  • 단위 테스트와 실제 PostgreSQL 기반 다중 Worker·Lease 복구 동시성 테스트를 추가했습니다.
  • 상세 설계와 실행 검증 결과를 문서화했습니다.

변경 이유

기존에는 Worker 등록, Claim, 파싱, Embedding, 완료·실패 기능이 개별적으로 존재했지만 이를 자동으로 연결하는 실행 Loop가 없어 관리자 API를 수동 호출해야 했습니다. 이 변경으로 Worker가 업로드된 문서를 제한된 동시성 안에서 자동으로 인덱싱 완료까지 처리합니다.

영향

  • 기본값 indexing.worker.enabled=false는 유지되어 API 전용 실행에는 영향이 없습니다.
  • Worker 활성화 환경에서는 설정한 최대 동시 실행 수만큼만 Job을 Claim합니다.
  • Claim Token과 Lease 소유권 검증은 기존 DB 계약을 재사용하며, 외부 I/O 중에는 장기 DB 잠금을 유지하지 않습니다.
  • 한 Job의 실패는 다른 실행 슬롯의 작업을 중단하지 않습니다.

검증

  • ./gradlew test: 576개 통과, 실패·오류·Skip 0
  • ./gradlew claimConcurrencyTest: 10개 통과, 실패·오류·Skip 0
  • ./gradlew build: 성공
  • 실제 PostgreSQL에서 다중 Poller 단일 Claim, 실행 슬롯 상한, Lease 갱신, 동시 만료 복구를 검증했습니다.

Closes #92

Summary by CodeRabbit

  • 새로운 기능

    • 인덱싱 작업의 자동 폴링과 단계별 처리 오케스트레이션을 추가했습니다.
    • 동시 실행 수를 제한하고 작업 임대(Lease)를 자동 갱신합니다.
    • 문서 버전의 현재 인덱싱 상태를 조회할 수 있습니다.
    • 작업 상태에 따라 중단된 인덱싱을 재개합니다.
    • 오류를 분류해 안전한 진단 정보로 기록하고, 작업 소유권 상실을 처리합니다.
    • 종료 시 진행 중인 작업을 기다린 뒤 안전하게 정리합니다.
  • 문서화

    • 워커 오케스트레이션 설계 및 검증 결과를 문서화했습니다.
    • 관련 단위·통합 테스트와 표준 빌드 검증을 완료했습니다.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Gimini-3, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d3b99f67-bedf-4354-be6f-ba340ebf569d

📥 Commits

Reviewing files that changed from the base of the PR and between 938628c and 13263a7.

📒 Files selected for processing (3)
  • docs/design/Gimini-3-#92-worker-polling-indexing-orchestration.md
  • docs/test-results/Gimini-3-#92-worker-polling-indexing-orchestration.md
  • src/main/resources/application.yml
📝 Walkthrough

Walkthrough

Worker 자동 Polling과 인덱싱 오케스트레이션을 추가했다. 실행 슬롯, Job Claim, Pipeline, Lease 갱신, 실패 보고, Graceful Shutdown을 구현했다. 단위·PostgreSQL 동시성 테스트와 빌드 결과를 문서화했다.

Changes

Worker 오케스트레이션

Layer / File(s) Summary
실행 기반과 설정
src/main/java/com/opensource/docgrid/domain/worker/config/*, src/main/java/com/opensource/docgrid/domain/worker/execution/*, src/main/java/com/opensource/docgrid/domain/document/service/query/*, src/main/resources/application.yml, src/test/java/com/opensource/docgrid/domain/worker/config/*, src/test/java/com/opensource/docgrid/domain/worker/execution/*
Polling, 동시 실행 수, Lease 갱신, 종료 유예 시간 설정과 유효성 검사를 추가했다. Executor, Lease Scheduler, 실행 슬롯 풀, 문서 상태 조회 서비스를 추가했다.
인덱싱 Pipeline과 실패 보고
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java, src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailure*.java, src/test/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipelineTest.java, src/test/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailure*Test.java
Claim과 상태를 검증하고 상태별 Chunk·Embedding 단계를 실행한다. 실패를 제한된 유형과 안전한 메시지로 분류하고 보고한다.
Lease 갱신 관리
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerLeaseRenewal*.java, src/test/java/com/opensource/docgrid/domain/worker/service/WorkerLeaseRenewalManagerTest.java
실행별 Lease 갱신 예약, 소유권 상실 처리, 예약 취소 및 전체 종료를 구현했다.
Polling과 종료 생명주기
src/main/java/com/opensource/docgrid/domain/worker/lifecycle/*, src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerLifecycleManager.java, src/test/java/com/opensource/docgrid/domain/worker/lifecycle/*
등록된 Worker만 Polling하도록 하고, 실행 슬롯 수만큼 Job을 Claim해 Executor에 제출한다. 종료 시 Polling을 중지하고 작업 대기, Lease 정리, Scheduler 종료를 수행한다.
동시성 검증과 결과 기록
src/test/java/com/opensource/docgrid/domain/worker/integration/*, docs/design/..., docs/test-results/...
다중 Poller Claim 경쟁, 실행 슬롯 제한, Lease 갱신·복구 경쟁, 종료 절차를 PostgreSQL 통합 테스트로 검증했다. 설계와 테스트·빌드 결과를 문서화했다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkerJobPollingScheduler
  participant WorkerExecutionSlotPool
  participant EmbeddingJobClaimService
  participant WorkerIndexingPipeline
  participant WorkerLeaseRenewalManager

  WorkerJobPollingScheduler->>WorkerExecutionSlotPool: 실행 슬롯 확보
  WorkerJobPollingScheduler->>EmbeddingJobClaimService: PENDING Job Claim
  EmbeddingJobClaimService-->>WorkerJobPollingScheduler: ClaimedEmbeddingJobResponse
  WorkerJobPollingScheduler->>WorkerIndexingPipeline: Pipeline 실행 제출
  WorkerIndexingPipeline->>WorkerLeaseRenewalManager: Lease 갱신 등록
  WorkerIndexingPipeline->>WorkerIndexingPipeline: 상태별 Chunk·Embedding 처리
  WorkerIndexingPipeline->>WorkerLeaseRenewalManager: Lease 소유권 확인
  WorkerIndexingPipeline->>WorkerExecutionSlotPool: 실행 슬롯 반환
Loading

Possibly related PRs

  • DocGrid/backend#49: EmbeddingJobClaimService와 Claim·Lease 소유권 모델을 기반으로 Polling을 구현했다.
  • DocGrid/backend#83: Chunk 및 Embedding 단계를 WorkerIndexingPipeline에서 연결했다.
  • DocGrid/backend#91: EmbeddingJobLeaseService의 소유권·복구 모델을 기반으로 Worker Lease 갱신을 추가했다.

Suggested labels: ✨ Feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.34% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 Worker 자동 Polling과 인덱싱 오케스트레이션이라는 주요 변경 사항을 명확하게 요약합니다.
Description check ✅ Passed 템플릿의 모든 제목을 따르지는 않지만 변경 내용, 이유, 영향, 검증 결과와 이슈 연결 정보를 충분히 포함합니다.
Linked Issues check ✅ Passed Polling, 실행 슬롯, Pipeline, Lease, 실패 처리, 종료 정리, 동시성 검증 등 #92의 주요 개발 목표를 구현하고 테스트했습니다.
Out of Scope Changes check ✅ Passed 변경 사항은 Worker 오케스트레이션 구현, 관련 테스트와 설계·검증 문서 범위에 포함됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/92

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.

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

🧹 Nitpick comments (3)
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java (1)

36-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

상태 변경 Service를 command 패키지로 분리하세요.

두 클래스는 Job 상태를 변경하는 Service를 직접 호출합니다. 현재 루트 service 패키지에 있으므로 command/query 책임 분리 규칙과 맞지 않습니다.

  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java#L36-L36: WorkerIndexingPipelinedomain.worker.service.command로 이동하고 참조를 갱신하세요.
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureReporter.java#L22-L22: WorkerIndexingFailureReporterdomain.worker.service.command로 이동하고 참조를 갱신하세요.

As per coding guidelines, "Separate service responsibilities into command packages for state changes and query packages for read-only operations."

🤖 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
`@src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java`
at line 36, The state-changing services are currently in the root service
package instead of the command package. Move WorkerIndexingPipeline in
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java:36-36
and WorkerIndexingFailureReporter in
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureReporter.java:22-22
to domain.worker.service.command, then update all package declarations, imports,
and references.

Source: Coding guidelines

src/test/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManagerTest.java (1)

62-71: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

중복 검증을 제거해 주세요.

라인 62의 should()should(times(1))과 같습니다. 라인 71이 같은 내용을 다시 검증합니다. 이 테스트는 shutdown()을 두 번 호출해도 한 번만 실행되는 것을 확인하므로 라인 71만 남기는 편이 의도를 더 잘 드러냅니다.

♻️ 제안 변경
-        then(pollingScheduler).should().stopPolling();
         then(jobExecutor).should().shutdown();
🤖 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
`@src/test/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManagerTest.java`
around lines 62 - 71, Remove the redundant pollingScheduler.stopPolling()
verification from the first interaction block in
WorkerExecutionLifecycleManagerTest, keeping the later should(times(1))
assertion as the sole verification that stopPolling() is called exactly once.
src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManager.java (1)

80-88: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

leaseScheduler 종료 대기를 추가하면 종료 로그가 더 정확해집니다.

shutdown()은 큐에 남은 작업만 막고 실행 중인 갱신 Task는 기다리지 않습니다. 라인 81의 stopAll()이 예약을 취소하므로 남는 것은 이미 시작된 갱신 1건뿐입니다. 그 갱신은 컨텍스트가 닫힌 뒤 DataSource 종료로 실패할 수 있습니다. renew가 예외를 삼키므로 기능 영향은 없지만 종료 로그에 오류가 섞입니다.

짧은 대기를 추가하면 종료 시점이 결정적이 됩니다.

♻️ 제안 변경
         leaseRenewalManager.stopAll();
         leaseScheduler.shutdown();
+        boolean leaseTerminated = awaitLeaseTermination();
         log.info(
-            "Worker 실행 종료를 완료했습니다. graceful={}, cancelledTaskCount={}, activeLeaseCount={}",
+            "Worker 실행 종료를 완료했습니다. graceful={}, leaseTerminated={}, cancelledTaskCount={}, activeLeaseCount={}",
             terminated,
+            leaseTerminated,
             cancelledTasks.size(),
             leaseRenewalManager.getActiveHandleCount()
         );

awaitLeaseTermination()awaitJobTermination()과 같은 형태로 추가합니다.

    // 남은 갱신 Task가 끝날 때까지 짧게 기다려 종료 시점을 결정적으로 만든다.
    private boolean awaitLeaseTermination() {
        try {
            return leaseScheduler.awaitTermination(1, TimeUnit.SECONDS);
        } catch (InterruptedException exception) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
🤖 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
`@src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManager.java`
around lines 80 - 88, Update WorkerExecutionLifecycleManager’s shutdown flow
after leaseScheduler.shutdown() to await lease-task termination for up to one
second via a new awaitLeaseTermination() helper, preserving the existing
interruption behavior by restoring the thread interrupt flag and returning
false. Invoke this wait before the completion log so shutdown timing is
deterministic.
🤖 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 `@docs/design/Gimini-3-`#92-worker-polling-indexing-orchestration.md:
- Around line 179-185: Update the documented failure-exclusion contract to match
WorkerIndexingFailureClassifier: either add EMBEDDING_JOB_NOT_FOUND to the
listed no-report ownership-loss errors if that behavior is intended, or remove
it from the classifier’s exclusion handling if it is not. Keep the documentation
and classifier behavior consistent.

In `@docs/test-results/Gimini-3-`#92-worker-polling-indexing-orchestration.md:
- Around line 16-18: 문서의 테스트 결과 섹션에 Swagger 수동 검증 절차와 결과를 추가하세요. Swagger 대상 API가
없는 Worker 내부 변경이라면 기존 자동 테스트 및 PostgreSQL 동시성 검증과 함께 `해당 없음`을 명시하고 그 근거를 기록하세요.

In
`@src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerJobPollingScheduler.java`:
- Around line 59-93: Separate the scheduled poll/claim work in
WorkerJobPollingScheduler from updateHeartbeat() and recoverExpiredLeases() by
configuring a dedicated TaskScheduler or increasing the scheduling pool through
WorkerSchedulingConfig. Ensure poll() can hold its database lock without
blocking heartbeat and lease-recovery executions, while preserving the existing
scheduling behavior.

---

Nitpick comments:
In
`@src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManager.java`:
- Around line 80-88: Update WorkerExecutionLifecycleManager’s shutdown flow
after leaseScheduler.shutdown() to await lease-task termination for up to one
second via a new awaitLeaseTermination() helper, preserving the existing
interruption behavior by restoring the thread interrupt flag and returning
false. Invoke this wait before the completion log so shutdown timing is
deterministic.

In
`@src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java`:
- Line 36: The state-changing services are currently in the root service package
instead of the command package. Move WorkerIndexingPipeline in
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java:36-36
and WorkerIndexingFailureReporter in
src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureReporter.java:22-22
to domain.worker.service.command, then update all package declarations, imports,
and references.

In
`@src/test/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManagerTest.java`:
- Around line 62-71: Remove the redundant pollingScheduler.stopPolling()
verification from the first interaction block in
WorkerExecutionLifecycleManagerTest, keeping the later should(times(1))
assertion as the sole verification that stopPolling() is called exactly once.
🪄 Autofix

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 Plus

Run ID: e34aec4c-6f6e-4bce-8b9f-9a84453fb496

📥 Commits

Reviewing files that changed from the base of the PR and between 22e6ebf and 938628c.

📒 Files selected for processing (25)
  • docs/design/Gimini-3-#92-worker-polling-indexing-orchestration.md
  • docs/test-results/Gimini-3-#92-worker-polling-indexing-orchestration.md
  • src/main/java/com/opensource/docgrid/domain/document/service/query/DocumentIndexingStageQueryService.java
  • src/main/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerProperties.java
  • src/main/java/com/opensource/docgrid/domain/worker/config/WorkerExecutionConfig.java
  • src/main/java/com/opensource/docgrid/domain/worker/execution/WorkerExecutionSlotPool.java
  • src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManager.java
  • src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerJobPollingScheduler.java
  • src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerLifecycleManager.java
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailure.java
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureClassifier.java
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureReporter.java
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipeline.java
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerLeaseRenewalHandle.java
  • src/main/java/com/opensource/docgrid/domain/worker/service/WorkerLeaseRenewalManager.java
  • src/main/resources/application.yml
  • src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/execution/WorkerExecutionSlotPoolTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/integration/WorkerOrchestrationIntegrationTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerExecutionLifecycleManagerTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerJobPollingSchedulerTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureClassifierTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingFailureReporterTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/service/WorkerIndexingPipelineTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/service/WorkerLeaseRenewalManagerTest.java

Comment thread docs/design/Gimini-3-#92-worker-polling-indexing-orchestration.md
Comment thread docs/test-results/Gimini-3-#92-worker-polling-indexing-orchestration.md Outdated
Comment on lines +59 to +93
@Scheduled(
fixedDelayString = "${indexing.worker.polling-interval:1s}",
initialDelayString = "${indexing.worker.polling-interval:1s}"
)
public void poll() {
if (!slotPool.isAccepting() || !polling.compareAndSet(false, true)) {
return;
}

try {
Optional<Long> registeredWorkerId = workerLifecycleManager.getWorkerId();
if (registeredWorkerId.isEmpty()) {
return;
}

// 1. 한 주기에는 설정된 전체 슬롯 수까지만 Claim을 시도하고 사용 중 슬롯은 즉시 건너뛴다.
for (int index = 0; index < slotPool.getCapacity(); index++) {
Optional<WorkerExecutionSlot> acquiredSlot = slotPool.tryAcquire();
if (acquiredSlot.isEmpty()) {
return;
}

// 2. 슬롯 획득 뒤 시작된 종료와 Claim 오류는 이 Slot만 반환하고 현재 주기를 끝낸다.
WorkerExecutionSlot executionSlot = acquiredSlot.get();
if (!slotPool.isAccepting()) {
executionSlot.close();
return;
}
Optional<ClaimedEmbeddingJobResponse> claimedJob = claim(
registeredWorkerId.get(),
executionSlot
);
if (claimedJob.isEmpty()) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. 스케줄러 풀 설정을 확인한다.
fd -e yml -e yaml -e properties . src/main/resources --exec rg -n 'scheduling|pool' {} \;

# 2. 커스텀 TaskScheduler 및 다른 `@Scheduled` 사용처를 확인한다.
rg -nP --type=java -C3 '`@Scheduled`|TaskScheduler|SchedulingConfigurer' src/main/java

Repository: DocGrid/backend

Length of output: 3039


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## Candidate files matching worker lifecycle schedulers\n'
fd -a 'Worker(JobPolling|Heartbeat|LeaseRecovery)Scheduler\.java' src/main/java || true

printf '\n## Scheduling configuration files\n'
fd -a 'application.*|application-local.*|application-test.*' src/main/resources src/test/resources || true

printf '\n## WorkerJobPollingScheduler relevant section\n'
FILE="src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerJobPollingScheduler.java"
if [ -f "$FILE" ]; then
  sed -n '1,180p' "$FILE" | cat -n
fi

printf '\n## WorkerHeartbeatScheduler relevant section\n'
FILE="src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerHeartbeatScheduler.java"
if [ -f "$FILE" ]; then
  sed -n '1,140p' "$FILE" | cat -n
fi

printf '\n## WorkerLeaseRecoveryScheduler relevant section\n'
FILE="src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerLeaseRecoveryScheduler.java"
if [ -f "$FILE" ]; then
  sed -n '1,140p' "$FILE" | cat -n
fi

printf '\n## Scheduling/TaskScheduler references in Java\n'
rg -n --type=java -C2 '`@Scheduled`|TaskScheduler|SchedulingConfigurer|ThreadPoolTaskScheduler|`@EnableScheduling`|spring\.task\.scheduling|task\.scheduling' . || true

printf '\n## Config references to scheduling/pool\n'
fd -e yml -e yaml -e properties . src/main/resources src/test/resources --exec rg -n -C2 -i 'scheduling|task\.scheduling|pool-name|pool-size|scheduler' {} \; || true

printf '\n## claim/findNextPendingForUpdate references\n'
rg -n --type=java -C4 'claim\(|findNextPendingForUpdate|PendingForUpdate|LockModeType\.PESSIMISTIC|FOR UPDATE' src/main/java src/test/java || true

Repository: DocGrid/backend

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## WorkerSchedulingConfig\n'
FILE="src/main/java/com/opensource/docgrid/domain/worker/config/WorkerSchedulingConfig.java"
if [ -f "$FILE" ]; then
  sed -n '1,240p' "$FILE" | cat -n
fi

printf '\n## All `@Scheduled` symbols with file names\n'
python3 - <<'PY'
import pathlib
for p in pathlib.Path('src/main/java').rglob('*.java'):
    text = p.read_text(errors='ignore')
    if '`@Scheduled`' in text:
        print(f'FILE {p}')
        for i,line in enumerate(text.splitlines(),1):
            if '`@Scheduled`' in line:
                start=max(1,i-2); end=min(len(text.splitlines()),i+8)
                lines=text.splitlines()
                for n in range(start,end+1):
                    print(f'{n}: {lines[n-1]}')
PY

printf '\n## TaskScheduler and pool-size references in tracked files\n'
git grep -n -i 'TaskScheduler\|ThreadPoolTaskScheduler\|SchedulingConfigurer\|spring\.task\.scheduling\.pool\.size\|task\.scheduling\.pool\.size\|pool\.size' -- '*.java' '**/*.yml' '**/*.yaml' '**/*.properties' || true

Repository: DocGrid/backend

Length of output: 2555


Polling과 Heartbeat를 전용 스케줄러로 분리하세요.

WorkerSchedulingConfig는 기본 @EnableScheduling만并提供 TaskScheduler 설정이 없습니다. 따라서 poll(), updateHeartbeat(), recoverExpiredLeases()가 Spring 기본 스케줄러를 공유하고, poll() 내부 DB 잠금을 가진 claim() 호출이 길어지면 Heartbeat도 기다립니다. 전용 TaskScheduler 또는 spring.task.scheduling.pool.size 설정으로 Claim 경로를 별도의 스레드로 분리해 주세요.

🤖 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
`@src/main/java/com/opensource/docgrid/domain/worker/lifecycle/WorkerJobPollingScheduler.java`
around lines 59 - 93, Separate the scheduled poll/claim work in
WorkerJobPollingScheduler from updateHeartbeat() and recoverExpiredLeases() by
configuring a dedicated TaskScheduler or increasing the scheduling pool through
WorkerSchedulingConfig. Ensure poll() can hold its database lock without
blocking heartbeat and lease-recovery executions, while preserving the existing
scheduling behavior.

@Gimini-3
Gimini-3 merged commit ca1d3e0 into develop Aug 5, 2026
1 check 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.

[Feat] Worker 자동 Polling 및 인덱싱 실행 오케스트레이션

1 participant