Recently I started playing with the Celluloid framework (http://celluloid.io). Right now it is the most popular implementation of the Actor model in Ruby and it is heavily inspired by Erlang. There are already some projects (like sidekiq for instance) built upon this technology, although it is still under development.
After doing some experiments with Celluloid, I realized that I could not wrap my head around the different options how to use Actors once they are defined. There are four built-in classes that help managing Actor instances:
I read the documentation and I was quite confused about when I was supposed to choose one over another. So I started writing some code to find out how the classes really behave.
I created a little Actor class called “Cat”. It implements the a method. A nice implementation detail that would also help in real world: Each time a cat sprays it will loose one of its lives. When a cat sprays too often, it will just die*.
The simpliest example to use an Actor is just to call Actor.new, like using a plain Ruby object. This works totally fine, until the Actor thread dies, because then it won’t come back by itself. Instead it is up to you to catch such a case and deal with it.
When you don’t like to loose your Actor instance and you want to have it respawn automatically you can just use the Actor.supervise method. This will spawn a second thread that respawns the Actor thread every time it dies.
Sometimes you may not want just one Actor instance but whole group. This can be useful when you want to gain performance by parallelization. You can do this easily by using the Actor.pool method to spawn a PoolManager instance. The PoolManager will then spawn multiple Actor threads of the same Actor class. Similar to using the Supervisor the PoolManager will also respawn Actor threads if they fail. For maximum performance the default pool size matches the size of CPU cores your system provides.
Finally there is the SupervisionGroup. In contrast to the previous examples the SupervisionGroup is designed to be used as a base class. It offers a declarative approach to combine a set of actors to a group together and make them depend on each other. So if one member of the group dies, the whole group will die. Of course, you can also add whole pool of Actors to the group. Hereby the group would only become dependent on the PoolManager itself. So when the PoolManager dies, the group dies. But when a member of the pool dies, it is respawned by the PoolManager. The following examples illustrates this.
To wrap things up, just ask yourself these questions:
- Who is responsible for starting and stopping Actors?
- How to deal with a dead Actor threads (e.g. when unhandled exception occur)?
- Will there be more than one instance of an Actor at a time?
* BTW: I love cats. Those lawless little bastards :)
- uberfork posted this