Redis example with pooled connection
The example here: https://github.com/actix/examples/blob/master/databases/redis/src/main.rs
could be updated with the r2d2 example of pooled Redis client connections from here: https://github.com/redis-rs/redis-rs/pull/388/files
The following code worked for me:
use actix_web::{get, web, App, HttpServer, Responder, Result};
use redis::Commands;
#[get("/simple/url/{id}")]
pub async fn example(
id: web::Path<String>,
redis_pool: web::Data<r2d2::Pool<redis::Client>>
) -> Result<impl Responder> {
let mut conn: r2d2::PooledConnection<redis::Client> = redis_pool.get().unwrap();
// use the connection as usual
let _: () = conn.set("KEY", "VALUE").unwrap();
let val: String = conn.get("KEY").unwrap();
println!("{}", val);
Ok(web::Json(val))
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
let client = redis::Client::open("redis://127.0.0.1/").unwrap();
let pool: r2d2::Pool<redis::Client> = r2d2::Pool::builder()
.max_size(15)
.build(client)
.unwrap_or_else(|e| panic!("Error building redis pool: {}", e));
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(pool.clone()))
.route("/hello", web::get().to(|| async { "Hello World!" }))
.service(example)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
no this code is not ok.
let mut conn: r2d2::PooledConnection<redis::Client> = redis_pool.get().unwrap();
This would block thread and can lead to pontential dead lock. You can check out this example for how to improve the usage of r2d2 inside actix-web.
Or even better use an async connection poolor redis-rs multiplexiable async connections for more efficient client side query.
Thanks for pointing out that the server thread would be blocked.
The code you mentioned makes it clearer that I need to use web::block:
https://github.com/actix/examples/blob/d259177eabf75a7bdd3a84b48d3467c4680c8110/databases/diesel/src/main.rs#L29-L34
The caution about blocking threads appears to be part of the documentation already:
Since each worker thread processes its requests sequentially, handlers which block the current thread will cause the current worker to stop processing new requests:
It may be worth adding a section heading for that part of the docs, something like "Async Long-Running Operations" or "Async and Blocking Threads".
There's some discussion about using MultiplexedConnection over in the redis-rs issue, I can give that a try: https://github.com/redis-rs/redis-rs/pull/388#issuecomment-1003639343
Example using web::block
The following code seemed to work:
use actix_web::{get, web, App, HttpServer, Responder, Result};
use redis::Commands;
#[get("/simple/url/{id}")]
pub async fn example(
id: web::Path<String>,
redis_pool: web::Data<r2d2::Pool<redis::Client>>
) -> Result<impl Responder> {
let val = web::block(move || {
let mut conn = redis_pool.get().unwrap();
conn.set::<&str, &str, ()>("KEY", "VALUE123")?;
conn.get::<&str, String>("KEY")
}).await?.map_err(actix_web::error::ErrorInternalServerError)?;
Ok(web::Json(val))
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
let client = redis::Client::open("redis://127.0.0.1/").unwrap();
let pool: r2d2::Pool<redis::Client> = r2d2::Pool::builder()
.max_size(15)
.build(client)
.unwrap_or_else(|e| panic!("Error building redis pool: {}", e));
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(pool.clone()))
.route("/hello", web::get().to(|| async { "Hello World!" }))
.service(example)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Multiplexed connections issue
Ran into an issue trying to get the multiplexed connection working.
This was the code:
// Cargo.toml line:
// redis = { version = "0.21.5", features = ["r2d2", "aio", "tokio-comp", "connection-manager"] }
let client = redis::Client::open("redis://127.0.0.1/").unwrap();
let mut manager = client.get_tokio_connection_manager().await?;
let pool: r2d2::Pool<redis::aio::ConnectionManager> = r2d2::Pool::builder()
.max_size(15)
.build(&manager)
.unwrap_or_else(|e| panic!("Error building redis pool: {}", e));
And this is the error:
error[E0277]: the trait bound `&ConnectionManager: ManageConnection` is not satisfied
--> src/main.rs:15:15
|
15 | let pool: r2d2::Pool<&redis::aio::ConnectionManager> = r2d2::Pool::builder()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ManageConnection` is not implemented for `&ConnectionManager`
|
= help: the trait `ManageConnection` is implemented for `Client`
note: required by a bound in `Pool`
--> /Users/rudolfo/.cargo/registry/src/github.com-1ecc6299db9ec823/r2d2-0.8.10/src/lib.rs:319:8
|
319 | M: ManageConnection;
| ^^^^^^^^^^^^^^^^ required by this bound in `Pool`
Not sure if I'm misunderstanding the docs or the examples for the multiplexed connection or if the ConnectionManager code in redis-rs is missing the trait and I should open a new issue over there.