spaad icon indicating copy to clipboard operation
spaad copied to clipboard

Not waiting for the response using await doesn't work

Open 05storm26 opened this issue 2 years ago • 2 comments

In the README it says:

If you do not want to await for the message to complete processing, you can do the following:

let _ = my_actor.print(); // Binding to avoid #[must_use] warning on Future

This is not actually true. If you do not await on a future it will not be executed. Since the generated handles will call xtra::Address::send the actual message will not be sent to the actor at all if the returned future is not awaited. The documentation for xtra::Address::send explicitly mentions this: "Like most futures, this must be polled to actually send the message."

You can test this by comparing the result of these:


use std::time::Duration;


#[spaad::entangled]
pub struct ScanTest {

}

#[spaad::entangled]
impl xtra::Actor for ScanTest {}

#[spaad::entangled]

impl ScanTest {
    #[spaad::spawn]
    pub fn new() -> Self {
        ScanTest {  }
    }

    #[spaad::handler]
    pub fn print(&mut self) {
        println!("called!");
    }
}

#[tokio::main]
async fn main() {
    let mut s = ScanTest::new(&mut xtra::spawn::Tokio::Global);

    s.print();

    std::thread::sleep(Duration::from_secs(5));
}

with this:

use std::time::Duration;


#[spaad::entangled]
pub struct ScanTest {

}

#[spaad::entangled]
impl xtra::Actor for ScanTest {}

#[spaad::entangled]

impl ScanTest {
    #[spaad::spawn]
    pub fn new() -> Self {
        ScanTest {  }
    }

    #[spaad::handler]
    pub fn print(&mut self) {
        println!("called!");
    }
}

#[tokio::main]
async fn main() {
    let mut s = ScanTest::new(&mut xtra::spawn::Tokio::Global);

    s.print().await;

    std::thread::sleep(Duration::from_secs(5));
}

However you can still archive the expected result (not waiting for the result) by passing the future to tokio::spawn:

async fn main() {
    let mut s = ScanTest::new(&mut xtra::spawn::Tokio::Global);

    tokio::spawn(s.print());
    println!("spawned!");
    
    std::thread::sleep(Duration::from_secs(5));
}

This outputs:

spawned!
called!

IMO the documentation in README is wrong and should be corrected. Also in general I think we shouldn't encourage users to not call await/poll/pass to an executor somehow a Future since ignoring a returned impl Future doesn't do what the user would expect. (i.e.: ignoring the future will just cause it to not be executed at all)

05storm26 avatar Dec 25 '22 18:12 05storm26

Thanks for noting this! It's likely that at some point this functioned differently as xtra has been through many rewrites and behaviour changes - more than it has version numbers, as many were internal.

It is likely that with xtra master, we could have

let _ = my_actor.print().split_receiver().await; // Binding to avoid #[must_use] warning on Future

function the same as

tokio::spawn(my_actor.print())

However this is slightly ugly as it exposes xtra internals in a way that spaad aims to avoid. I am not really sure that the concept of spaad was well executed. I liked it at first because it was cool to be able to reduce the boilerplate and treat what is essentially a system boundary like a normal function/API. In theory, spaad's concept could work over more than just xtra, and actually work over HTTPS with a REST API or any other system boundary protocol. I just did it because I thought it was cool and convenient. Now, however, I am not so sure that these details are actually something I want to hide, especially when it comes to more advanced things like split_receiver. I am not even sure that that is what you want in the first place in most cases - I think @thomaseizinger is of the opinion that it is almost never wanted due to backpressure reasons, and I am somewhat inclined to agree now after having seen some projects use it and run into issues as a result.

I think the crux of the issue is that I am not really sure what to do with spaad. I think once the latest version of xtra is released I might publish a last version of spaad in terms of feature completeness. I am not very happy with the concept so I might just keep it updated with xtra but not advance the functionality further since I don't think it's conducive to good design. If anyone else feels differently they must feel free to challenge me on this and we can see what a good path forward is :) i.e: I want to hear why people like or dislike spaad, and use that info to move forward better.

Restioson avatar Feb 15 '23 11:02 Restioson

Just confirming what you said above: Yes, not awaiting the response of a message should be used very cautiously because you are breaking the back-pressure chain. It is fine to use split_receiver but you really have to think through what you are doing.

thomaseizinger avatar Feb 15 '23 20:02 thomaseizinger