/kameo

Fault-tolerant Async Actors Built on Tokio

Primary LanguageRustApache License 2.0Apache-2.0

Kameo 🧚🏻

Crates.io Version Crates.io Total Downloads Crates.io License GitHub Repo stars

Fault-tolerant Async Actors Built on Tokio

  • Async: Built on tokio, actors run asyncronously in their own isolated spawned tasks.
  • Supervision: Link actors, creating dependencies through child/parent/sibbling relationships.
  • MPSC Bounded/Unbounded Channels: Uses mpsc channels for messaging between actors with boundedness configurable.
  • Concurrent Queries: Support concurrent processing of queries when mutable state isn't necessary.
  • Panic Safe: Catches panics internally, allowing actors to be restarted.

Installing

Install using cargo add:

cargo add kameo

or adding to your dependencies manually:

[dependencies]
kameo = "*"

Defining an Actor without Macros

use kameo::Actor;
use kameo::message::{Context, Message};

// Define the actor state
struct Counter {
    count: i64,
}

impl Actor for Counter {}

// Define messages
struct Inc { amount: u32 }

impl Message<Inc> for Counter {
    type Reply = i64;

    async fn handle(&mut self, msg: Counter, _ctx: Context<'_, Self, Self::Reply>) -> Self::Reply {
        self.count += msg.0 as i64;
        self.count
    }
}

Defining an Actor with Macros

use kameo::{messages, Actor};

// Define the actor state
#[derive(Actor)]
struct Counter {
    count: i64,
}

// Define messages
#[messages]
impl Counter {
    #[message]
    fn inc(&mut self, amount: u32) -> i64 {
        self.count += amount as i64;
        self.count
    }
}
See generated macro code
// Derive Actor
impl kameo::actor::Actor for Counter {
    type Mailbox = kameo::actor::UnboundedMailbox<Self>;

    fn name(&self) -> Cow<'_, str> {
        Cow::Borrowed("Counter")
    }
}

// Messages
struct Inc { amount: u32 }

impl kameo::message::Message<Inc> for Counter {
    type Reply = i64;

    async fn handle(&mut self, msg: &mut Inc, _ctx: Context<'_, Self, Self::Reply>) -> Self::Reply {
        self.inc(msg.amount)
    }
}
Actor#[messages]

Spawning an Actor & Messaging

let counter_ref = kameo::spawn(Counter { count: 0 });

let count = counter_ref.ask(Inc(42)).send().await?;
println!("Count is {count}");
ActorRef::ask

Benchmarks

13x higher throughput when compared with Actix

benchmark

Above shows a basic benchmark for sending a message to an actor in Kameo and Actix. Always benchmark for yourself.

Benchmark results

Sending a message to an actor

Benchmark Time
Kameo Unsync Message 432.26 ns
Kameo Sync Message 503.89 ns
Kameo Query 1.3000 µs
Actix Message 5.7545 µs

Processing fibonachi sequence in an actor up to 20

Benchmark Time
Kameo Unsync Message 18.229 µs
Kameo Sync Message 18.501 µs
Kameo Query 19.257 µs
Actix Message 27.442 µs

Contributing

Contributions are welcome! Feel free to submit pull requests, create issues, or suggest improvements.

License

kameo is dual-licensed under either:

at your option.