Another challenge is that the Baker needs to accept RawCookies from the Manager as required, but needs to bake them in batches because of the oven's limited capacity. Basically, it needs to manage a queue of the RawCookies.
We'll implement this by using a stash. By using stashing, our actor will buffer messages that cannot be handled by the current behavior and replay them before switching to the alternative behavior in which buffered messages are supposed to be handled.
Let's see how this approach is reflected in the baking behavior of the actor:
def baking(oven: ActorRef[Oven.Command],
manager: ActorRef[Manager.Command]): Behavior[Command] =
Behaviors.setup[Command] { context =>
val buffer = StashBuffer[Command](capacity = 100)
Behaviors.receiveMessage {
case CheckOven =>
oven ! Extract(context.self)
Behaviors.same
case c: TooManyCookies=>
buffer.stash(BakeCookies(c.raw, manager))
Behaviors.same
case c : BakeCookies =>
buffer.stash(c)
Behaviors.same
case CookiesReady(cookies) =>
manager ! ReceiveReadyCookies(cookies)
buffer.unstashAll(context, idle(oven))
}
}
First, we define a buffer that will contain our stashed messages.
Then, we handle four types of messages. CheckOven is a reminder that is sent to the Baker by the timer so that it does not forget to extract cookies from the Oven.
In the case of TooManyCookies (which is a message from the Oven returning cookies that did not fit into it) or BakeCookies being received from the manager, the Baker stashes them until it becomes idle again and is able to process baking work.
CookiesReady indicates that the Oven is now empty, so we forward the cookies to the Manager, unstash all of the messages, and go to the idle state.