Draft: Use batched pre-drain in Groups::DestroyService

What this does

Wires Gitlab::Database::BatchedPreDrain into Groups::DestroyService so a group's direct CASCADE foreign-key children are deleted in small, independently committed batches before group.destroy.

Most heavy data lives under projects, which are destroyed first. But direct group children (settings, members, labels, milestones, boards, epics) can still cause a long final transaction for large groups. The pre-drain removes them ahead of time so the final DELETE cascades little or nothing.

This is the group counterpart to the project integration (#619117).

How it works

  • batched_pre_drain! runs in unsafe_execute, after child projects and subgroups are destroyed and right before group.destroy, matching the existing destroy ordering. It runs outside a transaction so each batch commits on its own.
  • On :over_budget it raises BatchedPreDrainOverBudget, which is caught before the generic rescue: the group stays marked for deletion and GroupDestroyWorker is re-enqueued to resume.
  • Gitlab::QueryLimiting is already disabled in GroupDestroyWorker.

Strategy hooks

Group avatars and other group files live in the polymorphic uploads table, which is not a CASCADE FK child of namespaces, so the pre-drain never touches them. The direct CASCADE FK children of a group are plain relational rows that need no external cleanup, so no strategy hooks are registered. (The full side-effect audit for project children is handled separately in #619136.)

Feature flag

Guarded by the default-off batched_pre_drain_on_group_destroy flag (actor: the group).

Stacked MR

Targets 619116-batched-pre-drain-helper (the helper MR !251283 (closed)). Rebase onto master once the helper merges.

Data deletion and recoverability

The pre-drain only deletes rows PostgreSQL would delete anyway when the group DELETE cascades — it moves that deletion earlier and splits it into small committed batches, so the end state is identical to a normal cascading delete. Deleted rows are not independently recoverable; recovery follows the same path as any other row loss (point-in-time restore from database backups). The behaviour is behind a default-off flag.

Testing

New specs in spec/services/groups/destroy_service_spec.rb:

  • pre-drain runs before destroy when the flag is enabled
  • worker re-enqueues and the group is left intact on :over_budget
  • pre-drain does not run when the flag is disabled

Related #619118

Merge request reports

Loading
Loading