Summary
When a user needs to queue a background job during an after_save callback for a sharded document, and that job requires access to the document's atomic selector in order to query the updated document, current Mongoid code doesn't appear to permit this functionality.
Repro
module MyModule module_function def create_background_job(atomic_selector) # This line throws a DocumentNotFound error when called from Profile's # after_save callback, because atomic_selector will have the outdated # sharded field. Profile.where(atomic_selector) end end # Copied Profile class from profile.rb + an added after_save callback. class Profile include Mongoid::Document field :name, type: String embeds_one :profile_image shard_key :name after_save do |document| # This line will fail when the 'name' field (shard_key) has been updated. The # failure will be because at this point in time, document.atomic_selector # will contain the now-outdated value for the 'name' field. MyModule.create_background_job(document.atomic_selector) end end # Profile shard_key :name profile = Profile.create(name: "Alice") profile.name = "Bob" # This line won't fail, but its triggered after_save callback, which in-turn # will trigger MyModule.create_background_job(document.atomic_selector), # which will then try to query the document using its outdated atomic_selector, # will fail. profile.save()
Current Functionality
After digging through Mongoid source code & documentation, I understand the
reason that atomic_selector has an outdated value for shard keys during
after_save callbacks. It is built from shard_key_selector, which in-turn uses attribute_was. attribute_was is not updated until *after* the after_save callback has completed.
Request
However, supporting background jobs queued during an after_save callback querying sharded documents, does feel like an important use-case. Especially since this functionality works for non-sharded documents. I know we have multiple existing use cases where we leverage functionality like this, but we have to use some gross patterns to work around the atomic_selector difficulty.
I'm wondering if there exists a simple solution to this problem such as adding an optional parameter to atomic_selector such as
def atomic_selector(force_retrieve_newest_values = false)
Or maybe adding an atomic_selector variant which automatically retrieves the fields using only selector[field.to_s] = send(field) instead of selector[field.to_s] = new_record? ? send(field) : attribute_was(field). I'm still learning the Mongoid codebase, but it looks hopeful that there exists a solution which:
1. Would be simple to implement
2. Wouldn't break existing code.
3. Would unlock this desired functionality
- is caused by
-
MONGOID-5104 attribute_was returns previous value in after_save callback
- Closed
- related to
-
MONGOID-5076 reload fails to find document when the document's sharded field has been updated
- Closed
- links to