Skip to content

Commit fe45cac

Browse files
committed
Auto merge of #3922 - ferrous-systems:split-db-pool-size-env-vars, r=pietroalbini
Split DB_POOL_SIZE & DB_MIN_IDLE environment variables This PR splits the existing environment variables `DB_POOL_SIZE` & `DB_MIN_IDLE` into separate variables to provide different connection settings to the primary & replica databases. The main reason for this change is to be able to configure different values for both database pools. The new env variables are: * `DB_PRIMARY_POOL_SIZE` & `DB_REPLICA_POOL_SIZE` to configure the number of connections in the primary & replica pool * `DB_PRIMARY_MIN_IDLE` & `DB_REPLICA_MIN_IDLE` to set the number of idle connections each pool at least maintains * update descriptions of env vars. **Note** the descriptions of the `*_POOL_SIZE` variables in `app.json` are most likely off & need correct wording.
2 parents fcb1359 + dd14199 commit fe45cac

File tree

5 files changed

+66
-26
lines changed

5 files changed

+66
-26
lines changed

app.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,25 @@
2323
"value": "",
2424
"required": false
2525
},
26-
"DB_POOL_SIZE": {
26+
"DB_PRIMARY_POOL_SIZE": {
2727
"value": "10",
2828
"required": false,
29-
"description": "The maximum number of database connections managed by the pool. Set so that this value times the number of dynos is less than your connection limit."
29+
"description": "The maximum number of primary database connections managed by the pool. Set so that this value times the number of dynos is less than your connection limit."
3030
},
31-
"DB_MIN_IDLE": {
31+
"DB_REPLICA_POOL_SIZE": {
32+
"value": "3",
33+
"required": false,
34+
"description": "The maximum number of replica database connections managed by the pool. Set so that this value times the number of dynos is less than your connection limit."
35+
},
36+
"DB_PRIMARY_MIN_IDLE": {
3237
"value": "5",
3338
"required": false,
34-
"description": "The pool will try to maintain at least this many idle connections at all times, while respecting the maximum size of the pool."
39+
"description": "The pool will try to maintain at least this many idle connections on the primary database at all times, while respecting the maximum size of the pool."
40+
},
41+
"DB_REPLICA_MIN_IDLE": {
42+
"value": "1",
43+
"required": false,
44+
"description": "The pool will try to maintain at least this many idle connections on the replica database at all times, while respecting the maximum size of the pool."
3545
},
3646
"DB_HELPER_THREADS": {
3747
"value": "3",

src/app.rs

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,6 @@ impl App {
7676
),
7777
);
7878

79-
let db_pool_size = match (dotenv::var("DB_POOL_SIZE"), config.env()) {
80-
(Ok(num), _) => num.parse().expect("couldn't parse DB_POOL_SIZE"),
81-
(_, Env::Production) => 10,
82-
_ => 3,
83-
};
84-
85-
let db_min_idle = match (dotenv::var("DB_MIN_IDLE"), config.env()) {
86-
(Ok(num), _) => Some(num.parse().expect("couldn't parse DB_MIN_IDLE")),
87-
(_, Env::Production) => Some(5),
88-
_ => None,
89-
};
90-
9179
let db_helper_threads = match (dotenv::var("DB_HELPER_THREADS"), config.env()) {
9280
(Ok(num), _) => num.parse().expect("couldn't parse DB_HELPER_THREADS"),
9381
(_, Env::Production) => 3,
@@ -113,8 +101,8 @@ impl App {
113101
};
114102

115103
let primary_db_config = r2d2::Pool::builder()
116-
.max_size(db_pool_size)
117-
.min_idle(db_min_idle)
104+
.max_size(config.db.primary.pool_size)
105+
.min_idle(config.db.primary.min_idle)
118106
.connection_timeout(Duration::from_secs(db_connection_timeout))
119107
.connection_customizer(Box::new(primary_db_connection_config))
120108
.thread_pool(thread_pool.clone());
@@ -129,25 +117,25 @@ impl App {
129117
.unwrap()
130118
};
131119

132-
let replica_database = if let Some(url) = config.db.replica.as_ref().map(|c| &c.url) {
120+
let replica_database = if let Some(pool_config) = config.db.replica.as_ref() {
133121
if config.use_test_database_pool {
134-
Some(DieselPool::new_test(url))
122+
Some(DieselPool::new_test(&pool_config.url))
135123
} else {
136124
let replica_db_connection_config = ConnectionConfig {
137125
statement_timeout: db_connection_timeout,
138126
read_only: true,
139127
};
140128

141129
let replica_db_config = r2d2::Pool::builder()
142-
.max_size(db_pool_size)
143-
.min_idle(db_min_idle)
130+
.max_size(pool_config.pool_size)
131+
.min_idle(pool_config.min_idle)
144132
.connection_timeout(Duration::from_secs(db_connection_timeout))
145133
.connection_customizer(Box::new(replica_db_connection_config))
146134
.thread_pool(thread_pool);
147135

148136
Some(
149137
DieselPool::new(
150-
url,
138+
&pool_config.url,
151139
replica_db_config,
152140
instance_metrics
153141
.database_time_to_obtain_connection

src/config/database_pools.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
//!
33
//! - `DATABASE_URL`: The URL of the postgres database to use.
44
//! - `READ_ONLY_REPLICA_URL`: The URL of an optional postgres read-only replica database.
5+
//! - `DB_PRIMARY_POOL_SIZE`: The number of connections of the primary database.
6+
//! - `DB_REPLICA_POOL_SIZE`: The number of connections of the read-only / replica database.
7+
//! - `DB_PRIMARY_MIN_IDLE`: The primary pool will maintain at least this number of connections.
8+
//! - `DB_REPLICA_MIN_IDLE`: The replica pool will maintain at least this number of connections.
59
//! - `DB_OFFLINE`: If set to `leader` then use the read-only follower as if it was the leader.
610
//! If set to `follower` then act as if `READ_ONLY_REPLICA_URL` was unset.
711
//! - `READ_ONLY_MODE`: If defined (even as empty) then force all connections to be read-only.
@@ -20,6 +24,8 @@ pub struct DatabasePools {
2024
pub struct DbPoolConfig {
2125
pub url: String,
2226
pub read_only_mode: bool,
27+
pub pool_size: u32,
28+
pub min_idle: Option<u32>,
2329
}
2430

2531
impl DatabasePools {
@@ -29,6 +35,8 @@ impl DatabasePools {
2935
}
3036

3137
impl DatabasePools {
38+
const DEFAULT_POOL_SIZE: u32 = 3;
39+
3240
/// Load settings for one or more database pools from the environment
3341
///
3442
/// # Panics
@@ -38,6 +46,27 @@ impl DatabasePools {
3846
let leader_url = env("DATABASE_URL");
3947
let follower_url = dotenv::var("READ_ONLY_REPLICA_URL").ok();
4048
let read_only_mode = dotenv::var("READ_ONLY_MODE").is_ok();
49+
50+
let primary_pool_size = match dotenv::var("DB_PRIMARY_POOL_SIZE") {
51+
Ok(num) => num.parse().expect("couldn't parse DB_PRIMARY_POOL_SIZE"),
52+
_ => Self::DEFAULT_POOL_SIZE,
53+
};
54+
55+
let replica_pool_size = match dotenv::var("DB_REPLICA_POOL_SIZE") {
56+
Ok(num) => num.parse().expect("couldn't parse DB_REPLICA_POOL_SIZE"),
57+
_ => Self::DEFAULT_POOL_SIZE,
58+
};
59+
60+
let primary_min_idle = match dotenv::var("DB_PRIMARY_MIN_IDLE") {
61+
Ok(num) => Some(num.parse().expect("couldn't parse DB_PRIMARY_MIN_IDLE")),
62+
_ => None,
63+
};
64+
65+
let replica_min_idle = match dotenv::var("DB_REPLICA_MIN_IDLE") {
66+
Ok(num) => Some(num.parse().expect("couldn't parse DB_REPLICA_MIN_IDLE")),
67+
_ => None,
68+
};
69+
4170
match dotenv::var("DB_OFFLINE").as_deref() {
4271
// The actual leader is down, use the follower in read-only mode as the primary and
4372
// don't configure a replica.
@@ -46,6 +75,8 @@ impl DatabasePools {
4675
url: follower_url
4776
.expect("Must set `READ_ONLY_REPLICA_URL` when using `DB_OFFLINE=leader`."),
4877
read_only_mode: true,
78+
pool_size: primary_pool_size,
79+
min_idle: primary_min_idle,
4980
},
5081
replica: None,
5182
},
@@ -54,20 +85,26 @@ impl DatabasePools {
5485
primary: DbPoolConfig {
5586
url: leader_url,
5687
read_only_mode,
88+
pool_size: primary_pool_size,
89+
min_idle: primary_min_idle,
5790
},
5891
replica: None,
5992
},
6093
_ => Self {
6194
primary: DbPoolConfig {
6295
url: leader_url,
6396
read_only_mode,
97+
pool_size: primary_pool_size,
98+
min_idle: primary_min_idle,
6499
},
65100
replica: follower_url.map(|url| DbPoolConfig {
66101
url,
67102
// Always enable read-only mode for the follower. In staging, we attach the
68103
// same leader database to both environment variables and this ensures the
69104
// connection is opened read-only even when attached to a writeable database.
70105
read_only_mode: true,
106+
pool_size: replica_pool_size,
107+
min_idle: replica_min_idle,
71108
}),
72109
},
73110
}
@@ -78,6 +115,8 @@ impl DatabasePools {
78115
primary: DbPoolConfig {
79116
url: env("TEST_DATABASE_URL"),
80117
read_only_mode: false,
118+
pool_size: 1,
119+
min_idle: None,
81120
},
82121
replica: None,
83122
}

src/middleware.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn build_middleware(app: Arc<App>, endpoints: RouteBuilder) -> MiddlewareBui
8383
// In production we currently have 2 equally sized pools (primary and a read-only replica).
8484
// Because such a large portion of production traffic is for download requests (which update
8585
// download counts), we consider only the primary pool here.
86-
if let Ok(capacity) = env::var("DB_POOL_SIZE") {
86+
if let Ok(capacity) = env::var("DB_PRIMARY_POOL_SIZE") {
8787
if let Ok(capacity) = capacity.parse() {
8888
if capacity >= 10 {
8989
println!(
@@ -92,7 +92,9 @@ pub fn build_middleware(app: Arc<App>, endpoints: RouteBuilder) -> MiddlewareBui
9292
);
9393
m.around(balance_capacity::BalanceCapacity::new(capacity))
9494
} else {
95-
println!("BalanceCapacity middleware not enabled. DB_POOL_SIZE is too low.");
95+
println!(
96+
"BalanceCapacity middleware not enabled. DB_PRIMARY_POOL_SIZE is too low."
97+
);
9698
}
9799
}
98100
}

src/tests/server_binary.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ impl ServerBin {
8888
// Bind a random port every time the server is started.
8989
env.insert("PORT".into(), "0".into());
9090
// Avoid creating too many database connections.
91-
env.insert("DB_POOL_SIZE".into(), "2".into());
91+
env.insert("DB_PRIMARY_POOL_SIZE".into(), "2".into());
92+
env.insert("DB_REPLICA_POOL_SIZE".into(), "1".into());
9293
env.remove("DB_MIN_SIZE");
9394
// Other configuration variables needed for the application to boot.
9495
env.insert("WEB_ALLOWED_ORIGINS".into(), "http://localhost:8888".into());

0 commit comments

Comments
 (0)