awesome_nested_set icon indicating copy to clipboard operation
awesome_nested_set copied to clipboard

Active Storage Issue

Open MatthewKennedy opened this issue 4 years ago • 6 comments

I found a strange behaviour when using acts_as_nested_set and has_one_attached on the same model.

If acts_as_nested_set is placed above has_one_attached acts as the object is a newly created item, the active storage attachment is lost when saved.

But if you create your item and the go to attach the image using active storage it works fine.

If you place has_one_attached above acts_as_nested_set then the behaviour changes slightly, it stall fails.

  TRANSACTION (4.2ms)  COMMIT
[ActiveJob] Enqueued ActiveStorage::AnalyzeJob (Job ID: 9460cc6e-83cb-4c87-bc86-e2de446574e1) to Async(default) with arguments: #<GlobalID:0x00007fc9396e9588 @uri=#<URI::GID gid://navinew/ActiveStorage::Blob/20>>
Redirected to http://localhost:3000/admin/menus/1/menu_items/153/edit
Completed 302 Found in 75ms (ActiveRecord: 26.6ms | Allocations: 35954)

  ActiveStorage::Blob Load (0.1ms)  SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 LIMIT $2  [["id", 20], ["LIMIT", 1]]
[ActiveJob] [ActiveStorage::AnalyzeJob] [9460cc6e-83cb-4c87-bc86-e2de446574e1] Performing ActiveStorage::AnalyzeJob (Job ID: 9460cc6e-83cb-4c87-bc86-e2de446574e1) from Async(default) enqueued at 2021-05-08T13:32:24Z with arguments: #<GlobalID:0x00007fc93968add0 @uri=#<URI::GID gid://navinew/ActiveStorage::Blob/20>>
[ActiveJob] [ActiveStorage::AnalyzeJob] [9460cc6e-83cb-4c87-bc86-e2de446574e1]   Disk Storage (0.5ms) Downloaded file from key: gq0s3ce5ug1v8t9q6e957lqurzha
[ActiveJob] [ActiveStorage::AnalyzeJob] [9460cc6e-83cb-4c87-bc86-e2de446574e1] Error performing ActiveStorage::AnalyzeJob (Job ID: 9460cc6e-83cb-4c87-bc86-e2de446574e1) from Async(default) in 1.48ms: ActiveStorage::FileNotFoundError (ActiveStorage::FileNotFoundError):
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/service/disk_service.rb:145:in `rescue in stream'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/service/disk_service.rb:138:in `stream'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/service/disk_service.rb:29:in `block in download'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:203:in `block in instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:203:in `instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/service.rb:155:in `instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/service/disk_service.rb:28:in `download'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/downloader.rb:32:in `download'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/downloader.rb:13:in `block in open'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/downloader.rb:24:in `open_tempfile'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/downloader.rb:12:in `open'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/service.rb:90:in `open'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/app/models/active_storage/blob.rb:276:in `open'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/analyzer.rb:33:in `download_blob_to_tempfile'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/analyzer/image_analyzer.rb:32:in `read_image'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/lib/active_storage/analyzer/image_analyzer.rb:21:in `metadata'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/app/models/active_storage/blob/analyzable.rb:51:in `extract_metadata_via_analyzer'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/app/models/active_storage/blob/analyzable.rb:29:in `analyze'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activestorage-6.1.3.2/app/jobs/active_storage/analyze_job.rb:11:in `perform'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/execution.rb:48:in `block in perform_now'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:117:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/i18n-1.8.10/lib/i18n.rb:314:in `with_locale'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/translation.rb:9:in `block (2 levels) in <module:Translation>'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `instance_exec'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/core_ext/time/zones.rb:66:in `use_zone'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/timezones.rb:9:in `block (2 levels) in <module:Timezones>'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `instance_exec'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/instrumentation.rb:21:in `block in instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:203:in `block in instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:203:in `instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/instrumentation.rb:31:in `instrument'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/instrumentation.rb:14:in `block (2 levels) in <module:Instrumentation>'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `instance_exec'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/logging.rb:22:in `block in tag_logger'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/tagged_logging.rb:99:in `block in tagged'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/tagged_logging.rb:37:in `tagged'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/tagged_logging.rb:99:in `tagged'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/logging.rb:22:in `tag_logger'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/logging.rb:15:in `block (2 levels) in <module:Logging>'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `instance_exec'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:137:in `run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/execution.rb:47:in `perform_now'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/execution.rb:25:in `block in execute'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:117:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/railtie.rb:47:in `block (4 levels) in <class:Railtie>'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/execution_wrapper.rb:88:in `wrap'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/reloader.rb:72:in `block in wrap'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/execution_wrapper.rb:88:in `wrap'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/reloader.rb:71:in `wrap'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/railtie.rb:46:in `block (3 levels) in <class:Railtie>'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `instance_exec'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:137:in `run_callbacks'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/execution.rb:23:in `execute'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/activejob-6.1.3.2/lib/active_job/queue_adapters/async_adapter.rb:70:in `perform'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.8/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:363:in `run_task'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.8/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:352:in `block (3 levels) in create_worker'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.8/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:335:in `loop'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.8/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:335:in `block (2 levels) in create_worker'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.8/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `catch'
/Users/matt/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.8/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `block in create_worker'
Started GET "/admin/menus/1/menu_items/153/edit" for ::1 at 2021-05-08 14:32:24 +0100
Processing by Spree::Admin::MenuItemsController#edit as HTML
  Parameters: {"menu_id"=>"1", "id"=>"153"}

MatthewKennedy avatar May 08 '21 13:05 MatthewKennedy

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 19 '21 14:06 stale[bot]

This happened to me today with Rails 7.1.3 and ANS 3.6.0. I have worked around the issue by switching the order in the model:

class Record < ActiveRecord::Base
  # broken
  acts_as_nested_set
  has_one_attached :attachment

  # works
  has_one_attached :attachment
  acts_as_nested_set
end

botandrose avatar Feb 22 '24 15:02 botandrose

Digging deeper, it appears that the various calls to AR::Base#reload in awesome_nested_set/model.rb are to blame. Commenting them out resolves the issue. It looks like #reload resets some ActiveStorage hooks?

botandrose avatar Feb 23 '24 15:02 botandrose

Turns out this is expected behavior??

# lib/active_storage/attached/model.rb
module ActiveStorage::Attached::Model
  def reload(*) # :nodoc:
    super.tap { @attachment_changes = nil }
  end
end

Also perhaps related: https://github.com/rails/rails/issues/40630

Do we try to campaign to get this fixed in future Rails releases? Or work around it in ANS? Both?

At the very least, a note in the README.

botandrose avatar Feb 24 '24 14:02 botandrose

I have successfully worked around this issue by undoing the attachment reset behavior in ActiveStorage:

class Record < ActiveRecord::Base
  has_one_attached :attachment # must come before acts_as_nested_set
  acts_as_nested_set

  # undo ActiveStorage's reset behavior
  def reload(*)
    before = @attachment_changes
    super.tap { @attachment_changes = before }
  end
end

So the question is, where do we go with this to resolve the apparent incompatibility between AS and ANS? Should we incorporate these workarounds into ANS?

botandrose avatar Feb 24 '24 21:02 botandrose

@parndt Would you mind reopening this issue? I plan to figure this out next.

botandrose avatar Mar 02 '24 17:03 botandrose

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jan 31 '25 21:01 stale[bot]

I believe I'm likely having a similar issue with Shrine.

The #{name}_atttacher.changed? in the after_commit call here:

https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/activerecord.rb#L40

Is coming back false, so the promote_block isn't being executed.

When I comment out acts_as_nested_set, my model's Shrine stuff starts working as expected again

Update I just confirmed it is indeed the same issue, and this fixes it:

class FileUploader < Shrine
  # normal, run of the mill validation stuff
end

class Record < ApplicationRecord
  include FileUploader[:file]

  acts_as_nested_set # { ...options here... }
  
  def reload(*)
    previous = file_attacher.instance_variable_get(:@previous)
    super.tap { file_attacher.instance_variable_set(:@previous, previous) }
  end
end

Shrine.plugin :activerecord
Shring.plugin :backgrounding

Shrine.promote_block do
  # run of the mill backgrounding stuff is here
end

My promote_block started executing again and my test is finally passing after a long day of pulling my hair out 😅

joemsak avatar Feb 19 '25 04:02 joemsak