BZapper
BZapper

Reputation: 330

Scala Aggregate result from multiple Future calls

Consider a Model for Master/Slave election for a cluster.

Member{ id: Long, isMaster: Boolean } 

I have a Dao/Repo with following methods:

MemberDao.findById(id:Long):Future[Option[Member]]
MemberDao.update(id:Long, member: Member):Future[Unit]
MemberDao.all() : Future[List[Member]]

Within the MemberService, I'm trying to write a function to set isMaster to false for all existing members, and I'm ending up with this crazily bloated code:

class MemberService ... {
  def demoteAllMembers() : Future[Boolean] = {
    val futures = memberDao.all.map{ memberFuture =>
      memberFuture.map{ member =>
        memberDao.findById(member.id).map { existingMemberFuture =>
          existingMemberFuture.map { existingMember =>
            memberDao.update(existingMember.id, existingMember.copy(isMaster = false)
          }
        }
      }
      val results = Await.result(futures, 10 seconds)

      // return something here
    }
  }


}

My Questions are: 1. How should the return statement be written to handle success / errors? e.g. On success, return Future(true) and on failure, return Future(false) 2. Is this way of repetitively mapping future the correct way of doing async programming in scala? I understand this could be written differently in Actor paradigm and probably much better, but in case of OOP, is this the best Scala can do?

Thanks.

Upvotes: 1

Views: 1025

Answers (1)

Dima
Dima

Reputation: 40500

Why are you doing MemberDao.findById when you are already holding a member in hand??? (You are also treating the return as a Member, while it should really be an Option[Member]). Also, update does not need to take an id as a separate parameter (there is one available inside member). You don't need to Await your result, because your function is returning a Future, and you don't need to return a Boolean: just throw an exception to signal failure. Consider something like this:

def demoteAllMembers: Future[Unit] = memberDao.all.flatMap { 
   Future.sequence(_.foreach {
     memberDao.update(_.copy(isMaster = false))
   })
 }.map ( _ => () ) 

Not all that bloated, is it? :)

Upvotes: 2

Related Questions