Practical Rust Web Development - Connection Pool
Tags: diesel, rust, r2d2
Published: 2019-05-16 20:36:42

---
title: Practical Rust Web Development - Connection Pool
published: true
description: Create a database connection pool for Diesel
tags: #diesel #rust #r2d2
series: Practical Rust Web Development
---

In a previous post we create the basics Api for a [web store] (https://dev.to/werner/practical-rust-web-development-api-rest-29g1). This time I'll be covering an enhancement to the database connection, well be using a generic connection pool known as [r2d2](https://github.com/sfackler/r2d2), the main advantage is that we can improve the connection to the database by caching the [resource](https://en.wikipedia.org/wiki/Connection_pool), Diesel provides a module for that, we just need to enable it.

`Cargo.toml`:

```toml
diesel = { version = "1.0.0", features = ["postgres", "r2d2"] }
```

Then we need to configure our db to handle pools connections, we add a new function `init_pool` to instantiate a new db pool, we edit our `db_connection.rs` file:

`src/db_connection.rs`:

```rust
// This is my diff git showing what the changes are.

-use diesel::prelude::*;
 use diesel::pg::PgConnection;
 use dotenv::dotenv;
 use std::env;
+use diesel::r2d2::{ Pool, PooledConnection, ConnectionManager, PoolError };
 
-pub fn establish_connection() -> PgConnection {
+pub type PgPool = Pool<ConnectionManager<PgConnection>>;
+pub type PgPooledConnection = PooledConnection<ConnectionManager<PgConnection>>;
+
+fn init_pool(database_url: &str) -> Result<PgPool, PoolError> {
+    let manager = ConnectionManager::<PgConnection>::new(database_url);
+    Pool::builder().build(manager)
+}
+
+pub fn establish_connection() -> PgPool {
     dotenv().ok();
 
     let database_url = env::var("DATABASE_URL")
         .expect("DATABASE_URL must be set");
-    PgConnection::establish(&database_url)
-        .expect(&format!("Error connecting to {}", database_url))
+    init_pool(&database_url).expect("Failed to create pool")
 }

```

We need to perform other changes, I'll paste my diff git as well, you can see what lines have to be remove or change.


`src/models/product.rs`:

```rust
+use diesel::PgConnection;

 impl ProductList {
-    pub fn list() -> Self {
+    pub fn list(connection: &PgConnection) -> Self {
         use diesel::RunQueryDsl;
         use diesel::QueryDsl;
         use crate::schema::products::dsl::*;
-        use crate::db_connection::establish_connection;
-
-        let connection = establish_connection();
 
         let result = 
             products
                 .limit(10)
-                .load::<Product>(&connection)
+                .load::<Product>(connection)
                 .expect("Error loading products");
 
         ProductList(result)
...

 impl NewProduct {
-    pub fn create(&self) -> Result<Product, diesel::result::Error> {
+    pub fn create(&self, connection: &PgConnection) -> Result<Product, diesel::result::Error> {
         use diesel::RunQueryDsl;
-        use crate::db_connection::establish_connection;
 
-        let connection = establish_connection();
         diesel::insert_into(products::table)
             .values(self)
-            .get_result(&connection)
+            .get_result(connection)
     }
 }
...

 impl Product {
-    pub fn find(id: &i32) -> Result<Product, diesel::result::Error> {
+    pub fn find(id: &i32, connection: &PgConnection) -> Result<Product, diesel::result::Error> {
         use diesel::QueryDsl;
         use diesel::RunQueryDsl;
-        use crate::db_connection::establish_connection;
-
-        let connection = establish_connection();
 
-        products::table.find(id).first(&connection)
+        products::table.find(id).first(connection)
     }
 
-    pub fn destroy(id: &i32) -> Result<(), diesel::result::Error> {
+    pub fn destroy(id: &i32, connection: &PgConnection) -> Result<(), diesel::result::Error> {
         use diesel::QueryDsl;
         use diesel::RunQueryDsl;
         use crate::schema::products::dsl;
-        use crate::db_connection::establish_connection;
 
-        let connection = establish_connection();
-
-        diesel::delete(dsl::products.find(id)).execute(&connection)?;
+        diesel::delete(dsl::products.find(id)).execute(connection)?;
         Ok(())
     }
 
-    pub fn update(id: &i32, new_product: &NewProduct) -> Result<(), diesel::result::Error> {
+    pub fn update(id: &i32, new_product: &NewProduct, connection: &PgConnection) ->
+     Result<(), diesel::result::Error> {
         use diesel::QueryDsl;
         use diesel::RunQueryDsl;
         use crate::schema::products::dsl;
-        use crate::db_connection::establish_connection;
-
-        let connection = establish_connection();
 
         diesel::update(dsl::products.find(id))
             .set(new_product)
-            .execute(&connection)?;
+            .execute(connection)?;
         Ok(())
     }
 }

```

In `models/product.rs` We just add a new parameter to every method to pass the database connection.


`src/handlers/products.rs`:

```rust
 use actix_web::{HttpRequest, HttpResponse };
+use actix_web::web;
 
 use crate::models::product::ProductList;
+use crate::db_connection::{ PgPool, PgPooledConnection };
+
+fn pg_pool_handler(pool: web::Data<PgPool>) -> Result<PgPooledConnection, HttpResponse> {
+    pool
+    .get()
+    .map_err(|e| {
+        HttpResponse::InternalServerError().json(e.to_string())
+    })
+}
 
-pub fn index(_req: HttpRequest) -> HttpResponse {
-    HttpResponse::Ok().json(ProductList::list())
+pub fn index(_req: HttpRequest, pool: web::Data<PgPool>) -> Result<HttpResponse, HttpResponse> {
+    let pg_pool = pg_pool_handler(pool)?;
+    Ok(HttpResponse::Ok().json(ProductList::list(&pg_pool)))
 }
 
 use crate::models::product::NewProduct;
-use actix_web::web;
 
-pub fn create(new_product: web::Json<NewProduct>) -> Result<HttpResponse, HttpResponse> {
+pub fn create(new_product: web::Json<NewProduct>, pool: web::Data<PgPool>) -> Result<HttpResponse, HttpResponse> {
+    let pg_pool = pg_pool_handler(pool)?;
     new_product
-        .create()
+        .create(&pg_pool)
         .map(|product| HttpResponse::Ok().json(product))
         .map_err(|e| {
             HttpResponse::InternalServerError().json(e.to_string())

-pub fn show(id: web::Path<i32>) -> Result<HttpResponse, HttpResponse> {
-    Product::find(&id)
+pub fn show(id: web::Path<i32>, pool: web::Data<PgPool>) -> Result<HttpResponse, HttpResponse> {
+    let pg_pool = pg_pool_handler(pool)?;
+    Product::find(&id, &pg_pool)
         .map(|product| HttpResponse::Ok().json(product))
         .map_err(|e| {
             HttpResponse::InternalServerError().json(e.to_string())
         })
 }
 
-pub fn destroy(id: web::Path<i32>) -> Result<HttpResponse, HttpResponse> {
-    Product::destroy(&id)
+pub fn destroy(id: web::Path<i32>, pool: web::Data<PgPool>) -> Result<HttpResponse, HttpResponse> {
+    let pg_pool = pg_pool_handler(pool)?;
+    Product::destroy(&id, &pg_pool)
         .map(|_| HttpResponse::Ok().json(()))
         .map_err(|e| {
             HttpResponse::InternalServerError().json(e.to_string())
         })
 }
 
-pub fn update(id: web::Path<i32>, new_product: web::Json<NewProduct>) -> Result<HttpResponse, HttpResponse> {
-    Product::update(&id, &new_product)
+pub fn update(id: web::Path<i32>, new_product: web::Json<NewProduct>, pool: web::Data<PgPool>) -> Result<HttpResponse, HttpResponse> {
+    let pg_pool = pg_pool_handler(pool)?;
+    Product::update(&id, &new_product, &pg_pool)
         .map(|_| HttpResponse::Ok().json(()))
         .map_err(|e| {
             HttpResponse::InternalServerError().json(e.to_string())

```

In `handlers/products.rs` we add a new function to send the request parameter and return the db pool we need for our model, the source code with the new changes is available [here](https://github.com/werner/practical-rust-web/tree/v1.1).