scalamessage-queueactorconcurrent-processing

Scala actors as single-threaded queues


I'd like to use actors in a program where I'll have some kind of restriction around treating some of the actors as if they were queues. For example, suppose I have some external system to which change events are applied and also some cache of the external system's data. So I have 2 actors:

  1. ChangeApplicationActor
  2. CacheActor

As part of the ChangeApplicationActor, when I apply a change to some entity X in the external system, I want to send some event to tell the CacheActor to sync:

val changeApplicationActor = actor { 
  loop {
    react {
      case ChangeInstruction(x) => 
        externalSystem.applyChange(x)
        cacheActor ! Sync(x)
    }
  }
}

But I Now have two requirements:

  1. The CacheActor has internal state and ideally I'd like it to process its Sync instructions sequentially
  2. If I end up with the CacheActor's inbox containing two Sync(x) instructions for the same value of x, then I'd like to ignore the second one (i.e. I should only have one pending Sync instruction for any given value of x)

Is there any way of forcing an actor to be single-threaded? Is there any way I can access the actor's mailbox and remove any duplicate events? Can I not avoid implementing the CacheActor as, um, not an Actor?


Solution

  • An actor is guaranteed to only execute on one thread at a time, and the messages in the actor's mailbox are in FIFO order, so #1 is there.

    2 is trickier as there is no builtin support for it. There's an attribute on the actor called "mailbox." You can access the mailbox directly instead of through receive or react. All you have to do is pull call the matching Sync messages out of the mailbox before you are done processing the message. When doing this, you must synchronize on the actor in order to keep another thread from attempting the add stuff to the mailbox during a message send.

    It should be noted that synchronizing on the actor eliminates the deadlock freedom guarantees made by the library, and will reduce scalability. But from a practical point of view you probably will be ok.