Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-36405

Update should be smarter about recognizing noop updates by comparing original and "updated" documents

    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Backlog
    • Major - P3
    • Resolution: Unresolved
    • None
    • None
    • Write Ops
    • Query Optimization

    Description

      When any update results in modified document that's identical to original document, the update system should perform no update, create no oplog entry and indicate nModified:0.

      Original summary: replaceOne with no logical change may create oplog entry and report modifiedCount=1

      Original description: When a replacement is performed via the update command, a logical no-op replacement may report a modifiedCount of zero or one depending on whether the replacement document included the _id field or not, respectively.

      Consider:

      rs:PRIMARY> db.foo.drop()
      false
      rs:PRIMARY> db.foo.replaceOne({_id:1},{_id:1, x:11},{upsert:true})
      {
      	"acknowledged" : true,
      	"matchedCount" : 0,
      	"modifiedCount" : 0,
      	"upsertedId" : 1
      }
       
      rs:PRIMARY> db.foo.replaceOne({_id:1},{x:11},{upsert:true})
      { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
       
      rs:PRIMARY> db.foo.replaceOne({_id:1},{_id:1, x:11},{upsert:true})
      { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
      

      Upon further inspection, it appears that modifiedCount is merely correlating with whether or not the update resulted in creation of an oplog entry. After running the above four commands, I observed the following sequence of oplog entries:

      rs:PRIMARY> db.oplog.rs.find({op:{$ne:"n"}}).sort({ts:-1}).limit(4).pretty()
      {
      	"ts" : Timestamp(1533161874, 1),
      	"t" : NumberLong(1),
      	"h" : NumberLong("-7130161787299552975"),
      	"v" : 2,
      	"op" : "u",
      	"ns" : "test.foo",
      	"ui" : UUID("c689af9d-8f23-4897-8e3f-2b055e83cc20"),
      	"o2" : {
      		"_id" : 1
      	},
      	"wall" : ISODate("2018-08-01T22:17:54.422Z"),
      	"o" : {
      		"_id" : 1,
      		"x" : 11
      	}
      }
      {
      	"ts" : Timestamp(1533161867, 3),
      	"t" : NumberLong(1),
      	"h" : NumberLong("-1692372989673358044"),
      	"v" : 2,
      	"op" : "i",
      	"ns" : "test.foo",
      	"ui" : UUID("c689af9d-8f23-4897-8e3f-2b055e83cc20"),
      	"wall" : ISODate("2018-08-01T22:17:47.779Z"),
      	"o" : {
      		"_id" : 1,
      		"x" : 11
      	}
      }
      {
      	"ts" : Timestamp(1533161867, 2),
      	"t" : NumberLong(1),
      	"h" : NumberLong("-7615294753132425657"),
      	"v" : 2,
      	"op" : "c",
      	"ns" : "test.$cmd",
      	"ui" : UUID("c689af9d-8f23-4897-8e3f-2b055e83cc20"),
      	"wall" : ISODate("2018-08-01T22:17:47.779Z"),
      	"o" : {
      		"create" : "foo",
      		"idIndex" : {
      			"v" : 2,
      			"key" : {
      				"_id" : 1
      			},
      			"name" : "_id_",
      			"ns" : "test.foo"
      		}
      	}
      }
      {
      	"ts" : Timestamp(1533161867, 1),
      	"t" : NumberLong(1),
      	"h" : NumberLong("-7489916703840618831"),
      	"v" : 2,
      	"op" : "c",
      	"ns" : "test.$cmd",
      	"ui" : UUID("8cdadcb6-b47f-41aa-80d2-b353c0da5117"),
      	"wall" : ISODate("2018-08-01T22:17:47.125Z"),
      	"o" : {
      		"drop" : "foo"
      	}
      }
      

      The more general question (or bug if there is one) may be: why does a replacement that doesn't logically alter the document result in an oplog entry being created?

      If we contrast this with an atomic update that does not alter the matched document, we can see that both of the following examples produce no oplog entry:

      rs:PRIMARY> db.foo.updateOne({_id:1},{$set:{x:11}},{upsert:true})
      { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
       
      rs:PRIMARY> db.foo.updateOne({_id:1},{$set:{_id:1,x:11}},{upsert:true})
      { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
      

      This was observed on a single-member replica set running MongoDB 4.0.0; however, I fully expect older server versions also demonstrate this behavior.

      Attachments

        Issue Links

          Activity

            People

              backlog-query-optimization Backlog - Query Optimization
              jmikola@mongodb.com Jeremy Mikola
              Votes:
              3 Vote for this issue
              Watchers:
              17 Start watching this issue

              Dates

                Created:
                Updated: