acteur/lib.rs
1//! # Acteur Actor System
2//!
3//! An safe & opinionated actor-like framework written in Rust that just works. Simple, robust, fast, documented.
4//!
5//!<div align="center">
6//! <!-- Crates version -->
7//! <a href="https://crates.io/crates/acteur">
8//! <img src="https://img.shields.io/crates/v/acteur.svg?style=flat-square"
9//! alt="Crates.io version" />
10//! </a>
11//! <!-- docs.rs docs -->
12//! <a href="https://docs.rs/acteur">
13//! <img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
14//! alt="docs.rs docs" />
15//! </a>
16//! </div>
17//!
18//! ## Status update
19//!
20//! #### Update 1:
21//! So, I took some time to think about this framework and have intention to move it into business
22//! logic + distributed framework. The idea is to make a framework that allows you to write identified
23//! aggregates/models/actors without much burden.
24//!
25//! #### Update 2:
26//! I'm playing with raft and sled in order to implement the cluster part. You can it in the file
27//! playing_with_raft.rs
28//!
29//! ## Motivation
30//!
31//! Actors are cool. Many people write about them and Actix rules the benchmarks. But to write a backend system
32//! spawning several servers using actors is not easy. Actually, it bring many other complexities. But actors are
33//! not a bad abstraction, but they are a solution for concurrency, not for business logic organization. They
34//! tangentially solve some problems and that is nice, but introduce others. So, this framework seeks to implement
35//! a framework which implement something very similar to Actors but with many adaptations and niceties in order
36//! to write business logic.
37//!
38//! Said that, Acteur is provably **not** the tool you want if:
39//!
40//! - You want to have a full ACID compliant system
41//! - You want to fully follow the Actor model
42//! - You need to scale to A LOT of traffic. In which case you will need more than one server. (I'm planning to
43//! implement some multi-server clustering, but for now, only one server).
44//!
45//! But it may help you if you want:
46//!
47//! - To have a database but not incur in the cost of READ, APPLY, SAVE, and instead you want to keep object
48//! instances in RAM.
49//! - You don't want to deal with optimistic concurrency and you want the messages to process one by one for each
50//! ID, but concurrently between IDs.
51//! - You want to make an backend for an online videogame with many entities interacting at the same time but don't
52//! want to go all the way with ECS.
53//!
54//! ## Main features of Acteur
55//!
56//! This actor system is a bit different than other frameworks. It work under the following premises:
57//! - **High-level**: The framework is oriented to map business logic rather than task concurrency.
58//! - **Simple**: The API should be small, simple and intuitive. No surprises.
59//! - **Concurrent**: The system should be fast and use all available CPU cores.
60//! - **Documented**: Everything must be documented with exhaustive examples.
61//!
62//! ### Regarding the implementation:
63//!
64//! - Acteur is **asynchronous** and uses `async_std` under the hood.
65//! - Actors have an *ID* which its type is defined by the developer.
66//! - Messages are routed to an *Actor* and an *ID* .
67//! - Actor life-cycle is *automatically* managed by the framework.
68//! - Messages for the same Actor & ID are *sequential*. Everything else is executed **concurrently**.
69//! - Services are provided for other concurrency forms.
70//! - Services **don't** have ID and are concurrent.
71//! - Services can **subscribe** to messages and everyone can **publish** messages.
72//! - Acteur is **global**, only one instance can exist.
73//!
74//! ### State of the implementation
75//!
76//! My main focus of work now is in adding concurrency and improving ergonomics. Features already implemented:
77//!
78//! - ☑️ Actor / Service is activated on first message
79//! - ☑️ Actor can send messages to other actors / services
80//! - ☑️ System can send messages to any actor / service
81//! - ☑️ Actors / Services can optionally, respond to messages
82//! - ☑️ Services: statefull or stateless, without ID (like real actors) and concurrent.
83//! - ☑️ Automatic deallocation of unused actors (after 5 minutes without messages)
84//! - ☑️ Services can subscribe to messages
85//! - □ Actor deallocation configuration (based in RAM, Actor count or timeout)
86//! - □ Clustering: Implement Raft in order to assign each actor to a different server
87//!
88//! ## Acteur structure
89//!
90//! In order to use Acteur you just need to implement the correct trait and Acteur will
91//! automatically use your implementation when a message is routed to your Actor/Service.
92//!
93//! The main traits are:
94//!
95//! - [Actor](./trait.Actor.html): Represents an actor
96//! - [Service](./trait.Service.html): Represents a service
97//!
98//! Just implement them and your Actor/Service is ready to use.
99//!
100//! For Actors you have two traits in order to handle messages:
101//!
102//! - [Receive](./trait.Receive.html): Receives a message without responding to it. The most
103//! efficient way to handle messages.
104//! - [Respond](./trait.Respond.html): Receives a message and allows to respond to it. Forces
105//! to sender to await until the actor respond.
106//!
107//! For Services you have other two traits.
108//!
109//! - [Listen](./trait.Listen.html): Receives a message without responding to it. The most efficient way
110//! to handle messages.
111//! - [Serve](./trait.Serve.html): Receives a message and allows to respond to it. Forces to sender to
112//! await until the actor respond.
113//!
114//! ### Why are you using 4 different trait instead of 1 or 2?
115//!
116//! I tried to merge Traits but I didn't find how to do it because:
117//!
118//! A) The handle method contains the ActorAssistant and ServiceAssistant types in the signatures,
119//! witch have different types.
120//! B) I don't like to create a response channel for EVERY message when many messages don't need a response.
121//!
122//! Both blocks make 4 combinations. Receive/Respond for Actors and Listen/Serve for Services.
123//!
124//! I'm still trying to improve the naming and ergonomics. I think the concept will remain, but the ergonomics may change a bit.
125//!
126//! ## Actors vs Services
127//!
128//! Acteur provides 2 ways of concurrency. Actors and Services.
129//!
130//! ### Actors
131//!
132//! Actors have an ID and will consume messages directed to the same Actor's ID sequentially.
133//! That means that if you send 2 messages to the Actor User-32, they will be handled sequentially.
134//! On the other side, if you send a message to the Actor User-32 and other to the User-52 the
135//! messages will be handled concurrently.
136//!
137//! That means, Actors instances keep messages order for the same ID, but not between different IDs.
138//!
139//! ### Services
140//!
141//! Services, on the other side, have no ID and they are concurrent. That means that you choose
142//! how many instances of the Service there will be (Acteur provides a default). Services can
143//! or can't have an State, but if they have, they require to be Sync (aka Mutex<state>).
144//!
145//! In short. Services are more like normal Actors (or, you can think as normal web services)
146//! but with some preset concurrency factor. You can have many instances and there is
147//! no synchronization of any type when consuming messages. Think of them as the primitive you
148//! use when you want to create something that doesn't fit the Actors model in this framework.
149//!
150//! ### Use cases
151//!
152//! Choose Actor for Entities (Users, Invoices, Players, anything which their instances are identified).
153//!
154//! Choose Services for Business Logic, Infrastructure, Adapters, etc (Storage, DB access, HTTP services,
155//! calculations of some sort that doesn't belong to any Actor, etc) and for subscribing to messages (Pub/Sub)
156//!
157//! ## Subscription or Pub/Sub
158//!
159//! Sometime we don't want to know who should receive the message but to subscribe to a type and wait.
160//! Acteur models the Pub/Sub patter with Services. Actors in Acteur can't perform subscriptions as
161//! that would require the framework to know all possible IDs of all possible Actor instances in
162//! order to direct the message to the correct one (or all) and it doesn't play well with the deallocation
163//! of unused actors.
164//!
165//! If you want to send messages to some Actors from a Subscription, you can create a Service that
166//! subscribes to a message and then figures out to what Actor IDs to send the message. For example,
167//! doing a query in the DB/Service in order to get the set of IDs that need to receive some message.
168//!
169//! Unlike sending/calling to services/actors, publishing doesn't know who needs to receive the
170//! message in compilation time. That is the reason behind requiring the Services to subscribe in
171//! runtime to any message they want to receive. In order to ensure that services perform the
172//! subscriptions, it is a good idea to run `acteur.preload_service<Service>();` for each service
173//! that should perform any subscription at the beginning of your Application start.
174//!
175//! ## Simple Example
176//!
177//! ```rust,no_run
178//! use acteur::{Actor, Receive, ActorAssistant, Acteur};
179//! use async_trait::async_trait;
180//!
181//! #[derive(Debug)]
182//! struct Employee {
183//! salary: u32
184//! }
185//!
186//! #[async_trait]
187//! impl Actor for Employee {
188//! type Id = u32;
189//!
190//! async fn activate(_: Self::Id, _: &ActorAssistant<Self>) -> Self {
191//! Employee {
192//! salary: 0 // Load from DB or set a default,
193//! }
194//! }
195//! }
196//!
197//! #[derive(Debug)]
198//! struct SalaryChanged(u32);
199//!
200//! #[async_trait]
201//! impl Receive<SalaryChanged> for Employee {
202//! async fn handle(&mut self, message: SalaryChanged, _: &ActorAssistant<Employee>) {
203//! self.salary = message.0;
204//! }
205//! }
206//!
207//! fn main() {
208//! let sys = Acteur::new();
209//!
210//! sys.send_to_actor_sync::<Employee, SalaryChanged>(42, SalaryChanged(55000));
211//!
212//! sys.wait_until_stopped();
213//! }
214//!
215//! ```
216//!
217//! ## Why another Actors framework?
218//!
219//! Somethings bothered me.
220//!
221//! 1. Actor systems are a concurrency level but I see example of them being used for business logic. Using
222//! a normal HTTP framework + SQL feels more natural than using Actix.
223//! 2. In order to use Actix you need to learn how it works. You need to manage the concurrency,
224//! the addresses, etc
225//! 3. Unsafe. I don't want unsafe. I wouldn't trust myself to do something like this in C++,
226//! therefore, I don't want to have unsafe code. Rust opens the door to do these kind of projects
227//! to people with less than 10 years of experience in C/C++ in a safer way.
228//!
229//! After async_std 1.0 announcement and speaking with some friends I started to envision how I would
230//! like an actor framework be. Not that Actix and others are wrong, but they are too low level in my
231//! opinion and not for business logic. I wanted something that just runs without leaking so many underlying
232//! concepts. At the same time I don't think that competing for the last nanosecond is healthy. Even less
233//! if the framework is already super fast.
234//!
235//! ## Common patterns
236//!
237//! This section will be updated with common patters you can use in your applications. If
238//! you have one you want to add or just a question of how to so something, let me know with a GitHub Issue.
239//!
240//! ### Web server
241//!
242//! Given that all actors are managed by the framework, it is really easy to have, for
243//! example, Rocket or Tide getting new HTTP calls and just calling `acteur.call_service` or
244//! `acteur.call_actor` and wait for the response. You can use the sync version of the call
245//! if you are working with synchronous code. Keep in mind that you can clone Acteur and send
246//! it to as many threads/struct you need.
247//!
248//! ```rust,no_run
249//!
250//! use acteur::Acteur;
251//!
252//! let acteur = Acteur::new();
253//!
254//! // You can clone and send it to another thread/struct
255//! let acteur2 = acteur.clone();
256//!
257//! ```
258//!
259//! If you need actors to query databases it would, generally, be a good idea to keep the database
260//! connection / pool in a service, where you can handle connection errors, reconnect in case of error
261//! and where you can control the concurrency.
262//!
263//! ## Error handling
264//!
265//! If you have operation that can error it is better if you encode them in services and reserve
266//! Actors to operations that cannot fail. For example, database connections, network connections, etc.
267//!
268//! It is perfectly ok to encode a failure, from the point of view of the business rules, in an actor, for
269//! example, in a videogame, where a character cannot attack another character because the second is
270//! invulnerable.
271//!
272//! So, keep anything that can fail because external circumstances (network, hard drive, etc) in services
273//! and let actors to request the services for whatever they need.
274//!
275//! If you have an error that should stop the application startup like database connections, add them to
276//! a service construction and use the method `preload_service` for trying to start the service on the
277//! app startup and let the app crash is something goes wrong.
278//!
279//! ## Safe Rust
280//!
281//! No unsafe code was directly used in this crate. You can check in lib.rs the `#![deny(unsafe_code)]` line.
282//!
283//! ## Contributing
284//!
285//! First of all, I would be really happy if you decide to check the framework and contribute to it! Just open
286//! an issue / pull request and we can check what you would like to implement. Check more about contributing in
287//! here: [https://github.com/DavidBM/acteur-rs/blob/master/CONTRIBUTING.md]()
288//!
289//! ## License
290//!
291//! <sup>
292//! Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
293//! 2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
294//! </sup>
295//!
296//! <br/>
297//!
298//! <sub>
299//! Unless you explicitly state otherwise, any contribution intentionally submitted
300//! for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
301//! be dual licensed as above, without any additional terms or conditions.
302//! </sub>
303//!
304
305#![deny(unsafe_code)]
306
307#[macro_use]
308mod utils;
309mod actors;
310mod facade;
311mod services;
312mod system_director;
313
314pub use facade::Acteur;
315
316pub use actors::actor::Actor;
317pub use actors::assistant::ActorAssistant;
318pub use actors::handle::{Receive, Respond};
319
320pub use services::handle::{Listen, Serve};
321pub use services::service::{Service, ServiceConcurrency, ServiceConfiguration};
322pub use services::system_facade::ServiceAssistant;