Transactors (Scala)

Module stability: SOLID

Why Transactors?

Actors are excellent for solving problems where you have many independent processes that can work in isolation and only interact with other Actors through message passing. This model fits many problems. But the actor model is unfortunately a terrible model for implementing truly shared state. E.g. when you need to have consensus and a stable view of state across many components. The classic example is the bank account where clients can deposit and withdraw, in which each operation needs to be atomic. For detailed discussion on the topic see this JavaOne presentation.

on the other hand is excellent for problems where you need consensus and a stable view of the state by providing compositional transactional shared state. Some of the really nice traits of STM are that transactions compose, and it raises the abstraction level from lock-based concurrency.

Akka's Transactors, e.g. being able to optionally combine Actors and STM provides perhaps the best of the Actor model (concurrency and asynchronous event-based programming) and STM (compositional transactional shared state) by providing transactional, compositional, asynchronous, event-based message flows.

If you need Durability then you should not use one of the in-memory data structures but one of the persistent ones.

You can define an Typed Actor (Java "actor") and Actor to have transaction required semantics. This means that if it is invoked outside the scope of a transaction then a new transaction will always be started, but if it already is in a transaction then that transaction will be joined.

You can configure an Actor or Typed Actor to be transactional either declaratively or programatically.

Generally, the STM is not needed very often when working with Akka. Some use-cases (that we can think of) are:
  1. When you really need composable message flows across many actors updating their internal local state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it.
  2. When you want to share a datastructure across actors.
  3. When you need to use the persistence modules.

Declarative configuration

You can configure an Actor or Typed Actor to be fully transactional, e.g. every message it receives will be received within a transaction. In Actors it's done with traits and in Typed Actors with annotations.

Transactional Actors

Transaction required semantics for an Actor is defined by mixing in the 'Transactor' trait instead of the 'Actor' trait:

class MyActor extends Transactor {

There is also a lightweight syntax for creating anonymous transactors:

val a = transactor  {
  case msg => ... // handle message inside a transaction

You can also enable transaction required semantics for an Actor by invoking the ‘makeTransactionRequired’ method:

class MyActor extends Actor {

Finally you can also enable transaction required semantics for an Actor by invoking mixing in the 'TransactionRequired’ trait:

class MyActor extends Actor with TransactionRequired {

Creating transactional datastructures within an Actor

If you create a transactional Ref, Vector or Map in the constructor of the Transactor then you have to declare it a 'lazy' to ensure that it is created within the scope of a transaction. Another way would be to create the datastructure inside an 'atomic { ... }' block.

Transactional Typed Actors

Transaction required semantics for an Typed Actor is defined by adding the ‘se.scalablesolutions.akka.annotation.transactionrequired’ annotation to the class:

// Java code
class POJO {

Programmatic configuration

You can configure an Actor or Typed Actor to be transactional only for the messages you want using the programmatic Transaction API. This also gives you more flexibility in how you define the transaction semantics with the cost of more code.

See the STM section for details.

Ant Simulation Sample

One fun and very enlightening (visual) demo of STM, Transactors and Managed References is the Ant Simulation Demo. I encourage you to run it and read through the code since it explains very well both how to use 'Transactors', explicit 'atomic' blocks and managed references ('Refs').
Home Turn Off "Getting Started"