Database
Pre-fabricates sqlx database connection pools and closes connections during spindown.
Quickstart
- Enable one of the
database-{type}features. (Here we use an in-memory SQLite as an example.)
cargo add strut --features database-sqlite
- Configure the default database.
- YAML
- TOML
database:
in_memory: true
[database]
in_memory = true
use strut::database::sqlx::*;
use strut::Database;
#[strut::main]
async fn main() {
let pool: Pool<Sqlite> = Database::default();
// connection pool ready to use
}
Cargo features
⛳︎ Feature database-mysql
Enables connections to MySQL/MariaDB databases.
⛳︎ Feature database-postgres
Enables connections to PostgreSQL databases.
⛳︎ Feature database-sqlite
Enables connections to SQLite databases.
⛳︎ Feature database-default-mysql
When multiple database types are enabled, marks MySQL/MariaDB as the default type.
⛳︎ Feature database-default-postgres
When multiple database types are enabled, marks PostgreSQL as the default type.
⛳︎ Feature database-default-sqlite
When multiple database types are enabled, marks SQLite as the default type.
Default database type
When only one database-{type} feature is enabled, this database type is also implicitly treated as the default type.
When multiple database types are enabled, the ambiguity about the default type can be optionally broken using one of the database-default-{type} features.
No more than one of the database-default-{type} features may be enabled at a time.
Otherwise, a compilation error will occur.
It is also valid to not have a default database type at all.
Component structure
☑︎ Configuration
The database component allows configuring any number of database handles. Most applications, however, would need just one. If the features indicate a default database type, then an unnamed default database handle of that type can be configured. All other database handles are named.
A full configuration is structured as such:
- YAML
- TOML
database:
# default database handle goes here
mysql:
named_mysql_a: {} # named_mysql_a database handle goes here
named_mysql_b: {} # named_mysql_b database handle goes here
postgres:
named_postgres_a: {} # named_postgres_a database handle goes here
named_postgres_b: {} # named_postgres_b database handle goes here
sqlite:
named_sqlite_a: {} # named_sqlite_a database handle goes here
named_sqlite_b: {} # named_sqlite_b database handle goes here
[database]
# default database handle goes here
[database.mysql]
named_mysql_a = {} # named_mysql_a database handle goes here
named_mysql_b = {} # named_mysql_b database handle goes here
[database.postgres]
named_postgres_a = {} # named_postgres_a database handle goes here
named_postgres_b = {} # named_postgres_b database handle goes here
[database.sqlite]
named_sqlite_a = {} # named_sqlite_a database handle goes here
named_sqlite_b = {} # named_sqlite_b database handle goes here
An empty database handle (like in the example above) doesn’t make much sense (unless you are using a database on localhost with default port and credentials).
Below are handle examples for each supported database types.
MySQL/MariaDB handle
A handle may be given using a URL. Don’t use this if you intend to overwrite password (or any part of the handle) using environment variables.
MySQL/MariaDB URL handle
Various ways of configuring a MySQL/MariaDB handle as a URL:
- YAML
- TOML
database:
url: mysql://root:password@localhost:3306/default # assuming MySQL/MariaDB is the default type
mysql: # two syntaxes below are equivalent
named_mysql_a: mysql://root:password@localhost:3306/named_mysql_a
named_mysql_b:
url: mysql://root:password@localhost:3306/named_mysql_b
[database]
url = "mysql://root:password@localhost:3306/default" # assuming MySQL/MariaDB is the default type
# two syntaxes below are equivalent
[database.mysql]
named_mysql_a = "mysql://root:password@localhost:3306/named_mysql_a"
[database.mysql.named_mysql_b]
url = "mysql://root:password@localhost:3306/named_mysql_b"
The exploded form is more appropriate if you plan to overwrite some part of the handle (like the password) using environment variables.
MySQL/MariaDB exploded handle
Ways of configuring a MySQL/MariaDB handle in an exploded form:
- YAML
- TOML
database:
host: localhost
port: 3306 # assuming MySQL/MariaDB is the default type
database: default
mysql:
named_mysql:
host: localhost
port: 3306
database: named_mysql
[database]
host = "localhost"
port = 3306 # assuming MySQL/MariaDB is the default type
database = "default"
[database.mysql.named_mysql]
host = "localhost"
port = 3306
database = "named_mysql"
The full form is provided below for reference.
MySQL/MariaDB full handle
- YAML
- TOML
database: # assuming MySQL/MariaDB is the default type
host: localhost
port: 3306
username: root
password: password
database: db
socket: /var/run/mysqld/mysqld.sock
ssl_mode: preferred # disabled, preferred (default), required, verify_ca, verify_identity
ssl_ca: /etc/ssl/certs/ca-certificates.crt # or inline starting with `-----BEGIN`
ssl_client_cert: /etc/ssl/certs/client-cert.pem # as above
ssl_client_key: /etc/ssl/private/client-key.pem # as above
statement_cache_capacity: 100
charset: utf8mb4
collation: utf8mb4_unicode_ci
log_settings:
statements_level: debug # off, error, warn, info, debug, trace
slow_statements_level: warn # as above
slow_statements_duration: 2s 500ms # parsed using `humantime` crate
pipes_as_concat: true
enable_cleartext_plugin: false
no_engine_substitution: true
timezone: +00:00
set_names: true
pool_options:
min_connections: 0
max_connections: 10
test_before_acquire: true
acquire_time_level: off # off, error, warn, info, debug, trace
acquire_slow_level: warn # as above
acquire_slow_threshold: 2s # parsed using `humantime` crate
acquire_timeout: 30s # as above
max_lifetime: 30min # as above
idle_timeout: 10min # as above
[database] # assuming MySQL/MariaDB is the default type
host = "localhost"
port = 3306
username = "root"
password = "password"
database = "db"
socket = "/var/run/mysqld/mysqld.sock"
ssl_mode = "preferred" # disabled, preferred (default), required, verify_ca, verify_identity
ssl_ca = "/etc/ssl/certs/ca-certificates.crt" # or inline starting with `-----BEGIN`
ssl_client_cert = "/etc/ssl/certs/client-cert.pem" # as above
ssl_client_key = "/etc/ssl/private/client-key.pem" # as above
statement_cache_capacity = 100
charset = "utf8mb4"
collation = "utf8mb4_unicode_ci"
pipes_as_concat = true
enable_cleartext_plugin = false
no_engine_substitution = true
timezone = "+00:00"
set_names = true
[database.log_settings]
statements_level = "debug" # off, error, warn, info, debug, trace
slow_statements_level = "warn" # as above
slow_statements_duration = "2s 500ms" # parsed using `humantime` crate
[database.pool_options]
min_connections = 0
max_connections = 10
test_before_acquire = true
acquire_time_level = "off" # off, error, warn, info, debug, trace
acquire_slow_level = "warn" # as above
acquire_slow_threshold = "2s" # parsed using `humantime` crate
acquire_timeout = "30s" # as above
max_lifetime = "30min" # as above
idle_timeout = "10min" # as above
PostgreSQL handle
A handle may be given using a URL. Don’t use this if you intend to overwrite password (or any part of the handle) using environment variables.
PostgreSQL URL handle
Various ways of configuring a PostgreSQL handle as a URL:
- YAML
- TOML
database:
url: postgresql://user:secret@localhost:5433/default # assuming PostgreSQL is the default type
postgres: # two syntaxes below are equivalent
named_postgres_a: postgresql://user:secret@localhost:5433/named_postgres_a
named_postgres_b:
url: postgresql://user:secret@localhost:5433/named_postgres_b
[database]
url = "postgresql://user:secret@localhost:5433/default" # assuming PostgreSQL is the default type
# two syntaxes below are equivalent
[database.postgres]
named_postgres_a = "postgresql://user:secret@localhost:5433/named_postgres_a"
[database.postgres.named_postgres_b]
url = "postgresql://user:secret@localhost:5433/named_postgres_b"
The exploded form is more appropriate if you plan to overwrite some part of the handle (like the password) using environment variables.
PostgreSQL exploded handle
Ways of configuring a PostgreSQL handle in an exploded form:
- YAML
- TOML
database:
host: localhost
port: 5433 # assuming PostgreSQL is the default type
database: default
postgres:
named_postgres:
host: localhost
port: 5433
database: named_postgres
[database]
host = "localhost"
port = 5433 # assuming PostgreSQL is the default type
database = "default"
[database.postgres.named_postgres]
host = "localhost"
port = 5433
database = "named_postgres"
The full form is provided below for reference.
PostgreSQL full handle
- YAML
- TOML
database: # assuming PostgreSQL is the default type
host: localhost
port: 5433
username: user
password: secret
database: db
socket: /var/run/postgresql/.s.PGSQL.5432
ssl_mode: prefer # disable, allow, prefer (default), require, verify_ca, verify_full
ssl_root_cert: /etc/ssl/certs/ca-certificates.crt # or inline starting with `-----BEGIN`
ssl_client_cert: /etc/ssl/certs/client-cert.pem # as above
ssl_client_key: /etc/ssl/private/client-key.pem # as above
statement_cache_capacity: 100
application_name: my-app
log_settings:
statements_level: debug # off, error, warn, info, debug, trace
slow_statements_level: warn # as above
slow_statements_duration: 2s 500ms # parsed using `humantime` crate
extra_float_digits: 2
options:
search_path: myschema,public
pool_options:
min_connections: 0
max_connections: 10
test_before_acquire: true
acquire_time_level: off # off, error, warn, info, debug, trace
acquire_slow_level: warn # as above
acquire_slow_threshold: 2s # parsed using `humantime` crate
acquire_timeout: 30s # as above
max_lifetime: 30min # as above
idle_timeout: 10min # as above
[database] # assuming PostgreSQL is the default type
host = "localhost"
port = 5433
username = "user"
password = "secret"
database = "db"
socket = "/var/run/postgresql/.s.PGSQL.5432"
ssl_mode = "prefer" # disable, allow, prefer (default), require, verify_ca, verify_full
ssl_root_cert = "/etc/ssl/certs/ca-certificates.crt" # or inline starting with `-----BEGIN`
ssl_client_cert = "/etc/ssl/certs/client-cert.pem" # as above
ssl_client_key = "/etc/ssl/private/client-key.pem" # as above
statement_cache_capacity = 100
application_name = "my-app"
extra_float_digits = 2
[database.log_settings]
statements_level = "debug" # off, error, warn, info, debug, trace
slow_statements_level = "warn" # as above
slow_statements_duration = "2s 500ms" # parsed using `humantime` crate
[database.options]
search_path = "myschema,public"
[database.pool_options]
min_connections = 0
max_connections = 10
test_before_acquire = true
acquire_time_level = "off" # off, error, warn, info, debug, trace
acquire_slow_level = "warn" # as above
acquire_slow_threshold = "2s" # parsed using `humantime` crate
acquire_timeout = "30s" # as above
max_lifetime = "30min" # as above
idle_timeout = "10min" # as above
SQLite handle
A handle may be given using a URL. Don’t use this if you intend to overwrite password (or any part of the handle) using environment variables.
SQLite URL handle
Various ways of configuring a SQLite handle as a URL:
- YAML
- TOML
database:
url: sqlite://default.db?mode=ro # assuming SQLite is the default type
sqlite: # two syntaxes below are equivalent
named_sqlite_a: sqlite://named_sqlite_a.db?mode=ro
named_sqlite_b:
url: sqlite://named_sqlite_b.db?mode=ro
[database]
url = "sqlite://default.db?mode=ro" # assuming SQLite is the default type
# two syntaxes below are equivalent
[database.sqlite]
named_sqlite_a = "sqlite://named_sqlite_a.db?mode=ro"
[database.sqlite.named_sqlite_b]
url = "sqlite://named_sqlite_b.db?mode=ro"
The exploded form is more appropriate if you plan to overwrite some part of the handle (like the password) using environment variables.
SQLite exploded handle
Ways of configuring a SQLite handle in an exploded form:
- YAML
- TOML
database: # assuming SQLite is the default type
filename: 'default.db'
in_memory: false
read_only: false
create_if_missing: false
sqlite:
named_sqlite:
filename: 'named_sqlite.db'
in_memory: false
read_only: false
create_if_missing: false
[database] # assuming SQLite is the default type
filename = "default.db"
in_memory = false
read_only = false
create_if_missing = false
[database.sqlite.named_sqlite]
filename = "named_sqlite.db"
in_memory = false
read_only = false
create_if_missing = false
The full form is provided below for reference.
SQLite full handle
- YAML
- TOML
database: # assuming SQLite is the default type
filename: ':memory:'
in_memory: true
read_only: false
create_if_missing: false
shared_cache: false
statement_cache_capacity: 100
busy_timeout: 5s # parsed using `humantime` crate
log_settings:
statements_level: debug # off, error, warn, info, debug, trace
slow_statements_level: warn # as above
slow_statements_duration: 2s 500ms # parsed using `humantime` crate
immutable: false
vfs: unix-dotfile
pragmas:
foreign_keys: ON
extensions:
extension_a: /path/to/entrypoint
command_channel_size: 50
row_channel_size: 50
serialized: false
thread_name_prefix: sqlx-sqlite-worker-
optimize_on_close:
enabled: false
analysis_limit: 50
pool_options:
min_connections: 0
max_connections: 10
test_before_acquire: true
acquire_time_level: off # off, error, warn, info, debug, trace
acquire_slow_level: warn # as above
acquire_slow_threshold: 2s # parsed using `humantime` crate
acquire_timeout: 30s # as above
max_lifetime: 30min # as above
idle_timeout: 10min # as above
[database] # assuming SQLite is the default type
filename = ":memory:"
in_memory = true
read_only = false
create_if_missing = false
shared_cache = false
statement_cache_capacity = 100
busy_timeout = "5s" # parsed using `humantime` crate
immutable = false
vfs = "unix-dotfile"
command_channel_size = 50
row_channel_size = 50
serialized = false
thread_name_prefix = "sqlx-sqlite-worker-"
[database.log_settings]
statements_level = "debug" # off, error, warn, info, debug, trace
slow_statements_level = "warn" # as above
slow_statements_duration = "2s 500ms" # parsed using `humantime` crate
[database.pragmas]
foreign_keys = "ON"
[database.extensions]
extension_a = "/path/to/entrypoint"
[database.optimize_on_close]
enabled = false
analysis_limit = 50
[database.pool_options]
min_connections = 0
max_connections = 10
test_before_acquire = true
acquire_time_level = "off" # off, error, warn, info, debug, trace
acquire_slow_level = "warn" # as above
acquire_slow_threshold = "2s" # parsed using `humantime` crate
acquire_timeout = "30s" # as above
max_lifetime = "30min" # as above
idle_timeout = "10min" # as above
∅ Startup
This component does not include any startup logic.
☑︎ Facade
Use the Database facade to retrieve the pre-configured sqlx database connection pools.
use strut::database::sqlx::*;
use strut::Database;
#[strut::main]
async fn main() {
let default_mysql: Pool<MySql> = Database::default(); // assuming MySQL/MariaDB is the default type
let named_mysql: Pool<MySql> = Database::mysql("named_mysql");
let named_postgres: Pool<Postgres> = Database::postgres("named_postgres");
let named_sqlite: Pool<Sqlite> = Database::sqlite("named_sqlite");
}
- Returned pools are lazy — there cannot be a connection error until you try to use it.
- The default pool and each of the named pools are singletons (initialized once, then cloned).
- Retrieving an undefined name will result in a panic.
☑︎ Spindown
The component automatically attempts to close connections in all connection pools during spindown.