-
Notifications
You must be signed in to change notification settings - Fork 1
[Feat] 관리자 인덱싱 Job·Attempt·Event 조회 지원 추가 구현 #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
848f733
docs: #119 관리자 인덱싱 조회 설계 문서 추가
Gimini-3 f65e56b
feat: #119 관리자 조회 페이지 응답 계약 추가
Gimini-3 c08bc6e
feat: #119 Job·Attempt·Event 조회 쿼리 추가
Gimini-3 9ff37fb
feat: #119 관리자 인덱싱 조회 Service 구현
Gimini-3 cb26d39
feat: #119 관리자 인덱싱 조회 API 추가
Gimini-3 847e102
test: #119 관리자 인덱싱 조회 단위·Controller 테스트 추가
Gimini-3 829aefc
test: #119 관리자 인덱싱 조회 PostgreSQL 통합 검증 추가
Gimini-3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| # Issue #119 관리자 인덱싱 Job·Attempt·Event 조회 상세 설계 | ||
|
|
||
| closes #119 | ||
|
|
||
| ## 1. 배경과 목적 | ||
|
|
||
| 현재 관리자 API는 Worker 목록 조회와 인덱싱 Job의 Claim·Lease·Attempt 시작·파싱·임베딩·완료·실패· | ||
| 수동 재처리 같은 제어 명령을 제공한다. 그러나 운영자가 Job의 현재 상태와 과거 Attempt, 상태 전이 | ||
| Event를 조회할 API는 없어 장애 원인과 재처리 대상을 DB에서 직접 확인해야 한다. | ||
|
|
||
| 이번 작업은 기존 상태 전이와 Worker 실행 경로를 변경하지 않고, 관리자에게 필요한 읽기 전용 관측 | ||
| API를 추가한다. 기존 `/admin/workers`는 그대로 재사용하며 Job 목록·상세, Attempt 이력, Event | ||
| 타임라인만 새로 제공한다. | ||
|
|
||
| ### 1.1 성공 기준 | ||
|
|
||
| - Job 목록을 상태·문서·현재 소유 Worker 조건으로 필터링한다. | ||
| - 모든 다건 응답은 최대 100건의 Pagination을 적용한다. | ||
| - Job 상세에서 현재 Queue·Retry·Lease·종결 상태를 확인할 수 있다. | ||
| - Attempt와 Event는 각각 독립된 Pagination으로 조회한다. | ||
| - Claim Token, 내부 오류 메시지와 Event Metadata를 응답하지 않는다. | ||
| - 모든 API는 기존 `/admin/**` ADMIN 권한 정책을 따른다. | ||
| - 조회 API가 기존 인덱싱 상태나 감사 이력을 변경하지 않는다. | ||
|
|
||
| ## 2. 범위 | ||
|
|
||
| ### 2.1 포함 | ||
|
|
||
| - 관리자 Job 목록 조회 | ||
| - 관리자 Job 상세 조회 | ||
| - Job별 Attempt 이력 조회 | ||
| - Job별 Indexing Event 타임라인 조회 | ||
| - 공통 페이지 응답 계약 | ||
| - Query 전용 Repository·Converter·Service | ||
| - 단위·Controller·PostgreSQL 통합 테스트 | ||
|
|
||
| ### 2.2 제외 | ||
|
|
||
| - Worker 목록 API 재구현 | ||
| - Job 상태 변경 또는 일괄 수동 재처리 | ||
| - Claim·Retry·Lease 정책 변경 | ||
| - 로그 수집, Metrics, 알림과 Dashboard UI | ||
| - Event Metadata 원문 공개 | ||
| - Flyway Migration과 DB Index 변경 | ||
|
|
||
| ## 3. API 계약 | ||
|
|
||
| ### 3.1 Job 목록 | ||
|
|
||
| ```text | ||
| GET /admin/indexing-jobs | ||
| ?status=PENDING | ||
| &documentId=1 | ||
| &workerId=2 | ||
| &page=0 | ||
| &size=20 | ||
| ``` | ||
|
|
||
| - 모든 필터는 선택이다. | ||
| - `page` 기본값은 0, `size` 기본값은 20이다. | ||
| - `page >= 0`, `1 <= size <= 100`을 검증한다. | ||
| - 정렬은 `created_at DESC, id DESC`로 고정한다. | ||
| - Worker 필터는 `locked_by_worker_id`인 현재 소유 Worker를 의미한다. | ||
|
|
||
| 목록 항목은 다음 정보를 포함한다. | ||
|
|
||
| - Job ID, 상태, 우선순위, Retry Count, Max Retry Count, Next Retry At | ||
| - Document ID와 제목 | ||
| - Version ID와 Version 번호·상태 | ||
| - Embedding Model ID와 이름 | ||
| - 현재 Worker ID와 이름 | ||
| - 공개 가능한 Error Code | ||
| - 생성·시작·완료·실패 시각 | ||
|
|
||
| ### 3.2 Job 상세 | ||
|
|
||
| ```text | ||
| GET /admin/indexing-jobs/{jobId} | ||
| ``` | ||
|
|
||
| 목록 정보에 `locked_at`, `lock_expires_at`을 추가한다. Claim Token과 Error Message는 반환하지 않는다. | ||
|
|
||
| ### 3.3 Attempt 이력 | ||
|
|
||
| ```text | ||
| GET /admin/indexing-jobs/{jobId}/attempts?page=0&size=20 | ||
| ``` | ||
|
|
||
| - 정렬은 `attempt_no DESC, id DESC`로 고정한다. | ||
| - Attempt ID·번호·상태, Worker ID·이름, 시작·종료·소요 시간, Error Code를 반환한다. | ||
| - Claim Token과 Error Message는 반환하지 않는다. | ||
| - Attempt가 없어도 Job이 존재하면 빈 Page를 반환한다. | ||
|
|
||
| ### 3.4 Event 타임라인 | ||
|
|
||
| ```text | ||
| GET /admin/indexing-jobs/{jobId}/events?page=0&size=20 | ||
| ``` | ||
|
|
||
| - 정렬은 `occurred_at DESC, id DESC`로 고정한다. | ||
| - Event ID·Type, From/To 상태, 고정 메시지, 발생 시각을 반환한다. | ||
| - `metadata_json`은 Worker·Attempt 식별자 외에 실패 진단 Snapshot을 포함할 수 있으므로 공개하지 않는다. | ||
| - Event가 없어도 Job이 존재하면 빈 Page를 반환한다. | ||
|
|
||
| ## 4. 공통 Pagination 응답 | ||
|
|
||
| ```json | ||
| { | ||
| "content": [], | ||
| "page": 0, | ||
| "size": 20, | ||
| "totalElements": 0, | ||
| "totalPages": 0, | ||
| "first": true, | ||
| "last": true | ||
| } | ||
| ``` | ||
|
|
||
| Spring Data `Page`를 Controller에서 직접 직렬화하지 않는다. `PageResponse<T>`가 응답 구조를 고정하고 | ||
| Entity 대신 이미 변환된 DTO만 보유한다. | ||
|
|
||
| ## 5. 조회 구조 | ||
|
|
||
| ### 5.1 Job 목록 | ||
|
|
||
| `EmbeddingJobRepository`에 관리자 조회 전용 JPQL과 Count Query를 추가한다. DocumentVersion, | ||
| Document, EmbeddingModel, 현재 Worker를 Fetch Join해 Page 한 건당 추가 Lazy Query가 발생하지 않게 한다. | ||
| 필터는 Null이면 생략한다. | ||
|
|
||
| ### 5.2 Job 상세 | ||
|
|
||
| Job ID로 같은 연관관계를 Fetch Join한다. 없으면 기존 `EMBEDDING_JOB_NOT_FOUND` 오류를 반환한다. | ||
|
|
||
| ### 5.3 Attempt와 Event | ||
|
|
||
| 두 Repository는 Job ID 조건과 고정 정렬을 가진 Page Query를 제공한다. Attempt의 Worker는 Fetch | ||
| Join하고, Event는 Job 존재를 Query Service에서 먼저 확인한 뒤 이력만 조회한다. | ||
|
|
||
| ### 5.4 Query Service | ||
|
|
||
| `IndexingJobAdminQueryService`는 클래스 수준 `@Transactional(readOnly = true)`를 사용한다. | ||
|
|
||
| 1. Controller가 검증한 필터와 Pagination 값을 받는다. | ||
| 2. Repository에서 Entity Page 또는 상세 Snapshot을 조회한다. | ||
| 3. `IndexingJobAdminConverter`로 공개 DTO를 생성한다. | ||
| 4. `PageResponse`로 Pagination Metadata를 고정한다. | ||
|
|
||
| ## 6. 공개 정보 경계 | ||
|
|
||
| | 저장 필드 | 응답 | 이유 | | ||
| |---|---|---| | ||
| | Job `claim_token` | 제외 | 현재 소유권 증명 값 | | ||
| | Job `error_message` | 제외 | 내부 Provider·Storage 진단 포함 가능 | | ||
| | Attempt `claim_token` | 제외 | 과거 소유권 증명 값 | | ||
| | Attempt `error_message` | 제외 | 내부 예외 Snapshot 포함 가능 | | ||
| | Event `metadata_json` | 제외 | Worker·Attempt와 실패 진단 Metadata 원문 | | ||
| | Job·Attempt `error_code` | 포함 | 운영 분류에 필요한 제한된 코드 | | ||
| | Event `message` | 포함 | Command 계층에서 생성하는 고정된 안전 메시지 | | ||
|
|
||
| 응답 DTO에는 제외 필드 자체를 정의하지 않아 Jackson 설정 변경이나 실수로 노출될 가능성을 줄인다. | ||
|
|
||
| ## 7. 오류 계약 | ||
|
|
||
| | 상황 | HTTP | 코드 | | ||
| |---|---:|---| | ||
| | Job 없음 | 404 | `EMBEDDING-JOB-001` | | ||
| | `jobId`, `documentId`, `workerId`가 양수가 아님 | 400 | `COMMON-002` | | ||
| | `page < 0` | 400 | `COMMON-002` | | ||
| | `size < 1` 또는 `size > 100` | 400 | `COMMON-002` | | ||
| | 지원하지 않는 Job 상태 | 400 | 기존 Enum 변환 오류 처리 | | ||
| | 미인증 또는 ADMIN 아님 | 403 | 기존 Security 정책 | | ||
|
|
||
| `SecurityConfig`의 `/admin/** -> hasRole("ADMIN")` 규칙을 재사용하므로 Security 설정은 변경하지 않는다. | ||
|
|
||
| ## 8. 테스트 설계 | ||
|
|
||
| ### 8.1 단위 테스트 | ||
|
|
||
| - Job 목록 필터와 Page 값이 Repository로 전달되는지 검증 | ||
| - Job 상세 DTO 변환과 Not Found 검증 | ||
| - Attempt·Event 빈 Page와 데이터 Page 변환 검증 | ||
| - 민감 필드가 Response Record 구성요소에 존재하지 않는지 검증 | ||
|
|
||
| ### 8.2 Controller 테스트 | ||
|
|
||
| - 네 API의 정상 응답 구조 | ||
| - 상태·문서·Worker 필터 전달 | ||
| - Page·Size·양수 ID Validation | ||
| - ADMIN 성공, USER·미인증 403 | ||
| - 직렬화 JSON에 Claim Token, Error Message, Metadata JSON이 없는지 검증 | ||
|
|
||
| ### 8.3 PostgreSQL 통합 테스트 | ||
|
|
||
| - 상태·문서·Worker 필터 조합 | ||
| - `created_at DESC, id DESC` 고정 정렬과 목록 Pagination | ||
| - Attempt `attempt_no DESC, id DESC` 정렬 | ||
| - Event `occurred_at DESC, id DESC` 정렬 | ||
| - 종료 Job처럼 현재 Worker가 Null인 항목 조회 | ||
|
|
||
| ## 9. 커밋 분할 | ||
|
|
||
| 1. `docs: #119 관리자 인덱싱 조회 설계 문서 추가` | ||
| 2. `feat: #119 관리자 조회 페이지 응답 계약 추가` | ||
| 3. `feat: #119 Job·Attempt·Event 조회 쿼리 추가` | ||
| 4. `feat: #119 관리자 인덱싱 조회 Service 구현` | ||
| 5. `feat: #119 관리자 인덱싱 조회 API 추가` | ||
| 6. `test: #119 관리자 인덱싱 조회 단위·Controller 테스트 추가` | ||
| 7. `test: #119 관리자 인덱싱 조회 PostgreSQL 통합 검증 추가` | ||
|
|
||
| ## 10. 완료 조건 | ||
|
|
||
| - Job 목록·상세와 Attempt·Event Page 조회 가능 | ||
| - 상태·문서·Worker 필터 및 고정 정렬 동작 | ||
| - 민감한 소유권·내부 오류·Metadata 미노출 | ||
| - 기존 Worker 조회와 인덱싱 Command API 회귀 없음 | ||
| - 관리자 권한과 입력 검증 통과 | ||
| - PostgreSQL 통합 테스트와 전체 회귀 테스트 통과 |
84 changes: 84 additions & 0 deletions
84
docs/test-results/gimin-#119-admin-indexing-observability.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # #119 관리자 인덱싱 Job·Attempt·Event 조회 검증 결과 | ||
|
|
||
| ## 1. 검증 정보 | ||
|
|
||
| - 실행일: 2026-08-08 (Asia/Seoul) | ||
| - 대상 브랜치: `feature/119` | ||
| - 애플리케이션: Spring Boot 3.5.16, Java 17 | ||
| - 데이터베이스: 로컬 PostgreSQL 17.8 + pgvector 0.8.1 컨테이너 | ||
| - 검증 범위: 관리자 조회 Service·Converter·Controller·PostgreSQL Query와 전체 회귀 Test | ||
| - 최종 결과: 706개 통과, 실패·오류·Skip 0개 | ||
|
|
||
| 각 PostgreSQL 통합 Test는 독립 Schema와 전체 Flyway Migration을 사용한다. DB 접속 정보와 인증 값은 | ||
| 실행 Process의 Test 전용 환경변수로만 주입했으며 실제 운영 값은 사용하거나 기록하지 않았다. | ||
|
|
||
| ## 2. 전체 회귀 검증 | ||
|
|
||
| 실행 명령의 값은 공개 가능한 Test 전용 Placeholder로 대체한다. | ||
|
|
||
| ```bash | ||
| DB_SSLMODE=disable \ | ||
| JWT_SECRET='<test-only-secret>' \ | ||
| MINIO_ENDPOINT='<local-test-endpoint>' \ | ||
| MINIO_ACCESS_KEY='<test-only-value>' \ | ||
| MINIO_SECRET_KEY='<test-only-value>' \ | ||
| MINIO_BUCKET='<test-bucket>' \ | ||
| ./gradlew test | ||
| ``` | ||
|
|
||
| 결과: | ||
|
|
||
| ```text | ||
| BUILD SUCCESSFUL | ||
| tests=706 failures=0 errors=0 skipped=0 | ||
| ``` | ||
|
|
||
| 기존 Command API Controller Test도 새 Query Service 의존성을 Mock으로 보강해 전체 Controller Context가 | ||
| 정상 기동하는지 함께 검증했다. | ||
|
|
||
| ## 3. 단위 검증 | ||
|
|
||
| | Test Class | Test 수 | 검증 범위 | 결과 | | ||
| |---|---:|---|---| | ||
| | `IndexingJobAdminQueryServiceTest` | 6 | 필터·Pagination 전달, 상세 Not Found, Attempt·Event 조회 | 통과 | | ||
| | `IndexingJobAdminConverterTest` | 4 | Job·Attempt·Event 공개 DTO 변환, 민감 필드 계약 제외 | 통과 | | ||
| | `IndexingJobAdminQueryControllerTest` | 17 | 네 API, Validation, ADMIN 권한, 오류·JSON 응답 | 통과 | | ||
|
|
||
| ## 4. PostgreSQL 통합 검증 | ||
|
|
||
| 실행: | ||
|
|
||
| ```bash | ||
| DB_SSLMODE=disable \ | ||
| ./gradlew test \ | ||
| --tests 'com.opensource.docgrid.domain.embedding.integration.IndexingJobAdminQueryIntegrationTest' | ||
| ``` | ||
|
|
||
| 결과: | ||
|
|
||
| ```text | ||
| tests=5 failures=0 errors=0 skipped=0 | ||
| ``` | ||
|
|
||
| | 시나리오 | 확인 항목 | 결과 | | ||
| |---|---|---| | ||
| | Job 복합 필터 | 상태·문서·현재 소유 Worker 조건이 같은 한 건으로 수렴 | 통과 | | ||
| | Job 목록 정렬·Page | `created_at DESC, id DESC`, Page 경계와 전체 건수 보존 | 통과 | | ||
| | Attempt 이력 | `attempt_no DESC, id DESC`, 내부 Claim Token·오류 메시지 미노출 | 통과 | | ||
| | Event 타임라인 | `occurred_at DESC, id DESC`, Metadata JSON 미노출 | 통과 | | ||
| | 종료 Job 상세 | Worker와 Lease가 없는 종료 상태를 Null로 안전하게 반환 | 통과 | | ||
|
|
||
| ## 5. 보안·읽기 전용 경계 | ||
|
|
||
| - `/admin/**`의 기존 `ADMIN` 권한 정책을 그대로 적용했다. | ||
| - Job·Attempt의 Claim Token과 내부 Error Message를 응답 DTO에 정의하지 않았다. | ||
| - Event의 `metadata_json`을 응답 DTO에 정의하지 않았다. | ||
| - Query Service는 `@Transactional(readOnly = true)`로만 동작한다. | ||
| - 조회 테스트 전후에 Job·Attempt·Event 상태 전이가 발생하지 않는다. | ||
|
|
||
| ## 6. 결론 | ||
|
|
||
| - 관리자는 Job 목록·상세와 Attempt·Event 이력을 고정된 Pagination 계약으로 조회할 수 있다. | ||
| - 상태·문서·현재 Worker 필터와 재현 가능한 역순 정렬이 실제 PostgreSQL에서 동작한다. | ||
| - 운영에 필요한 제한된 Error Code는 제공하면서 소유권 증명 값과 내부 진단 원문은 노출하지 않는다. | ||
| - 기존 Worker 조회 및 인덱싱 Command API를 포함한 전체 706개 Test가 실패 없이 통과한다. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Swagger 수동 검증 결과를 추가하세요.
현재 문서는 자동 테스트 결과만 기록합니다. 네 개의 관리자 GET API에 대한 Swagger 요청, 상태 코드, Pagination 응답, 민감 필드 미노출 결과를 함께 기록하세요.
As per coding guidelines,
docs/test-results/*.mdmust “combine Swagger manual-test results with automated-test results.”🤖 Prompt for AI Agents
Source: Coding guidelines