Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing request context implicitly in an actor system

I would like to propagate a request context implicitly in a system of collaborating actors.

To simplify and present the situation, my system has multiple actors and the messages passed to these actors need to include this RequestContext object.

ActorA receives messages of type MessageA ActorB receives messages of type MessageB

when ActorA needs to send a message to ActorB, as part of the handling of MessageA, it performs business logic, and then constructs a MessageB from results of the logic as well as the RequestContext available in MessageA and then sends it to ActorB

def handle(ma:MessageA) {
 val intermediateResult = businessLogic(ma)
 actorB ! MessageB(intermediateResult, ma.requestContext)
}

We have a slew of messages to be handled, and explicitly passing around the requestContext is cumbersome.

I am trying of creative ways to use Scala's implicits feature to avoid explicitly injecting the RequestContext embedded within incoming message into the outgoing message.

The messages are case classes (and they need to be). I have read about implicits rules but bringing an attribute of an object into current implicit scope appears far-fetched.

This, I am sure should be a common requirement. Any suggestions ?

Thanks.

like image 253
vishr Avatar asked Apr 06 '13 15:04

vishr


People also ask

What is actor context?

The actor context - the view of the actor cell from the actor. Exposes contextual information for the actor and the current message. There are several possibilities for creating actors (see Props for details on props ): // Java or Scala context. actorOf(props, "name") context.

How can I send a message to an actor in Akka?

1) Akka Actor tell() Method It works on "fire-forget" approach. You can also use ! (bang) exclamation mark to send message. This is the preferred way of sending messages.

Which method provides initial Behaviour to actor?

Start Hook This method is called when the actor is first created.

What is ActorRef in Akka?

ActorRefFactory, an interface which is implemented by ActorSystem and akka. actor. ActorContext. This means actors can be created top-level in the ActorSystem or as children of an existing actor, but only from within that actor. ActorRefs can be freely shared among actors by message passing.


2 Answers

In my opinion, the easiest way is to make your val implicit in the case class.

case class MessageA(req: RequestA)(implicit val ctx: RequestContext)

case class MessageB(req: RequestB)(implicit val ctx: RequestContext)

def businessLogic(req:RequestA):RequestB


def handle(ma: MessageA): Unit = {
  // import all the members of ma so that there is a legal implicit RequestContext in scope
  import ma._
  val intermediateResult = businessLogic(req)
  actorB ! MessageB(intermediateResult)
}
like image 154
Edmondo1984 Avatar answered Nov 15 '22 06:11

Edmondo1984


In your example your handling of the message in question is factored out into a method already, which makes this straight-forward:

trait RequestContext

case class MessageA(req: RequestA, ctx: RequestContext)
object MessageA {
  def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx)
}

case class MessageB(req: RequestB, ctx: RequestContext)
object MessageB {
  def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx)
}

class Example extends Actor {

  def receive = {
    case MessageA(req, ctx) => handle(req)(ctx)
  }

  def handle(req: RequestA)(implicit ctx: RequestContext): Unit = {
    val intermediateResult = businessLogic(req) // could take implicit ctx as well
    actorB ! MessageB(intermediateResult)
  }
}

But as you can see there is still some overhead when declaring the message types, and the handle method’s signature also needs to be altered. Whether this scheme is worth it depends on the ratio between consumers and producers of these implicit values (i.e. if more than one thing in handle uses the context it makes more sense).

A variation of the above could be:

case class MessageA(req: RequestA, ctx: RequestContext)
object MessageA {
  def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx)
  implicit def toContext(implicit msg: MessageA) = msg.ctx
}

case class MessageB(req: RequestB, ctx: RequestContext)
object MessageB {
  def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx)
  implicit def toContext(implicit msg: MessageB) = msg.ctx
}

...
def handle(implicit ma: MessageA): Unit = {
  val intermediateResult = businessLogic(req)
  actorB ! MessageB(intermediateResult)
}
like image 32
Roland Kuhn Avatar answered Nov 15 '22 05:11

Roland Kuhn