ActiveStorage - attached file quietly discarded if record is later locked in the same transaction #53737
Open
Description
Steps to reproduce
- Open a transaction (using
#transaction
or#with_lock
) - Attach a file
- Call
#with_lock
while in the same transaction - Close the transaction
- Try to download the attachment
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails", '~> 7.2'
# If you want to test against edge Rails replace the previous line with this:
# gem "rails", github: "rails/rails", branch: "main"
gem 'securerandom'
gem "sqlite3"
end
require "active_record/railtie"
require "active_storage/engine"
require "minitest/autorun"
ENV["DATABASE_URL"] = "sqlite3::memory:"
class TestApp < Rails::Application
config.load_defaults Rails::VERSION::STRING.to_f
config.root = __dir__
config.hosts << "example.org"
config.eager_load = false
config.session_store :cookie_store, key: "cookie_store_key"
config.secret_key_base = "secret_key_base"
config.logger = Logger.new($stdout)
Rails.logger = config.logger
config.active_storage.service = :local
config.active_storage.service_configurations = {
local: {
root: Dir.tmpdir,
service: "Disk"
}
}
end
Rails.application.initialize!
require ActiveStorage::Engine.root.join("db/migrate/20170806125915_create_active_storage_tables.rb").to_s
ActiveRecord::Schema.define do
CreateActiveStorageTables.new.change
create_table :users, force: true
end
class User < ActiveRecord::Base
has_one_attached :profile
end
class BugTest < ActiveSupport::TestCase
def test_upload_and_download
user = User.create!
user.transaction do
user.profile.attach(content_type: "text/plain",
filename: "dummy.txt",
io: ::StringIO.new("dummy"))
user.with_lock {}
end
assert_equal "dummy", user.profile.download
end
end
Expected behavior
File should be properly saved in the backend or an error raised if that is not possible.
Actual behavior
The attachment appears to work. However, when you later call #download
on it, you receive an ActiveStorage::FileNotFoundError
error.
If you are updating an existing attachment, the old saved file will still be deleted, but the new data will be lost.
System configuration
Rails version: 7.2.1.1
Ruby version: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [x86_64-linux]