-2

i want to use akka actors in parent child hierarchy where the parent can send the final computed result to the calling code which may/may not be an actor here is my use case

parent->child->childManager

childManager has three worker actor (3 childs) childManager (worker1,worker2,worker3)

now worker1 gets the value process it and its response value sends to worker2

  • worker1->process the value & send result to worker2
  • worker2->process the worker1 result & send worker2 result to worker3
  • worker3->process the worker2 result & send final computed value to childManager
  • childManager send result to child
  • child send result to parent

here is what i am trying to do

final case class GetValue(value:String)
final case class sendToChildManager(value:String)
final case class sendToChild(value:String)

final case class processToken(value:String)
final case class processPublicKey(value:String)
final case class processUserDeatils(accessToken:string,publicKey:PublicKey)

 class worker1 extends Actor {
  def receive {
    case ProcessToken(token)=>
    val request = httpRequest(token)
    request.onComplete{
      case Success(accessToken)=>
        //generate access token 
        sender ! accessToken
      case Failure(e)=>throw e
   }
  }
}

class worker2 extends Actor {
  def receive {
    case processPublicKey(token)=>
    val request=HttpRequest(token)//based on accessToken compute publicKey 
    request.onComplete {
      case Suceess(publicKey)=>
        // calculate publicKey from accessToken 
        sender ! publicKey
      case Failure(e)=>throw e
    }
  }
}

class worker3 extends Actor {

  def receive {
    case processUserDeatils(accessToken,publicKey)=>
    val request = HttpRequest(accessToken,publicKey)
    request.onCompelete {
      case Success(value)=>
        //process the resulted value got userInfo as a result 
        sender ! UserInfo
      case Failure(e)=> throw e
    }
  }
}

class ChildManager extends Actor {

  def receive {
    case sendtoChildManager(value)=>
    val worker1=context.actorOf//
    val worker2=context.actorOf//
    val worker3=context.actorOf//

    val futureToken = ask(worker1,processToken)
    futureToken.onComplete {
      case Sucess(token)=>
        val futurePublickKey = ask(worker2,processPublicKey(token))
        futurePublickKey.onComplete {
          case Sucess(publicKey)=>
            val futureVerified=ask(worker3,processUserDeatils(token,publicKey))
            futureVerified.pipeTo(sender)
          case Failure(e)=> throw e
        }
      case Failure(e)=>throw e
    }
  }
}

 class child extends Actor {
  val childMnager = context.actorOf//
  def receive {
    case sendToChild(value)=>
    childMnager.forward(sendtoChildManager(value))
  }

  class parent extends Actor {
    val child = context.actorOf()//

    def receive {
      case Getvalue(value)=>
      child.forward(sendtoChild(value))
    }
  }
}

object Main {
  def main {
    val system =ActorSystem("myActorsystem")
    val parent=system.actorOf(Props[Parent],"parent")
    val future=ask(parent,GetValue("token-id"))
    future.onComplete {
      case Success(result)=>Complete("user information is",result)
      case Failure(e) Complete("InternelserverError",e)
    }
  }
}

i have read that using onComplete is not recommended to use inside actors and it should be pipeTo to the sender

Warning When using future callbacks, such as onComplete, or mapsuch as thenRun, or thenApply inside actors you need to carefully avoid closing over the containing actor’s reference, i.e. do not call methods or access mutable state on the enclosing actor from within the callback. This would break the actor encapsulation and may introduce synchronization bugs and race conditions because the callback will be scheduled concurrently to the enclosing actor. Unfortunately there is not yet a way to detect these illegal accesses at compile time. See also: Actors and shared mutable state Warning When using future callbacks, such as onComplete, or mapsuch as thenRun, or thenApply inside actors you need to carefully avoid closing over the containing actor’s reference, i.e. do not call methods or access mutable state on the enclosing actor from within the callback. This would break the actor encapsulation and may introduce synchronization bugs and race conditions because the callback will be scheduled concurrently to the enclosing actor. Unfortunately there is not yet a way to detect these illegal accesses at compile time. See also: Actors and shared mutable state https://doc.akka.io/docs/akka/current/actors.html#ask-send-and-receive-future

but i want to do all the processing inside an actor so the parent actor return the final computed value to the calling code how can i achieve that ?
i am using akka http

**Edit**

my problem is how can i use future inside actors as its recommended to not to use .map or onComplete or await,i don't want to send multiple future to the calling code and then calculate the final result i want to send the final calculated result to the calling code

swaheed
  • 3,671
  • 10
  • 42
  • 103

2 Answers2

2

Don´t use the 'sender' property if you work with async apis inside the actor. The actor offers mutual exclusion as long as you write sync code inside the receive function. But when you create the future you create a new async task that can be executed in another thread, meanwhile messages are still pouring into the actor mailbox and the 'sender' property changes.

You have two options:

  1. Use Akka Typed. Akka has improved a lot since Akka Typed became ready for production. I recommend this option. With Akka Typed, you are forced to include the reference in the data type of the message.

  2. Create an anonymous actor for each message that executes the Http request and holds the original sender actor reference.

Emiliano Martinez
  • 4,073
  • 2
  • 9
  • 19
  • my problem is how can i use future inside actors as its recommended to not to use .map or onComplete or await,i don't want to send multiple future to the calling code and then calculate the final result i want to send the final calculated result to the calling code – swaheed Jan 14 '21 at 08:43
  • Take a look to this: https://jaxenter.com/tutorial-asynchronous-programming-with-akka-actors-105682.html, it is old but the classic actors api has not changed. – Emiliano Martinez Jan 14 '21 at 09:01
  • Not sure where this recommendation comes from, but it is OK as long as you don't access external state (like `sender`) inside the `Future`. The best thing is to just send a message and have the processing done synchronously inside the `receive` method, which seems to be what you are already doing. – Tim Jan 14 '21 at 10:07
  • it comes from jamie allen, writter of Effective Akka book. And the problem he describes is the same, although the case is when one actor has to call another actor. It can be extrapolated to the case with the Future – Emiliano Martinez Jan 14 '21 at 11:04
  • can anyone please provide a example of doing it correctly – swaheed Jan 14 '21 at 11:12
0

You can't use sender inside the Future because it is mutable Actor state. The value of sender can change each time the receive method is called and might be different by the time the Future completes.

The solution is to capture the value of sender and used that captured value in the Future:

def receive = {
  case ProcessToken(token)=>
    val replyTo = sender // Capture current value of sender
    val request = httpRequest(token)

    request.onComplete{
      case Success(accessToken)=>
        //generate access token 
        replyTo ! accessToken
      case Failure(e) =>
        throw e
  }
}

Typed actors do not support sender so there would need to be a replyTo field in the ProcessToken message itself. This message is not actor state and can be safely accessed inside a Future.

Tim
  • 26,753
  • 2
  • 16
  • 29