-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: remove runtime features and async-std support. Still using async-std? Please make yourself heard! #1669
Comments
This may be a good moment to enumerate features that sqlx uses that aren't currently runtime-agnostic. |
I'm using sqlx in production with async-std and Tide, and I'd like to avoid additionally introducing a Tokio runtime on top of that. I do use the With my libs team hat on: we're working on introducing traits and other facilities into the standard library that would help you be more runtime-agnostic. I'm also hopeful that we'll gain a runtime-agnostic way to |
We've been hearing about efforts to add more async interfaces to the standard library since 1.39.0. Are there any concrete developments as of late? I don't really have the energy to keep track of all the RFCs anymore. |
@abonander Even if there are efforts for this I doubt that they will be adopted at this point. The common example here is that people are not expecting Personally I'm very interested in the outcome of this as I'm also myself hoping that consolidation around tokio makes it easier to build async APIs going forward. In particular supporting tracing is something I am very interested in. |
@abonander Yes, wg-async is actively working on standardizing |
Having experienced this mainly from This was just a very simple example, but we hit something similar in #1668. What do we do? Use tokio semaphores when tokio is enabled and something else for async-std? That would then complicate the implementation though, because they have different APIs and introduce even more variables that wouldn't be there if we could just agree on a general implementation for doing this kind of stuff that everybody would see as mature as their runtime. Even if you don't agree with the above "fanboy-ism" this is clearly an issue. Sorry if this sounds like a rant, I love tokio and have nothing against async-std, but I feel like this is also what other people think. At least that's what I gather when reading issues around other people's projects. |
As far as what SQLx needs, obviously the most important part is an async We also need integration with TLS libs. Currently we support Right now we have to use a different crate for each (runtime, TLS lib) pair, minus Actix as that just uses the Tokio stuff. We've considered rolling our own versions but it goes back to the same issues of runtime-dependence and the lack of standard traits. We've also been asked to support BoringSSL although there's not a lot of weight being thrown behind that at the moment.
We'd need to figure out a different strategy for the idle reaper, but it could theoretically just be a thread instead of a task.
Again, that's great but it's really not anything new. It's been talked about in some form or another since async hit stable over two years ago. Do you have any idea of a concrete timeline on this stuff? Is it waiting for something, like Moreover, even if or when that stuff is standardized, and assuming it makes its way to stable Rust relatively quickly, it would rely on both Tokio and async-std updating to use it which would mean a 2.0 release for both, which may or may not be a trivial refactor, and which would take a while to trickle out through the ecosystem anyway. Given the current pace of development, I don't see that happening in less than a year. How long do you want us to wait? I'm not being sarcastic here, it's a legitimate question. |
I know several major async-std users that I can sadly not disclose :/. Particularly, async-std is ported to proprietary platforms. Most of those users actively pick async-std bc. of its different runtime behaviour. Not sure how many of them would actually need sqlx support, but I see the major issue here as much as everyone else: a failure to provide even basic common interfaces. That being said, I'm actually in favor of running IO for something like SQLx in its own library reactor rather than hooking into a global application runtime. From my perspective, a major pain here is the belief that we need to schedule everything on one runtime. It makes libraries and the main application very entangled while rusts async/.await makes it very easy to await across runtimes. |
Just reiterating that this current behavior also affects libraries that have to pick a runtime to make builds compile Lines 1 to 25 in 135d16a
Where in fact such feature should be chosen by end-users applications or testing environments. Here goes two known workarounds:
So yeah, big thumbs up for a single runtime or anytime else that won't restrict downstream libraries. |
I'm very much in favor of smol as well, though I'd still rather have a single runtime in the entire program (where I can control things like how many threads to run in a thread pool and similar whole-program concerns) rather than having multiple runtimes. @abonander Standardized async I/O traits are waiting on a subset of async-fn-in-traits, which doesn't depend on GATs or anything else complex; a simple version that's sufficient for most of what folks need is in the accepted RFC 3185, and being implemented now. At this point I think we're talking months, not years. |
The proposal makes sense to me, although
I'd flip that default. As a rustls maintainer, I'm biased of course, but I think selecting improved security (including TLS 1.3 support) over compatibility with old database versions would make sense. |
I just want to state one assumption out loud for confirmation/rejection by @abonander : If/when async executor support arrives in If one were heavily invested into one of the other async runtimes one might be motivated to assist the AsyncWg/Library/Compiler teams in order to speed that effort along. |
We're using async-std + Pool in production, but we don't use the TLS features (sqlite rather). |
Given the outpouring of support for async-std, we're considering extracting We would also probably need to implement a TLS crate built on it that acts in the same vein but for RusTLS, @djc RusTLS would probably be the default recommendation but if people have trouble connecting with it, one of the troubleshooting steps would involve enabling |
There are already a few existing crates for that, would be nice to see some consolidation in that space so library maintainers all use the same thing... |
Neither crate provides a unified interface for async I/O types, just the very basic runtime capabilities ( |
@abonander Thank you and your team for being so open minded and flexible. |
Well, we're just trying to be considerate. If we had never implemented Tokio support or started out on Tokio rather than async-std then we probably wouldn't be having this conversation. We easily could have just closed #6 as wontfix and moved on like so many other projects did. Still, I'll reiterate that this really shouldn't be necessary. The efforts of the Rust libs team to provide standard async I/O traits should be lauded, but it's coming in quite late and the onus will still be on Tokio and async-std to actually adopt them, and do so in a timely fashion. We've also only arrived at this point after almost three years of tribalism and what can only be described as vendor lock-in, which really shouldn't happen in an open-source ecosystem. Moving on from this, we're going to work on an abstraction layer unifying the APIs of Tokio and async-std which is more efficient than I strongly encourage the Tokio and async-std maintainers to be ready to adopt the async I/O traits when they land in std which should hopefully make everyone's jobs easier in the future. Thanks everyone for your feedback. |
TL;DR: we want to drop existing runtime features and use Tokio everywhere in SQLx.
If you are still using
runtime-async-std-[native-tls, rustls]
, please give this issue a 👎 so we know you're out there!The
runtime-actix-[native-tls, rustls]
features would also go away, but as long as Actix is still based on Tokio, SQLx should continue to work with it just fine. Currently, these features exist purely to try to prevent confusion, but they also seem to cause a good bit of confusion at the same time, so getting rid of them doesn't seem controversial to me.Background and Motivation
SQLx is rather unique in its class as an async library that needs heavy runtime support but still tries to support multiple runtimes.
Most async libraries are either entirely runtime-agnostic, or committed solely to one runtime.
SQLx was initially envisioned as an async-std-only library. We had intended to use it with Tide as our web backend stack at Launchbadge. Very soon after release, the request to support Tokio became very popular (heh, we initially labeled that issue
wontfix
), being echoed both on Discord and on Reddit, and so we added Tokio in 0.2.0.We soon added support for the Actix runtime (which is Tokio under the hood) as well, and also settled on Actix-web for our web stack for various reasons. (We're considering Axum for new projects, which is also Tokio-based.)
Since then, supporting multiple runtimes has turned out to be quite the maintenance burden.
We had to create a whole subcrate just to provide a unified interface, using mutually exclusive Cargo features because we conditionally re-export items from one of
tokio
,async-std
oractix-rt
under conflicting names, and so can't have more than one enabled at once. This has caused a lot of issues, especially because we force the user to pick a runtime even when they would prefer not to (like if they're just trying to use the derive macros).The mutual exclusion is also necessary to pick between
tokio-native-tls
,tokio-rustls
,async-std-native-tls
andasync-std-rustls
even though they're all more or less identical, just linking different crates. We believe this could also be resolved by Cargo weak features which will be stabilized in Rust 1.60, but is still worth noting.We have 69 CI passes, which take around 15 minutes to run, thanks to having to cover all the possible combinations of the databases, database versions, runtimes and TLS providers that we support, although to keep from going overboard we only test the oldest and newest versions we support of each database.
Getting rid of runtime features in general and committing solely to Tokio would simplify SQLx, and usage of it, quite a bit.
These days, Tokio is the superior option.
async-std/master
was 2 months ago.hyper
,reqwest
,actix-web
,rusoto
,axum
,tokio-tungstenite
,tonic
,redis
,tokio-postgres
Implementation
runtime-*
matrix of features.sqlx-rt
crate and replace usages ofsqlx_rt
withtokio
in other crates.native-tls
andrustls
features which would no longer have to be mutually exclusive; if both are enabled,native-tls
should take priority.What would happen to async-std users?
If there's still a significant cohort of async-std SQLx users, we don't just want to kick them to the curb. In order to make our decision, we want to try to gauge how many of them there are which is why you should 👎 this issue if you would be negatively affected by this change.
There's also
async-compat
which can be used to make Tokio-based crates work on async-std by using a single-threaded Tokio runtime spawned into a background thread.It should work well enough for SQLx, with the exception of
Pool
. BecausePoolConnection
wants to spawn a task on-drop to make sure the buffers are flushed and the connection is still usable, just wrapping a future that usesPool
orPoolConnection
inCompat
won't be sufficient, as the drop handler will be run after thetokio::runtime::EnterGuard
is dropped. It shouldn't panic as the drop handler already checks if the runtime is available or not, but it does mean that every connection checked out fromPool
would be immediately closed after use, which kind-of defeats the purpose.The solution I'm thinking is to allow providing a
tokio::runtime::Handle
to use inPoolOptions
, but getting a handle to the Tokio runtime thatasync-compat
spawns would require depending on Tokio in your application, as it doesn't provide direct access to it:Alternatively, this could work without depending on Tokio (I'm thinking we'd provide both):
The text was updated successfully, but these errors were encountered: