7

I am trying to find out what message delivery guarantees Akka supports. I came to the following conclusion:

At-most-once : Supported by default

At-least-once : Supported with Akka Persistence

Exactly-once : ?

Does Akka support exactly-once? How would I be able to achieve this if it doesn't?

stefana
  • 2,606
  • 3
  • 29
  • 47
  • Application specific IDs can be used for messages transferred to detect and discard duplicates, please check, https://groups.google.com/forum/#!topic/akka-user/Wy1E5aGJPqA – Karthikeyan Mar 16 '15 at 11:22
  • 2
    Another link to intuitively understand why exactly once is not possible in Fault tolerant systems, http://brooker.co.za/blog/2014/11/15/exactly-once.html – Karthikeyan Mar 16 '15 at 11:23
  • You could also look into the Reliable Proxy Pattern. May not be exactly what you are looking for, but in the same neighborhood. http://doc.akka.io/docs/akka/2.3.9/contrib/reliable-proxy.html – cmbaxter Mar 16 '15 at 12:53
  • Thank you for the links. If I understand correctly, the short answer is that Akka doesn't support exactly-once? – stefana Mar 16 '15 at 13:06
  • 4
    The short answer is that exactly-once doesn't exist in the real world. – Ryan Mar 16 '15 at 14:55

1 Answers1

11

Akka out of the box provides At-Most-Once delivery, as you've discovered. At-Least-Once is available in some libraries such as Akka Persistence, and you can create it yourself fairly easily by creating an ACK-RETRY protocol in your actors. The Sender keeps periodically sending the message until the receiver acknowledges receipt of it.

Put simply, for At-Least-Once the responsibility is with the Sender. E.g in Scala:

class Sender(receiver: ActorRef) extends Actor {

  var acknowledged = false

  override def preStart() {
    receiver ! "Do Work"
    system.scheduler.scheduleOnce(50 milliseconds, self, "Retry")
  }

  def receive = {
    case "Retry" => 
      if(!acknowledged) {
        receiver ! "Do Work"
        system.scheduler.scheduleOnce(50 milliseconds, self, "Retry")
      }

    case "Ack" => acknowledged = true
  }
}

class Receiver extends Actor {

  def receive = {
    case "Do Work" => 
      doWork()
      sender ! "Ack"
  }

  def doWork() = {...}
}

But with At-Most-Once processing, the receiver has to ensure that repeated instances of the same message only result in work being done once. This could be achieved through making the work done by the receiver idempotent so it can be repeatedly applied, or by having the receiver keep a record of what it has processed already. For At-Most-Once the responsibility is with the receiver:

class AtMostOnceReceiver extends Actor {

  var workDone = false

  def receive = {

    case "Do Work" =>
      if(!workDone) {
        doWork()
        workDone = true
      }
      sender ! Ack
  }

}
mattinbits
  • 10,370
  • 1
  • 26
  • 35