mirror of
https://github.com/getml/sqlgen.git
synced 2026-01-04 08:30:30 -06:00
230 lines
6.4 KiB
Markdown
230 lines
6.4 KiB
Markdown
# 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`:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
```cpp
|
|
// 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:
|
|
```cpp
|
|
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)
|