Beginner coming across what seems to be 'async hell'

I'm 4 weeks into Rust and just go lost in async/await hell.

In my pet project for learning Rust I want to interact with etcd and I came across the etcd-client crate: Dive into async...

I find out about async/await syntax from the Rust book, learn about tokio and async runtimes in general and somehow end up reading The State of Async Rust: Runtimes which paints a somewhat bleak picture, but spurs me to look into alternatives to tokio.

Enter glommio - a single thread per core architecture async runtime, that I would like to use.

So now I am realising that things are not so rosy: it seems async runtimes pollute your code with the specific implementation, therefore I can't use the runtime of my choice (glommio) with the library of my choice (etcd-client)...

As a beginner, this is not something I want to deal with but I am much more inclined to go with glommio and io_uring than tokio...

Any words of wisdom on how to proceed? Is there any way to adapt code written to run with tokio to use glommio? Is there any way to write async code in a runtime-agnostic way? Are there any example libraries like that out there to study?

Thanks!

1 Like

Depends on what you mean by "adapt." If you want to use the etcd-client library as-is, you need to use tokio, and it will not work with glommio. If, on the other hand, you're willing to fork etcd-client and rewrite all the tokio references to use glommio constructs instead, that might work, but (as I'm familiar with neither etcd-client nor glommio, I can't say for sure) you might run into spots where there's no readily-available equivalent for something in glommio.

Personally, I think the linked blog post's case against multithreaded async runtimes is overblown, and using tokio is fine.

The Rust ecosystem is very tokio-centric. There's no standard way to abstract away the choice of runtime — it might be possible if there were standard traits for that, but there aren't any yet. Future alone is not enough. This creates a winner-takes-all dynamic, so other runtimes are doomed to be for specialized purposes only (e.g. for niche ecosystems like embedded, or used with your dedicated code only).

A lot of libraries also depend on tokio unnecessarily, and e.g. overuse spawn where runtime-agnostic constructs like join! would work better [1].

If you want to change tokio-dependent code to glommio-dependent code for fun, have fun!

If you just need something working while learning Rust, and have decent performance, just use tokio. You probably won't notice any difference in a toy project. Cloudflare handles a fifth of the world's Web traffic, on lots of 96-core machines, and it works fine with vanilla tokio.


  1. IMHO 90% of complaints about 'static and Send futures being painful are symptoms of trying to shove spawn where it doesn't belong. â†Šī¸Ž

6 Likes

So the main beef the author of the link has with Tokio seems to be that

The easiest way to get started is to enable all features. [...] By doing so, one would set up a work-stealing, multi-threaded runtime which mandates that types are Send and 'static and makes it necessary to use synchronization primitives such as Arc and Mutex for all but the most trivial applications.

If we wanted to write code that can work with any runtime, then such code would have to obey these exact requirements, too. To escape these requirements, we would have to restrict ourselves to runtimes that guarantee that each future is evaluated on only a single thread.

Maybe that makes sense on single-threaded targets such as WASM.

In general, it would mean that any time we create futures from existing futures, all of the futures involved would have to run on the same thread. For example, map-reducish calculations where we first perform many tasks (ideally in parallel) and then fold the results into a single result, will then all run on one single thread.

There is a reason we have Rust's promise of fearless concurrency with Send and Sync and all the Arc and Mutex etc when needed. There may be other concerns regarding Tokio, including its bloatedness, but the multi-threadedness with all the requirements it comes with is just a modest price we pay for using all of our cores when appropriate.