slawlor / ractor Goto Github PK
View Code? Open in Web Editor NEWRust actor framework
License: MIT License
Rust actor framework
License: MIT License
Description of the problem encountered
I tried to build examples with ractor and ractor_cluster on windows 10 machine
and encountered following error:
Compiling protobuf-src v1.1.0+21.5
error: failed to run custom build command for `protobuf-src v1.1.0+21.5`
Caused by:
process didn't exit successfully: `D:\nolmelab\daily_rust\target\debug\build\protobuf-src-fa7f52d64a0497cb\build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at '
`sh` is required to run `configure`
build script failed, must exit now', C:\Users\keedongpark\.cargo\registry\src\index.crates.io-6f17d22bba15001f\autotools-0.2.6\src\lib.rs:781:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
autotools requires configure & make, which are not available on windows by default.
Describe the solution you'd like
I tried to remove dependency on protobuf-src in ractor repository by removing
protobuf-src line in ractor_cluster/Cargo.toml and std::env::set_var("PROTOC", ... )
line in build.rs file.
Then the build complains about missing google.protobuf.Timestamp and some other files while compiling *.proto files. (I made protoc.exe available via PATH env variable)
If ractor_cluster requires some of the .proto files from protobuf and protoc only,
then I think the dependency on protobuf-src can be removed.
It will make using ractor_cluster on windows really simple.
Describe alternatives you've considered
I also consider that dependency on protobuf can be removed entirely using rust serialization
(binary or json) in platform compatible way. Then it can make building and using ractor_cluster easier than now still supporting (possibly) other languages and platforms.
Additional context
Describe the bug
When a Err is returned during actor startup, the err gets wrapped with a SpawnErr::StartupPanic, while there was no panic. There should probably be a SpawnErr::StartupError or something similar, or StartupPanic should be renamed, to avoid confusion.
The actor mailbox is currently implemented with an unbounded mpsc channel.
This seems to allow an actor input port to get filled up with lots of messages and does not provide any backpressure mechanism.
It would be nice to have a configuration option to use a bounded channel instead, with message delivery either blocking until capacity is available, or failing immediately . (with different methods)
Motiviation: both providing back pressure and preventing infinite buildup of messages are important design properties that are easy to do by just using regular channels.
This could be emulated by a top level actor that delegates to child actors and keeps track of in-flight messages being processed, but that's a lot more complicated.
We should have a strong typed ActorRef
which gets passed around most often. It'll just be a wrapper over actorcell but with a phantom data pointing to the ActorHandler
implementation so that you don't need to always pass the type.
Then we should implement From
both ways between actorcell so you can easily convert to/from.
That should cleanup usage a lot
Before starting this discussion, I had already conducted tests. Here is how I tested it:
sampler.get_cell().link(proc.get_cell());
proc.get_cell().link(sampler.get_cell());
concurrency::sleep(concurrency::Duration::from_secs(3)).await;
// sampler.stop(Some("user shutdown".to_owned()));
proc.stop(Some("user shutdown".to_owned()));
However, during runtime, it crashed with the following error message:
"thread 'tokio-runtime-worker' has overflowed its stack, fatal runtime error: stack overflow".
Did I use the link method incorrectly?
Is your feature request related to a problem? Please describe.
If a message handler fails when calling .call
on an actor, CallResult::SenderError
is returned with no error information.
Describe the solution you'd like
CallResult::Failed(err)
should be added to the CallResult
enum containing if an error occured when handling the message.
Ideally, I'd be able to write the following code:
#[async_trait::async_trait]
impl Actor for TestActor {
async fn handle(...) -> Result<(), ActorProcessingErr> {
Err(anyhow!("command failed"))
}
}
let result = call!(actor, MessageFormat::TestRpc, "Something".to_string());
match result {
Ok(value) => { println!("{value}"); }
Err(CallError::Failed(err)) => // Handle the error returned in the handle function
Err(CallError::Timeout) => // Call timed out
Err(CallError::SenderError) => // Is this still needed if we add Failed?
}
Describe alternatives you've considered
Using a RpcReplyPort<Result<T, E>>
to send the error back. However this makes it difficult to work with, as I cannot use the ?
operator to simply return the error.
When using types like factory::Job
, it is impossible to clone it due to lack of derives. It will be useful to add them. If this is desirable, I can make a PR.
While using ractor, I found some inconvinience while dealing with the API, especially DiscardHandler
and WorkerBuilder
. Is it possible to impl those two traits for closures? For now, I have these shim code:
fn worker_builder(&self) -> Box<dyn WorkerBuilder<SourceWorker>> {
struct SourceWorkerBuilder {
db: Collection<Record>,
}
impl WorkerBuilder<SourceWorker> for SourceWorkerBuilder {
fn build(&self, id: WorkerId) -> SourceWorker {
SourceWorker {
id,
db: self.db.clone(),
}
}
}
Box::new(SourceWorkerBuilder {
db: self.db.clone(),
})
}
and when initializing:
let builder = self.worker_builder();
Actor::spawn(None, factory, builder).await?;
which if we can rewrite it with closure:
let builder = Box::new({
let db = self.db.clone();
move |id| SourceWorkerBuilder { id, db: db.clone() }
})
Actor::spawn(None, factory, builder).await?;
Also, it might be good if we can use
pub fn where_is<K: Borrow<ActorName>>(name: K) -> Option<ActorCell>
Which should work no matter what ActorName
is because underlying API provided by DashMap
.
async_fn_in_trait is still WIP but the current implementation mostly works. It would be neat if there was a flag to radioactively enable it until it is stabilized to avoid unnecessary allocations caused by async_trait.
It seems that ractor_cluster::protocol::auth::NameMessage
is private (the whole protocol
package is private)
And so I can't pass anything valid to https://docs.rs/ractor_cluster/latest/ractor_cluster/node/node_session/struct.NodeSession.html#method.new
Is this a mistake or am I doing something wrong?
(A more general question might be: what's the correct way to connect to a remote actor during runtime?)
Edit: I believe this can just have pub
added: https://github.com/slawlor/ractor/blob/main/ractor_cluster/src/lib.rs#L56
I was kicking around adding ractor
as a dependency to one of my projects recently, and kept running into some issues with some tokio
-related build issues. After spending some time changing around features in other deps to try to match tokio
, I decided to try a rustup update
on my toolchain, which seems to have resolved the issues I was seeing. I believe this update from from rustc 1.65.0-nightly
to rustc 1.71-nightly
.
I'm not sure if anyone else has seen something similar (maybe it was just a quirk of my system), but I know that some Rust projects add a Minimum Supported Rust Version (MSRV), with a specific build in their CI toolchain to make sure new releases don't break people who haven't update their toolchain very recently, or can at least make informed decisions about when to do so. I was wondering if you might consider doing something similar for ractor
. Thanks working on this crate--I'm really interested about the possibilities, and it's nice to see something actively worked on now that actix
and bastion
don't really seem to be being developed any longer!
Relating to #177 (comment):
This is where having another index might be helpful, i.e. Scope -> Group name in a separate mapping. But that's an optimization that can be added in the future.
To make sure I understand you correctly before tackling this:
Are we talking about sth. along the lines of substituting ScopeGroupKey
with sth. like this?
pub struct Scope {
name: ScopeName,
groups: HashSet<GroupName>,
}
See #73 for example how this might work and discussion on how to do it better
We should be able to setup an "output" port kind of publish-subscribe model so actors can "emit" messages without knowing exactly who is listening.
This would require actors to emit some kind of ports which can be wired up to with a disposable (drop-able) handle to the subscription
We have 0 log messages in the actor flow, and definitely need some. Let's standardize on the log
crate.
Hello, I'm very interested in ractor and am keen to use it in my project. I've previously used C++ CAF, which is also an open-source actor framework inspired by Erlang's actor model. I've noticed many similarities between ractor and CAF, which allows me to get started with ractor more quickly. However, I couldn't find any examples regarding communication between local and remote actors. Could you provide me with some examples of how to communicate with remote actors in ractor and ractor_cluster? For instance: an actor on Host A communicating with an actor on Host B, where the actor on Host B is considered a remote actor from the perspective of Host A.
Following from Erlang, we want to support distributed nodes
https://www.erlang.org/doc/reference_manual/distributed.html
We need
control.proto
)NodeSession
who propagates it to the remote system to cleanup the RemoteActor
instance)A
connects to B
who's also connected to C
, then A
also initiates a connection to C
. This should be relatively simple to implement as an optional functionality. #53Already present:
I was looking for a way to be able to communicate with a remote actor without having to join it to a process group, and found this section in the code that prevents the RemoteActors from registering with the remote actor's name into the local registry:
ractor/ractor/src/actor/actor_cell/mod.rs
Lines 222 to 225 in 6e36b8b
Uncommenting this code (and cloning the name to get around the move into ActorProperties::new_remote
) seems to work as expected, with all the remote actors successfully being reigstered.
Is there an issue that this approach causes, or could this be enabled?
JoinHandle<()>
instance and block shutdown on successful shutdown of their workers. Otherwise there's a slight chance that process shutdown may occur prior to fully shutting down workers, resulting in a mem leak.Is there any chance there might be some documentation that I've missed on how to re-start a panicked Actor once the supervisor is notified it has failed using the handle_supervisor_evt()
function, or another method? I've been banging my head against doing this in the supervisor
example for a while, but can't seem to properly update the held leaf_actor
ID under MidLevelActorState
, with a newly-spawn_linked()
Leaf Actor, which then causes my code to panic.
I'm primarily just interested in dealing with a case where I've written an Actor
, spawned it as a child of a supervisor, then after it's panicked (because I was lazy while doing error -handling and missed an unwrap or something), have the supervisor node catch the unhandled panic and just re-initialize the actor appropriately.
One of the things I like about actix, is the possibility to define handlers for multiple messages per actor, which is something that erlang, caf and akka (iirc) allow as well. I know that this can be emulated with enums, but having it work without would be a good quality of life improvement.
Actix Example:
use actix::prelude::*;
// this is our Message
// we have to define the response type (rtype)
#[derive(Message)]
#[rtype(result = "usize")]
struct Sum(usize, usize);
// Actor definition
struct Calculator;
impl Actor for Calculator {
type Context = Context<Self>;
}
// now we need to implement `Handler` on `Calculator` for the `Sum` message.
impl Handler<Sum> for Calculator {
type Result = usize; // <- Message response type
fn handle(&mut self, msg: Sum, ctx: &mut Context<Self>) -> Self::Result {
msg.0 + msg.1
}
}
#[actix::main] // <- starts the system and block until future resolves
async fn main() {
let addr = Calculator.start();
let res = addr.send(Sum(10, 5)).await; // <- send message and get future for result
match res {
Ok(result) => println!("SUM: {}", result),
_ => println!("Communication to the actor has failed"),
}
}
Hi again!
I have implemented BytesConvertable
for a custom type, something like this:
impl BytesConvertable for Value { ... }
This works well and fine on its own, but I wanted to express an RpcReplyPort
in terms of a wrapper around Value
, i.e. Option<Value>
or Vec<Value>
Unfortunately, there's no blanket impl <T> BytesConvertable for Vec<T> where T: BytesConvertable
, so this doesn't work.
Further, as a user, I can't impl BytesConvertable for Vec<Value>
because it's not permitted to implement foreign traits for foreign types
I had a look at your existing BytesConvertable
impls and noticed that you manually implement a family of serdes using macros.
I did also try to build a blanket impl myself but it got very messy when I ran into a lack of numeric traits
For reference, I was trying to wrangle some code like this:
impl<T> BytesConvertable for Vec<T> where T: BytesConvertable + Sized {
fn into_bytes(self) -> Vec<u8> {
let mut result = vec![0u8; self.len() * std::mem::size_of::<T>()];
for (offset, item) in self.into_iter().enumerate() {
result[offset * std::mem::size_of::<T>()
..offset * std::mem::size_of::<T>() + std::mem::size_of::<T>()]
.copy_from_slice(&item.to_be_bytes());
}
result
}
fn from_bytes(bytes: Vec<u8>) -> Self {
let num_el = bytes.len() / std::mem::size_of::<T>();
let mut result = vec![T::MIN; num_el];
let mut data = [0u8; std::mem::size_of::<T>()];
for offset in 0..num_el {
data.copy_from_slice(
&bytes[offset * std::mem::size_of::<T>()
..offset * std::mem::size_of::<T>() + std::mem::size_of::<T>()],
);
result[offset] = <T>::from_be_bytes(data);
}
result
}
}
(and some other variations, e.g. trying to use const generics in place of std::mem::size_of::<T>()
, etc)
I came across this PR that I think would add the special sauce to do an appropriate blanket impl for numerics and therefore I think also blanket impl for Vec<T> where T: BytesConvertable
.
In summary:
RpcReplyPort<Collection<T>>
seems to be to wrap Vec
with a custom struct and then impl BytesConvertable for MyVecWrapper<Value>
impl BytesConvertable for Vec<T> where T: BytesConvertable
BytesConvertable
feels clumsy compared to using serde -- it would be nice to have serde integration, perhaps behind a feature flag?Have you looked into the possibility of more strongly typed SupervisionEvents
and ActorProcessingErrs
?
For example, a supervising actor could receive a ActorTerminated
with a typed response as opposed to Option<String>
. That way actors can clearly signal what happened in a way more idiomatic, akin to rusts Result
handling.
It feels like the exactly erlang model of actors panicing and leaving it to the supervisor to handle does not map 1:1. "Most" rust code will use ?
and From
some error types into others to surface errors to callers as opposed to the throw exception model. So a library/developer experience focused on panic handling as the primary error communication method doesn't allow rust to work to it's fullest. Perhaps it's me attempting to fit actors into my mental model though, and I should rethink.
My understanding is there's no type level link between two actors, one of which is supervising the other, so implementing this currently would be interesting, though the conversion between ActorCell
and ActorRef
feels like it could be related.
Just spit balling, but hopefully communicates the idea
use ractor::{Actor, ActorRef};
enum ChildError {
DatabaseError(String),
ProcessingError(String),
}
struct Parent;
#[async_trait::async_trait]
impl Actor for Parent {
type Msg = ();
type State = ();
type Arguments = ();
async fn pre_start(
&self,
myself: ractor::ActorRef<Self>,
_: Self::Arguments,
) -> Result<Self::State, ractor::ActorProcessingErr> {
Actor::spawn_linked(None, Child, (), myself.get_cell()).await?;
Ok(())
}
}
impl Supervisor<Child> for Parent {
type StopReason = ChildError;
async fn handle_supervisor_evt(
&self,
_this_actor: ActorRef<Self>,
message: SupervisionEvent<Self::StopReason>,
_state: &mut Self::State,
) -> Result<(), ActorProcessingErr> {
match message {
SupervisionEvent::ActorTerminated(_, _, reason) => {
match reason {
ChildError::DatabaseError(msg) => {
warn!("child experience database error");
// restart
}
ChildError::ProcessingError(msg) => {
error!("child experiences processing error");
// don't restart
}
}
}
_ => {
// usual Supervision events
}
}
}
}
struct Child;
#[async_trait::async_trait]
impl Actor for Child {
type Msg = ();
type State = ();
type Arguments = ();
async fn pre_start(
&self,
_myself: ractor::ActorRef<Self>,
_args: Self::Arguments,
) -> Result<Self::State, ractor::ActorProcessingErr> {
Ok(())
}
async fn handle(
&self,
myself: ActorRef<Self>,
_message: Self::Msg,
state: &mut Self::State,
) -> Result<(), ractor::ActorProcessingErr> {
// alternative to manually calling stop would be returning Result<(), ChildError> and implementing From<DbError> and From<ProcessingError> so we could just `.await?` instead
if let Err(msg) = database_work(state).await {
myself.stop(ChildError::DatabaseError(msg));
return Ok(());
}
if let Err(msg) = processing_work(state).await {
myself.stop(ChildError::ProcessingError(msg));
}
Ok(())
}
}
Describe the bug
Monte_carlo example gives me an error like this:
Finished dev [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/examples/monte_carlo`
thread 'tokio-runtime-worker' panicked at 'Failed to send message: Messaging(SendErr)', ractor/examples/monte_carlo.rs:105:14
thread 'tokio-runtime-worker' panicked at 'Failed to send message: Messaging(SendErr)', ractor/examples/monte_carlo.rs:105:14
thread 'thread 'tokio-runtime-workertokio-runtime-worker' panicked at '' panicked at 'Failed to send message: Messaging(SendErr)Failed to send message: Messaging(SendErr)', ', ractor/examples/monte_carlo.rsractor/examples/monte_carlo.rs::105105::1414
Seems like when child actor ends itself, parent actor ends as well, but there are still several child actors who tries to send messages to their parent
To Reproduce
Steps to reproduce the behavior:
alexromensky@Alexs-MacBook-Pro ractor % cargo run --example monte_carlo
Expected behavior
supposed to run without an error
Additional context
MacOS 13.5.2, rustc 1.72.0 (5680fa18f 2023-08-23)
While the documentation around it is quite sparse, the current implementation notes that long-running asynchronous tasks "block" the actor's message handler, and that delays or similar should be translated to timers and message events.
For use as a sort of in-app microservice architecture, this makes it difficult to run long-running tasks as an actor, as something like sending them a graceful stop request could have the message handler trigger a cancellation token stored in the actor's state, but the message pump is not being polled as long as the actor's state is claimed.
If actor state were held under async lock guard
s, messages could be queued immediately any time the state is not currently locked, allowing for actors to "yield" back to the loop any time they are not currently processing work.
For any task which must be performed in a serial manner, holding the guard
across the await
allows the message pump to be disabled until the runtime can achieve a new lock, allowing actors to opt in to non-concurrency.
This may even be simpler to implement than the above- in that the message handler could always be called by the runtime, with the expectation that any message handler that uses "state" will simply await
a lock on the state guard. This means that messages will be processed as concurrently as possible, but has the performance drawback in that it could result in many message futures being queued and awaiting a lock on that actor's state.
futures
-esque combinators atop timers to allow network-serializable poll-able events
yield(myState)
-like construct required in order to allow concurrency by temporarily returning state control to the runtimeThe root of the issue is that concurrency is limited by mutable access to Actor::State
. Bevy's scheduling approach of reader / writer isolation may also be of interest here, in that queries which read a resource may be concurrent, while queries which write to that resource must be run in isolation from each other.
To protect against runtime crashes due to downcasting to the wrong message type, we should add the type ID to the actor properties struct and check it every time and interaction is done with the actor properties, returning an error that someone tries to interact with the actor for an unsupported type
We have a gen_server
equivalent (basic actor), now we can expand on that
Other possible utility actors that might be helpful to have:
We have written up a (crude) utility function to send multiple messages to the same actor and receive the response with a mpsc channel.
This is mainly to minimize overhead.
Is it possible to include such feature in ractor?
Here's my implementation. One big flaw is that this requires making another message type for MpscSender instead of using RpcReplyPort
pub async fn call_multi<TMessage, TReply, TMsgBuilder>(
actor: &ActorCell,
msg_builder: TMsgBuilder,
size: usize,
) -> Result<Vec<TReply>, MessagingErr<TMessage>>
where
TMessage: Message,
TMsgBuilder: FnOnce(MpscSender<TReply>) -> Vec<TMessage>,
{
let (tx, mut rx) = concurrency::mpsc_bounded(size);
let msgs = msg_builder(tx);
for msg in msgs {
actor.send_message::<TMessage>(msg)?;
}
let mut replies = Vec::with_capacity(size);
// wait for the reply
while let Some(result) = rx.recv().await {
replies.push(result);
}
Ok(replies)
}
Presently ActorRef
is bound to the TActor: Actor
type, which means the ActorRef references the
which is generally unnecessary, since all we really care about is the message type. This means that we should move from
ActorRef<TActor: Actor> -> ActorRef<TMessage: Message>
which doesn't require leaking as many types for simple message passing. This however is a large refactor and contract break so would be a major version bump across all libs and downstream usages.
Make the pre-start, supervision, handle, post-start, post-stop calls all return a Result of
Result<_, Box<Error>>
and support From
conversions to ease usage and support ?
sytanx.
Some context: https://blog.burntsushi.net/rust-error-handling/
After #1 merges, we'll need additional test coverage of
Can Actor state store to physicial storage,and restore Actor state from storage?e.g. Akka-persistence
In one of the examples in the Quickstart mentions sending a message and not expecting a reply. Does that mean that there is a way of sending a message and getting a reply?
I'm trying to replicate the ASK pattern found in Akka and other Actor platforms. Blocking an actor (in an async way, obviously) while waiting for a message to come back.
With the possibility of keeping state either as part of the Actor trait or on the actor struct itself, it's unclear to me the tradeoffs between these.
Most of the examples are using the State tied to the Actor trait, however the supervisor example uses a field on the struct itself.
It would be helpful if the docs on Actor
could clarify this situation :)
We need
lib.rs
The current implementation creates a Vec of the correct capacity, bind it to results, and then index it.
This won't work because that's the capacity, not the length, the error message discloses that index out of bounds: len is 0 while index is 0
.
Simple solution: (this is not the only way to write it!)
let mut results = Vec::new();
results.resize_with(join_set.len(), || CallResult::Timeout);
Which will resize the results, allocate space as needed, and fill the space with Timeout's. Note that CallResult is not Clone so you can't just use vec!.
ractor
could potentially expose a ChildSpec
trait that inherits Clone
and exposes a crate_child
function that returns an instance of the child. This way a supervisor could pass an instance of its own state to create children in a generic way.
when an error occurs while an actor handling a message, the actor should tell the supervisor, and if this is an unhandled error, the supervisor should restart a new actor
however I don't see how to let the supervisor restart an actor, the https://docs.rs/ractor/0.7.6/ractor/actor/trait.Actor.html#associatedtype.Arguments is not Clone
, so we can't clone the argument and pass the argument to some restart function, let it create a new actor
Our process groups don't have "scopes" like the Erlang ones (https://www.erlang.org/doc/man/pg.html)
As we move towards distributed actors, scopes will probably help limit which actors are being referenced in process groups and when.
This is a tracking issue to add scopes to our pg
module.
It appears that if we don't unlink the actor first, the supervisor would get stopped as well.
Is this an intended behaviour? If so, what's the reasoning?
Small issues on https://slawlor.github.io/ractor/quickstart/ (I couldn't find a repo for these docs, so posting here):
type MyFirstActor;
should be struct MyFirstActor;
(or pub struct
, perhaps combined with pub enum
)[tokio::main]
should be #[tokio::main]
There are also warnings about unused variables, but I guess that's fine in a hello world example.
Thanks for a very interesting project!
Based on the implementation of send_interval
being through sleeping, the interval will drift over time in long running programs.
The sources of drift can come from both the sleep timer potentially not being woken up on time, and the time taken to run the user code to produce the message, send_message
itself, and the loop mechanism will all introduce delay before the next call to sleep
.
Since the sleep period does not take into account the delta between the last and current, it is unable to compensate for added execution time and therefore will be subject to drift over time.
As a source of prior art, the tokio Interval
default behaviour accounts for this and will schedule the next accordingly. This behaviour can be configured on the interval through set_missed_tick_behavior
with a MissedTickBehaviour
.
From looking at the code, I think the error is that in ActorProperties the type_id comes from TActor::Msg where ActorRef::where_is expects the type_id to come from TActor itself.
Looking at the initial PR for ActorRef, type_id was first assigned from TActor: https://github.com/slawlor/ractor/pull/13/files#diff-13f6bef8e8c77187a4a13283938bbac0dbe687e3ef686ce925e1c49d079f4d5aR58
So I guess, it was just forgotten to update the ActorRef::where_is implementation after the type_id was changed?
This fails with a ActorAlreadyRegistered Error when called twice with the same id:
let user = if let Some(user) = ActorRef::where_is(jwt.uid.clone()) {
user
} else {
Actor::spawn(Some(jwt.uid.clone()), UserActor, ()).await.unwrap().0
};
This works flawlessly
let user = if let Some(user) = registry::where_is(jwt.uid.clone()) {
user.into()
} else {
Actor::spawn(Some(jwt.uid.clone()), UserActor, ()).await.unwrap().0
};
I've read a lot of the existing tests and example code and much (all?) of the documentation on ractor_cluster and RactorClusterMessage and I haven't been able to figure out how to get past problems with BytesConvertable.
It's not clear which trait bounds I haven't satisfied when I attempt to work with something like:
MidLevelActorMessage::GetLeaf( RpcReplyPort<ActorRef<LeafActorMessage>> )
I started with an existing example, the one that shows how to create a supervision tree, and then extend that to have the Actors be accessible from a different node, by deriving RactorClusterMessage and annotating GetLeaf with #[Rpc].
An example of how to use this code would be very helpful.
Is your feature request related to a problem? Please describe.
Ractor seems to be very close to capable of running in a WASM environment, even a browser one. It would be amazing to have an actor system that could be used client- and server-side in a Rust app.
Describe the solution you'd like
I was able to get ractor running in the browser very very simply and with minimal changes over at https://github.com/tekacs/ractor/tree/wasm-support from master by using the WIP swappable runtimes in concurrency.rs
.
The implementation is janky and a little incorrect, but mostly works and does the job nicely. Making this more correct and validating tests would be the next step.
I would love to incorporate such changes into upstream ractor to make it possible to keep going with an approach like this in the longer term.
Describe alternatives you've considered
I currently have a custom client-side actor system in my WASM environments... it would be nice to unify things under the banner of ractor.
They have identical implementations. Only one is needed?
Hey there, I am coming from actix actors background, Suffering from some limitations, and I on search for an alternative or an easy convert.
I am going to state down my use case, and I wonder if it will be feasible to make the convert upon your answers. It is also good to know if the crate is being used on production.
Not going to mention supervisoring as I already read that it is there.
Making the move to an already crate or using pure rust / tokio channels is the way to go for this project of mine, but knwoing a crate already has the potentials to solve the issues am facing will just make things easier, It will come at the end how much time the conversion of the project will take. So your answers are really important.
Thank you.
Blocking thread. // lack of async/await support.
I also tried atomicResponse but in that case I losse access to self, and being static issues.
Not trying to figure out a fix, as moving forward async/await is the future to go.
impl Handler<DbOperations> for McActor {
type Result = String;
//type Result = ResponseFuture<String>;
fn handle(&mut self, msg: DbOperations, ctx: &mut Self::Context) -> Self::Result {
return match msg {
DbOperations::Get { key } => {
block_on(async {
sqlx::query_as::<_, DbOperationValues>(select_query)
.bind(key)
.fetch_one(self.db.as_ref().unwrap())
.await
.unwrap()
}).value
}
DbOperations::Set { key, value } => {
block_on(async {
sqlx::query_as::<_, DbOperationValues>(insert_query)
.bind(key)
.bind(value)
.fetch_one(self.db.as_ref().unwrap())
.await
.unwrap()
}).value
}
};
}
}
** Is your feature request related to a problem? Please describe**. Fake GPS features or applications that are accurate and undetectable By the company and the cyber IT maps application team [...]
Describe the solution you'd like
I'm an online driver. Can you make a Ghost MOD for the Shopee application with an accurate fake GPS driver? . I am a Shope Food driver partner. Make me a fake GPS application so that my work runs smoothly and the signal is strong and wide. Not detected by the company.
Describe alternatives you've considered
How do I make sure the provider's signal is strong and not weak? To expand the Google Maps network and coordinate points
Additional context
SHOPEE DRIVER
We should support distributed named process groups for paging processes by a given name.
Is there a way to get notified if a node becomes unavailable?
I'd like to try reconnecting when a node has to restart or network is not reliable
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.