ActiveRecord reload deletes Carrierwave uploading image

Ats
3 min readApr 13, 2024

--

This is the notes about what I experienced last week.

Photo by Markus Spiske on Unsplash

Background

I work with Ruby on Rails normally. Last week, I worked on a project to upload images to S3 using Carrierwave.

Carrierwave is a quite popular gem to upload images and S3 is also popular for the place where the images are stored. I have had many projects using those techniques before and I thought I was enough familiar with them. However, I experienced a situation where the uploaded images disappeared before uploading to S3. The following code snippets are what I had at the beginning. They are simplified but I can show the cause with the snippets.

ActiveRecord.transaction do
user.update!(remote_avatar_url: 'https://smaple_image.jpg')
# More saves/updates here
Users::DoSomethinService.new(user: user.reload).call
end

Then I started investigating it.

What I did

First of all, I needed to figure out which code caused the problem. So I removed one line by one line and executed the code snippets until the uploading succeeded. Then it succeeded when I removed the following code line.

Users::DoSomethingService.new(user: user.reload).call

Then it succeeded when I removed the user.reload . Based on that, I assumed something went wrong with the reload method. So I googled with reload and Carrierwave and I got the issue in its GitHub repository.

From the conversation, Carrierwave uploads the images to S3 using an after_commit callback. I also checked the code itself and currently, the afrer_save callback is used for storing images in the cloud.

But I still use an old version of Carrierwave so the after_commit does in my case. Then I checked the order of commits for my code snippets with logs, which is below.

D, [2024-04-11T16:15:48.691945 #8092] DEBUG -- :    (8.6ms)  BEGIN
D, [2024-04-11T16:15:48.719280 #8092] DEBUG -- : User Load (8.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 100], ["LIMIT", 1]]
D, [2024-04-11T16:15:56.640862 #8092] DEBUG -- : User Update (9.9ms) UPDATE "users" SET "avatar" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["avatar", "\"b28d4650dd6f-4776FA66_1DD8_423D_B934_A92DED482C4A.jpeg\""], ["updated_at", "2024-04-11 16:15:56.629934"], ["id", 100]]
D, [2024-04-11T16:15:56.650267 #8092] DEBUG -- : User Load (8.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 100], ["LIMIT", 1]]
D, [2024-04-11T16:15:56.881915 #8092] DEBUG -- : (10.5ms) COMMIT

Then I noticed what happened to me. The reload was called before the database commit was executed because of the transaction block, which deleted the image data from the instance of the user active record. So for this case, I fixed my codes like below.

ActiveRecord.transaction do
user.update!(remote_avatar_url: 'https://smaple_image.jpg')
# More saves/updates here
end
Users::DoSomethinService.new(user: user.reload).call

In my case, there weren’t any database transactions in DoSomethinService class. It was just to communicate the data with a third-party service. So I just put it outside of the transaction , let it commit first, and let the user record reload. The solution is totally up to your codes so I won’t dig further.

That’s it!

--

--

Ats
Ats

Written by Ats

I like building something tangible like touch, gesture, and voice. Ruby on Rails / React Native / Yocto / Raspberry Pi / Interaction Design / CIID IDP alumni

No responses yet