Steve B.
Steve B.

Reputation: 57284

sequential actions and DB state using slick; out of order behavior

Given a number of methods that pass tests and seem to be reliable, accessing and updating an independently incrementing column (incremented per record, so it's not an auto-incrementing column). e.g:

  def getVersion(id:Int): Future[Int] =  db.run(query.filter(_.id === id).map(o => o.version).result.head)  

def getAndIncrementVersion(id: Int): Future[Try[Int]] = {

    val incrementAction = sql"""UPDATE TABLE SET VERSION = VERSION + 1 WHERE ID = ${id}""".as[Int]
    val getVersionAction = query.filter(_.id === id).map(o => o.version).result.headOption
    val dbAction = incrementAction.andThen(getVersionAction)

    db.run(dbAction).map {
      case Some(i) => Success(i)
      case None => Failure[Int](new Exception("Error incrementing org counter"))
    }
  }

Now, if I call these methods in sequence, even if I resolve each future sequentially, I sometimes get out of order result, e.g.

   for {
     i <- getVersion(1)
     j <- getAndIncrementVersion(1)
     k <- getVersion(1)
    } yield (i,j,k)

which should yield, e.g. (71,Success(72), 72) will sometimes yield (71,72,71). Anyone have any ideas on why this would be happening?

Some more information:

Thanks in advance.

Upvotes: 0

Views: 152

Answers (1)

Richard Dallaway
Richard Dallaway

Reputation: 4320

Your database may not guarantee when a change (from the update) is visible (to the getVersion). As you are running each part as an independent query, it's likely you'll get different results. If you need stricter behaviour, I suggest you combine the actions into a single action and run in a transaction.

For example (and apologies for typos--I've not tried to compile this):

 val action: DBIO[(Int,Int,Int)] = (for {
    i <- getVersionAction
    j <- incrementAction
    k <- getVersionAction
  } yield (i,j,k)).transactionally

I'd expect that to give you consistent results.

Upvotes: 1

Related Questions