diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb index b021b5f2d0..fa15e0451d 100644 --- a/activestorage/app/jobs/active_storage/purge_job.rb +++ b/activestorage/app/jobs/active_storage/purge_job.rb @@ -2,7 +2,7 @@ # Provides asynchronous purging of ActiveStorage::Blob records via ActiveStorage::Blob#purge_later. class ActiveStorage::PurgeJob < ActiveStorage::BaseJob - discard_on ActiveRecord::RecordNotFound, ActiveRecord::InvalidForeignKey + discard_on ActiveRecord::RecordNotFound def perform(blob) blob.purge diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index bf87598a66..e7f2615b0f 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -207,6 +207,7 @@ class ActiveStorage::Blob < ActiveRecord::Base def purge destroy delete + rescue ActiveRecord::InvalidForeignKey end # Enqueues an ActiveStorage::PurgeJob to call #purge. This is the recommended way to purge blobs from a transaction, diff --git a/activestorage/test/models/attached/many_test.rb b/activestorage/test/models/attached/many_test.rb index 334254d099..3b563b3fc8 100644 --- a/activestorage/test/models/attached/many_test.rb +++ b/activestorage/test/models/attached/many_test.rb @@ -468,6 +468,33 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase end end + test "purging attachment with shared blobs" do + [ + create_blob(filename: "funky.jpg"), + create_blob(filename: "town.jpg"), + create_blob(filename: "worm.jpg") + ].tap do |blobs| + @user.highlights.attach blobs + assert @user.highlights.attached? + + another_user = User.create!(name: "John") + shared_blobs = [blobs.second, blobs.third] + another_user.highlights.attach shared_blobs + assert another_user.highlights.attached? + + @user.highlights.purge + assert_not @user.highlights.attached? + + assert_not ActiveStorage::Blob.exists?(blobs.first.id) + assert ActiveStorage::Blob.exists?(blobs.second.id) + assert ActiveStorage::Blob.exists?(blobs.third.id) + + assert_not ActiveStorage::Blob.service.exist?(blobs.first.key) + assert ActiveStorage::Blob.service.exist?(blobs.second.key) + assert ActiveStorage::Blob.service.exist?(blobs.third.key) + end + end + test "purging later" do [ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ].tap do |blobs| @user.highlights.attach blobs @@ -485,6 +512,35 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase end end + test "purging attachment later with shared blobs" do + [ + create_blob(filename: "funky.jpg"), + create_blob(filename: "town.jpg"), + create_blob(filename: "worm.jpg") + ].tap do |blobs| + @user.highlights.attach blobs + assert @user.highlights.attached? + + another_user = User.create!(name: "John") + shared_blobs = [blobs.second, blobs.third] + another_user.highlights.attach shared_blobs + assert another_user.highlights.attached? + + perform_enqueued_jobs do + @user.highlights.purge_later + end + + assert_not @user.highlights.attached? + assert_not ActiveStorage::Blob.exists?(blobs.first.id) + assert ActiveStorage::Blob.exists?(blobs.second.id) + assert ActiveStorage::Blob.exists?(blobs.third.id) + + assert_not ActiveStorage::Blob.service.exist?(blobs.first.key) + assert ActiveStorage::Blob.service.exist?(blobs.second.key) + assert ActiveStorage::Blob.service.exist?(blobs.third.key) + end + end + test "purging dependent attachment later on destroy" do [ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ].tap do |blobs| @user.highlights.attach blobs diff --git a/activestorage/test/models/attached/one_test.rb b/activestorage/test/models/attached/one_test.rb index 3333fd9323..561c3e9d23 100644 --- a/activestorage/test/models/attached/one_test.rb +++ b/activestorage/test/models/attached/one_test.rb @@ -412,6 +412,22 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase end end + test "purging an attachment with a shared blob" do + create_blob(filename: "funky.jpg").tap do |blob| + @user.avatar.attach blob + assert @user.avatar.attached? + + another_user = User.create!(name: "John") + another_user.avatar.attach blob + assert another_user.avatar.attached? + + @user.avatar.purge + assert_not @user.avatar.attached? + assert ActiveStorage::Blob.exists?(blob.id) + assert ActiveStorage::Blob.service.exist?(blob.key) + end + end + test "purging later" do create_blob(filename: "funky.jpg").tap do |blob| @user.avatar.attach blob @@ -427,6 +443,25 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase end end + test "purging an attachment later with shared blob" do + create_blob(filename: "funky.jpg").tap do |blob| + @user.avatar.attach blob + assert @user.avatar.attached? + + another_user = User.create!(name: "John") + another_user.avatar.attach blob + assert another_user.avatar.attached? + + perform_enqueued_jobs do + @user.avatar.purge_later + end + + assert_not @user.avatar.attached? + assert ActiveStorage::Blob.exists?(blob.id) + assert ActiveStorage::Blob.service.exist?(blob.key) + end + end + test "purging dependent attachment later on destroy" do create_blob(filename: "funky.jpg").tap do |blob| @user.avatar.attach blob diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb index c2e7aae13a..88c106a08b 100644 --- a/activestorage/test/models/blob_test.rb +++ b/activestorage/test/models/blob_test.rb @@ -174,10 +174,10 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase assert_not ActiveStorage::Blob.service.exist?(variant.key) end - test "purge fails when attachments exist" do + test "purge does nothing when attachments exist" do create_blob.tap do |blob| User.create! name: "DHH", avatar: blob - assert_raises(ActiveRecord::InvalidForeignKey) { blob.purge } + assert_no_difference(-> { ActiveStorage::Blob.count }) { blob.purge } assert ActiveStorage::Blob.service.exist?(blob.key) end end