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

findAndModify using a sorting will fail when done concurrently

    • Type: Icon: Bug Bug
    • Resolution: Duplicate
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 3.2.16
    • Component/s: Concurrency, Querying
    • None
    • ALL
    • Hide
      1. We'll create 300 documents with a running index i (0..299).
      2. We will do a findAndModify 300 times at once, querying for i != 0 and updating i = 0
      3. We'll see that a huge amount of findAndModify s fail.

      Steps:

      1. Run a MongoDB instance (3.2.16) on localhost:27017
      2. Install the latest sbt
      3. Type sbt in the shell and the interactive sbt will start.
        sbt:mongobug>
        sbt:mongobug> set libraryDependencies += "org.reactivemongo" %% "reactivemongo" % "0.12.6"
        sbt:mongobug> set libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.25"
        sbt:mongobug> console
        scala>
        scala> :paste
        // Entering paste mode (ctrl-D to finish)
        import com.typesafe.config.ConfigFactory
        import reactivemongo.api._
        import reactivemongo.api.collections.bson.BSONCollection
        import reactivemongo.bson._
        
        import scala.concurrent.Await
        import scala.concurrent.ExecutionContext.Implicits._
        import scala.concurrent.duration.Duration
        
        val driver = new MongoDriver(Some(ConfigFactory.empty()), None)
        val conn = driver.connection(Seq("localhost:27017"))
        val db = Await.result(conn.database("test"), Duration.Inf)
        val col = db.collection[BSONCollection]("col")
        
        Await.ready(col.remove(document()), Duration.Inf)
        
        for (i <- 1 to 300) Await.ready(col.insert(document("i" -> i)), Duration.Inf)
        
        val results = for (_ <- 1 to 300) yield {
          col.findAndUpdate(
            document("i" -> document("$ne" -> 0)),
            document("$set" -> document("i" -> 0)),
            sort = Some(document("i" -> 1))
          )
        }
        
        results.map(_.map(println))
        
        // Exiting paste mode, now interpreting.
        
        

      After the application is ran, the output will look something like this:

      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      FindAndModifyResult(Some(UpdateLastError(true,None,1,None)),Some(BSONDocument(<non-empty>)))
      FindAndModifyResult(Some(UpdateLastError(true,None,1,None)),Some(BSONDocument(<non-empty>)))
      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      ...
      ...
      

      Every line that looks like this:

      FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None)
      

      means a failure in updating.

      When we remove the sorting, all the updates will succeed.

      Show
      We'll create 300 documents with a running index i (0..299). We will do a findAndModify 300 times at once, querying for i != 0 and updating i = 0 We'll see that a huge amount of findAndModify s fail. Steps: Run a MongoDB instance (3.2.16) on localhost:27017 Install the latest sbt Type sbt in the shell and the interactive sbt will start. sbt:mongobug> sbt:mongobug> set libraryDependencies += "org.reactivemongo" %% "reactivemongo" % "0.12.6" sbt:mongobug> set libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.25" sbt:mongobug> console scala> scala> :paste // Entering paste mode (ctrl-D to finish) import com.typesafe.config.ConfigFactory import reactivemongo.api._ import reactivemongo.api.collections.bson.BSONCollection import reactivemongo.bson._ import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits._ import scala.concurrent.duration.Duration val driver = new MongoDriver(Some(ConfigFactory.empty()), None) val conn = driver.connection(Seq("localhost:27017")) val db = Await.result(conn.database("test"), Duration.Inf) val col = db.collection[BSONCollection]("col") Await.ready(col.remove(document()), Duration.Inf) for (i <- 1 to 300) Await.ready(col.insert(document("i" -> i)), Duration.Inf) val results = for (_ <- 1 to 300) yield { col.findAndUpdate( document("i" -> document("$ne" -> 0)), document("$set" -> document("i" -> 0)), sort = Some(document("i" -> 1)) ) } results.map(_.map(println)) // Exiting paste mode, now interpreting. After the application is ran, the output will look something like this: FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) FindAndModifyResult(Some(UpdateLastError(true,None,1,None)),Some(BSONDocument(<non-empty>))) FindAndModifyResult(Some(UpdateLastError(true,None,1,None)),Some(BSONDocument(<non-empty>))) FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) ... ... Every line that looks like this: FindAndModifyResult(Some(UpdateLastError(false,None,0,None)),None) means a failure in updating. When we remove the sorting, all the updates will succeed.

      When there are multiple {{findAndModify}}s being ran on the same time with sort, about 50% of them will fail.

            Assignee:
            mark.agarunov Mark Agarunov
            Reporter:
            oripwk Ori Popowski
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: