Skip to content

Implement delete for Security::Attribute to enable async association cleanup

Problem

Currently, when deleting a Security::Attribute, the database foreign key constraint has on_delete: :cascade (see db/migrate/20250717114003_create_project_to_security_attributes.rb:12), which automatically deletes all associated project_to_security_attributes records immediately.

This causes two issues:

  1. Performance: An attribute can have up to 15,000+ associated projects, making synchronous deletion slow
  2. Audit trail: We cannot properly audit the deletion of project associations since they cascade automatically

The current Security::Attributes::DestroyService enqueues a cleanup worker after deletion (line 45), but by then the associations are already gone due to the cascade.

Proposed Solution

Implement soft delete for Security::Attribute to allow asynchronous batch deletion with proper auditing:

Implementation Steps

  1. Add deleted_at column to security_attributes table

    • Add migration with t.datetime_with_timezone:deleted_at
    • Add an index on deleted_at for performance
  2. Implement soft delete behavior in Security::Attribute model

    • Add scope to exclude deleted records: scope:not_deleted, -> { where(deleted_at: nil) }
    • Override destroy to set deleted_at instead of hard delete
    • Update existing queries to filter out soft-deleted records
  3. Update Security::Attributes::DestroyService

    • Change attribute.destroy to attribute.update(deleted_at: Time.current)
    • Keep the worker enqueued for async cleanup
  4. Update Security::Attributes::CleanupProjectToSecurityAttributeWorker

    • Process project_to_security_attributes in batches (e.g., 1000 records at a time)
    • Audit each batch deletion
    • After all associations are deleted, perform hard delete: attribute.destroy! or attribute.really_destroy!
  5. Remove cascade from foreign key (optional but recommended)

    • Change on_delete: :cascade to on_delete: :restrict to prevent accidental cascades

Benefits

  • Attribute appears deleted to users immediately (no performance impact)
  • Associations cleaned up asynchronously in batches
  • Proper audit trail for all project association removals
  • Can recover if cleanup fails
  • No data integrity issues

Related Files

  • ee/app/models/security/attribute.rb
  • ee/app/services/security/attributes/destroy_service.rb
  • db/migrate/20250717114003_create_project_to_security_attributes.rb
  • Worker: ee/app/workers/security/attributes/cleanup_project_to_security_attribute_worker.rb
Edited by Miki Amos