Race Condition in Document Segment Deletion Causing Child Chunk Cleanup Failure #16385

Closed
opened 2026-02-21 19:25:55 -05:00 by yindo · 1 comment
Owner

Originally created by @kenwoodjw on GitHub (Aug 24, 2025).

Self Checks

  • I have read the Contributing Guide and Language Policy.
  • This is only for bug report, if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report, otherwise it will be closed.
  • 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
  • Please do not modify this template :) and fill in all the required fields.

Dify version

1.7.2

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

  1. Create a dataset with parent_child index processor (hierarchical indexing)
  2. Upload a document that generates parent segments with child chunks
  3. Delete one or more segments using the API or UI
  4. Check the vector database and child_chunks table

Root Cause Analysis

The race condition occurs in this sequence:

  1. delete_segment_from_index_task.delay() starts async task
  2. db.session.delete(segment) immediately deletes segment
  3. db.session.commit() commits deletion
  4. Async task executes parent_child_index_processor.clean()
  5. Clean method tries to query child chunks via JOIN with DocumentSegment
  6. Query returns empty because parent segment was already deleted
  7. Child chunks are never cleaned up

Problematic code in parent_child_index_processor.clean():

https://github.com/langgenius/dify/blob/5ff39473123e2b0cb273f4b9777c332b72fd2978/api/core/rag/index_processor/processor/parent_child_index_processor.py#L118-L126

✔️ Expected Behavior

  • Parent segment is deleted from database
  • Child chunks are deleted from vector index
  • Child chunks are deleted from child_chunks table
  • No orphaned records remain in vector database

Actual Behavior

  • Parent segment is deleted from database
  • Child chunks are NOT deleted from vector index
  • Child chunks are NOT deleted from child_chunks table
  • Orphaned child chunk records remain, causing:
    • Storage waste in vector database
    • Potential retrieval of deleted content
    • Database inconsistency
Originally created by @kenwoodjw on GitHub (Aug 24, 2025). ### Self Checks - [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542). - [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general). - [x] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones. - [x] I confirm that I am using English to submit this report, otherwise it will be closed. - [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :) - [x] Please do not modify this template :) and fill in all the required fields. ### Dify version 1.7.2 ### Cloud or Self Hosted Self Hosted (Docker) ### Steps to reproduce 1. Create a dataset with parent_child index processor (hierarchical indexing) 2. Upload a document that generates parent segments with child chunks 3. Delete one or more segments using the API or UI 4. Check the vector database and child_chunks table Root Cause Analysis The race condition occurs in this sequence: 1. delete_segment_from_index_task.delay() starts async task 2. db.session.delete(segment) immediately deletes segment 3. db.session.commit() commits deletion 4. Async task executes parent_child_index_processor.clean() 5. Clean method tries to query child chunks via JOIN with DocumentSegment 6. Query returns empty because parent segment was already deleted 7. Child chunks are never cleaned up Problematic code in parent_child_index_processor.clean(): https://github.com/langgenius/dify/blob/5ff39473123e2b0cb273f4b9777c332b72fd2978/api/core/rag/index_processor/processor/parent_child_index_processor.py#L118-L126 ### ✔️ Expected Behavior - Parent segment is deleted from database - Child chunks are deleted from vector index - Child chunks are deleted from child_chunks table - No orphaned records remain in vector database ### ❌ Actual Behavior - Parent segment is deleted from database - Child chunks are NOT deleted from vector index - Child chunks are NOT deleted from child_chunks table - Orphaned child chunk records remain, causing: - Storage waste in vector database - Potential retrieval of deleted content - Database inconsistency
yindo added the 🐞 bug label 2026-02-21 19:25:55 -05:00
yindo closed this issue 2026-02-21 19:25:55 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Aug 24, 2025):

You've uncovered a real gap in Dify v1.7.2: when deleting a parent segment in hierarchical (parent_child) indexing, the code deletes the segment but does not reliably cascade deletion to child chunks or their vector index entries. This happens because the deletion logic for segments (in DatasetDocumentStore.delete_document) and for child chunks/vector entries (in ParentChildIndexProcessor.clean) are not coordinated in a single atomic transaction or orchestration layer. As a result, if a crash or concurrent mutation occurs between these steps, orphaned child chunks and vector records can remain, causing the inconsistencies you described. There is also no explicit concurrency control or locking to prevent race conditions during deletion, and no background cleanup for orphans in this version [code analysis][docstore code].

Recent PRs have improved validation and error handling, but these changes landed after v1.7.2 and do not fully resolve the atomicity/race condition issue [PR 24374][PR 23713].

Workarounds:
Until a more robust fix is available, you can manually invoke the clean method from ParentChildIndexProcessor with the relevant segment node IDs and delete_child_chunks=True to clean up orphaned child chunks and vector entries. Alternatively, you can use the documented API endpoint to delete individual child chunks if needed:
DELETE /v1/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}
API docs.

To minimize risk, avoid concurrent deletions and ensure that cleanup routines are run after segment deletions. For production, consider scripting a periodic cleanup using the clean method or direct database queries until an atomic, transactional deletion flow is implemented in a future release.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Aug 24, 2025): <!-- Answer --> You've uncovered a real gap in Dify v1.7.2: when deleting a parent segment in hierarchical (parent_child) indexing, the code deletes the segment but does not reliably cascade deletion to child chunks or their vector index entries. This happens because the deletion logic for segments (in `DatasetDocumentStore.delete_document`) and for child chunks/vector entries (in `ParentChildIndexProcessor.clean`) are not coordinated in a single atomic transaction or orchestration layer. As a result, if a crash or concurrent mutation occurs between these steps, orphaned child chunks and vector records can remain, causing the inconsistencies you described. There is also no explicit concurrency control or locking to prevent race conditions during deletion, and no background cleanup for orphans in this version [[code analysis](https://github.com/langgenius/dify/blob/5ff39473123e2b0cb273f4b9777c332b72fd2978/api/core/rag/index_processor/processor/parent_child_index_processor.py)][[docstore code](https://github.com/langgenius/dify/blob/5ff39473123e2b0cb273f4b9777c332b72fd2978/api/core/rag/docstore/dataset_docstore.py)]. Recent PRs have improved validation and error handling, but these changes landed after v1.7.2 and do not fully resolve the atomicity/race condition issue [[PR 24374](https://github.com/langgenius/dify/pull/24374)][[PR 23713](https://github.com/langgenius/dify/pull/23713)]. **Workarounds:** Until a more robust fix is available, you can manually invoke the `clean` method from `ParentChildIndexProcessor` with the relevant segment node IDs and `delete_child_chunks=True` to clean up orphaned child chunks and vector entries. Alternatively, you can use the documented API endpoint to delete individual child chunks if needed: `DELETE /v1/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}` [API docs](https://cloud.dify.ai/datasets?category=api#delete_child_chunk). To minimize risk, avoid concurrent deletions and ensure that cleanup routines are run after segment deletions. For production, consider scripting a periodic cleanup using the `clean` method or direct database queries until an atomic, transactional deletion flow is implemented in a future release. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/390007d4-6774-4be0-9842-e7f97987fc57?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/6088a4f4-073a-43f3-b3d4-4f06fb0c05a0/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/langgenius/dify/issues/24407)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#16385