Skip to content

[Feat] 인덱싱 실패 종료 및 지연 재시도 - #89

Merged
Gimini-3 merged 6 commits into
developfrom
codex/feature-88-indexing-failure-retry
Aug 3, 2026
Merged

[Feat] 인덱싱 실패 종료 및 지연 재시도#89
Gimini-3 merged 6 commits into
developfrom
codex/feature-88-indexing-failure-retry

Conversation

@Gimini-3

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

Copy link
Copy Markdown
Contributor

변경 사항

  • Worker가 현재 Claim 소유권으로 인덱싱 실패를 보고하는 관리자 API를 추가했습니다.
  • 서버가 실패 유형별 재시도 가능 여부를 결정하고 10초부터 지수 증가하는 Backoff를 최대 5분까지 적용합니다.
  • embedding_jobs.next_retry_at과 실행 가능 시각 기반 Queue 조회 조건을 추가했습니다.
  • 영구 실패 또는 Retry 소진 시 Attempt, Job, Version, Document와 Embedding 상태를 하나의 Transaction에서 정리합니다.
  • 새 Version 실패 시 이전 INDEXED Version과 검색 가능한 Embedding Set은 유지합니다.
  • 동일 실패 요청의 멱등 재생과 완료/실패 동시 요청의 Job 행 잠금 직렬화를 보장합니다.
  • 실패 단계, Retry, 최종 실패 이벤트를 비민감 Metadata와 함께 기록합니다.
  • 레거시 또는 수동 오염된 실패 코드는 임의 실패 유형으로 변환하지 않고 데이터 불일치 오류로 처리합니다.

영향

일시적인 저장소·Embedding Provider·Worker 장애는 즉시 Job을 종료하지 않고 예약 시각 이후 기존 Claim Queue에서 다시 처리됩니다. 데이터 또는 상태 불변식 오류는 즉시 최종 실패하며, 기존에 검색 가능했던 문서는 새 Version 실패의 영향을 받지 않습니다.

검증

  • ./gradlew test: 514개 통과
  • ./gradlew claimConcurrencyTest: 2개 통과
  • 신규 PostgreSQL 실패/Retry/완료 경쟁 통합 테스트: 5개 통과
  • Swagger/OpenAPI 실제 HTTP 검증: 최초 실패·멱등 재생 200, 요청 검증 400, 소유권·상태 충돌 409
  • Retry 이벤트 저장 실패 시 전체 Transaction Rollback 검증

Closes #88

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

인덱싱 실패 종료 API를 추가했습니다. 서버가 실패 유형과 재시도 횟수를 기준으로 지연 재시도 또는 최종 실패를 처리합니다. Job Claim에 next_retry_at 조건을 적용하고, 멱등성·동시성·Rollback을 PostgreSQL 통합 테스트로 검증했습니다.

Changes

인덱싱 실패 및 재시도

Layer / File(s) Summary
재시도 계약과 상태 저장
docs/design/..., src/main/java/com/opensource/docgrid/domain/embedding/{entity,enums,repository}/*, src/main/java/com/opensource/docgrid/domain/document/entity/DocumentVersion.java, src/main/java/com/opensource/docgrid/domain/worker/config/*, src/main/resources/*
실패 유형별 재시도 정책과 next_retry_at 저장을 추가했습니다. Job Claim은 실행 가능 시각이 지난 PENDING Job만 선택합니다. Job·Attempt·Version의 중복 실패 전이를 차단합니다.
실패 처리 서비스와 최종 전이
src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java
Worker 소유권과 Attempt Context를 검증합니다. 재시도 시 Job을 PENDING으로 복귀시키고, 최종 실패 시 Version·Document·Embedding·Job 및 이벤트를 갱신합니다.
실패 API와 응답 변환
src/main/java/com/opensource/docgrid/domain/embedding/controller/IndexingJobAdminController.java, src/main/java/com/opensource/docgrid/domain/embedding/dto/*, src/main/java/com/opensource/docgrid/domain/embedding/converter/*
POST /admin/indexing-jobs/{jobId}/attempts/{attemptId}/fail API를 추가했습니다. 요청 필드를 검증하고, 최초 처리와 동일한 멱등 재요청에 200 OK를 반환합니다.
상태·재시도 통합 검증
src/test/java/com/opensource/docgrid/domain/{document,embedding,worker}/*, docs/test-results/*
상태 전이, Backoff, Claim 경계, API 오류, 멱등성, 동시 완료·실패 경쟁, 검색 Version 보존 및 트랜잭션 Rollback을 검증했습니다. 회귀 테스트와 PostgreSQL 통합 테스트 결과를 기록했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant Worker
  participant IndexingJobAdminController
  participant DocumentIndexingFailureService
  participant EmbeddingJob
  participant DocumentVersion
  participant Document
  Worker->>IndexingJobAdminController: 실패 API 요청
  IndexingJobAdminController->>DocumentIndexingFailureService: 실패 처리 호출
  DocumentIndexingFailureService->>EmbeddingJob: 소유권 검증 및 Job 잠금
  DocumentIndexingFailureService->>DocumentVersion: Version 상태 확인
  DocumentIndexingFailureService->>Document: 검색 상태 확인 및 갱신
  DocumentIndexingFailureService->>EmbeddingJob: 재시도 예약 또는 최종 실패
  DocumentIndexingFailureService-->>IndexingJobAdminController: 고정 실패 응답
Loading

Possibly related PRs

  • DocGrid/backend#87: 동일한 문서 인덱싱 흐름의 Job, Attempt, Version 상태를 확장합니다.
  • DocGrid/backend#53: EmbeddingJobClaimServiceEmbeddingJobRepository Claim 흐름을 공유합니다.
  • DocGrid/backend#64: EmbeddingJobAttempt 수명주기와 공통 실패 응답 흐름에 연결됩니다.

Suggested labels: ✨ Feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.06% 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
Linked Issues check ✅ Passed 실패 API, 재시도 정책, 최종 실패, 상태 보존, 멱등성, 동시성, 트랜잭션 롤백 요구사항을 모두 구현하고 검증합니다.
Out of Scope Changes check ✅ Passed 변경된 문서, 구현, 마이그레이션, 테스트는 모두 인덱싱 실패 처리와 지연 재시도 목표에 직접 관련됩니다.
Title check ✅ Passed 제목이 인덱싱 실패 종료와 지연 재시도라는 주요 변경 사항을 간결하고 명확하게 설명합니다.
Description check ✅ Passed 변경 사항, 영향, 검증 결과와 이슈 연결을 포함해 PR 목적과 구현 내용을 충분히 설명합니다.
✨ 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 codex/feature-88-indexing-failure-retry

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: 5

🧹 Nitpick comments (5)
src/main/java/com/opensource/docgrid/domain/embedding/repository/EmbeddingJobRepository.java (1)

42-53: 🚀 Performance & Scalability | 🔵 Trivial

Queue 조회 인덱스 선택을 실행 계획으로 확인하십시오.

이 쿼리는 status로 필터하고 priority DESC, created_at ASC, id ASC로 정렬합니다. 기존 idx_embedding_jobs_status_priority_created_at가 정렬을 담당합니다. 새 idx_embedding_jobs_status_next_retry_at는 정렬 컬럼을 포함하지 않으므로 이 쿼리에서 선택되지 않을 가능성이 큽니다. PENDING Job이 많고 대부분 Retry 예약 상태이면, 정렬 인덱스를 스캔하며 next_retry_at 조건으로 대량 행을 버리게 됩니다.

운영 규모가 커지면 EXPLAIN ANALYZE로 실제 계획을 확인하고, 필요 시 (status, priority, created_at, id) 기반 부분 인덱스 또는 next_retry_at을 포함한 복합 인덱스를 검토하십시오. 현재 변경 자체는 정확합니다.

🤖 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/embedding/repository/EmbeddingJobRepository.java`
around lines 42 - 53, Validate the query plan for findNextPendingForUpdate with
EXPLAIN ANALYZE at production-like scale, focusing on index selection and rows
filtered by next_retry_at. If the existing indexes cause excessive scanning, add
or adjust an index using status, priority, created_at, and id, optionally
incorporating next_retry_at as appropriate, while preserving the query’s
filtering and ordering behavior.
src/test/java/com/opensource/docgrid/domain/embedding/entity/EmbeddingJobTest.java (1)

121-148: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

scheduleRetry의 나머지 두 방어 분기도 테스트하십시오.

현재 테스트는 Retry 소진 분기만 검증합니다. scheduleRetry에는 두 개의 방어 로직이 더 있습니다.

  1. PROCESSING이 아닌 Job은 IllegalStateException("PROCESSING 상태의 Job만 Retry를 예약할 수 있습니다.")를 던진다.
  2. nextRetryAt이 null이면 IllegalArgumentException("다음 Retry 시각은 필수입니다.")를 던진다.

두 분기는 현재 커버되지 않습니다.

♻️ 추가 테스트 제안
`@Test`
`@DisplayName`("PROCESSING이 아닌 Job은 Retry를 예약할 수 없다")
void scheduleRetry_throws_when_jobIsNotProcessing() {
    EmbeddingJob pendingJob = createPendingJob();

    assertThatThrownBy(() -> pendingJob.scheduleRetry(
        "STORAGE_UNAVAILABLE",
        "Storage timeout",
        CLAIMED_AT.plusSeconds(10)
    )).isInstanceOf(IllegalStateException.class)
        .hasMessage("PROCESSING 상태의 Job만 Retry를 예약할 수 있습니다.");
    assertThat(pendingJob.getStatus()).isEqualTo(EmbeddingJobStatus.PENDING);
}

`@Test`
`@DisplayName`("다음 Retry 시각이 없으면 예약할 수 없다")
void scheduleRetry_throws_when_nextRetryAtIsNull() {
    EmbeddingJob embeddingJob = createPendingJob();
    embeddingJob.claim(createActiveWorker(), CLAIM_TOKEN, CLAIMED_AT, EXPIRES_AT);

    assertThatThrownBy(() -> embeddingJob.scheduleRetry("STORAGE_UNAVAILABLE", "Storage timeout", null))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("다음 Retry 시각은 필수입니다.");
    assertThat(embeddingJob.getStatus()).isEqualTo(EmbeddingJobStatus.PROCESSING);
    assertThat(embeddingJob.getRetryCount()).isZero();
}
🤖 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/embedding/entity/EmbeddingJobTest.java`
around lines 121 - 148, Extend EmbeddingJobTest with coverage for the two
untested scheduleRetry guards: add a test using a non-PROCESSING job that
asserts the expected IllegalStateException message and unchanged PENDING status,
and a test using a claimed PROCESSING job with null nextRetryAt that asserts the
expected IllegalArgumentException message, unchanged PROCESSING status, and zero
retry count.
src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java (1)

49-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

경계값과 음수 케이스를 추가하십시오.

isRetryDelayValidretryMaxDelay.compareTo(retryInitialDelay) >= 0을 사용합니다. 두 값이 같을 때 유효하다는 경계 동작이 현재 고정되지 않았습니다. 누군가 >=>로 바꿔도 테스트가 통과합니다. 또한 인접한 leaseDuration_isInvalid_when_notPositive는 음수까지 검증하지만, 이 테스트는 Duration.ZERO만 검증합니다.

♻️ 단언 추가 제안
     void retryDelay_isInvalid_when_nonPositiveOrReversed() {
         IndexingWorkerProperties properties = new IndexingWorkerProperties();
 
         properties.setRetryInitialDelay(Duration.ZERO);
         assertThat(properties.isRetryDelayValid()).isFalse();
 
+        properties.setRetryInitialDelay(Duration.ofSeconds(-1));
+        assertThat(properties.isRetryDelayValid()).isFalse();
+
         properties.setRetryInitialDelay(Duration.ofSeconds(10));
         properties.setRetryMaxDelay(Duration.ofSeconds(9));
         assertThat(properties.isRetryDelayValid()).isFalse();
+
+        properties.setRetryMaxDelay(Duration.ofSeconds(10));
+        assertThat(properties.isRetryDelayValid()).isTrue();
     }
🤖 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/config/IndexingWorkerPropertiesTest.java`
around lines 49 - 60, 보강된 retry delay 테스트에서 isRetryDelayValid의 경계값과 음수 입력을
고정하십시오. retryInitialDelay와 retryMaxDelay가 동일한 양수 Duration일 때 유효함을 단언하고,
retryInitialDelay가 음수일 때 유효하지 않음을 검증하십시오. 기존의 0 이하 및 역전된 최대 지연 검증은 유지하십시오.
src/main/resources/db/migration/V35__add_embedding_job_next_retry_at.sql (1)

5-6: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

대용량 테이블이면 인덱스 생성 방식을 검토하십시오.

일반 CREATE INDEX는 생성 중 해당 테이블의 쓰기를 차단합니다. embedding_jobs 행이 많으면 배포 중 인덱싱 Queue 쓰기가 멈출 수 있습니다. CREATE INDEX CONCURRENTLY는 트랜잭션 안에서 실행할 수 없으므로, 사용하려면 해당 마이그레이션의 트랜잭션 실행을 비활성화해야 합니다. 현재 테이블 규모가 작으면 지금 형태를 유지해도 됩니다.

참고: ADD COLUMN next_retry_at TIMESTAMP는 기본값이 없어 테이블 rewrite를 유발하지 않습니다. 이 부분은 안전합니다.

🤖 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/resources/db/migration/V35__add_embedding_job_next_retry_at.sql`
around lines 5 - 6, Review the migration’s expected table size and deployment
requirements for the index created by idx_embedding_jobs_status_next_retry_at.
If embedding_jobs may be large, switch this index creation to the concurrent
form and disable transactional execution for the migration; otherwise retain the
current CREATE INDEX behavior. Leave the next_retry_at column addition
unchanged.

Source: Linters/SAST tools

src/test/java/com/opensource/docgrid/domain/worker/entity/EmbeddingJobAttemptTest.java (1)

103-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

최초 durationMs도 유지되는지 검증하세요.

재실패 요청은 최초 실패 결과 전체를 보존해야 합니다. Lines 110-112는 종료 시각과 오류 정보만 확인합니다. assertThat(attempt.getDurationMs()).isEqualTo(2_000L);를 추가하세요.

As per path instructions, 테스트 커버리지, 스프링 테스트 어노테이션, mock 사용법, 네이밍 규칙을 확인한다.

🤖 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/entity/EmbeddingJobAttemptTest.java`
around lines 103 - 112, Update the existing assertions in
EmbeddingJobAttemptTest around markFailed to also verify that the original
failure duration is preserved: assert attempt.getDurationMs() remains 2_000L
after the repeated failure request, alongside the existing endedAt and error
assertions.

Source: Path instructions

🤖 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/test-results/Gimini-3-`#88-document-indexing-failure-retry.md:
- Around line 14-70: 문서의 자동 테스트 결과에 새 관리자 실패 API의 Swagger 수동 검증 결과를 추가하세요. 최초
실패, 멱등 재시도, 요청 검증 실패, 소유권 충돌 및 상태 충돌 각각에 대해 실제 요청과 응답 상태를 기록하고, 기존 자동 테스트 결과와 같은
문서에서 함께 정리하세요. 파일명은 {github아이디}-#{이슈번호}-{설명}.md 규칙을 유지하세요.

In
`@src/main/java/com/opensource/docgrid/domain/embedding/converter/EmbeddingJobAttemptConverter.java`:
- Around line 31-40: Validate failure codes before persistence in the
Application save path, especially where
FailDocumentIndexingRequest.failureType().name() is stored, and reject or
normalize values not defined by IndexingFailureType. Update
EmbeddingJobAttemptConverter.toFailureResponse to handle legacy or manually
stored unknown codes without allowing IndexingFailureType.valueOf to throw,
mapping them to the established compatible failure type or excluding them
according to the domain contract.

In `@src/main/resources/application.yml`:
- Around line 27-28: 운영 기본 Retry Backoff 정책을 테스트와 일치시키세요.
src/main/resources/application.yml 27-28행의 retry-max-delay를 고정 10s-20s-40s 정책에
맞게 40s로 조정하고,
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java
88-90행의 검증도 동일한 상한을 사용하도록 정리하세요. 5분 상한을 유지해야 한다면 해당 정책에 맞춰 두 위치의 테스트 기대값과 지연 검증을
확장하세요.

In
`@src/test/java/com/opensource/docgrid/domain/document/entity/DocumentVersionTest.java`:
- Line 16: Update the class-level comment in DocumentVersionTest to include the
UPLOADED-to-FAILED state transition alongside the existing pipeline states,
keeping the documented test scope consistent with the assertions around the
UPLOADED failure transition.

In
`@src/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingFailureIntegrationTest.java`:
- Around line 43-54: Update the class Javadoc and `@DisplayName` in
DocumentIndexingFailureIntegrationTest to replace “OpenSQL” with “PostgreSQL,”
preserving the existing description and test behavior.

---

Nitpick comments:
In
`@src/main/java/com/opensource/docgrid/domain/embedding/repository/EmbeddingJobRepository.java`:
- Around line 42-53: Validate the query plan for findNextPendingForUpdate with
EXPLAIN ANALYZE at production-like scale, focusing on index selection and rows
filtered by next_retry_at. If the existing indexes cause excessive scanning, add
or adjust an index using status, priority, created_at, and id, optionally
incorporating next_retry_at as appropriate, while preserving the query’s
filtering and ordering behavior.

In `@src/main/resources/db/migration/V35__add_embedding_job_next_retry_at.sql`:
- Around line 5-6: Review the migration’s expected table size and deployment
requirements for the index created by idx_embedding_jobs_status_next_retry_at.
If embedding_jobs may be large, switch this index creation to the concurrent
form and disable transactional execution for the migration; otherwise retain the
current CREATE INDEX behavior. Leave the next_retry_at column addition
unchanged.

In
`@src/test/java/com/opensource/docgrid/domain/embedding/entity/EmbeddingJobTest.java`:
- Around line 121-148: Extend EmbeddingJobTest with coverage for the two
untested scheduleRetry guards: add a test using a non-PROCESSING job that
asserts the expected IllegalStateException message and unchanged PENDING status,
and a test using a claimed PROCESSING job with null nextRetryAt that asserts the
expected IllegalArgumentException message, unchanged PROCESSING status, and zero
retry count.

In
`@src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java`:
- Around line 49-60: 보강된 retry delay 테스트에서 isRetryDelayValid의 경계값과 음수 입력을
고정하십시오. retryInitialDelay와 retryMaxDelay가 동일한 양수 Duration일 때 유효함을 단언하고,
retryInitialDelay가 음수일 때 유효하지 않음을 검증하십시오. 기존의 0 이하 및 역전된 최대 지연 검증은 유지하십시오.

In
`@src/test/java/com/opensource/docgrid/domain/worker/entity/EmbeddingJobAttemptTest.java`:
- Around line 103-112: Update the existing assertions in EmbeddingJobAttemptTest
around markFailed to also verify that the original failure duration is
preserved: assert attempt.getDurationMs() remains 2_000L after the repeated
failure request, alongside the existing endedAt and error assertions.
🪄 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 Plus

Run ID: d68bb01d-58eb-47c6-8e52-0a211e2decf1

📥 Commits

Reviewing files that changed from the base of the PR and between d8d7bd4 and 19b3697.

📒 Files selected for processing (31)
  • docs/design/Gimini-3-#88-document-indexing-failure-retry.md
  • docs/test-results/Gimini-3-#88-document-indexing-failure-retry.md
  • src/main/java/com/opensource/docgrid/domain/document/entity/DocumentVersion.java
  • src/main/java/com/opensource/docgrid/domain/embedding/controller/IndexingJobAdminController.java
  • src/main/java/com/opensource/docgrid/domain/embedding/converter/EmbeddingJobAttemptConverter.java
  • src/main/java/com/opensource/docgrid/domain/embedding/dto/request/FailDocumentIndexingRequest.java
  • src/main/java/com/opensource/docgrid/domain/embedding/dto/response/DocumentIndexingFailureResponse.java
  • src/main/java/com/opensource/docgrid/domain/embedding/entity/EmbeddingJob.java
  • src/main/java/com/opensource/docgrid/domain/embedding/enums/IndexingFailureType.java
  • src/main/java/com/opensource/docgrid/domain/embedding/repository/EmbeddingJobRepository.java
  • src/main/java/com/opensource/docgrid/domain/embedding/repository/EmbeddingRepository.java
  • src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java
  • src/main/java/com/opensource/docgrid/domain/embedding/service/command/EmbeddingJobClaimService.java
  • src/main/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerProperties.java
  • src/main/java/com/opensource/docgrid/domain/worker/entity/EmbeddingJobAttempt.java
  • src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java
  • src/main/java/com/opensource/docgrid/global/exception/GlobalExceptionHandler.java
  • src/main/resources/application.yml
  • src/main/resources/db/migration/V35__add_embedding_job_next_retry_at.sql
  • src/test/java/com/opensource/docgrid/domain/document/entity/DocumentVersionTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/controller/IndexingJobAdminControllerTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/converter/EmbeddingJobAttemptConverterTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/dto/request/FailDocumentIndexingRequestTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/entity/EmbeddingJobTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/enums/IndexingFailureTypeTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingFailureIntegrationTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/integration/EmbeddingJobClaimIntegrationTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/EmbeddingJobClaimServiceTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java
  • src/test/java/com/opensource/docgrid/domain/worker/entity/EmbeddingJobAttemptTest.java

Comment thread docs/test-results/Gimini-3-#88-document-indexing-failure-retry.md Outdated
Comment on lines +27 to +28
retry-initial-delay: ${INDEXING_WORKER_RETRY_INITIAL_DELAY:10s}
retry-max-delay: ${INDEXING_WORKER_RETRY_MAX_DELAY:5m}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Locate relevant files"
git ls-files | rg '(^|/)(application(\..+)?\.(yml|yaml)|DocumentIndexingFailureServiceTest\.java|.*IndexingWorker.*)$' || true

echo
echo "## application.yml around indexing retry"
if [ -f src/main/resources/application.yml ]; then
  nl -ba src/main/resources/application.yml | sed -n '1,80p' | rg -n -C 5 'indexing|retry-initial-delay|retry-max-delay|maxRetryCount|retry|job|worker' || true
fi

echo
echo "## Search relevant symbols"
rg -n "retry-initial-delay|retry-max-delay|maxRetryCount|IndexingWorkerProperties|DocumentIndexingFailureServiceTest|Retry" src/main src/test || true

echo
echo "## Candidate Java outlines/files"
fd -a 'IndexingWorker|DocumentIndexingFailure' src/main src/test 2>/dev/null || true
for f in $(git ls-files | rg '(IndexingWorker|DocumentIndexingFailure).*\.java$'); do
  echo "--- $f"
  wc -l "$f"
  ast-grep outline "$f" || true
done

Repository: DocGrid/backend

Length of output: 33872


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import math
from pathlib import Path
import re

paths = {
    "application": Path("src/main/resources/application.yml"),
    "properties_test": Path("src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java"),
    "service_test": Path("src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java"),
    "integration_test": Path("src/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingFailureIntegrationTest.java"),
    "service": Path("src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java"),
}

for name, path in paths.items():
    print(f"--- {name}: {path}")
    s = path.read_text()
    print(f"  has_retry_config: {bool(re.search(r'([\\$]?indexing\\.worker\\.(retry-initial-delay|retry-max-delay)|INDEXING_WORKER_(RETRY_INITIAL_DELAY|RETRY_MAX_DELAY))', s, re.I))}")
    print(f"  relevant snippets:")
    for i, line in enumerate(s.splitlines(), 1):
        if any(tok in line for tok in [
            "retry-initial-delay", "retry-max-delay",
            "INDEXING_WORKER_RETRY", "DEFAULT_RETRY", "ofSeconds", "ofMinutes",
            "40s", "40秒", "40 초", "5m", "5 분", "5 minutes",
            "fail_capsRetryBackoffAtMaximumDelay", "fail_doublesRetryDelay",
            "calculateRetryDelay"
        ]):
            print(f"    {i}: {line}")
    print()

def retry_delay(retry_count, initial, max_delay, max_retry_count):
    if retry_count == 0:
        return 0
    for i in range(retry_count):
        pass
    delay = min(initial, max_delay)
    for attempt in range(retry_count - 1):
        delay = min(delay * 2, max_delay)
    return delay

initial = 10
for max_delay in [40, 300]:
    print(f"max_delay={max_delay}s table:")
    for max_retry_count in [3, 4, 5];
        print(f"  maxRetryCount={max_retry_count}, retryable_count={max_retry_count}, last_delay_s={retry_delay(max_retry_count, initial, max_delay, max_retry_count)}")
PY

echo
echo "## Exact retry delay implementation"
nl -ba src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java | sed -n '360,395p'

echo
echo "## Configuration property defaults and tests"
nl -ba src/main/resources/application.yml | sed -n '20,32p' 2>/dev/null || sed -n '20,32p' src/main/resources/application.yml
nl -ba src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java | sed -n '38,48p'
nl -ba src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java | sed -n '84,182p'
nl -ba src/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingFailureIntegrationTest.java | sed -n '68,82p'

Repository: DocGrid/backend

Length of output: 614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import math
import re
from pathlib import Path

paths = {
    "application": Path("src/main/resources/application.yml"),
    "properties_test": Path("src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java"),
    "service_test": Path("src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java"),
    "integration_test": Path("src/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingFailureIntegrationTest.java"),
    "service": Path("src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java"),
}

for name, path in paths.items():
    s = path.read_text()
    print(f"--- {name}: {path}")
    has = bool(re.search(
        r'([\\$]?indexing\.worker\.(retry-initial-delay|retry-max-delay)|INDEXING_WORKER_(RETRY_INITIAL_DELAY|RETRY_MAX_DELAY))',
        s,
        re.I,
    ))
    print(f"  has_retry_config: {has}")
    print(f"  relevant snippets:")
    for i, line in enumerate(s.splitlines(), 1):
        if any(tok in line for tok in [
            "retry-initial-delay", "retry-max-delay",
            "INDEXING_WORKER_RETRY", "DEFAULT_RETRY", "ofSeconds", "ofMinutes",
            "40s", "5m",
            "fail_capsRetryBackoffAtMaximumDelay", "fail_doublesRetryDelay",
            "calculateRetryDelay"
        ]):
            print(f"    {i}: {line}")
    print()

initial = 10
for max_delay in [40, 300]:
    print(f"max_delay={max_delay}s table:")
    for max_retry_count in [3, 4, 5]:
        delay = min(initial, max_delay)
        for attempt in range(max_retry_count - 1):
            delay = min(delay * 2, max_delay)
        print(f"  maxRetryCount={max_retry_count}, last_delay_s={delay}")
PY

echo
echo "## Exact retry delay implementation"
nl -ba src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java | sed -n '360,395p'

echo
echo "## Configuration property defaults and tests"
nl -ba src/main/resources/application.yml | sed -n '20,32p'
nl -ba src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java | sed -n '38,48p'
nl -ba src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java | sed -n '84,182p'
nl -ba src/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingFailureIntegrationTest.java | sed -n '68,82p'

Repository: DocGrid/backend

Length of output: 2692


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

for path, ranges in [
    ("src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureService.java", [(379, 396)]),
    ("src/main/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerProperties.java", [(26, 82)]),
    ("src/test/java/com/opensource/docgrid/domain/worker/config/IndexingWorkerPropertiesTest.java", [(38, 48), (54, 59)]),
]:
    s = Path(path).read_text()
    lines = s.splitlines()
    print(f"--- {path}")
    for start, end in ranges:
        for i in range(start, min(end, len(lines))+1):
            print(f"{i}: {lines[i-1]}")
    print()
PY

Repository: DocGrid/backend

Length of output: 4071


운영 기본 Retry Backoff 정책과 테스트 설정을 맞춰세요.

application.yml의 기본값은 10s5m이지만, DocumentIndexingFailureServiceTest와 통합 테스트는 40s를 상한으로 검증합니다. maxRetryCount가 4 이상이면 5분 상한에서 80초·160초 지연까지 사용할 수 있어, 고정 10s-20s-40s 정책이라면 retry-max-delay40s로 통합해야 합니다. 5분 상한이 의도라면 해당 정책을 명시하고 관련 테스트를 그 동작에 맞게 확장하세요.

src/main/resources/application.yml#L27-L28
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java#L88-L90

📍 Affects 2 files
  • src/main/resources/application.yml#L27-L28 (this comment)
  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java#L88-L90
🤖 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/resources/application.yml` around lines 27 - 28, 운영 기본 Retry Backoff
정책을 테스트와 일치시키세요. src/main/resources/application.yml 27-28행의 retry-max-delay를 고정
10s-20s-40s 정책에 맞게 40s로 조정하고,
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java
88-90행의 검증도 동일한 상한을 사용하도록 정리하세요. 5분 상한을 유지해야 한다면 해당 정책에 맞춰 두 위치의 테스트 기대값과 지연 검증을
확장하세요.

Source: Path instructions

@Gimini-3
Gimini-3 merged commit 2512635 into develop Aug 3, 2026
1 check passed
@Gimini-3 Gimini-3 self-assigned this Aug 3, 2026
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] 인덱싱 실패 종료 및 지연 재시도

1 participant