Deleting a large document subtree still issues one update per descendant
Is there an existing issue for this?
- I have searched the existing issues
This is not related to configuring Outline
- The issue is not related to self-hosting config
Current Behavior
Deleting a document also soft-deletes all of its descendants. When the document has a large subtree, the delete request may time out or return a gateway error before the operation completes.
The subtree is resolved in one recursive query, but the descendants are then loaded and destroyed one at a time. A subtree with N descendants therefore still performs N soft-delete updates within the request transaction.
If the request or database timeout is reached, the transaction is rolled back and the document remains undeleted.
Expected Behavior
Deleting a document with a large number of descendants should complete without exceeding the request timeout or issuing one update per descendant.
Steps To Reproduce
- Create or import a document with a large number of descendants.
- Delete the root document.
- Observe the duration and database queries for the delete request.
- With a sufficiently large subtree, the request times out or returns a gateway error.
Environment
- Outline: current
main - Database: PostgreSQL
Anything else?
#13311 reduced the subtree lookup to a recursive query, but intentionally kept individual destroy() calls so that model hooks run for each descendant.
This leaves the remaining soft-delete updates as the part that still scales with the number of descendants. Replacing them with a bulk update would address the query count, but would also skip instance-level destroy hooks. #13484 currently relies on one of those hooks to record deletedById.
The alternatives I can see are:
- Bulk-update the descendants and explicitly preserve the required deletion fields.
- Keep the existing destroy behavior but process descendants asynchronously in bounded batches.
- Separate the bulk database update from retryable per-document side effects.
I’m opening this issue before preparing a change because the appropriate fix depends on which deletion lifecycle behavior needs to remain synchronous.
Source: outline/outline