-
A little preamble to avoid the XY problem. I am trying to create a factory actor. The intention of the factory actor is to create a group of actors that make requests to and handle responses from an external service. Each worker would represent a connection to the same external service. The idea is I send the request message one of the Factory workers picks it up and makes the request. I have created a sort of sample actor below. // Pseudo code, might not compile
pub(crate) struct MyActor;
#[derive(Clone)]
pub struct MyActorState {
client: MyClient,
}
pub struct MyActorArguments {
pub id: String,
pub host: String,
pub port: u16,
pub timeout: Option<Duration>,
}
pub enum MyActorMessage {
A,
B,
C,
}
#[cfg(feature = "cluster")]
impl ractor::Message for MyActorMessage {}
#[async_trait::async_trait]
impl Actor for MyActor {
type Msg = MyActorMessage;
type State = MyActorState;
type Arguments = MyActorArguments;
async fn pre_start(
&self,
_myself: ActorRef<Self::Msg>,
args: MyActorArguments,
) -> Result<Self::State, ActorProcessingErr> {
// Init a client
let client = MyClient(
args.id,
args.host,
args.port,
args.timeout
.unwrap_or_else(|| Duration::from_secs(DEFAULT_CONNECT_TIMEOUT)),
)
.await?;
Ok(MyActorState { client })
}
async fn handle(
&self,
_myself: ActorRef<Self::Msg>,
message: Self::Msg,
state: &mut Self::State,
) -> Result<(), ActorProcessingErr> {
match message {
MyActorMessage::A => {
state.client.request("A")
}
MyActorMessage::B => {
state.client.request("B")
}
MyActorMessage::C => {
state.client.request("C")
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use ractor::Actor;
#[tokio::test]
async fn create_test_connection_actor() {
let (client, actor_handle) = Actor::spawn(
Some("Test Actor".to_string()),
MyActor,
MyActorArguments {
id: String::from("remote-host"),
host: String::from("remote-host.com"),
port: 8080,
timeout: None,
},
)
.await
.expect("Failed to start connection actor");
let res =ractor::cast!(client, MyActorMessage::A).unwrap();
}
} In order to create my factory actor I am was using the https://github.com/slawlor/ractor/blob/main/ractor/src/factory/tests/mod.rs as a template. The problem I am running into is for the regular actor above I can pass the MyActorArguments and then create my client in the This build function of this trait returns a worker actor. The problem is I cant seem to figure out how to pass MyActorArguments or init the client in the build function since my client is async and the build function is sync. Where can I put my // Init a client
let client = MyClient(
args.id,
args.host,
args.port,
args.timeout
.unwrap_or_else(|| Duration::from_secs(DEFAULT_CONNECT_TIMEOUT)),
)
.await?; so that each factory worker has a client it can use? Any help or recommendation is greatly appreciated! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
You shouldn't be "init'ing" the worker at all, you just need to create an instance of the struct. The factory will take care of spawning + setting up your actor's |
Beta Was this translation helpful? Give feedback.
I think I understand what you're going for, but it's kind of against the notion of a factory. You need something, which is async initialized, available to each worker. The problem is that the
WorkerBuilder
is invoked from the factory's context, so if that operation were to fail to properly initialize a worker, the whole factory would crash, rather than a single worker (which could be restarted).Think timeouts, spurious network issues, etc. All that shouldn't take down the whole factory. Instead each worker should be able to