I have a working example using MySQL in an Actix endpoint. The gist of it is here:
extern crate mysql;
use actix_web::web::{Data};
use actix_web::{get, App, Error, HttpResponse, HttpServer, web};
use r2d2_mysql::mysql::{Opts,OptsBuilder};
use serde::Serialize;
use r2d2::Pool;
use r2d2_mysql::MysqlConnectionManager;
use r2d2_mysql::mysql::prelude::Queryable;
#[derive(Serialize)]
pub struct Organization {
pub id: Option<i32>,
pub name: String,
pub country: String
}
#[get("/orgs")]
async fn get_orgs(
pool: Data<Pool<MysqlConnectionManager>>,
) -> Result<HttpResponse, Error> {
let mut conn = pool
.get()
.map_err(|_| HttpResponse::InternalServerError())?;
let orgs = conn
.query_map(
"SELECT id, name, country from organization",
|(id, name, country)| {
Organization { id, name, country }
},
).map_err(|_| HttpResponse::InternalServerError())?;
Ok(HttpResponse::Ok().json(orgs))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let opts = Opts::from_url("mysql://root:mysql@127.0.0.1:33061/ebdb").unwrap();
let builder = OptsBuilder::from_opts(opts);
let m = MysqlConnectionManager::new(builder);
let p = Pool::new(m).unwrap();
let cache = set_mysql_orgs(p.clone());
HttpServer::new(move || App::new()
.data(p.clone())
.service(get_orgs))
.bind("127.0.0.1:8090")?
.run()
.await
}
Which works!
Now, I want to try to populate an in-memory cache to get a single "org". How do I provide an LRU Cache to an Actix endpoint?
Example code that definitely does not work:
extern crate mysql;
use actix_web::web::{Data};
use actix_web::{get, App, Error, HttpResponse, HttpServer, web};
use r2d2_mysql::mysql::{Opts,OptsBuilder};
use serde::Serialize;
use r2d2::Pool;
use r2d2_mysql::MysqlConnectionManager;
use r2d2_mysql::mysql::prelude::Queryable;
use lru::LruCache;
use r2d2_mysql::mysql::uuid::Uuid;
#[derive(Serialize)]
pub struct Organization {
pub id: Option<i32>,
pub name: String,
pub country: String
}
fn set_mysql_orgs(pool: Pool<MysqlConnectionManager>) -> LruCache<Option<i32>, Organization, ()> {
let mut conn = pool
.get()
.map_err(|_| HttpResponse::InternalServerError())?;
let orgs = conn
.query_map(
"SELECT id, name, country from organization",
|(id, name, country)| {
Organization { id, name, country }
},
).map_err(|_| HttpResponse::InternalServerError())?;
// populate the cache with the orgs by ID
let mut cache = LruCache::new(500);
for org in orgs {
cache.put(org.id, org);
}
cache
}
#[get("/org/{id}")]
async fn get_org(org_id: web::Path<Uuid>) -> Result<HttpResponse, Error> {
let org_id = org_id.into_inner();
let org = cache.get(&org_id);
Ok(HttpResponse::Ok().json(org))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let opts = Opts::from_url("mysql://root:mysql@127.0.0.1:33061/ebdb").unwrap();
let builder = OptsBuilder::from_opts(opts);
let m = MysqlConnectionManager::new(builder);
let p = Pool::new(m).unwrap();
let cache = set_mysql_orgs(p.clone());
HttpServer::new(move || App::new()
.data(p.clone())
.service(get_org))
.bind("127.0.0.1:8090")?
.run()
.await
}
I want to:
- Populate the cache in
fn main
- Somehow provide the data(cache) to the
get_org
endpoint instead of MySQL. - Parse the org_id in
get_org
, look up the cache, return the org if it's there.
I'm very new to Rust, so apologies for the "big" question. Trying to learn on the go, but I get stuck sometimes where I end up brute-forcing my way out of problems using thought of languages I already know(Go), which is probably a bad idea.
(Consider the last example almost pseudo code.. Definitely wont compile)