Search code examples
rustrust-axum

How to make axum handler work with Mutex-protected cache?


Below is my simple Axum server code:

use std::num::NonZeroUsize;
use std::sync::{Arc, Mutex};

use axum::response::IntoResponse;
use axum::routing::get;
use axum::{Extension, Router};
use bytes::Bytes;
use lru::LruCache;

type Cache = Arc<Mutex<LruCache<u64, Bytes>>>;

async fn generate(Extension(cache): Extension<Cache>) -> impl IntoResponse {
    let key: u64 = ...;

    let mut g = cache.lock().unwrap();
    let data = match g.get(&key) {
        Some(v) => {
            println!("Match cache {}", key);
            v.to_owned()
        }
        None => {
            println!("Retrieve url");
            match reqwest::get("https://www.rust-lang.org/").await {
                Ok(response) => match response.bytes().await {
                    Ok(body) => {
                        g.put(key, body.clone());
                        body
                    }
                    Err(_) => Bytes::new(),
                },
                Err(_) => Bytes::new(),
            }
        }
    };

    ...
}

#[tokio::main]
async fn main() {
    let cache: Cache = Arc::new(Mutex::new(LruCache::new(NonZeroUsize::new(100).unwrap())));

    let app = Router::new()
        .route("/image/:spec/:url", get(generate))
        .layer(Extension(cache));

    ...
}

But I'm getting this error:

error[E0277]: the trait bound `fn(Extension<Arc<std::sync::Mutex<LruCache<u64, bytes::Bytes>>>>) -> impl Future<Output = impl IntoResponse> {generate}: Handler<_, _>` is not satisfied
   --> src/main.rs:42:41
    |
42  |         .route("/image/:spec/:url", get(generate))
    |                                     --- ^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extension<Arc<std::sync::Mutex<LruCache<u64, bytes::Bytes>>>>) -> impl Future<Output = impl IntoResponse> {generate}`
    |                                     |
    |                                     required by a bound introduced by this call
    |
    = help: the following other types implement trait `Handler<T, S>`:
              <Layered<L, H, T, S> as Handler<T, S>>
              <MethodRouter<S> as Handler<(), S>>

I spent a long time but didn't figure it out, I think the problem is here:

let mut g = cache.lock().unwrap();

because if I changed it to let mut g: HashMap<u64, Bytes> = HashMap::new();, the error will be gone. What is the issue here? How can I use my cache?


Solution

  • I needed to change my cache to use tokio::sync::Mutex instead of std::sync::Mutex:

    The lock guard for the standard library Mutex is not Send. When held over an .await point, that prevents the Future generated by the async function from implementing Send, which is required by axum. Tokio's Mutex does not have this problem.

    See also: