Ormer
Home
Quick Start
GitHub
  • 简体中文
  • English
Home
Quick Start
GitHub
  • 简体中文
  • English
  • Introduction to Ormer
  • Quick Start
  • Model Definition
  • Database Connection
  • Data Operations
  • Query Builder
  • Advanced Queries
  • Transaction Management
  • Connection Pool

Connection Pool

Connection pooling is an important mechanism for managing database connections, significantly improving application performance and resource utilization.

What is a Connection Pool

A connection pool maintains a set of pre-created database connections that applications can reuse, avoiding the overhead of frequently creating and destroying connections.

Advantages

  • Performance Improvement - Avoids creating new connections for each operation
  • Resource Control - Limits maximum connections to prevent database overload
  • Connection Reuse - Automatically manages connection lifecycle
  • Fault Recovery - Automatically detects and replaces failed connections

Creating a Connection Pool

Basic Usage

use ormer::{Database, DbType, ConnectionPool};

let pool = Database::create_pool(DbType::PostgreSQL, "postgresql://user:pass@localhost/dbname")
    .max_size(10)  // Maximum connections
    .build()
    .await?;

Configuration Options

let pool = Database::create_pool(DbType::PostgreSQL, connection_string)
    .max_size(20)        // Maximum connections (default: 10)
    .min_size(5)         // Minimum idle connections (optional)
    .idle_timeout(300)   // Idle timeout (seconds) (optional)
    .build()
    .await?;

Using Connection Pool

Getting Connections

// Get connection from pool
let conn = pool.get().await?;

// Use connection
let users: Vec<User> = conn
    .select::<User>()
    .collect()
    .await?;

// Connection automatically returns to pool

Automatic Connection Management

The connection pool automatically manages connections:

async fn handle_request(pool: &ConnectionPool) -> Result<(), Box<dyn std::error::Error>> {
    // Get connection
    let conn = pool.get().await?;
    
    // Execute operations
    conn.insert(&user).await?;
    
    // conn automatically returns to pool when out of scope
    Ok(())
}

Complete Examples

Web Application Example

use ormer::{Database, DbType, ConnectionPool, Model};
use std::sync::Arc;
use tokio::sync::Mutex;

#[derive(Debug, Model)]
#[table = "users"]
struct User {
    #[primary(auto)]
    id: i32,
    name: String,
    email: String,
}

struct AppState {
    pool: ConnectionPool,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create connection pool
    let pool = Database::create_pool(
        DbType::PostgreSQL,
        "postgresql://user:pass@localhost/mydb"
    )
    .max_size(20)
    .build()
    .await?;
    
    let state = Arc::new(AppState { pool });
    
    // Simulate handling multiple requests
    let mut handles = vec![];
    
    for i in 0..10 {
        let state = state.clone();
        let handle = tokio::spawn(async move {
            // Get connection from pool
            let conn = state.pool.get().await.unwrap();
            
            // Execute database operations
            let users: Vec<User> = conn
                .select::<User>()
                .range(0..10)
                .collect()
                .await
                .unwrap();
            
            println!("Request {}: {} users", i, users.len());
        });
        
        handles.push(handle);
    }
    
    // Wait for all requests to complete
    for handle in handles {
        handle.await.unwrap();
    }
    
    Ok(())
}

Connection Pool Configuration Example

use ormer::{Database, DbType};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Development environment: Few connections
    let dev_pool = Database::create_pool(
        DbType::Turso,
        "file:dev.db"
    )
    .max_size(5)
    .build()
    .await?;
    
    // Production environment: More connections
    let prod_pool = Database::create_pool(
        DbType::PostgreSQL,
        "postgresql://user:pass@localhost/prod_db"
    )
    .max_size(50)
    .min_size(10)
    .idle_timeout(600)  // 10 minutes
    .build()
    .await?;
    
    Ok(())
}

Connection Pool Best Practices

1. Set Connection Count Reasonably

// ✅ Recommended: Set based on server configuration
let pool = Database::create_pool(DbType::PostgreSQL, &db_url)
    .max_size(num_cpus::get() * 4)  // 4 times CPU cores
    .build()
    .await?;

// ❌ Avoid: Connection count too large or too small
.max_size(1000)  // Too large, wastes resources
.max_size(1)     // Too small, poor performance

2. Use Connection Pool Instead of Single Connection

// ✅ Recommended: Use connection pool
let pool = Database::create_pool(DbType::PostgreSQL, &db_url)
    .max_size(20)
    .build()
    .await?;

async fn handle_request(pool: &ConnectionPool) {
    let conn = pool.get().await?;
    // Use conn...
}

// ❌ Avoid: Create new connection each time
async fn handle_request_bad(db_url: &str) {
    let db = Database::connect(DbType::PostgreSQL, db_url).await?;
    // Use db...
} // Connection is dropped

3. Release Connections Promptly

// ✅ Recommended: Connection automatically returns to pool after use
{
    let conn = pool.get().await?;
    conn.insert(&user).await?;
} // conn is released here

// ❌ Avoid: Holding connections for too long
let conn = pool.get().await?;
tokio::time::sleep(Duration::from_secs(60)).await;  // Bad!
conn.insert(&user).await?;

4. Error Handling

async fn safe_query(pool: &ConnectionPool) -> Result<Vec<User>, Box<dyn std::error::Error>> {
    let conn = pool.get().await?;
    
    let users = conn
        .select::<User>()
        .collect::<Vec<_>>()
        .await?;
    
    Ok(users)
}

Monitoring Connection Pool

Connection Pool Status

// Some connection pool implementations provide status monitoring
println!("Active connections: {}", pool.active_connections());
println!("Idle connections: {}", pool.idle_connections());
println!("Total connections: {}", pool.total_connections());

Performance Metrics

Monitor the following metrics:

  • Wait Time - Time waiting to get a connection
  • Active Connections - Number of connections in use
  • Idle Connections - Number of available connections
  • Connection Error Rate - Number of connection failures

Connection Pool Configuration for Different Databases

Turso/SQLite

let pool = Database::create_pool(DbType::Turso, "file:test.db")
    .max_size(10)  // SQLite typically doesn't need many connections
    .build()
    .await?;

PostgreSQL

let pool = Database::create_pool(
    DbType::PostgreSQL,
    "postgresql://user:pass@localhost/dbname"
)
.max_size(20)  // PostgreSQL supports many concurrent connections
.min_size(5)
.build()
.await?;

MySQL

let pool = Database::create_pool(
    DbType::MySQL,
    "mysql://user:pass@localhost/dbname"
)
.max_size(20)
.min_size(5)
.build()
.await?;

Common Issues

1. Connection Pool Exhaustion

When all connections are in use, new requests will wait:

// Connection pool is full, this will block until a connection is available
let conn = pool.get().await?;

Solutions:

  • Increase max_size
  • Optimize queries to reduce connection占用 time
  • Use timeout mechanism
// Get connection with timeout
let conn = tokio::time::timeout(
    Duration::from_secs(5),
    pool.get()
).await??;

2. Connection Leaks

Forgetting to release connections leads to connection leaks:

// ❌ Wrong: Connection not released
let conn = pool.get().await?;
// Function returns without releasing conn

// ✅ Correct: Use scope
{
    let conn = pool.get().await?;
    // Use conn
} // conn automatically released

3. Connection Failures

Database restarts or network issues can cause connection failures:

// Connection pool typically handles this automatically
let conn = pool.get().await?;

// If connection fails, reacquire
match conn.select::<User>().collect().await {
    Ok(users) => println!("Got {} users", users.len()),
    Err(_) => {
        // Connection pool will automatically replace failed connection
        let conn = pool.get().await?;
        let users: Vec<User> = conn.select::<User>().collect().await?;
    }
}

Performance Tuning

1. Adjust Connection Count Based on Load

// Low load application
.max_size(5)

// Medium load
.max_size(20)

// High load application
.max_size(50)

2. Set Reasonable Timeouts

// Connection acquisition timeout
.max_wait_time(5)  // 5 seconds

// Idle timeout
.idle_timeout(300)  // 5 minutes

3. Pre-create Connections

// Create minimum connections at startup
let pool = Database::create_pool(DbType::PostgreSQL, &db_url)
    .min_size(10)  // Pre-create 10 connections
    .build()
    .await?;
Prev
Transaction Management