#batch #cache #data-loader #key-value

ultra-batch

Tokio-based library to batch and cache database queries or other data lookups

3 unstable releases

0.2.0 Feb 15, 2022
0.1.1 Aug 20, 2020
0.1.0 Aug 17, 2020

#329 in Caching

Download history 12/week @ 2023-12-15 5/week @ 2024-02-09 31/week @ 2024-02-16 48/week @ 2024-02-23 60/week @ 2024-03-01 148/week @ 2024-03-08 136/week @ 2024-03-15 54/week @ 2024-03-22 63/week @ 2024-03-29

413 downloads per month
Used in warpgrapher

MIT/Apache

30KB
365 lines

ultra-batch

Crate Docs Tests

ultra-batch is a Rust library to batch and cache database queries or other potentially expensive data lookups. The main motivation for this library is to solve the "N + 1" query problem seen in GraphQL and elsewhere. This library takes heavy influence from the GraphQL Foundation's DataLoader. It's designed primarily for dealing with database queries, but it can be used to batch any potentially expensive data loading operations.

Example Use

First, add tokio, async-trait, and anyhow (optional) as dependencies.

use async_trait::async_trait;
use ultra_batch::{Fetcher, Batcher, Cache};

#[derive(Debug, Clone)]
struct User {
    id: u64,
    // User model from your DB, etc.
}

struct UserFetcher {
    // Database connection, etc.
}

#[async_trait]
impl Fetcher for UserFetcher {
    // The thing we can use to look up a single `User` (like an ID)
    type Key = u64;

    // The thing we are trying to look up
    type Value = User;

    // Used to indicate the batch request failed (DB connection failed, etc)
    type Error = anyhow::Error;

    // Fetch a batch of users
    async fn fetch(
        &self,
        keys: &[Self::Key],
        values: &mut Cache<'_, Self::Key, Self::Value>,
    ) -> Result<(), Self::Error> {
        let users: Vec<User> = todo!(); // Fetch users based on the given keys
        for user in users {
            values.insert(user.id, user); // Insert all users we found
        }

        Ok(())
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let fetcher = UserFetcher { /* ... */ };
    let batcher = Batcher::build(fetcher).finish();

    // Retrieve a user by ID. If `load` gets called in other tasks/threads
    // at the same time, then all the requested IDs will get batched together
    let user = batcher.load(123).await?;

    println!("User: {:?}", user);

    Ok(())
}

Minimum Supported Rust Version (MSRV)

ultra-batch currently supports Rust v1.56. Changes to the MSRV are tracked in the Changelog.

Alternative projects

License

Licensed under either the MIT license or Apache 2.0 license (licensee's choice).

Dependencies

~3.5–5.5MB
~89K SLoC