Handling errors as a Future[Result] in ReactiveMongo 16.6 - scala

How can I output mongoDB errors in a Result with ReactiveMongo (16.6)? I've spent virtually the whole day looking through samples but have not been able to achieve this as of yet. The error section of the documentation returns a Future[Unit] rather than a Future[Result]. And every other example/sample that I can find either is outdated or does not do this; example_1, example2
Here is what I would like to do:
def updateById(collName: String, id: BSONObjectID) = authAction.async(parse.json) { implicit request: Request[JsValue] =>
val oWriteJso = request.body.asOpt[JsObject]
lazy val qJso = Json.obj("_id" -> id)
val res = oWriteJso.map(
wJso => mongoRepo.update(collName)(qJso, wJso)().recoverWith {
case WriteResult.Code(11000) => Future.successful(BadRequest("it went bad"))
case _ => Future.successful(BadRequest("also bad"))
}
)
res
}
Of course with the function signature as recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] this code above will return an error as it needs to return a Future[WriteResult]. But how then would I be able to put any error messages, codes, etc (from mongoDB) into a Result?

The documentation indicates how to recover a Future[WriteResult]:
.recover {
case WriteResult.Code(11000) =>
// if the result is defined with the error code 11000 (duplicate error)
println("Match the code 11000")
case WriteResult.Message("Must match this exact message") =>
println("Match the error message")
// ...
}
Thanks to Future combinators (not specific to ReactiveMongo), it can be used whatever is the type of the successful value to be lifted inside the Future.
def foo[T](future: Future[WriteResult], recoveredValue: => T)(success: WriteResult => T): Future[T] = future.map(success).recover {
case WriteResult.Code(11000) =>
// if the result is defined with the error code 11000 (duplicate error)
recoveredValue
case WriteResult.Message("Must match this exact message") =>
recoveredValue
}

Related

Propagate errors through a chain of Scala futures

Considering a sequence of futures each returning Either[Status, Resp].
How would you propagate error status codes through a for comprehension which is using Future and not Either?
The code bellow does not work, since the parsing exception is not caught by .recover of the last future
The use case is Scala Play ActionRefiners which returns Future[Either[Status, TRequest[A]]].
def parseId(id: String):Future[Int] = {
Future.successful(Integer.parseInt(id))
}
def getItem(id: Int)(implicit ec: ExecutionContext): Future[Either[Status, String]] =
Future(Some("dummy res from db " + id)).transformWith {
case Success(opt) => opt match {
case Some(item) => Future.successful(Right(item))
case _ => Future.successful(Left(NotFound))
}
case Failure(_) => Future.successful(Left(InternalServerError))
}
(for {
id <- parseId("bad request")
resp <- getItem(id)
} yield resp).recover {
case _:NumberFormatException => Left(BadRequest)
}
I could move the .recover to parseId, but this makes the for comprehension very ugly - having to treat the Either[Status, id] in the middle
def parseId(id: String):Future[Either[Status, Int]] = {
Future.successful(Right(Integer.parseInt(id))).recover {
case _:NumberFormatException => Left(BadRequest)
}
}
Your exception is not caught because you are not throwing it inside the Future: Future.successful is immediately satisfied with the result of the expression you give it, if it throws an exception, it is executed on the current thread.
Try removing the .successful: Future(id.toInt) will do what you want.
Also, I would recommend to get rid of all the Eithers: these are highly overrated/overused, especially in the context of Future (that already wrap their result into Try anyhow), and just make the code more complicated and less readable without offering much benefit.
case class FailureReason(status: Status)
extends Exception(status.toString)
def notFound() = throw FailureReason(NotFound)
def internalError() = throw FailureReason(InternalError)
def badRequest() = throw FailureReason(BadRequest)
def parseId(id: String):Future[Int] = Future(id.toInt)
def getItem(id: Int): Future[String] = Future(Some("dummy"))
.map { _.getOrElse(notFound) }
.recover { _ => internalError }
// this is the same as your for-comprehension, just looking less ugly imo :)
parseId("foo").flatMap(getItem).recover {
case _: NumberFormatException => badRequest()
}
// if you still want `Either` in the end for some reason:
.map(Right.apply[Status, String])
.recover {
case _: NumberFormatException => Left(BadRequest) // no need for the first recover above if you do this
case FailureReason(status) => Left(status)
}

Pattern matching after update with ReactiveMongo

I try to make pattern matching for checking error after an update document on mongodb with scala.
def update(id: BSONObjectID, post: Post): Future[WriteResult] =
collection.flatMap(_.update.one(BSONDocument("_id" -> id), BSONDocument(
f"$$set" -> BSONDocument(
"title" -> post.title,
"description" -> post.description
)
),
true))
this is my update function in productrepository, and this is my controller
def update (id: String) = Action.async(parse.json) { /*implicit request =>*/
val bsonId = BSONObjectID.parse(id)
_.body.validate[Post].map { post =>
postsRepo.update(bsonId.get, post).map {
case Some(post) => Ok(Json.toJson(post))
case _ => NotFound
// _ => Created
}
}.getOrElse(Future.successful(BadRequest("Invalid Format")))
}
I obtain this error:
constructor cannot be istantiated to expected type;
Found Some[A]
Required Reactivemongo.api.command.writeResult
My goal is to run pattern matching after update
If you have a look at IDE/compiler message, you can see that result of update operation is Future[UpdateWriteResult], so matching on Option in Future.map cannot be OK.
If you read the documentation, you will find a "error handling" section, explaining how to recover write result according DB code or message.
val done: Future[Unit] = future.map(_ => {}).recover {
case WriteResult.Code(11000) =>
// if the result is defined with the error code 11000 (duplicate error)
println("Match the code 11000")
case WriteResult.Message("Must match this exact message") =>
println("Match the error message")
case _ => ()
}
If you are trying to produce a specific response if nothing has been updated, it's not to be done with pattern matching, but just check UpdateWriteResult.n (number of updated docs) in .map.

How to NOT throw an exception?

I have the following Slick code that given an id returns a customer (if exists). If there's a problem (such as connectivity lost) a Failure clause will throw an exception:
def read (id: Int): Future[Option[Customer]] = {
val db = // ....
val customers = TableQuery[CustomerDB]
val action = customers.filter(_.id === id).result
val future = db.run(action.asTry)
future.map{
case Success(s) =>
if (s.length>0)
Some(s(0))
else
None
case Failure(f) => throw new Exception (f.getMessage)
}
}
Now, my understanding is that instead of using try/catch/finally of exceptions, in Scala one should use Try. In addition, no exceptions should be thrown. But if the exception is not thrown, how to notify the upper layer that a problem occurred?
Future itself does already have Try inside. So, I would say that you need to just flatten (also you code a bit complicated, I simplified):
future.flatMap {
case Success(s) => Future.successful(s.headOption)
case Failure(f) => Future.failed(f)
}
Result Future when in failed state notifies caller that execution failed (with wrapped original exception). Otherwise, successful.
The right way to do report errors is by using Either.
trait Error
case class NotFound(id: Int) extends Error
case class QueryFailed(msg: String) extends Error
def read (id: Int): Future[Either[Error, Customer]] = {
val db = // ....
val customers = TableQuery[CustomerDB]
val action = customers.filter(_.id === id).result
val future = db.run(action.asTry)
future.map{
case Success(s) =>
if (s.length>0)
Right(s(0))
else
Left(NotFound(id))
case Failure(f) => Left(QueryFailed(f.getMessage))
}
}
Ok so, in general you can use Future.successful or Future.failed(msg: String) to "signal" the upper level (aka calling method) you got the value or not.
Better approach
A good approach is however to use .recoverWith{} on a Future in case of failure.
For example:
def getUserFromCloud (userId: String): Future[String] = Future{
cloudProviderApi.getUsername(userId)
}.recoverWith{
Future.failed(s"$userId does not exist.")
}
What about the calling method?
Well you just map the success with and underscode and deal with the error by using recover:
getUserFromCloud("test").map(_ => {
//In case of success
}).recover{
//In case of failure, like return BadRequest.
}
More on recover and recoverWith in case you are interested: Scala recover or recoverWith

Handling errors in ReactiveMongo

I am working on a simple RESTful web service using Play Framework 2.1.5 and ReactiveMongo 0.9 using ReactiveMongo Play plugin. It has been a long time since I used Play Framework for the last time. I am trying to insert a document using:
def create = Action(parse.json) { request =>
Async {
val coll = db.collection[JSONCollection](...)
val obj = Json.obj(
"username" -> ...,
...
)
users.insert(obj).map { err => err match {
case e if !e.ok => InternalServerError(Json.obj("result" -> 0, "error" -> e.message))
case _ => Ok(Json.obj("result" -> 1))
}}
}
}
I have expected that once the query execution fails (e.g. due to the duplicate value in an index), I will handle it without any problem. But it is working differently - in case of failure a DatabaseException is thrown instead of satisfying the Promise[LastError] with an appropriate value. What am I missing please?
When an exception happens in a future any calls to map will be ignored and the exception will be passed along the chain of futures.
Explicitly handling the exceptions in a chain of Futures can be done with recover and recoverWith. You can read more about it in the overview of futures in the scala-lang docs:
http://docs.scala-lang.org/overviews/core/futures.html#exceptions
Try this code-
def insert(coll: BSONCollection, doc: BSONDocument): Future[Unit] = {
val p = Promise[Unit]
val f = coll.insert(doc)
f onComplete {
case Failure(e) => p failure (e)
case Success(lastError) => {
p success ({})
}
}
p.future
}
I hope this simplifies your need...
def create = Action (parse.json) { request =>
Async {
val coll = db.collection[JSONCollection](...)
val obj = Json.obj ("username" -> ...)
users.insert(obj).map {
case ins if ins.ok => OK (...)
case ins => InternalServerError (...)
} recover {
case dex: DatabaseException =>
log.error(..)
InternalServerEror(...)
case e: Throwable =>
log.error (..)
InternalServerError (...)
}
}
}

waiting for "recursive" futures in scala

a simple code sample that describes my problem:
import scala.util._
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
class LoserException(msg: String, dice: Int) extends Exception(msg) { def diceRoll: Int = dice }
def aPlayThatMayFail: Future[Int] = {
Thread.sleep(1000) //throwing a dice takes some time...
//throw a dice:
(1 + Random.nextInt(6)) match {
case 6 => Future.successful(6) //I win!
case i: Int => Future.failed(new LoserException("I did not get 6...", i))
}
}
def win(prefix: String): String = {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => "%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll)
case Failure(e) => e match {
case ex: LoserException => win("%s, and then i got %d".format(prefix, ex.diceRoll))
case _: Throwable => "%s, and then somebody cheated!!!".format(prefix)
}
})
"I want to do something like futureGameLog.waitForRecursiveResult, using Await.result or something like that..."
}
win("I started playing the dice")
this simple example illustrates what i want to do. basically, if to put it in words, i want to wait for a result for some computation, when i compose different actions on previous success or failed attampts.
so how would you implement the win method?
my "real world" problem, if it makes any difference, is using dispatch for asynchronous http calls, where i want to keep making http calls whenever the previous one ends, but actions differ on wether the previous http call succeeded or not.
You can recover your failed future with a recursive call:
def foo(x: Int) = x match {
case 10 => Future.successful(x)
case _ => Future.failed[Int](new Exception)
}
def bar(x: Int): Future[Int] = {
foo(x) recoverWith { case _ => bar(x+1) }
}
scala> bar(0)
res0: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#64d6601
scala> res0.value
res1: Option[scala.util.Try[Int]] = Some(Success(10))
recoverWith takes a PartialFunction[Throwable,scala.concurrent.Future[A]] and returns a Future[A]. You should be careful though, because it will use quite some memory when it does lots of recursive calls here.
As drexin answered the part about exception handling and recovering, let me try and answer the part about a recursive function involving futures. I believe using a Promise will help you achieve your goal. The restructured code would look like this:
def win(prefix: String): String = {
val prom = Promise[String]()
def doWin(p:String) {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => prom.success("%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll))
case Failure(e) => e match {
case ex: LoserException => doWin("%s, and then i got %d".format(prefix, ex.diceRoll))
case other => prom.failure(new Exception("%s, and then somebody cheated!!!".format(prefix)))
}
})
}
doWin(prefix)
Await.result(prom.future, someTimeout)
}
Now this won't be true recursion in the sense that it will be building up one long stack due to the fact that the futures are async, but it is similar to recursion in spirit. Using the promise here gives you something to block against while the recursion does it's thing, blocking the caller from what's happening behind the scene.
Now, if I was doing this, I would probable redefine things like so:
def win(prefix: String): Future[String] = {
val prom = Promise[String]()
def doWin(p:String) {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => prom.success("%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll))
case Failure(e) => e match {
case ex: LoserException => doWin("%s, and then i got %d".format(prefix, ex.diceRoll))
case other => prom.failure(new Exception("%s, and then somebody cheated!!!".format(prefix)))
}
})
}
doWin(prefix)
prom.future
}
This way you can defer the decision on whether to block or use async callbacks to the caller of this function. This is more flexible, but it also exposes the caller to the fact that you are doing async computations and I'm not sure that is going to be acceptable for your scenario. I'll leave that decision up to you.
This works for me:
def retryWithFuture[T](f: => Future[T],retries:Int, delay:FiniteDuration) (implicit ec: ExecutionContext, s: Scheduler): Future[T] ={
f.recoverWith { case _ if retries > 0 => after[T](delay,s)(retryWithFuture[T]( f , retries - 1 , delay)) }
}