-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
bugSomething isn't workingSomething isn't working
Description
🐛 문서 삭제 기능 오류: DELETING 상태에서 멈춤
📋 문제 설명
문서 삭제 시 상태가 DELETING으로 변경되지만, 실제로 삭제가 완료되지 않고 무한히 DELETING 상태로 남아있습니다.
🔴 현재 동작 (버그)
- Admin UI에서 문서 삭제 버튼 클릭
- 문서 상태가
DELETING으로 변경됨 - 계속
DELETING상태로 유지됨 ❌ - 문서가 실제로 삭제되지 않음
- 새로고침해도 여전히
DELETING상태
✅ 예상 동작
- Admin UI에서 문서 삭제 버튼 클릭
- 문서 상태가
DELETING으로 변경됨 - 백엔드에서 삭제 처리 완료
- 문서가 DB 및 스토리지에서 완전히 제거됨
- UI에서 문서가 사라짐
🔍 재현 방법
단계:
- Admin UI 접속: http://localhost:3001
- 업로드된 문서 목록 확인
- 임의의 문서에서 삭제 버튼 클릭
- 문서 상태 확인
실제 결과:
상태: DELETING
→ 몇 초 대기...
→ 여전히 DELETING
→ 새로고침 후에도 DELETING
→ 문서가 삭제되지 않음
예상 결과:
상태: DELETING
→ 삭제 완료
→ 문서가 목록에서 사라짐
🖼️ 스크린샷
Before (정상):
[문서 목록]
├─ document1.pdf - COMPLETED
├─ document2.pdf - COMPLETED
└─ document3.pdf - COMPLETED
After 삭제 시도 (버그):
[문서 목록]
├─ document1.pdf - COMPLETED
├─ document2.pdf - DELETING ← 여기서 멈춤!
└─ document3.pdf - COMPLETED
Expected After:
[문서 목록]
├─ document1.pdf - COMPLETED
└─ document3.pdf - COMPLETED
↑ document2.pdf 삭제됨
🔧 영향받는 컴포넌트
Backend (Spring Boot)
- Controller:
SourceDocumentController.deleteDocument() - Service: 삭제 로직 처리
- Repository:
SourceDocumentRepository,DocumentChunkRepository
Database
- PostgreSQL:
source_documents테이블document_chunks테이블 (외래키 제약)
Storage
- MinIO: 원본 파일 삭제
- Elasticsearch: 인덱싱된 청크 삭제
🐞 가능한 원인
1. 외래키 제약 위반
-- document_chunks가 source_documents를 참조
-- CASCADE DELETE가 설정되지 않았을 가능성확인 방법:
SELECT * FROM source_documents WHERE ingestion_status = 'DELETING';
SELECT * FROM document_chunks WHERE source_document_id = 'xxx';2. Elasticsearch 삭제 실패
Elasticsearch에서 청크 삭제 중 에러 발생
→ 트랜잭션 롤백
→ 상태만 DELETING으로 남음
3. MinIO 파일 삭제 실패
MinIO에서 파일 삭제 권한 문제
→ 삭제 실패
→ 전체 삭제 프로세스 중단
4. 비동기 처리 문제
// 삭제가 비동기로 처리되지만
// 완료 콜백이 호출되지 않음
@Async
public void deleteDocument(UUID documentId) {
// 삭제 로직...
// 상태 업데이트 누락?
}5. 예외 처리 누락
try {
// 삭제 로직
deleteFromElasticsearch();
deleteFromMinio();
deleteFromPostgres();
} catch (Exception e) {
// 예외 발생 시 상태 복구 로직 없음
log.error("Delete failed", e);
// DELETING 상태로 남음!
}🔬 디버깅 체크리스트
Backend 로그 확인
docker compose logs -f open-context-core | grep -i delete
docker compose logs -f open-context-core | grep -i errorDatabase 상태 확인
-- DELETING 상태 문서 확인
SELECT id, original_filename, ingestion_status, error_message
FROM source_documents
WHERE ingestion_status = 'DELETING';
-- 관련 청크 확인
SELECT COUNT(*)
FROM document_chunks
WHERE source_document_id = 'xxx';Elasticsearch 확인
# 해당 문서의 청크가 여전히 존재하는지 확인
curl "http://localhost:9200/document_chunks_index/_search?q=sourceDocumentId:xxx"MinIO 확인
# MinIO에 파일이 여전히 존재하는지 확인
curl http://localhost:9001
# 또는 MinIO Console에서 확인💡 제안된 해결 방법
해결책 1: 트랜잭션 관리 개선
@Transactional
public void deleteDocument(UUID documentId) {
try {
// 1. 상태 변경
updateStatus(documentId, IngestionStatus.DELETING);
// 2. Elasticsearch에서 청크 삭제
deleteChunksFromElasticsearch(documentId);
// 3. MinIO에서 파일 삭제
deleteFileFromMinio(documentId);
// 4. PostgreSQL에서 청크 삭제 (CASCADE)
documentChunkRepository.deleteBySourceDocumentId(documentId);
// 5. PostgreSQL에서 문서 삭제
sourceDocumentRepository.deleteById(documentId);
log.info("Document deleted successfully: {}", documentId);
} catch (Exception e) {
// 에러 발생 시 상태를 ERROR로 변경
updateStatus(documentId, IngestionStatus.ERROR);
updateErrorMessage(documentId, "Delete failed: " + e.getMessage());
throw new BusinessException(ErrorCode.DELETE_FAILED, e.getMessage());
}
}해결책 2: CASCADE DELETE 설정
DocumentChunk 엔티티 수정:
@Entity
@Table(name = "document_chunks")
public class DocumentChunk {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "source_document_id",
nullable = false,
foreignKey = @ForeignKey(
name = "fk_chunk_source_document",
foreignKeyDefinition = "FOREIGN KEY (source_document_id) " +
"REFERENCES source_documents(id) " +
"ON DELETE CASCADE" // ← 추가!
))
private SourceDocument sourceDocument;
}또는 Flyway Migration:
-- V{version}__add_cascade_delete_to_chunks.sql
ALTER TABLE document_chunks
DROP CONSTRAINT IF EXISTS fk_chunk_source_document;
ALTER TABLE document_chunks
ADD CONSTRAINT fk_chunk_source_document
FOREIGN KEY (source_document_id)
REFERENCES source_documents(id)
ON DELETE CASCADE;해결책 3: 재시도 로직 추가
@Retryable(
value = {ElasticsearchException.class, MinioException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 2000)
)
public void deleteDocument(UUID documentId) {
// 삭제 로직...
}해결책 4: 삭제 작업 순서 최적화
올바른 삭제 순서:
1. 상태를 DELETING으로 변경
2. Elasticsearch 청크 삭제 (검색 불가능하게)
3. PostgreSQL 청크 삭제 (메타데이터 제거)
4. MinIO 파일 삭제 (스토리지 정리)
5. PostgreSQL 문서 삭제 (완전 제거)
6. 커밋
실패 시:
- 롤백
- 상태를 ERROR로 변경
- 에러 메시지 기록
해결책 5: 수동 복구 스크립트
임시 해결책 (긴급):
-- DELETING 상태 문서 강제 삭제 (주의!)
BEGIN;
-- 1. 해당 문서 ID 확인
SELECT id, original_filename FROM source_documents WHERE ingestion_status = 'DELETING';
-- 2. 청크 삭제
DELETE FROM document_chunks WHERE source_document_id = '{documentId}';
-- 3. 문서 삭제
DELETE FROM source_documents WHERE id = '{documentId}';
COMMIT;Elasticsearch 정리:
curl -X POST "http://localhost:9200/document_chunks_index/_delete_by_query" \
-H "Content-Type: application/json" \
-d '{
"query": {
"match": {
"sourceDocumentId": "{documentId}"
}
}
}'🧪 테스트 계획
단위 테스트
@Test
void deleteDocument_success() {
// Given
UUID documentId = createTestDocument();
// When
documentService.deleteDocument(documentId);
// Then
assertFalse(sourceDocumentRepository.existsById(documentId));
assertEquals(0, documentChunkRepository.countBySourceDocumentId(documentId));
// Elasticsearch 확인
// MinIO 확인
}
@Test
void deleteDocument_elasticsearchFailure_shouldRollback() {
// Elasticsearch 삭제 실패 시 전체 롤백 확인
}통합 테스트
@Test
void deleteDocument_endToEnd() {
// 1. 문서 업로드
// 2. 인덱싱 완료 대기
// 3. 삭제 요청
// 4. 모든 스토리지에서 제거 확인
}📊 우선순위
- 심각도: 🔴 High (핵심 기능 오류)
- 영향도: 사용자가 문서를 삭제할 수 없음
- 긴급도: High (데이터 관리에 영향)
🎯 수용 기준
- 문서 삭제 시 모든 관련 데이터가 완전히 제거됨
- PostgreSQL: source_documents 삭제
- PostgreSQL: document_chunks 삭제 (CASCADE)
- Elasticsearch: 청크 인덱스 삭제
- MinIO: 원본 파일 삭제
- 삭제 실패 시 에러 메시지 표시
- 삭제 성공 시 문서가 UI에서 즉시 사라짐
- 삭제 중 에러 발생 시 상태가 ERROR로 변경되고 롤백됨
- 로그에 삭제 과정이 명확히 기록됨
🔗 관련 코드
확인이 필요한 파일들:
core/src/main/java/com/opencontext/controller/SourceController.javacore/src/main/java/com/opencontext/service/DocumentDeletionService.java(존재한다면)core/src/main/java/com/opencontext/repository/SourceDocumentRepository.javacore/src/main/java/com/opencontext/repository/DocumentChunkRepository.javacore/src/main/java/com/opencontext/entity/DocumentChunk.java(외래키 설정)
🏷️ Labels
bug🐛priority: high🔴component: backendcomponent: databaseneeds-investigation🔍
👥 Assignees
- Backend 담당자
추가 정보가 필요하시면 댓글로 남겨주세요.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working