Akka background process and progress

1,207 views
Skip to first unread message

TonyB

unread,
May 6, 2014, 2:12:07 PM5/6/14
to play-fr...@googlegroups.com
I could use some help understanding Akka.  I've been doing some research for a few days now, googling, and reading documentation, but I'm still a bit confused with Akka and how I can accomplish a feature.  I've been using Play for some time, but only now getting into Akka.  Could someone help explain if what I want to do is possible with Akka and Play?

Here is what I want to accomplish:
- HTTP request comes into Play controller, have this Play controller launch an Akka actor supervisor for a long running background [sub]process.  
- The previous HTTP request returns a response with some kind of Akka actor ID (does an actor ID even exist?  Create and manage this myself?)

- Akka Supervisor will launch two other child actors to run in parallel
   - Child 1 - runs the long running process, will report back to parent when done
   - Child 2 - runs another background process, will report back to parent when done

- As the supervisor actor is running I'd like consecutive HTTP requests to Play controller to lookup the actor (by that ID?) and ask it for it's status/progress.  And have this status report back in the HTTP response to the frontend user.

- When both Child 1 and 2 are done they will tell the Supervisor but how do I report the results from the supervisor?  There is no sender since the original HTTP request is gone.  Do I have to persist something in my DB and have my next HTTP request check the DB?  Or can my supervisor sit and wait for another message to ask for final status?


The real problem I'm having understanding is the lifecycle of an actor instance; how they are created, managed, and used within the actor system.  Do I need a custom actor system to lookup a specific actor instance by ID?  Is there even such a thing as an actor ID?  If an Actor is running and I ask it something, does that message sit and wait in its mailbox until the current message process is complete, effectively not allowing you to check the progress of a long running process? 

And on top of it all, it would be nice to be able to run multiple of these scenarios at once for different frontend users, meaning have multiple supervisors all with different IDs each tied to a particular frontend user, each user watching the progress of their own background process.

Maybe I should post this to Akka group but thought since I'm using Play I'd start here.

Thanks for any help,
Tony

TonyB

unread,
May 6, 2014, 2:13:01 PM5/6/14
to play-fr...@googlegroups.com
Sorry, forgot to put int he subject that this was for Play 2.2.2 - Java.

Will Sargent

unread,
May 6, 2014, 2:40:17 PM5/6/14
to play-fr...@googlegroups.com
I've got a sample application you can look at that shows how to work Akka into a Play application:

https://github.com/wsargent/play-akka-command-app/blob/master/app/controllers/Application.scala

It's in Scala, but you get the general idea.  There's a little bit of mapping you have to do, because Play uses Future, so you generally have to use ask and pipeTo to get Futures and Actors working together.

http://doc.akka.io/api/akka/2.3.2/index.html#akka.pattern.package

If you want to have lots of users communicating with each other, you should check out actor-room:

http://mandubian.com/2013/09/22/play-actor-room/

This will be easier in Play 2.3 (when it comes out), where you have a Websocket / Actor integration (although you do lose the backpressure feature you get with Iteratees):

http://www.playframework.com/documentation/2.3-M1/ScalaWebSockets

Will Sargent
Consultant, Professional Services
Typesafe, the company behind Play Framework, Akka and Scala


On Tue, May 6, 2014 at 11:13 AM, TonyB <aab...@gmail.com> wrote:
Sorry, forgot to put int he subject that this was for Play 2.2.2 - Java.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

TonyB

unread,
May 8, 2014, 5:34:08 PM5/8/14
to play-fr...@googlegroups.com
Will, Thanks for your feedback.  With continued research I think I've figured out what I was trying to accomplish.  I've used uni-directional Akka actors from Play 2.2.2 to start some background tasks that just blindly run.  This is the first time I had a need to do something more complex and while the docs are very abundant I couldn't make sense of a lot of it since I don't really know Scala too well.

I've done some searching around and it seems there are a few posts on this topic.  So here is my solution to the problem.  Please review and let me know if it looks good or if I'm doing something completely crazy wrong.  It's the result of reading 50 different forums, groups, mailing list, and issue tracker posts...and of course official documentation.

From Play 2.2.2 for Java Controller
public static Result inviteImport( String id ) {

// akka actor
ActorRef ref = null;
   
// selection path
String actorId = "InviteSupervisor-" + id;
String path = "/user/" + actorId;
ActorSelection sel = Akka.system().actorSelection(path);
// ask to identify
Timeout t = new Timeout(3000);
AskableActorSelection asker = new AskableActorSelection(sel);
Future<Object> fut = asker.ask(new Identify(id), t);
ActorIdentity ident;
try {
// wait results
ident = (ActorIdentity)Await.result(fut, t.duration());
ref = ident.getRef();
} catch (Exception e) {
Logger.error(TAG + e.getMessage(), e);
// TODO handle timeout or other errors
return badRequest("Selection Error");
}

// actor exist or not
if ( ref == null ) {
// start new actor
// kick off process queue
ref = Akka.system().actorOf(InviteSupervisor.props(id), actorId);

// start invite import process
ref.tell(new InviteSupervisor.ImportMessage(id), ActorRef.noSender());

return ok( "Actor not found :(  but one was created :) " + ref.path().toString() );
} else {
// already running, get progress
String status = "";
// get progress
Future<Object> futProg = asker.ask(new InviteSupervisor.ProgressMessage(null), t);
try {
// wait results
InviteSupervisor.ProgressMessage msg = (InviteSupervisor.ProgressMessage)Await.result(futProg, t.duration());
status = msg.getProgress().toString();
} catch (Exception e) {
Logger.error(TAG + e.getMessage(), e);
// TODO handle timeout or other errors
}
return ok( "Actor Found! " + ref.path().toString() + " [" + status + "]");
}
}


This looks for an actor instance by id on the selection path.  If not found, it creates the actor.  If found it ask the actor for its progress.  Hitting this same URL multiple times gives the results I was looking for.  

The Actor side doesn't do anything special since I'm putting the id in the path.  It never receives the Identify/ActorIndentity message, I guess its handled by the actor system or something internal?

It might be better to wrap some of this in a Play Promise and return that.  Get ride of the asker future timeout as well.  But I'm wondering if I'm interacting with Akka selection paths correctly here.  How's it look?

-Tony

PS - cross posted this on the akka user group too.

Ryan Tanner

unread,
May 8, 2014, 11:46:07 PM5/8/14
to play-fr...@googlegroups.com
Generally, if I find myself reaching for ActorSelections, I start to wonder if there's a better way to do it.

You might be better served by having a "master" actor which creates your per-user child actors.  Have the controller create the master actor on start (or put it in a plugin—I sometimes find that having those actors running can mess with tests).

Your master actor would just keep a Map of child actors by ID.  In your controller, you would ask the master actor for the status of that ID.  It would create a new actor or get the current status from an existing child actor.

Chanan Braunstein

unread,
May 9, 2014, 7:20:15 AM5/9/14
to play-fr...@googlegroups.com
Building on what Ryan said, we do the same, but you can get rid of the map. If your id which must be unique anyway for it to have been in map, is the name of your actor, then you can use getChild(id) to get the actorref from the parent (master) actor.

TonyB

unread,
May 9, 2014, 11:56:17 AM5/9/14
to play-fr...@googlegroups.com
Thanks for the feedback, this helps a lot.  It's funny because my supervisor is already making child actors and managing those for its task but it didn't occur to me to create another higher parent actor that would manage all of the current top level id actors.  That would certainly clean up the controller.  I got caught up with all the reading about how actorFor is deprecated and actorSelection is the new preferred way of creating actors so I ran with it, ha!

Thank you.
Reply all
Reply to author
Forward
0 new messages