1 unstable release

0.1.0 Jan 7, 2023

#15 in #bb8-connection

MIT license

13KB
149 lines

bb8-rusqlite

crates.io version MIT licence Documentation Build state

This crate provides a connection manager that you can use with bb8 to provide a pool of rusqlite connections.

Example

See examples/basic.rs for a self-contained example, but essentially, this is how it looks:

let manager = RusqliteConnectionManager::new("my-database.db");
let pool = bb8::Pool::builder().build(manager).await?;

// ...

let conn = pool.get().await?;
// conn is a rusqlite::Connection, so do whatever you'd normally do with it!

Caveats

No in-memory databases

rusqlite allows in-memory databases to be created with the Connection::open_in_memory family of methods. In memory SQLite databases are per-connection, which means that having a pool of these connections would result in each connection having its own, completely separate database!

As this would be rather confusing, no wrappers are provided for those methods. Additionally, you shouldn't use rusqlite::OpenFlags::SQLITE_OPEN_MEMORY, unless you like very weird bugs in your code.

rusqlite::Connection is still very synchronous

bb8 uses tokio heavily under the hood, and so does this connection manager. However, you'll ultimately end up handling rusqlite::Connection instances, and these do not provide any sort of async API.

If you care about concurrency, you should take care to avoid starving the runtime for long periods of time by marking tasks using Connections as blocking. You can do this either by moving the Connection onto another blocking task, or by using tokio::task::block_in_place(). In practice, the latter is probably what you'll want in most cases: moving Connection instances is fraught, since they don't implement Sync.

tokio's multi-threaded runtime is required

Due to the aforementioned Connection synchronicity, bb8-rusqlite must be used with a multi-threaded executor so tokio::task::block_in_place() is available.

Future possibilities

bb8-diesel takes an interesting alternative approach: by wrapping the synchronous Diesel API using block_in_place(), it provides a safer, still synchronous API that makes it less likely to starve the thread pool.

An interesting future piece of work here would be to do the same: wrap rusqlite types in wrappers using block_in_place(), and then implement a connection manager that returns those, instead of raw rusqlite::Connection instances. This is harder because Connection is a concrete type and not a trait, but with a lot of boilerplate and some Deref implementations to fall back on, it should be possible. Selfishly, I just don't need it right now!

Dependencies

~31MB
~508K SLoC