Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveStorage race condition on attach #51209

Open
alexeevit opened this issue Feb 27, 2024 · 0 comments
Open

ActiveStorage race condition on attach #51209

alexeevit opened this issue Feb 27, 2024 · 0 comments

Comments

@alexeevit
Copy link

alexeevit commented Feb 27, 2024

I've ran into a race condition on attaching a file to a has_many_attached association. It tries to create 2 ActiveStorage::Attachment records, even though I attach only 1 file, so the other file is actually an ActiveStorage::Blob that was already created by another request.

I've done a little research and found, that when the model gets saved the attachment_changes['images'].send(:subchanges) has 2 records:

  • Changes::CreateOneOfMany for the existing ActiveStorage::Blob
  • Changes::CreateOneOfMany for the new ActionDispatch::Http::UploadedFile

I think, the problem is that when the subchanges get built, it loads record.attachments right before the blob is created in another request, and after the blob is created in another request, it loads all blobs for the subchanges. So in the end, it perceives the blob as a completely new attachment.

My specific case is super simple. I don't need to know anything about already existing attachments, so the only thing I need is to correctly save and upload the file. So I came up with the next workaround:

subchange = ActiveStorage::Attached::Changes::CreateOneOfMany.new('images', @storage, params[:image])
storage.images_attachments << subchange.attachment
subchange.upload

Steps to reproduce

class Storage < ApplicationRecord
  has_many_attached :images
end

class Storage::ImagesController < ApplicationController
  def create
    @storage.attach(params[:image])
  end
end

Expected behavior

When we're doing multiple requests simultaneously, all the images should get saved successfully, in other words only 1 pair of ActiveStorage::Attachment and ActiveStorage::Blob is created.

Actual behavior

ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_active_storage_attachments_uniqueness"
DETAIL:  Key (record_type, record_id, name, blob_id)=(Storage, e51d4675-fe21-42cc-8633-3619ee4e9390, images, f306419f-bc1f-4aaf-b859-ba48e2055514) already exists.
)

System configuration

Rails version: 7.1.1

Ruby version: 3.2.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant