4
votes

I have an API I'm implementing where I have an expensive function that needs to be called. I'd like to either memoize this function or use a key value cache to look up previous results. I'd also consider a Mutex or similar structure, but I would like to avoid something external like Redis or Memcached. From what I've read, a Mutex has bad performance for caching though. How can I use a key value store like a HashMap or memoize a function inside an actix_web async route? Right now, I am trying a simple HashMap but I get this error: can't borrow data in an Arc as mutable.

async fn index(
    kv: web::Data<HashMap<&str, i64>>,
) -> Result<HttpResponse> {
    dbg!(kv);   
    kv.insert("four", 4);
    Ok(HttpResponse::Ok().json(kv)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let tuples: Vec<(&str, i64)> = vec![("one", 1), ("two", 2), ("three", 3)];
    let mut m = tuples.into_iter().collect::<HashMap<&str, i64>>();
    let mut kv = web::Data::new(m);
    // move is necessary to give closure below ownership of counter1
    HttpServer::new(move || {
            App::new()
                .app_data(kv.clone())
                .route("/", web::get().to(index))
        })
        .bind("127.0.0.1:8080")?
        .run()
        .await
}
1

1 Answers

1
votes

Seems like you can use the cached crate.

Move the logic to another function. Something like the following code works:

use actix_web::{web, App, HttpServer, HttpResponse};
use cached::proc_macro::cached;

async fn index() -> HttpResponse {
    dbg!("in index");
    let number = get_number(2);
    HttpResponse::Ok().body(number.to_string())
}

#[cached]
fn get_number(n: i32) -> i32 {
    dbg!("in get_number");
    n * 2
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(move || {
            App::new()
                .route("/", web::get().to(index))
        })
        .bind("127.0.0.1:8080")?
        .run()
        .await
}