has_many_attachedを使用した際、transaction内で複数枚同時にattachすると一枚しかアップロードされない
環境
Ruby: 3.0.1
Rails: 6.1.4.1
storage: Google Cloud Storage
(おそらくS3等でも同じ事象が起きると思いますが未検証です)
発生する問題
has_many_attached :images
設定時に
ActiveRecord::Base.transaction do
article = Article.find(params[:id])
params[:images].count #=> 3
params[:images].each do |image|
article.image.attach(image)
end
article.save
end
みたいなことをすると、Transactionは成功するが、タイトル通りStorageには一枚しかファイルがアップロードされず、成功した一枚以外のファイルのURLを参照すると404となる。
ActiveStorage::Blob
は問題なく作成されており、Rails上はURLを見ない限りこの事象が起きたかどうかを確認することはできない。
なお、transaction後に発火する ActiveStorage::AnalyzerJob
ではファイルを参照しに行くためそこでErrorが出る。
[ActiveJob] [ActiveStorage::AnalyzeJob] [...] 404
[ActiveJob] [ActiveStorage::AnalyzeJob] [...] #<HTTP::Message:0x0000000116c2e838 @http_header=#<HTTP::Message::Headers:0x0000000116c2e810 @http_version="1.1", @body_size=0, @chunked=false, @request_method="GET", @request_uri=#<Addressable::URI:0x4b00 URI:https://storage.googleapis.com/storage/v1/b/*****?>, @request_query=nil, @request_absolute_uri=nil, @status_code=404, @reason_phrase="Not Found", @body_type=nil, @body_charset=nil, @body_date=nil, @body_encoding=#<Encoding:UTF-8>, @is_request=false, @header_item=[["X-GUploader-UploadID", "****"], ["Content-Type", "application/json; charset=UTF-8"], ["Date", "Tue, 28 Sep 2021 14:44:38 GMT"], ["Vary", "Origin"], ["Vary", "X-Origin"], ["Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"], ["Expires", "Mon, 01 Jan 1990 00:00:00 GMT"], ["Pragma", "no-cache"], ["Content-Length", "289"], ["Server", "UploadServer"], ["Alt-Svc", "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\""]], @dumped=false>, @peer_cert=#<OpenSSL::X509::Certificate: subject=#<OpenSSL::X509::Name CN=*.storage.googleapis.com>, issuer=#<OpenSSL::X509::Name CN=GTS CA 1C3,O=Google Trust Services LLC,C=US>, serial=#<OpenSSL::BN:0x0000000116e2c748>, not_before=2021-08-30 02:53:55 UTC, not_after=2021-11-22 02:53:54 UTC>, @http_body=#<HTTP::Message::Body:0x0000000116c2e770 @body="{\n \"error\": {\n \"code\": 404,\n \"message\": \"No such object: ******\",\n \"errors\": [\n {\n \"message\": \"No such object: *******\",\n \"domain\": \"global\",\n \"reason\": \"notFound\"\n }\n ]\n }\n}\n", @size=0, @positions=nil, @chunk_size=nil>, @previous=nil>
[ActiveJob] [ActiveStorage::AnalyzeJob] [...] Caught error notFound: No such object: ******
なぜ起きるのか、対応状況
Rails 5.2 -> 6.0 で、ActiveStorageのファイル追加・アップロード処理の発火タイミングが変わった
Store newly-uploaded files on save rather than assignment #33303
これによりデグレが起きたようで、rails/railsにもissueが上がっている
Multiple attachment files are not persisted to storage service when attached separately within a transaction #41661
Calling attach multiple times inside a transaction only uploads the last one #42343
対応するPRも上がっている
Fix #41661 attaching multiple times within transaction #42300
が、どうやら直し方に課題があったのか、優先度が低いのかはわからないが暫く反応が無かったためbotによりCloseされている。
現状は該当のattachの処理部だけをtransactionから外す、になりそう。
Discussion