Files
sqlgen/docs/connection_pool.md
2025-07-23 22:32:26 +02:00

6.4 KiB

Connection Pool

sqlgen provides a thread-safe connection pool implementation that efficiently manages database connections. The pool automatically handles connection lifecycle, ensures thread safety, and provides RAII-based session management.

Configuration

The connection pool can be configured using ConnectionPoolConfig:

using namespace sqlgen;

ConnectionPoolConfig config{
    .size = 4,                    // Number of connections in the pool
    .num_attempts = 10,          // Number of retry attempts when acquiring a connection
    .wait_time_in_seconds = 1    // Wait time between retry attempts
};

// Create a pool with the specified configuration
const auto pool = make_connection_pool<postgres::Connection>(
    config,
    postgres::Credentials{
        .user = "postgres",
        .password = "password",
        .host = "localhost",
        .dbname = "postgres"
    }
);

Configuration Parameters

  • size: The number of connections to maintain in the pool
  • num_attempts: Maximum number of attempts to acquire a connection before giving up
  • wait_time_in_seconds: Time to wait between retry attempts when no connections are available

Basic Usage

Creating a Connection Pool

Create a connection pool with a specified number of connections:

using namespace sqlgen;

const auto pool = make_connection_pool<postgres::Connection>(
    config,  // ConnectionPoolConfig
    credentials // Variables necessary to create the connections
);

if (!pool) {
    // Handle error
    std::cerr << "Failed to create pool: " << pool.error() << std::endl;
    return;
}

Acquiring Sessions

Sessions are acquired from the pool using the session() function:

using namespace sqlgen;

// Acquire a session from the pool
const auto session_result = session(pool);

if (!session_result) {
    // Handle error (e.g., no available connections)
    std::cerr << "Failed to acquire session: " << session_result.error() << std::endl;
    return;
}

Chaining Operations

Sessions can be used to chain database operations:

using namespace sqlgen;

const auto result = session(pool)
    .and_then(drop<Person> | if_exists)
    .and_then(write(std::ref(people)))
    .and_then(read<std::vector<Person>>);

if (!result) {
    // Handle error
    std::cerr << "Operation failed: " << result.error() << std::endl;
    return;
}

const auto& people = result.value();

Thread Safety

The connection pool is designed to be thread-safe:

  • Each connection is protected by an atomic flag
  • Sessions are automatically released when they go out of scope
  • The pool uses atomic operations for connection management
  • Multiple threads can safely acquire and release sessions

Example of thread-safe usage with monadic style:

using namespace sqlgen;
using namespace sqlgen::literals;

// Create pool
const auto pool = make_connection_pool<postgres::Connection>(config, credentials);


std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
    threads.emplace_back([&]() {
        session(pool)
            .and_then(update<Person>("age"_c.set(46) | where("id"_c == i)));
    });
}

for (auto& thread : threads) {
    thread.join();
}

Session Management

Sessions are managed through RAII (Resource Acquisition Is Initialization) and support monadic operations:

  • Sessions automatically release their connections when destroyed
  • Connections are returned to the pool when sessions go out of scope
  • No explicit connection release is required
using namespace sqlgen;
using namespace sqlgen::literals;

// Using monadic style for session management with exec
session(pool)
    .and_then(begin_transaction)
    .and_then(update<Person>("age"_c.set(46)))
    .and_then(commit);

Pool Statistics

The connection pool provides methods to monitor its state:

using namespace sqlgen;

const auto pool = make_connection_pool<postgres::Connection>(config, credentials);

// Get total number of connections
const size_t total_connections = pool.value().size();  // Returns 4

// Get number of available connections
const size_t available_connections = pool.value().available();  // Returns 4 initially

Connection Acquisition

The pool implements a retry mechanism when acquiring connections:

  • If no connection is available, the pool will retry up to num_attempts times
  • Between each attempt, it waits for wait_time_in_seconds
  • If all attempts fail, an error is returned
  • This helps handle temporary connection unavailability

Example of handling connection acquisition with retries:

using namespace sqlgen;

// Configure pool with retry behavior
ConnectionPoolConfig config{
    .size = 4,
    .num_attempts = 5,
    .wait_time_in_seconds = 2
};

const auto pool = make_connection_pool<postgres::Connection>(config, credentials);

// Acquire a session - will retry if no connections are available
const auto session_result = session(pool);

Best Practices

  1. Pool Size: Choose an appropriate pool size based on your application's needs:

    • Too small: May cause connection wait times
    • Too large: May waste resources
    • Rule of thumb: Start with (2 * number of CPU cores)
  2. Retry Configuration: Configure retry behavior based on your use case:

    • For high-availability systems: Use more retry attempts with shorter wait times
    • For resource-constrained systems: Use fewer retries with longer wait times
    • Consider your application's latency requirements when setting wait times
  3. Session Lifetime: Keep sessions as short as possible:

    // Good: Session is released immediately after use
    session(pool).and_then(execute_query).value();
    
    // Bad: Session is held longer than necessary
    const auto sess = session(pool).value();
    // ... other operations ...
    sess.execute_query();
    
  4. Transaction Management: Use transactions within sessions for atomic operations:

    session(pool)
        .and_then(begin_transaction)
        .and_then(update<Person>("age"_c.set(46)))
        .and_then(commit)
        .value();
    

Notes

  • The connection pool is template-based and works with any sqlgen connection type
  • Sessions are move-only types (cannot be copied)
  • The pool automatically cleans up connections when destroyed
  • All operations return Result types for error handling
  • The pool is designed to be efficient and minimize contention
  • Connection acquisition is non-blocking (returns error if no connections available)