This project demonstrates a working example of cluster singleton i.e. it ensures exactly one actor of a certain type is running somewhere in the cluster.
The cluster configuration uses auto-downing; meaning unreachable nodes will be marked as down by the leader of the cluster.
Actor that ensures only one instance of singleton actor is running
- for all nodes of the cluster (in case of a global singleton) or
- for a group of nodes tagged with a specific role (in case of singleton actor per specific role of a node).
In order to accomplish this, the cluster singleton manager actor runs either on
- all nodes of the cluster or
- a group of nodes tagged with a specific role depending on the requirements of the singleton i.e. global singleton or singleton for a specific role.
On the oldest node of the cluster, cluster singleton manager creates the singleton actor, as a child actor from supplied props. If the oldest nodes becomes unreachable, the cluster failure detector will notice it to turn it down. A new oldest node takes over and a new singleton actor is created on that node.
Cluster singleton proxy will route all messages it receives to current instance of the singleton, by keeping track of the oldest node in the cluster that's up and running.
If the oldest node in the cluster holding the singleton actor becomes unreachable, it'll stash messages to the singleton actor until next oldest node in the cluster takes over.
An actor that persists mutations to its internal state with a journal (instead of saving the internal state directly) so that its internal state can be reconstructed by re-applying stored events, when required.
A persistent actor handles two types of messages namely command and events.
- Commands represent operations from outside world
- Events represent applied operations that are stored in a journal.
- Incoming commands are validated. They're checked if they can be applied to the current internal state of the actor.
- If preconditions are met and validation of a command succeeds, events are generated from the command. Generated events represent the effect of applying the command.
- After the events are successfully persisted, they are used to mutate the actor's internal state.
- Persisted events are replayed to restore the state of an actor, as it's known they can be successfully applied. Typically, it's done when a persistent actor is started or restarted.
This application implements akka cluster singleton for taxi dispatcher use case. The actors of our system are
- Customer: A regular actor that orders a taxi. It uses the proxy for singleton to place an order.
- Taxi Dispatcher: A persistent actor that is contacted solely via its singleton i.e. taxiDispatcherSingletonProxy. The singleton holds state by saving a set of registered taxi drivers and taxi orders from customers. When a registered taxi driver polls and checks for a ride request, a taxi ride order from customer is allocated to the taxi driver.
- Taxi Driver: A regular actor that first registers with the taxi dispatcher and then periodically checks for ride requests from customers.
Actors | Singleton | Persistent Actor | Request messages |
---|---|---|---|
Customer | No | No | OrderRide |
TaxiDispatcher | Yes | Yes | RequestTaxiCommand, RegisterDriverCommand, CheckRideRequestCommand |
TaxiDriver | No | No | Drive, WorkUnavailable, UnAuthorised |
Sender | Command | Event | Response | Receiver |
---|---|---|---|---|
TaxiDriver | RequestTaxiCommand | DriverRegisteredEvent | ||
TaxiDriver | CheckRideRequestCommand | UnAuthorised | TaxiDriver | |
TaxiDriver | CheckRideRequestCommand | WorkUnavailable | TaxiDriver | |
TaxiDriver | CheckRideRequestCommand | RideAllocatedEvent | Drive | TaxiDriver |
Customer | RequestTaxiCommand | TaxiRequestReceivedEvent |