ExportXMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Unknown
    • 9.1.2
    • Affects Version/s: 9.1.1
    • Component/s: Persistence
    • None
    • None
    • Fully Compatible
    • Ruby Drivers
    • Not Needed
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?
    • None
    • None
    • None
    • None
    • None
    • None

      Description

      Mongoid 9.1.1 and the current master branch can send conflicting update paths when one save changes an embeds_many array and one of its embedded documents. MongoDB rejects the update with Mongo::Error::OperationFailure code 40 (ConflictingUpdateOperators), so none of the changes are applied.

      Mongoid can produce this update through its public association APIs. For example, building another child and changing an existing child's HABTM foreign keys in the same save can put $push children and $addToSet children.0.grade_ids in one update.

      Reproduction

      class Grade
        include Mongoid::Document
      end
      
      class Child
        include Mongoid::Document
        has_and_belongs_to_many :grades, inverse_of: nil
        embedded_in :parent
      end
      
      class Parent
        include Mongoid::Document
        embeds_many :children
      end
      
      grades = Array.new(2) { Grade.create! }
      parent = Parent.create!(children: [
        Child.new(grade_ids: [grades[0].id]),
        Child.new(grade_ids: [grades[0].id])
      ])
      parent.reload
      child = parent.children.first
      
      parent.children.build
      child.grade_ids = child.grade_ids + [grades[1].id]
      parent.save!
      

      The save raises Mongo::Error::OperationFailure code 40 because $push children conflicts with $addToSet children.0.grade_ids.

      Other affected cases combine $pull children with $pullAll or $addToSet under a surviving child, use pulls at different nested array depths, or combine $push children with a safe $unset under an existing child. Assigning an empty embeds_many array to a surviving child while a preceding sibling is removed also produces an $unset plus $pull conflict on the same array root.

      Expected behaviour

      Mongoid should apply operations in separate writes when it can do so without changing which embedded document or array position they target. Unsupported $unset plus $pull combinations should continue to fail without changing stored data, rather than silently modifying a different child.

      Actual behaviour

      The conflicting paths are sent in one MongoDB update. MongoDB rejects the update with code 40, and none of its changes are applied.

      Root cause

      Mongoid::Atomic::Modifiers detects some conflicts by root field, but $addToSet, $pullAll, and $pull are not consistently checked against $push and other pulls. The order of $unset and $pull on the same array root also cannot always be chosen safely.

      Related issue

      The stale embedded array path is tracked in MONGOID-5364. That path correction matters when evaluating conflicts involving positional child updates.

      Environment

      Mongoid 9.1.1 and the current master branch.

            Assignee:
            Jamis Buck
            Reporter:
            Tatsuhiro Ujihisa (EXT)
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: