Remove sweep_orphaned_mounted_uploads feature flag

What does this MR do and why?

Removes the sweep_orphaned_mounted_uploads feature flag and makes the sweeping behavior unconditional.

The flag gated a before_destroy callback in the shared WithUploads concern that cleans up orphaned Upload rows. When a model is destroyed, CarrierWave may leave mounted uploads in the database if the underlying file is already missing from storage. The callback queues an after-commit deletion of these orphaned rows. When CarrierWave cleaned up correctly, the sweep is a no-op safety net.

This MR:

  • Removes the Feature.enabled? check in app/models/concerns/with_uploads.rb so the sweep always runs
  • Deletes the feature flag definition at config/feature_flags/gitlab_com_derisk/sweep_orphaned_mounted_uploads.yml
  • Removes the flag-disabled test context from spec/models/concerns/with_uploads_spec.rb

GitLab Dedicated does not support feature flags, so this behavior only reaches Dedicated customers once the flag is removed. The rollout work item schedules this removal for milestone 19.5.

Rollout status: The feature flag has been enabled on all non-production environments (dev, pre, staging, staging-ref) since 2026-09-02 and on GitLab.com production since 2026-09-07, per https://gitlab.com/gitlab-com/gl-infra/feature-flag-log/-/issues/63739. No rollbacks have occurred, and monitoring showed no increase in destroy-path failure rates. The flag's database record is deleted via chatops only after the removal code is fully deployed, so old-version nodes keep the flag-gated sweep behavior (enabled) throughout the rolling deploy window.

References

Screenshots or screen recordings

No UI changes.

How to set up and validate locally

  1. In a rails console, pick a user with an avatar and remove the file from storage so CarrierWave's own cleanup skips the row:

    user = User.find_by(username: 'root')
    # upload an avatar first via the UI if the user has none
    FileUtils.rm_f(user.avatar.path)
  2. Destroy the user and check that no orphaned Upload row is left behind:

    user.destroy!
    Upload.where(model_type: 'User', model_id: user.id).count # => 0

    Before this change (with the flag disabled), the count stayed at 1.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Post-merge

Run these chatops commands to complete the rollout:

/chatops gitlab run release check <merge-request-url> 19.5
/chatops gitlab run feature delete sweep_orphaned_mounted_uploads --dev --pre --staging --staging-ref --production
Edited by Smriti Garg

Merge request reports

Loading
Loading