Deleting a document orphans its segment-attachment vectors and destroys attachments other documents still reference

未关闭
#42,641 1 条评论 1 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
72/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
python
领域
backend, databases

调研方向

Start with api/tasks/clean_document_task.py and the batch-clean path dispatched from api/services/dataset_service.py:2074, then compare their cleanup flow with delete_segment_from_index_task.py and document_indexing_update_task.py. Run the shared-segment-image case in test_segment_index_cleanup_tasks.py and verify document deletion removes unreferenced attachment vectors, rows, and blobs while preserving attachments still referenced elsewhere.

由索引模型根据 Issue 内容生成。

描述

Self Checks
  • I have read the Contributing Guide and Language Policy
  • I have searched for existing issues, including closed ones
  • I confirm that I am using English to submit this report
Dify version

main at 22aff66cc0

Cloud or Self Hosted

Self Hosted (Source), Cloud

Steps to reproduce

In a multimodal knowledge base, delete a document that has segments with attachments. Both deletion paths mishandle those attachments: clean_document_task (single delete, dispatched from api/events/event_handlers/clean_when_document_deleted.py:13) and batch_clean_document_task (batch delete, dispatched from api/services/dataset_service.py:2074).

1. The attachment vectors are never removed.

Attachment vectors are written under doc_id == UploadFile.id (api/core/rag/datasource/vdb/vector_factory.py:201,209), but the node id list handed to index_processor.clean is built only from DocumentSegment.index_node_id:

# api/tasks/clean_document_task.py:64
index_node_ids = [segment.index_node_id for segment in segments if segment.index_node_id]

so the attachment vectors outlive the document permanently. RetrievalService.get_segment_attachment_infos then finds no UploadFile/binding for them and drops them silently, so they keep consuming top-k slots and quietly degrade recall for every subsequent query against that collection.

2. An attachment another document still references is destroyed.

SegmentAttachmentBinding.attachment_id is a user-supplied UploadFile id copied verbatim in SegmentService.create_segment (api/services/dataset_service.py:3512-3521) and carries no uniqueness constraint, so the same image may legitimately be bound to segments of several documents. Both tasks delete the UploadFile row and its storage blob for every binding the deleted document held, with no check for bindings that survive — and in the single-delete path the UploadFile delete even runs before the binding delete:

# api/tasks/clean_document_task.py:137-145
if attachment_ids:
    session.execute(delete(UploadFile).where(UploadFile.id.in_(attachment_ids)))
if binding_ids:
    session.execute(delete(SegmentAttachmentBinding).where(SegmentAttachmentBinding.id.in_(binding_ids)))

The rest of the codebase already treats this as the contract. delete_segment_from_index_task.py:86-121 and document_indexing_update_task.py:103-150 both delete the bindings, flush(), recompute orphan_attachment_ids from the bindings that remain, clean only those from the vector store, and only then drop the rows and blobs. test_segment_index_cleanup_tasks.py even has a shared-segment-image case pinning it. The two document-deletion tasks are the only paths that skip both steps.

✔️ Expected Behavior

Deleting a document removes the vectors, rows and blobs of the attachments nothing else references, and leaves attachments that another document still binds untouched — the same contract delete_segment_from_index_task already implements.

❌ Actual Behavior

Attachment vectors are orphaned in the collection forever, and an attachment shared with another document has its row and blob deleted out from under that document, so the surviving document's segment renders a broken attachment.

主要语言
TypeScript
星标
157k
派生
24.7k
平均合并
22 小时 32 分钟
30 天内合并 PR
611

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

langgenius/dify 的其他 Issue

查看 langgenius/dify 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。