Skip to content

[Feat] Embedding Job 상태 전이 이벤트 발행 및 대시보드 구독 - #152

Merged
kangcheolung merged 11 commits into
developfrom
feature/151
Aug 11, 2026
Merged

[Feat] Embedding Job 상태 전이 이벤트 발행 및 대시보드 구독#152
kangcheolung merged 11 commits into
developfrom
feature/151

Conversation

@kangcheolung

@kangcheolung kangcheolung commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

  • Worker가 만드는 PENDING → PROCESSING → INDEXED/FAILED 상태 전이를 대시보드가 새로고침 없이 실시간 반영하도록, EmbeddingJobClaimService/DocumentIndexingCompletionService/DocumentIndexingFailureService(A 담당자 소유) 세 지점에 EmbeddingJobStatusChangedEvent 발행 한 줄씩만 추가 — 상태 전환 로직 자체는 전혀 변경하지 않음
  • 이벤트는 dashboard가 아닌 embedding 패키지에 정의 (dashboard → embedding 단방향 의존 유지, A 소유 코드가 B 패키지를 import하지 않도록)
  • @TransactionalEventListener(phase = AFTER_COMMIT)로 구독 — 발행 Transaction이 실제로 커밋된 경우에만 반응, 롤백 시 push 없음
  • 이벤트 즉시 push 대신 DashboardUpdateFlag(AtomicBoolean) + DashboardPushScheduler(300ms 주기, fixedDelayString) 조합으로 debounce — burst 상황(retry-all, 다건 장애)에서 이벤트 개수만큼 집계 쿼리가 느는 것을 방지
  • 대시보드 push 스케줄러는 A의 indexing.worker.enabled(기본 false) 설정과 무관하게 항상 동작해야 하므로, 조건 없는 DashboardSchedulingConfig를 별도로 추가 (기존 WorkerSchedulingConfig@EnableScheduling은 조건부라 얹혀가면 안 됨)

설계 결정 — debounce 근거

burst 상황에서 이벤트마다 즉시 getSummary()(약 9개 쿼리)를 재계산하면 27건 burst에서 27 × 9 ≈ 243개 쿼리가 몰릴 수 있습니다. DashboardUpdateFlag.consumeIfDirty()(check-and-clear를 compareAndSet으로 원자화)와 짧은 주기 스케줄러 조합으로, 몇 번의 상태 전이가 몰리든 debounce 주기당 최대 1회(9개 안팎)로 억제됩니다. 이 수치는 Hibernate Statistics.getPrepareStatementCount()로 통합 테스트에서 실측 검증했습니다. 상세 근거는 docs/design/kangcheolung-#151-ragops-dashboard-realtime-update.md 3장·5장 참고.

Test plan

  • DashboardUpdateFlagTest — markDirty/consumeIfDirty 원자성, 다건 markDirty coalesce, 초기 상태 검증 (단위 테스트 3건)
  • EmbeddingJobStatusChangedEventListenerTest — 이벤트 수신 시 플래그만 세우고 그 외 상호작용 없음 검증 (단위 테스트 1건)
  • DashboardPushSchedulerTest — dirty일 때만 집계·push, dirty 아니면 무상호작용 검증 (단위 테스트 2건)
  • EmbeddingJobClaimServiceTest/DocumentIndexingCompletionServiceTest/DocumentIndexingFailureServiceTest — 생성자 시그니처 변경(ApplicationEventPublisher 추가)에 따른 Mock 반영, 기존 회귀 유지
  • DashboardPushDebounceIntegrationTest — 27개 이벤트를 REQUIRES_NEW로 각각 독립 커밋시켜 실제 burst 재현, Hibernate Statistics + Awaitility로 쿼리 수가 <20(예상 243이 아님) 수준으로 억제됨을 실측
  • EmbeddingJobStatusChangedAfterCommitIntegrationTest — 실제 STOMP Client로 (1) 커밋 시 push 도착, (2) 롤백 시 800ms(=debounce 4주기 이상) 대기해도 push 없음을 종단 검증
  • ./gradlew build 전체 회귀 테스트 통과 — 총 769개, failures 0, errors 0

상세 설계

docs/design/kangcheolung-#151-ragops-dashboard-realtime-update.md

closes #151

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 새 기능

    • 임베딩 작업 상태 변경 시 RAGOps 대시보드가 자동으로 실시간 갱신됩니다.
    • 잦은 변경 사항은 debounce 방식으로 묶어 대시보드 업데이트 횟수를 최적화합니다.
    • 커밋된 변경만 대시보드에 반영되며, 롤백된 변경은 전송되지 않습니다.
  • 테스트

    • 실시간 갱신, debounce 처리, 커밋·롤백 동작에 대한 단위 및 통합 테스트를 추가했습니다.

kangcheolung and others added 8 commits August 11, 2026 21:03
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ingConfig) 구현

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 44 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: d6134e16-e110-4b76-86b0-2854a1396095

📥 Commits

Reviewing files that changed from the base of the PR and between e7cf990 and 63108a0.

📒 Files selected for processing (4)
  • docs/design/kangcheolung-#151-ragops-dashboard-realtime-update.md
  • src/main/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushScheduler.java
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushDebounceIntegrationTest.java
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushSchedulerTest.java
📝 Walkthrough

Walkthrough

임베딩 작업의 Claim·Completion·Failure 상태 전이가 이벤트를 발행합니다. 커밋 후 리스너가 dirty 플래그를 설정합니다. debounce 스케줄러가 최신 집계를 /topic/dashboard로 전송합니다. 단위 테스트와 PostgreSQL 통합 테스트가 동작을 검증합니다.

Changes

임베딩 상태 이벤트 발행

Layer / File(s) Summary
상태 변경 이벤트와 발행 흐름
src/main/java/com/opensource/docgrid/domain/embedding/event/..., src/main/java/com/opensource/docgrid/domain/embedding/service/command/..., src/test/java/com/opensource/docgrid/domain/embedding/service/command/...
EmbeddingJobStatusChangedEvent를 추가했습니다. Claim·Completion·Failure 처리 후 이벤트를 발행하도록 변경했습니다. 관련 테스트 생성자에 ApplicationEventPublisher를 주입했습니다.

대시보드 debounce 갱신

Layer / File(s) Summary
커밋 후 dirty 플래그와 push 스케줄러
src/main/java/com/opensource/docgrid/domain/dashboard/config/..., src/main/java/com/opensource/docgrid/domain/dashboard/event/..., src/main/resources/application.yml, src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardUpdateFlagTest.java, src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushSchedulerTest.java, src/test/java/com/opensource/docgrid/domain/dashboard/event/EmbeddingJobStatusChangedEventListenerTest.java, docs/design/...
AFTER_COMMIT 리스너가 dirty 플래그만 설정합니다. 스케줄러가 설정된 주기마다 플래그를 소비하고 변경 시 대시보드 집계를 WebSocket으로 전송합니다. 스케줄링과 debounce 설정을 추가했습니다.

통합 검증

Layer / File(s) Summary
burst·커밋·롤백 통합 테스트
src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushDebounceIntegrationTest.java, src/test/java/com/opensource/docgrid/domain/dashboard/event/EmbeddingJobStatusChangedAfterCommitIntegrationTest.java, build.gradle
27개 독립 커밋 이벤트의 집계 쿼리 수를 검증합니다. 커밋 시 WebSocket 메시지 수신을 검증하고 롤백 시 메시지 미수신을 검증합니다. Awaitility 테스트 의존성을 추가했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant EmbeddingJobStatusService
  participant ApplicationEventPublisher
  participant EmbeddingJobStatusChangedEventListener
  participant DashboardUpdateFlag
  participant DashboardPushScheduler
  participant WebSocket
  EmbeddingJobStatusService->>ApplicationEventPublisher: 상태 변경 이벤트 발행
  ApplicationEventPublisher->>EmbeddingJobStatusChangedEventListener: 커밋 후 이벤트 전달
  EmbeddingJobStatusChangedEventListener->>DashboardUpdateFlag: markDirty()
  DashboardPushScheduler->>DashboardUpdateFlag: consumeIfDirty()
  DashboardPushScheduler->>WebSocket: 최신 대시보드 집계 전송
Loading

Possibly related PRs

  • DocGrid/backend#53: 동일한 EmbeddingJobClaimService 경로의 동시성 통합 테스트와 연결됩니다.
  • DocGrid/backend#87: DocumentIndexingCompletionService의 상태 전이 흐름을 확장합니다.
  • DocGrid/backend#139: 기존 대시보드 WebSocket 인프라와 /topic/dashboard 경로를 사용합니다.

Suggested labels: ✨ Feature

Suggested reviewers: gimini-3

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 제목이 Embedding Job 상태 전이 이벤트 발행과 대시보드 구독이라는 주요 변경 사항을 정확히 설명합니다.
Description check ✅ Passed 필수 작업 내용, 상세 설계, 테스트 계획과 이슈 종료 정보가 포함되어 설명이 충분합니다.
Linked Issues check ✅ Passed [151] 상태 전이 이벤트, AFTER_COMMIT 처리, debounce, 의존 방향 및 통합 테스트 요구사항을 모두 충족합니다.
Out of Scope Changes check ✅ Passed 변경 사항이 이벤트 기반 대시보드 갱신, debounce 구성, 설정 및 관련 테스트 범위에 포함됩니다.
✨ 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/151

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

🧹 Nitpick comments (1)
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionServiceTest.java (1)

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

세 단위 테스트가 이벤트 발행 계약을 검증하지 않습니다.

세 파일 모두 ApplicationEventPublisher Mock만 주입합니다. publishEvent(...)가 호출되지 않아도 테스트가 통과합니다.

  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionServiceTest.java#L76-L76: 완료 성공 시 EmbeddingJobStatusChangedEvent.jobId()를 검증하고, 완료 재생 시 재발행하지 않는지 확인하세요.
  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java#L79-L79: 재시도 예약과 최종 실패 시 이벤트를 검증하고, 실패 재생 시 중복 발행하지 않는지 확인하세요.
  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/EmbeddingJobClaimServiceTest.java#L63-L63: 정상 Claim 시 이벤트를 검증하고, Claim 대상이 없을 때는 발행하지 않는지 확인하세요.
검증 예시
+then(applicationEventPublisher).should()
+    .publishEvent(new EmbeddingJobStatusChangedEvent(JOB_ID));

As per path instructions, src/test/**/*.java는 테스트 커버리지, 스프링 테스트 어노테이션, 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/embedding/service/command/DocumentIndexingCompletionServiceTest.java`
at line 76, 세 단위 테스트가 ApplicationEventPublisher.publishEvent 호출 계약을 검증하도록 보강하세요.
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionServiceTest.java
76-76에서는 성공 완료 이벤트의 EmbeddingJobStatusChangedEvent.jobId()를 검증하고 재생 시 재발행되지 않음을
확인하세요.
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java
79-79에서는 재시도 예약 및 최종 실패 이벤트를 검증하고 실패 재생 시 중복 발행이 없음을 확인하세요.
src/test/java/com/opensource/docgrid/domain/embedding/service/command/EmbeddingJobClaimServiceTest.java
63-63에서는 정상 Claim 이벤트를 검증하고 Claim 대상이 없을 때 publishEvent가 호출되지 않음을 검증하세요.

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/design/kangcheolung-`#151-ragops-dashboard-realtime-update.md:
- Line 106: markdownlint MD040 경고를 해결하도록
docs/design/kangcheolung-#151-ragops-dashboard-realtime-update.md의 106-106 아키텍처
다이어그램 펜스와 335-335 흐름 예시 펜스에 text 언어 식별자를 지정하세요.

In
`@src/main/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushScheduler.java`:
- Around line 19-22: DashboardPushScheduler의 클래스 주석에서 비공개 순번 라벨인 “(이슈1)”과
“(이슈2)”를 제거하고, 각각 “대시보드 집계 조회”와 “WebSocket 전송” 같은 설명형 이름으로 교체하세요.
- Around line 41-44: DashboardPushScheduler의 consumeIfDirty() 블록에서 getSummary()
또는 sendDashboardUpdate()가 실패하면 예외를 다시 전파하기 전에 dashboardUpdateFlag.markDirty()를
호출해 dirty 상태를 복구하세요. 정상적인 push 경로는 기존 동작을 유지하고, 이 예외 경로를 검증하는 단위 테스트를 추가하세요.

In
`@src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushDebounceIntegrationTest.java`:
- Around line 113-118: 강화된 대시보드 집계 검증을 위해 DashboardQueryService.getSummary() 호출
횟수를 계측하고 정확히 한 번인지 검증하세요. 이벤트 발행이 완료된 뒤 최소 한 debounce 주기 이상 대기한 후 단일 호출 조건을
확인하도록 await/assertion 흐름을 변경하고, 기존 prepare statement 수 기반의 느슨한 검증은 제거하거나 보조 검증으로
대체하세요.

---

Nitpick comments:
In
`@src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionServiceTest.java`:
- Line 76: 세 단위 테스트가 ApplicationEventPublisher.publishEvent 호출 계약을 검증하도록 보강하세요.
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionServiceTest.java
76-76에서는 성공 완료 이벤트의 EmbeddingJobStatusChangedEvent.jobId()를 검증하고 재생 시 재발행되지 않음을
확인하세요.
src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingFailureServiceTest.java
79-79에서는 재시도 예약 및 최종 실패 이벤트를 검증하고 실패 재생 시 중복 발행이 없음을 확인하세요.
src/test/java/com/opensource/docgrid/domain/embedding/service/command/EmbeddingJobClaimServiceTest.java
63-63에서는 정상 Claim 이벤트를 검증하고 Claim 대상이 없을 때 publishEvent가 호출되지 않음을 검증하세요.
🪄 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: d61ea00d-9475-4f41-a3b8-d878035c9bc4

📥 Commits

Reviewing files that changed from the base of the PR and between f0e935b and e7cf990.

📒 Files selected for processing (19)
  • build.gradle
  • docs/design/kangcheolung-#151-ragops-dashboard-realtime-update.md
  • src/main/java/com/opensource/docgrid/domain/dashboard/config/DashboardSchedulingConfig.java
  • src/main/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushScheduler.java
  • src/main/java/com/opensource/docgrid/domain/dashboard/event/DashboardUpdateFlag.java
  • src/main/java/com/opensource/docgrid/domain/dashboard/event/EmbeddingJobStatusChangedEventListener.java
  • src/main/java/com/opensource/docgrid/domain/embedding/event/EmbeddingJobStatusChangedEvent.java
  • src/main/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionService.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/resources/application.yml
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushDebounceIntegrationTest.java
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardPushSchedulerTest.java
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/DashboardUpdateFlagTest.java
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/EmbeddingJobStatusChangedAfterCommitIntegrationTest.java
  • src/test/java/com/opensource/docgrid/domain/dashboard/event/EmbeddingJobStatusChangedEventListenerTest.java
  • src/test/java/com/opensource/docgrid/domain/embedding/service/command/DocumentIndexingCompletionServiceTest.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

Comment thread docs/design/kangcheolung-#151-ragops-dashboard-realtime-update.md Outdated
@kangcheolung
kangcheolung merged commit 2e45c49 into develop Aug 11, 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] Embedding Job 상태 전이 이벤트 발행 및 대시보드 구독

1 participant