rusqlite: Transactions don't work with async/await
I’m migrating a pretty big codebase to use async/await and ran into problem with making rusqlite work.
I understand why Connection is not Sync and normally i would use a Mutex to hold a connection in a multi threaded environment, but in this case i’m holding a reference to a Connection and &T: Send is not Send.
playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c25f81c48a319e5fbaf88b79ac1989eb
use rusqlite;
use tokio; // 0.2.13
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
let handle = rt.spawn(async {
let conn = rusqlite::Connection::open_in_memory().unwrap();
let tx = conn.transaction().unwrap();
async {
// do something async
}
.await;
tx.execute_batch("SELECT 1").unwrap();
tx.commit();
});
rt.block_on(handle).unwrap();
}
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 3
- Comments: 18 (11 by maintainers)
sqlxis extremely slow with SQLite, up to 500% slower thanrusqlitein my tests.spawn_blockingis also not an option in cases where you need to call async functions during transaction as async is not possible withinspawn_blocking.Currently there doesn’t seem to be any good way to use SQLite in async Rust.
I’m attempting to create async-wrapper for
rusqlitebased on actor which runs insidestd::thread::spawnand keepsConnectionandTransactionthere. Actor can receive closure viampsc. Closure takesConnectionorTransactionas parameter and is run within the thread, returning result viaoneshotchannel.This would fix the main problem of
sqlxwhich is really high async overhead, as each query is separate async task: The closure can run any number of queries with fixed async overhead per closure.This would also allow running async operations during transaction:
rusqlitedoesn’t provide an async API, please usespawn_blocking.Not using
spawn_blockingis just asking for trouble, and withspawn_blockingthings should just work.(This also roughly relates the question raised at https://github.com/rusqlite/rusqlite/issues/188#issuecomment-631169811 the other day).
From some discussion, I’ve heard that doing blocking operations on async worker threads is a big problem, and that it’s probably good that rusqlite behave as it does currently, as a way to signal to users that they’re doing something very wrong.
In the mean time, you should either follow @xfix’s advice (use
spawn_blocking), or if that is is not-viable, perhaps you could use thesqlxcrate, which seems to make SQLite async by performing IO on a background thread.Eventually there’s a reasonable chance that similar async support will become desirable for rusqlite (I suspect Firefox may eventually need it…), but there are no short term plans for this. (And if this happens it wouldn’t be the default though, it would be behind in a feature; or possibly even a separate
rusqlite-asynccrate).Because of all that I’m going to close this as works-as-intended for now, even if it’s a bit surprising.
Are you able to use
Statementacrossawait? I’m guessing not… I know of a solution forTransaction(make transaction hold&mut Connection. Sadly this requires killing the newunchecked_transactionAPI, which has valid use cases… That said it’s new so not that big of a deal I guess…)It would be nice to fix the general problem though – I suspect fixing that would mean many other APIs don’t work still.