fresskoma
fresskoma

Reputation: 25791

Changing Akka actor state by passing a method with arguments to "become"

I am having some trouble using become in my Akka actor. Basically, my actor has a structure like so:

// This is where I store information received by the actor
// In my real application it has more fields, though.
case class Information(list:List[AnyRef]) {
    def received(x:AnyRef) = {
        Information(list :+ x)
    }
}

class MyActor extends Actor {

    // Initial receive block that simply waits for a "start" signal
    def receive = {
        case Start => {
            become(waiting(Information(List())))
        }
    } 

    // The main waiting state. In my real application, I have multiple of
    // these which all have a parameter of type "Information"
    def waiting(info:Information):Receive = {

        // If a certain amount of messages was received, I decide what action
        // to take next.
        if(someCondition) {
            decideNextState(x)
        }

        return {
            case Bar(x) => {
                //
                // !!! Problem occurs here !!!
                //
                // This is where the problem occurs, apparently. After a decision has been
                // made, (i.e. decideNextState was invoked), the info list should've been
                // cleared. But when I check the size of the info list here, after a decision
                // has been made, it appears to still contain all the messages received
                // earlier.
                //
                become(waiting(info received x))
            }
        }
    }

    def decideNextState(info:Information) {
        // Some logic, then the received information list is cleared and 
        // we enter a new state.
        become(waiting((Information(List())))
    }
}

Sorry for the long code snippet, but I couldn't really make it any smaller.

The part where the problem occurs is marked in the comments. I am passing a parameter to the method that returns the Receive partial function which is then passed to the become method. However, the created partial function seems to somehow preserve state from an earlier invocation. I find the problem a bit difficult to explain, but I did my best to do so in the comments in the code, so please read those and I'll answer anything that is unclear.

Upvotes: 3

Views: 2358

Answers (1)

Marius Danila
Marius Danila

Reputation: 10431

Your logic is a little convoluted but I'll take a shot at what could be the problem:

If someCondition is true then your actor steps into a state, let's call it S1 characterized by a value Information(List()). And then you return (by the way, avoid using return unless it is absolutely necessary) a receive method which will put your actor into a state S2 characterized by a list Information(somePreviousList :+ x). So at this point your stack of states has S1 on top. But when you receive a Bar(x) message the state S2 will be pushed, thus covering S1 and you actually transition into a state characterized by an Information with the old values + your new x.

Or something like that, the recursion in your actor is a bit mesmerizing.

But I'll suggest rewriting that code since it seems that the state which changes is something of type Information and you are manipulating this state using Akka's actor state transitions which is not at all the best tool to do that. become and unbecome are meant to be used to transition from different states of the actor's behavior. That is, an actor can have a different behavior at any time and you use become and unbecome to change between these behaviors.

Why not do something like this ?

class MyActor extends Actor {

    private var info = Information(List.empty)

    def receive = {
        case Start => info = Information(List()) //a bit redundant, but it's just to match 1:1 with your code
        case Bar(x) => {
            if (someCondition) {
                info = Information(List.empty)
            }
            info = info received x
        }
    }

}

I might not have captured your entire idea, but you get the picture.

Upvotes: 3

Related Questions