Shurik Agulyansky Shurik Agulyansky - 9 days ago 7
Scala Question

Dealing with a Future wrapped with Try in scala

I have a repo method that returns a future:

def insert(newUser: User): Future[User]

This method can fail for different reasons, however, I want to be able to catch one of these reasons and rethrow others.
If I am simply wrapping the call with Try

Try(userRepository.insert(usr)) match {
case Success(newUserFuture) => newUserFuture

case Failure(e) if e.getCause.getMessage.contains("duplicate key") => {
throw DuplicateEntityException("duplicate user record")

case Failure(e) => {
throw e

As a result, I am getting
, and in this situations, neither of the Failures will ever happen, since the Future is successfully created.
And at this point, I am not sure how to deal with it. Ideally, I want to get
Future[Either[String, User]]
out of it (or something similar).

How should I deal with this situation, or am I in completely wrong direction?

EDIT following recommendations
Possible solution
I've tried something like this:

userRepository.insert(usr).map(newUser => Right(newUser)) recoverWith {
case e: PSQLException if e.getMessage.contains("duplicate key") =>
Future.successful(Left("duplicate user record"))


Future will handles the exceptions for you. You need not wrap future code with Try.

Future does the job of the Try as well.

This line of code is not necessary


I hope your insert method is something like this

def insert(newUser: User): Future[User] = Future { doSomeDBCall() }

In the above method. If doSomeDBCall() fails. The exception will be caught by the future safely and will be delivered to caller as Failure(exception). So you need not wrap the Future with a Try

Catch specific exceptions and throw others (propagate others).

You can use recoverWith and handle exceptions and propagate exceptions which you are not interested in.

In the below code SQLException is handled and other exceptions are propagated to the caller.

userRepository.insert(usr).recoverWith { 
 case ex: SQLException => Future.successful(defaultValue)
 case ex => Future.failed(ex)