diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index d00bd74bf..4a4a0343d 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -4,9 +4,17 @@ ActiveSupport.on_load(:active_storage_blob) do end end -ActiveSupport.on_load(:active_storage_record) do - configure_replica_connections -end +# Don't configure replica connections for ActiveStorage::Record. +# When ActiveStorage uses `connects_to`, it creates a separate connection pool +# from ApplicationRecord. This causes after_commit callbacks to fire in +# non-deterministic order - the Attachment's create_variants callback can fire +# before the User model's upload callback, causing FileNotFoundError when +# using `process: :immediately` for variants. +# See: https://github.com/rails/rails/issues/53694 +# +# ActiveSupport.on_load(:active_storage_record) do +# configure_replica_connections +# end module ActiveStorageControllerExtensions extend ActiveSupport::Concern diff --git a/test/controllers/users/avatars_controller_test.rb b/test/controllers/users/avatars_controller_test.rb index be4532448..3ded884b8 100644 --- a/test/controllers/users/avatars_controller_test.rb +++ b/test/controllers/users/avatars_controller_test.rb @@ -27,9 +27,7 @@ class Users::AvatarsControllerTest < ActionDispatch::IntegrationTest end test "show own image redirects to the blob url" do - # Create blob separately to ensure file is uploaded before variant processing - blob = ActiveStorage::Blob.create_and_upload!(io: File.open(file_fixture("moon.jpg")), filename: "moon.jpg", content_type: "image/jpeg") - users(:david).avatar.attach(blob) + users(:david).avatar.attach(io: File.open(file_fixture("moon.jpg")), filename: "moon.jpg", content_type: "image/jpeg") assert users(:david).avatar.attached? get user_avatar_path(users(:david)) @@ -38,9 +36,7 @@ class Users::AvatarsControllerTest < ActionDispatch::IntegrationTest end test "show other image redirects to the blob url" do - # Create blob separately to ensure file is uploaded before variant processing - blob = ActiveStorage::Blob.create_and_upload!(io: File.open(file_fixture("moon.jpg")), filename: "moon.jpg", content_type: "image/jpeg") - users(:kevin).avatar.attach(blob) + users(:kevin).avatar.attach(io: File.open(file_fixture("moon.jpg")), filename: "moon.jpg", content_type: "image/jpeg") assert users(:kevin).avatar.attached? get user_avatar_path(users(:kevin)) diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index d8441d033..385bacaf3 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -79,10 +79,9 @@ class UsersControllerTest < ActionDispatch::IntegrationTest test "update with valid avatar" do sign_in_as :kevin - # Create blob separately to ensure file is uploaded before variant processing - blob = ActiveStorage::Blob.create_and_upload!(io: File.open(file_fixture("avatar.png")), filename: "avatar.png", content_type: "image/png") + png_file = fixture_file_upload("avatar.png", "image/png") - put user_path(users(:kevin)), params: { user: { avatar: blob.signed_id } } + put user_path(users(:kevin)), params: { user: { avatar: png_file } } assert_redirected_to user_path(users(:kevin)) assert users(:kevin).reload.avatar.attached? assert_equal "image/png", users(:kevin).avatar.content_type diff --git a/test/mailers/notification/bundle_mailer_test.rb b/test/mailers/notification/bundle_mailer_test.rb index 63647e68a..fbc4274c9 100644 --- a/test/mailers/notification/bundle_mailer_test.rb +++ b/test/mailers/notification/bundle_mailer_test.rb @@ -22,12 +22,11 @@ class Notification::BundleMailerTest < ActionMailer::TestCase end test "renders avatar with external image URL when avatar is attached" do - blob = ActiveStorage::Blob.create_and_upload!( + @user.avatar.attach( io: File.open(Rails.root.join("test", "fixtures", "files", "avatar.png")), filename: "avatar.png", content_type: "image/png" ) - @user.avatar.attach(blob) create_notification(@user) diff --git a/test/models/user/avatar_test.rb b/test/models/user/avatar_test.rb index 58b78969c..ed502d7ab 100644 --- a/test/models/user/avatar_test.rb +++ b/test/models/user/avatar_test.rb @@ -2,8 +2,7 @@ require "test_helper" class User::AvatarTest < ActiveSupport::TestCase test "avatar_thumbnail returns variant for variable images" do - blob = ActiveStorage::Blob.create_and_upload!(io: File.open(file_fixture("moon.jpg")), filename: "moon.jpg", content_type: "image/jpeg") - users(:david).avatar.attach(blob) + users(:david).avatar.attach(io: File.open(file_fixture("moon.jpg")), filename: "moon.jpg", content_type: "image/jpeg") assert users(:david).avatar.variable? assert_equal users(:david).avatar.variant(:thumb).blob, users(:david).avatar_thumbnail.blob @@ -17,8 +16,7 @@ class User::AvatarTest < ActiveSupport::TestCase end test "allows valid image content types" do - blob = ActiveStorage::Blob.create_and_upload!(io: File.open(file_fixture("moon.jpg")), filename: "test.jpg", content_type: "image/jpeg") - users(:david).avatar.attach(blob) + users(:david).avatar.attach(io: File.open(file_fixture("moon.jpg")), filename: "test.jpg", content_type: "image/jpeg") assert users(:david).valid? end @@ -31,19 +29,7 @@ class User::AvatarTest < ActiveSupport::TestCase end test "thumb variant is processed immediately on attachment" do - # Create blob separately to ensure file is uploaded before variant processing. - # - # Root cause: When ActiveStorage::Record uses `connects_to` for read replica support - # (as in SAAS mode), it creates a separate connection pool from application models. - # Since after_commit callbacks are tracked per connection pool, the callback order - # between User's pool (upload) and Attachment's pool (create_variants) isn't guaranteed. - # In MySQL/SAAS mode, the Attachment callback fires before the file is uploaded. - blob = ActiveStorage::Blob.create_and_upload!( - io: File.open(file_fixture("avatar.png")), - filename: "avatar.png", - content_type: "image/png" - ) - users(:david).avatar.attach(blob) + users(:david).avatar.attach(io: File.open(file_fixture("avatar.png")), filename: "avatar.png", content_type: "image/png") assert users(:david).avatar.variant(:thumb).processed? end