Skip to content

Commit ef0218a

Browse files
authored
Merge pull request #7422 from Turbo87/testing-realism
Use fresh databases for all tests
2 parents 5d270ca + 866d121 commit ef0218a

File tree

9 files changed

+136
-245
lines changed

9 files changed

+136
-245
lines changed

Cargo.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,3 @@ claims = "=0.7.1"
107107
hyper-tls = "=0.5.0"
108108
insta = { version = "=1.34.0", features = ["json", "redactions"] }
109109
tokio = "=1.34.0"
110-
111-
[build-dependencies]
112-
diesel = { version = "=2.1.3", features = ["postgres"] }
113-
diesel_migrations = { version = "=2.1.0", features = ["postgres"] }
114-
dotenvy = "=0.15.7"

build.rs

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/app.rs

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ impl App {
9292

9393
let thread_pool = Arc::new(ScheduledThreadPool::new(config.db.helper_threads));
9494

95-
let primary_database = if config.use_test_database_pool {
96-
DieselPool::new_test(&config.db, &config.db.primary.url)
97-
} else {
95+
let primary_database = {
9896
let primary_db_connection_config = ConnectionConfig {
9997
statement_timeout: config.db.statement_timeout,
10098
read_only: config.db.primary.read_only_mode,
@@ -119,33 +117,29 @@ impl App {
119117
};
120118

121119
let replica_database = if let Some(pool_config) = config.db.replica.as_ref() {
122-
if config.use_test_database_pool {
123-
Some(DieselPool::new_test(&config.db, &pool_config.url))
124-
} else {
125-
let replica_db_connection_config = ConnectionConfig {
126-
statement_timeout: config.db.statement_timeout,
127-
read_only: true,
128-
};
129-
130-
let replica_db_config = r2d2::Pool::builder()
131-
.max_size(pool_config.pool_size)
132-
.min_idle(pool_config.min_idle)
133-
.connection_timeout(config.db.connection_timeout)
134-
.connection_customizer(Box::new(replica_db_connection_config))
135-
.thread_pool(thread_pool);
136-
137-
Some(
138-
DieselPool::new(
139-
&pool_config.url,
140-
&config.db,
141-
replica_db_config,
142-
instance_metrics
143-
.database_time_to_obtain_connection
144-
.with_label_values(&["follower"]),
145-
)
146-
.unwrap(),
120+
let replica_db_connection_config = ConnectionConfig {
121+
statement_timeout: config.db.statement_timeout,
122+
read_only: true,
123+
};
124+
125+
let replica_db_config = r2d2::Pool::builder()
126+
.max_size(pool_config.pool_size)
127+
.min_idle(pool_config.min_idle)
128+
.connection_timeout(config.db.connection_timeout)
129+
.connection_customizer(Box::new(replica_db_connection_config))
130+
.thread_pool(thread_pool);
131+
132+
Some(
133+
DieselPool::new(
134+
&pool_config.url,
135+
&config.db,
136+
replica_db_config,
137+
instance_metrics
138+
.database_time_to_obtain_connection
139+
.with_label_values(&["follower"]),
147140
)
148-
}
141+
.unwrap(),
142+
)
149143
} else {
150144
None
151145
};
@@ -178,15 +172,15 @@ impl App {
178172

179173
/// Obtain a read/write database connection from the primary pool
180174
#[instrument(skip_all)]
181-
pub fn db_write(&self) -> Result<DieselPooledConn<'_>, PoolError> {
175+
pub fn db_write(&self) -> Result<DieselPooledConn, PoolError> {
182176
self.primary_database.get()
183177
}
184178

185179
/// Obtain a readonly database connection from the replica pool
186180
///
187181
/// If the replica pool is disabled or unavailable, the primary pool is used instead.
188182
#[instrument(skip_all)]
189-
pub fn db_read(&self) -> Result<DieselPooledConn<'_>, PoolError> {
183+
pub fn db_read(&self) -> Result<DieselPooledConn, PoolError> {
190184
let read_only_pool = self.read_only_replica_database.as_ref();
191185
match read_only_pool.map(|pool| pool.get()) {
192186
// Replica is available
@@ -215,7 +209,7 @@ impl App {
215209
///
216210
/// If the primary pool is unavailable, the replica pool is used instead, if not disabled.
217211
#[instrument(skip_all)]
218-
pub fn db_read_prefer_primary(&self) -> Result<DieselPooledConn<'_>, PoolError> {
212+
pub fn db_read_prefer_primary(&self) -> Result<DieselPooledConn, PoolError> {
219213
match (
220214
self.primary_database.get(),
221215
&self.read_only_replica_database,

src/config/server.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ pub struct Server {
4848
pub downloads_persist_interval: Duration,
4949
pub ownership_invitations_expiration_days: u64,
5050
pub metrics_authorization_token: Option<String>,
51-
pub use_test_database_pool: bool,
5251
pub instance_metrics_log_every_seconds: Option<u64>,
5352
pub force_unconditional_redirects: bool,
5453
pub blocked_routes: HashSet<String>,
@@ -209,7 +208,6 @@ impl Server {
209208
.unwrap_or(Duration::from_secs(60)),
210209
ownership_invitations_expiration_days: 30,
211210
metrics_authorization_token: var("METRICS_AUTHORIZATION_TOKEN")?,
212-
use_test_database_pool: false,
213211
instance_metrics_log_every_seconds: var_parsed("INSTANCE_METRICS_LOG_EVERY_SECONDS")?,
214212
force_unconditional_redirects: var("FORCE_UNCONDITIONAL_REDIRECTS")?.is_some(),
215213
blocked_routes: var("BLOCKED_ROUTES")?

src/db.rs

Lines changed: 31 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
use diesel::prelude::*;
2-
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
2+
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection, State};
33
use prometheus::Histogram;
44
use secrecy::{ExposeSecret, SecretString};
5-
use std::sync::{Arc, Mutex, MutexGuard};
6-
use std::{
7-
ops::{Deref, DerefMut},
8-
time::Duration,
9-
};
5+
use std::time::Duration;
106
use thiserror::Error;
117
use url::Url;
128

@@ -17,15 +13,9 @@ pub mod sql_types;
1713
pub type ConnectionPool = r2d2::Pool<ConnectionManager<PgConnection>>;
1814

1915
#[derive(Clone)]
20-
pub enum DieselPool {
21-
Pool {
22-
pool: ConnectionPool,
23-
time_to_obtain_connection_metric: Histogram,
24-
},
25-
BackgroundJobPool {
26-
pool: ConnectionPool,
27-
},
28-
Test(Arc<Mutex<PgConnection>>),
16+
pub struct DieselPool {
17+
pool: ConnectionPool,
18+
time_to_obtain_connection_metric: Option<Histogram>,
2919
}
3020

3121
impl DieselPool {
@@ -48,9 +38,9 @@ impl DieselPool {
4838
// serving errors for the first connections until the pool is initialized) and if we can't
4939
// establish any connection continue booting up the application. The database pool will
5040
// automatically be marked as unhealthy and the rest of the application will adapt.
51-
let pool = DieselPool::Pool {
41+
let pool = DieselPool {
5242
pool: r2d2_config.build_unchecked(manager),
53-
time_to_obtain_connection_metric,
43+
time_to_obtain_connection_metric: Some(time_to_obtain_connection_metric),
5444
};
5545
match pool.wait_until_healthy(Duration::from_secs(5)) {
5646
Ok(()) => {}
@@ -62,67 +52,39 @@ impl DieselPool {
6252
}
6353

6454
pub fn new_background_worker(pool: r2d2::Pool<ConnectionManager<PgConnection>>) -> Self {
65-
Self::BackgroundJobPool { pool }
66-
}
67-
68-
pub(crate) fn new_test(config: &config::DatabasePools, url: &SecretString) -> DieselPool {
69-
let mut conn = PgConnection::establish(&connection_url(config, url.expose_secret()))
70-
.expect("failed to establish connection");
71-
conn.begin_test_transaction()
72-
.expect("failed to begin test transaction");
73-
DieselPool::Test(Arc::new(Mutex::new(conn)))
55+
Self {
56+
pool,
57+
time_to_obtain_connection_metric: None,
58+
}
7459
}
7560

7661
#[instrument(name = "db.connect", skip_all)]
77-
pub fn get(&self) -> Result<DieselPooledConn<'_>, PoolError> {
78-
match self {
79-
DieselPool::Pool {
80-
pool,
81-
time_to_obtain_connection_metric,
82-
} => time_to_obtain_connection_metric.observe_closure_duration(|| {
83-
if let Some(conn) = pool.try_get() {
84-
Ok(DieselPooledConn::Pool(conn))
85-
} else if !self.is_healthy() {
86-
Err(PoolError::UnhealthyPool)
87-
} else {
88-
Ok(DieselPooledConn::Pool(pool.get()?))
89-
}
90-
}),
91-
DieselPool::BackgroundJobPool { pool } => Ok(DieselPooledConn::Pool(pool.get()?)),
92-
DieselPool::Test(conn) => Ok(DieselPooledConn::Test(
93-
conn.try_lock()
94-
.map_err(|_e| PoolError::TestConnectionUnavailable)?,
95-
)),
62+
pub fn get(&self) -> Result<DieselPooledConn, PoolError> {
63+
match self.time_to_obtain_connection_metric.as_ref() {
64+
Some(time_to_obtain_connection_metric) => time_to_obtain_connection_metric
65+
.observe_closure_duration(|| {
66+
if let Some(conn) = self.pool.try_get() {
67+
Ok(conn)
68+
} else if !self.is_healthy() {
69+
Err(PoolError::UnhealthyPool)
70+
} else {
71+
Ok(self.pool.get()?)
72+
}
73+
}),
74+
None => Ok(self.pool.get()?),
9675
}
9776
}
9877

99-
pub fn state(&self) -> PoolState {
100-
match self {
101-
DieselPool::Pool { pool, .. } | DieselPool::BackgroundJobPool { pool } => {
102-
let state = pool.state();
103-
PoolState {
104-
connections: state.connections,
105-
idle_connections: state.idle_connections,
106-
}
107-
}
108-
DieselPool::Test(_) => PoolState {
109-
connections: 0,
110-
idle_connections: 0,
111-
},
112-
}
78+
pub fn state(&self) -> State {
79+
self.pool.state()
11380
}
11481

11582
#[instrument(skip_all)]
11683
pub fn wait_until_healthy(&self, timeout: Duration) -> Result<(), PoolError> {
117-
match self {
118-
DieselPool::Pool { pool, .. } | DieselPool::BackgroundJobPool { pool } => {
119-
match pool.get_timeout(timeout) {
120-
Ok(_) => Ok(()),
121-
Err(_) if !self.is_healthy() => Err(PoolError::UnhealthyPool),
122-
Err(err) => Err(PoolError::R2D2(err)),
123-
}
124-
}
125-
DieselPool::Test(_) => Ok(()),
84+
match self.pool.get_timeout(timeout) {
85+
Ok(_) => Ok(()),
86+
Err(_) if !self.is_healthy() => Err(PoolError::UnhealthyPool),
87+
Err(err) => Err(PoolError::R2D2(err)),
12688
}
12789
}
12890

@@ -131,37 +93,7 @@ impl DieselPool {
13193
}
13294
}
13395

134-
#[derive(Debug, Copy, Clone)]
135-
pub struct PoolState {
136-
pub connections: u32,
137-
pub idle_connections: u32,
138-
}
139-
140-
#[allow(clippy::large_enum_variant)]
141-
pub enum DieselPooledConn<'a> {
142-
Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
143-
Test(MutexGuard<'a, PgConnection>),
144-
}
145-
146-
impl Deref for DieselPooledConn<'_> {
147-
type Target = PgConnection;
148-
149-
fn deref(&self) -> &Self::Target {
150-
match self {
151-
DieselPooledConn::Pool(conn) => conn.deref(),
152-
DieselPooledConn::Test(conn) => conn.deref(),
153-
}
154-
}
155-
}
156-
157-
impl DerefMut for DieselPooledConn<'_> {
158-
fn deref_mut(&mut self) -> &mut Self::Target {
159-
match self {
160-
DieselPooledConn::Pool(conn) => conn.deref_mut(),
161-
DieselPooledConn::Test(conn) => conn.deref_mut(),
162-
}
163-
}
164-
}
96+
pub type DieselPooledConn = r2d2::PooledConnection<ConnectionManager<PgConnection>>;
16597

16698
pub fn oneoff_connection_with_config(
16799
config: &config::DatabasePools,

src/tests/read_only_mode.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use http::StatusCode;
77
#[test]
88
fn can_hit_read_only_endpoints_in_read_only_mode() {
99
let (_app, anon) = TestApp::init()
10-
.without_test_database_pool()
1110
.with_config(|config| {
1211
config.db.primary.read_only_mode = true;
1312
})
@@ -20,7 +19,6 @@ fn can_hit_read_only_endpoints_in_read_only_mode() {
2019
#[test]
2120
fn cannot_hit_endpoint_which_writes_db_in_read_only_mode() {
2221
let (app, _, user, token) = TestApp::init()
23-
.without_test_database_pool()
2422
.with_config(|config| {
2523
config.db.primary.read_only_mode = true;
2624
})
@@ -39,7 +37,6 @@ fn cannot_hit_endpoint_which_writes_db_in_read_only_mode() {
3937
#[test]
4038
fn can_download_crate_in_read_only_mode() {
4139
let (app, anon, user) = TestApp::init()
42-
.without_test_database_pool()
4340
.with_config(|config| {
4441
config.db.primary.read_only_mode = true;
4542
})

0 commit comments

Comments
 (0)