How to set up connection pools for production¶
Connection pools manage a fixed set of warehouse connections, reusing them across requests instead of opening a new connection each time. This guide covers pool sizing, lifecycle management, and running multiple pools side-by-side.
Size the pool¶
create_pool() accepts pool_size (steady-state connections)
and max_overflow (burst capacity above pool_size). The defaults are 5 and 3
respectively, meaning up to 8 concurrent connections:
from adbc_poolhouse import SnowflakeConfig, create_pool
from semolina import register
config = SnowflakeConfig(
account="xy12345.us-east-1",
user="svc_analytics",
password="...",
database="analytics",
warehouse="compute_wh",
)
pool = create_pool(
config,
pool_size=10,
max_overflow=5,
timeout=30,
recycle=1800,
)
register("default", pool, dialect="snowflake")
from adbc_poolhouse import DatabricksConfig, create_pool
from semolina import register
config = DatabricksConfig(
server_hostname="workspace.cloud.databricks.com",
http_path="/sql/1.0/warehouses/abc123",
access_token="dapi...",
)
pool = create_pool(
config,
pool_size=10,
max_overflow=5,
timeout=30,
recycle=1800,
)
register("default", pool, dialect="databricks")
The pool parameters control connection behaviour:
Parameter |
Default |
Description |
|---|---|---|
|
|
Number of connections kept open and reused |
|
|
Extra connections allowed above |
|
|
Seconds to wait for a connection before raising an error |
|
|
Seconds before a connection is replaced with a fresh one |
|
|
Ping connections before checkout ( |
Tip
Start with pool_size matching your expected concurrent query count (e.g. web
server worker count), and set max_overflow to 50–100% of pool_size for
traffic spikes. A recycle of 1800 seconds (30 minutes) prevents stale connections
from accumulating during low-traffic periods.
Load pool settings from TOML¶
pool_from_config() reads .semolina.toml and passes all fields
(after removing type) to the adbc-poolhouse config class. Add pool parameters
directly in the TOML section:
[connections.default]
type = "snowflake"
account = "xy12345.us-east-1"
user = "svc_analytics"
password = "..."
database = "analytics"
warehouse = "compute_wh"
from semolina import register, pool_from_config
pool, dialect = pool_from_config()
register("default", pool, dialect=dialect)
Warning
pool_from_config() passes extra TOML fields through to the adbc-poolhouse
config class. Pool sizing parameters (pool_size, max_overflow, etc.) are
arguments to create_pool(), not fields on the config class. To customise pool
sizing with TOML-loaded credentials, construct the pool manually as shown in the
section above.
Manage pool lifecycle¶
Create pools at application startup and close them at shutdown. Use
close_pool() from adbc-poolhouse to release both the SQLAlchemy pool and
the underlying ADBC source connection:
from adbc_poolhouse import (
SnowflakeConfig,
create_pool,
close_pool,
)
from semolina import register, unregister
# Startup
config = SnowflakeConfig(
account="xy12345.us-east-1",
user="svc_analytics",
password="...",
database="analytics",
warehouse="compute_wh",
)
pool = create_pool(config, pool_size=10)
register("default", pool, dialect="snowflake")
# ... application runs ...
# Shutdown
unregister("default")
close_pool(pool)
unregister() removes the pool from the registry so no new queries
use it. close_pool() then disposes the pool and closes the ADBC driver connection.
Warning
Call close_pool() instead of pool.dispose() directly. close_pool()
also closes the underlying ADBC source connection, preventing resource leaks.
Register multiple pools with .using()¶
Register pools under different names to query multiple warehouses or use different credentials for different workloads:
from adbc_poolhouse import SnowflakeConfig, create_pool
from semolina import register
# Production pool -- large, for dashboard queries
prod_config = SnowflakeConfig(
account="xy12345.us-east-1",
user="svc_dashboard",
password="...",
database="analytics",
warehouse="large_wh",
)
prod_pool = create_pool(
prod_config, pool_size=20, max_overflow=10
)
register("default", prod_pool, dialect="snowflake")
# Reporting pool -- small, for scheduled reports
report_config = SnowflakeConfig(
account="xy12345.us-east-1",
user="svc_reports",
password="...",
database="analytics",
warehouse="small_wh",
)
report_pool = create_pool(report_config, pool_size=3)
register("reports", report_pool, dialect="snowflake")
Use .using() on a query to select which pool to execute against:
from semolina import SemanticView, Metric, Dimension
class Sales(SemanticView, view="sales"):
revenue = Metric()
country = Dimension()
# Uses "default" pool (implicit)
cursor = (
Sales.query()
.metrics(Sales.revenue)
.dimensions(Sales.country)
.execute()
)
# Uses "reports" pool (explicit)
cursor = (
Sales.query()
.metrics(Sales.revenue)
.dimensions(Sales.country)
.using("reports")
.execute()
)
Pool resolution is lazy – it happens at .execute() time, not when .using()
is called. You can build queries before pools are registered.
Use named TOML sections for multiple pools¶
Define multiple connection sections in .semolina.toml and load each by name:
[connections.default]
type = "snowflake"
account = "xy12345.us-east-1"
user = "svc_dashboard"
password = "..."
database = "analytics"
warehouse = "large_wh"
[connections.reports]
type = "snowflake"
account = "xy12345.us-east-1"
user = "svc_reports"
password = "..."
database = "analytics"
warehouse = "small_wh"
from semolina import register, pool_from_config
pool, dialect = pool_from_config(connection="default")
register("default", pool, dialect=dialect)
report_pool, report_dialect = pool_from_config(
connection="reports"
)
register("reports", report_pool, dialect=report_dialect)
The connection parameter of pool_from_config() maps to the section
name after connections. in the TOML file.
Close all pools at shutdown¶
When running multiple pools, close each one individually:
from adbc_poolhouse import close_pool
from semolina import unregister
for name, pool_ref in [
("default", prod_pool),
("reports", report_pool),
]:
unregister(name)
close_pool(pool_ref)
Keep references to your pool objects so you can close them during shutdown.
unregister() does not return the pool – it only removes
the registry entry.
See also¶
How to choose and configure a backend – connection patterns and backend selection
How to connect to Snowflake – Snowflake TOML fields and credentials
How to connect to Databricks – Databricks TOML fields and credentials
How to use Semolina in a web API – pool lifecycle in a FastAPI application