Reputation: 57284
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:
sbt test
it will generally pass. If I'm in sbt and repeatedly invoke test
it will probably fail after 3 or 4 times. There's a warning that the existing pool has not been released.Thanks in advance.
Upvotes: 0
Views: 152
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