alifirat alifirat - 29 days ago 11
Scala Question

Wait future completion to execute a another one for a sequence

I met a read / write problem this last days and I cannot fix an issue in my test. I have a JSON document based on the following model

package models

import play.api.libs.json._

object Models {

case class Record
(
id : Int,
samples : List[Double]
)

object Record {
implicit val recordFormat = Json.format[Record]
}
}


I have two functions : one to read a record and an another one to update.

case class MongoIO(futureCollection : Future[JSONCollection]) {

def readRecord(id : Int) : Future[Option[Record]] =
futureCollection
.flatMap { collection =>
collection.find(Json.obj("id" -> id)).one[Record]
}

def updateRecord(id : Int, newSample : Double) : Future[UpdateWriteResult]= {
readRecord(id) flatMap { recordOpt =>
recordOpt match {
case None =>
Future { UpdateWriteResult(ok = false, -1, -1, Seq(), Seq(), None, None, None) }
case Some(record) =>
val newRecord =
record.copy(samples = record.samples :+ newSample)
futureCollection
.flatMap { collection =>
collection.update(Json.obj("id" -> id), newRecord)
}
}
}
}
}


Now, I have a
List[Future[UpdateWriteResult]]
corresponds to a many updates on the document but what I want is that : wait the future is complete to execute the second one then wait the completion of the second to execute the third. I tried to do that with a
foldLeft and flatMap
like this :

val l : List[Future[UpdateWriteResult]] = ...
println(l.size) // give me 10
l
.foldLeft(Future.successful(UpdateWriteResult(ok = false, -1, -1, Seq(), Seq(), None, None, None))) {
case (cur, next) => cur.flatMap(_ => next)
}


but the document is never updated like excepted : instead to have a document with a samples list of size 10, I got a list of 1 samples. So the read is faster than the write (impression that I have) and also using a combinaison of
foldLeft / flatMap
seems to do not wait the completion of the current future so how can I fix this issue properly (without Await) ?

Update



val futureCollection = DB.getCollection("foo")
val mongoIO = MongoIO(futureCollection)
val id = 1
val samples = List(1.1, 2.2, 3.3)
val l : List[Future[UpdateWriteResult]] = samples.map(sample => mongoIO.updateRecord(id, sample))

Answer

You have to do the foldLeft on the samples:

(mongoIO.updateRecord(id, samples.head) /: samples.tail) {(acc, next) =>
  acc.flatMap(_ => mongoIO.updateRecord(id, next))
}

updateRecord is what triggers the future, so you have to make sure not to call it until the previous one finishes.