acteur 0.10.1

A safe actor system that just works. Simple, robust, fast, documented.
Documentation

Main Features

Acteur uses async_std under the hood. You can find the all the information in the documentation.

This actor system is a bit different than other frameworks. It work under the following premises:

  • High-level: The framework is oriented to map business logic rather than concurrent tasks.
  • Simple: The API should be small, simple and intuitive. No surprises.
  • Fast: The system should be fast and use all available CPU cores.
  • Documented: Everything must be documented with exhaustive examples.

Regarding the implementation:

  • Acteur is asynchronous and uses async_std under the hood. (Even for mutexes)
  • Actors have an ID which type is defined by the developer.
  • Messages are routed to an Actor and an *ID.
  • Actor life-cycle is automatically managed by the framework.
  • Messages for the same Actor & ID are sequential. Everything else is executed concurrently.
  • Services are provided for other concurrency forms.
  • Services don't have ID and are concurrent.
  • Services can subscribe to messages and everyone can publish messages.
  • Acteur is global, only one instance can exist.

Note about calling Acteur and Actor system

Acteur takes inspiration in Actors but it takes a different path. Actor based concurrency model is a concurrency modeling tool, not a business logic one, therefore, you cannot expect to solve logic organization problrms with Actors. Still, Actors is a very nice abstraction that can, tangencially, solve certain nuances of logic organization. Even more when the code scales.

In the way Acteur is implemented it searches to help you splitting your instances and keep request ordered and concurrent as much as possible betwen instances.

Said that, Acteur is provably not the tool you want if:

  • You want to have a full ACID compilant system
  • You want to fully follow the Actor model
  • You need to scale to A LOT of traffic. In wich case you will need more than one server. (I'm planning to implement some multiserver clusteing, but for now, only one server).

But it may help you if you want:

  • To have a database but not incur in the cost of READ, APPLY, SAVE, and instad you want to keep object instances in RAM.
  • You don't want to deal with optimistic concurrency and you want the messages to process one by one for each ID, but concurrently between IDs.

State of the implementation

The overall feature set is complete. Acteur will continue improving and adding improvements. As for now, this framework is actively supported/developed.

The current focus is on ergonomics and use cases. Hopefully we can have an stable API soon.

  • ☑️ Actor / services is activated on first message
  • ☑️ Actor can send messages to other actors / services
  • ☑️ System can send messages to any actor / service
  • ☑️ Actors / Services can optimally, respond to messages
  • ☑️ Services (statefull or stateless, like actors, without ID and concurrent)
  • ☑️ Automatic deallocation of unused actors (after 5 minutes without messages)
  • ☑️ Services can subscribe to messages
  • ☑️ Different concurrency modes for services (fixed, core cound, unlimited, etc)

Examples

Simple example

use acteur::{Actor, Receive, Assistant, Acteur};
use async_trait::async_trait;

#[derive(Debug)]
struct Employee {
    salary: u32
}

#[async_trait]
impl Actor for Employee {
    type Id = u32;
    async fn activate(_: Self::Id, _: &Assistant<Self>) -> Self {
        Employee {
            salary: 0 // Load from DB or set a default,
        }
    }
}

#[derive(Debug)]
struct SalaryChanged(u32);

#[async_trait]
impl Receive<SalaryChanged> for Employee {
    async fn handle(&mut self, message: SalaryChanged, _: &Assistant<Employee>) {
        self.salary = message.0;
    }
}

fn main() {
  let sys = Acteur::new();

  sys.send_to_actor_sync::<Employee, _>(42, SalaryChanged(55000));

  sys.wait_until_stopped();
}

You can find more examples in the examples folder.

Safe Rust

No unsafe code was directly used in this crate. You can check in lib.rs the #![deny(unsafe_code)] line.

License