Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-4982

Cannot update a field on embedded association member, add another member and add a nested association to the first member in the same save call

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 7.3.0
    • Affects Version/s: None
    • Component/s: Persistence
    • None
    • Fully Compatible

      Given the following setup:

      • Truck embeds many crates
      • Crate embeds many toys
      • There is a truck with one crate

      If we attempt to perform the following three operations on the truck in question:

      • change the attribute of the crate
      • add a toy to the crate
      • add another crate

      ... then Mongoid fails to save the resulting truck.

      
              let!(:truck) { Truck.create! }
              let!(:crates) { truck.crates.create!(volume: 1) }
      
      ...
      
                  truck.crates.first.volume = 2
                  truck.crates.first.toys.build(name: 'Bear')
                  truck.crates.build
      
                  truck.save!
      

      While processing the save, the following updates and conflicts are generated:

      [132, 141] in /home/w/apps/mongoid/lib/mongoid/persistable/updatable.rb
         132:       # @since 1.0.0
         133:       def update_document(options = {})
         134:         prepare_update(options) do
         135:           updates, conflicts = init_atomic_updates
         136:       byebug
      => 137:           unless updates.empty?
         138:             coll = collection(_root)
         139:             selector = atomic_selector
         140:             coll.find(selector).update_one(positionally(selector, updates), session: _session)
         141:             conflicts.each_pair do |key, value|
      (byebug) updates
      {"$set"=>{"crates.0.volume"=>2}}
      (byebug) conflicts
      {"$push"=>{"crates.0.toys"=>{"$each"=>[{"_id"=>BSON::ObjectId('5f4bda5f2c97a61e2d76a7f9'), "name"=>"Bear"}]}, "crates"=>{"$each"=>[{"_id"=>BSON::ObjectId('5f4bda5c2c97a61e2d76a7f8')}]}}}
      

      So, what is happening here is Mongoid will perform one update of the crate attribute and then attempt to add a toy and add a crate together in the next write.

      Server fails the latter operation with:

      > Updating the path 'crates' would create a conflict at 'crates' (40) (on localhost:14440, modern retry, attempt 1)

      This problem is easier to encounter when timestamps and callback cascades are enabled, in which case Mongoid internally performs the timestamp updates on the embedded documents. To encounter this issue, then, the application only needs to add a top-level association and add a nested association to an existing top-level association member in the same save call (provided the top-level association cascades callbacks and the nested model has timestamps enabled).

            Assignee:
            oleg.pudeyev@mongodb.com Oleg Pudeyev (Inactive)
            Reporter:
            oleg.pudeyev@mongodb.com Oleg Pudeyev (Inactive)
            Votes:
            2 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: