Skip to content

Commit e7ee68b

Browse files
committed
db: use a connection pool even during tests
1 parent a418fea commit e7ee68b

File tree

4 files changed

+65
-85
lines changed

4 files changed

+65
-85
lines changed

src/db/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub use self::file::add_path_into_database;
77
pub use self::migrate::migrate;
88
pub use self::pool::{Pool, PoolError};
99

10+
#[cfg(test)]
11+
pub(crate) use self::pool::PoolConnection;
12+
1013
mod add_package;
1114
pub mod blacklist;
1215
mod delete_crate;

src/db/pool.rs

Lines changed: 45 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
use crate::Config;
22
use postgres::Connection;
3-
use std::marker::PhantomData;
3+
use r2d2_postgres::PostgresConnectionManager;
44

5-
#[cfg(test)]
6-
use std::sync::{Arc, Mutex, MutexGuard};
5+
pub(crate) type PoolConnection = r2d2::PooledConnection<PostgresConnectionManager>;
6+
7+
const DEFAULT_SCHEMA: &str = "public";
78

89
#[derive(Debug, Clone)]
9-
pub enum Pool {
10-
R2D2(r2d2::Pool<r2d2_postgres::PostgresConnectionManager>),
11-
#[cfg(test)]
12-
Simple(Arc<Mutex<Connection>>),
10+
pub struct Pool {
11+
pool: r2d2::Pool<PostgresConnectionManager>,
1312
}
1413

1514
impl Pool {
1615
pub fn new(config: &Config) -> Result<Pool, PoolError> {
16+
Self::new_inner(config, DEFAULT_SCHEMA)
17+
}
18+
19+
#[cfg(test)]
20+
pub(crate) fn new_with_schema(config: &Config, schema: &str) -> Result<Pool, PoolError> {
21+
Self::new_inner(config, schema)
22+
}
23+
24+
fn new_inner(config: &Config, schema: &str) -> Result<Pool, PoolError> {
1725
crate::web::metrics::MAX_DB_CONNECTIONS.set(config.max_pool_size as i64);
1826

19-
let manager = r2d2_postgres::PostgresConnectionManager::new(
27+
let manager = PostgresConnectionManager::new(
2028
config.database_url.as_str(),
2129
r2d2_postgres::TlsMode::None,
2230
)
@@ -25,73 +33,54 @@ impl Pool {
2533
let pool = r2d2::Pool::builder()
2634
.max_size(config.max_pool_size)
2735
.min_idle(Some(config.min_pool_idle))
36+
.connection_customizer(Box::new(SetSchema::new(schema)))
2837
.build(manager)
2938
.map_err(PoolError::PoolCreationFailed)?;
3039

31-
Ok(Pool::R2D2(pool))
32-
}
33-
34-
#[cfg(test)]
35-
pub(crate) fn new_simple(conn: Arc<Mutex<Connection>>) -> Self {
36-
Pool::Simple(conn)
40+
Ok(Pool { pool })
3741
}
3842

39-
pub fn get(&self) -> Result<DerefConnection<'_>, PoolError> {
40-
match self {
41-
Self::R2D2(r2d2) => match r2d2.get() {
42-
Ok(conn) => Ok(DerefConnection::Connection(conn, PhantomData)),
43-
Err(err) => {
44-
crate::web::metrics::FAILED_DB_CONNECTIONS.inc();
45-
Err(PoolError::ConnectionError(err))
46-
}
47-
},
48-
49-
#[cfg(test)]
50-
Self::Simple(mutex) => Ok(DerefConnection::Guard(
51-
mutex.lock().expect("failed to lock the connection"),
52-
)),
43+
pub fn get(&self) -> Result<PoolConnection, PoolError> {
44+
match self.pool.get() {
45+
Ok(conn) => Ok(conn),
46+
Err(err) => {
47+
crate::web::metrics::FAILED_DB_CONNECTIONS.inc();
48+
Err(PoolError::ConnectionError(err))
49+
}
5350
}
5451
}
5552

5653
pub(crate) fn used_connections(&self) -> u32 {
57-
match self {
58-
Self::R2D2(conn) => conn.state().connections - conn.state().idle_connections,
59-
60-
#[cfg(test)]
61-
Self::Simple(..) => 0,
62-
}
54+
self.pool.state().connections - self.pool.state().idle_connections
6355
}
6456

6557
pub(crate) fn idle_connections(&self) -> u32 {
66-
match self {
67-
Self::R2D2(conn) => conn.state().idle_connections,
68-
69-
#[cfg(test)]
70-
Self::Simple(..) => 0,
71-
}
58+
self.pool.state().idle_connections
7259
}
7360
}
7461

75-
pub enum DerefConnection<'a> {
76-
Connection(
77-
r2d2::PooledConnection<r2d2_postgres::PostgresConnectionManager>,
78-
PhantomData<&'a ()>,
79-
),
80-
81-
#[cfg(test)]
82-
Guard(MutexGuard<'a, Connection>),
62+
#[derive(Debug)]
63+
struct SetSchema {
64+
schema: String,
8365
}
8466

85-
impl<'a> std::ops::Deref for DerefConnection<'a> {
86-
type Target = Connection;
87-
88-
fn deref(&self) -> &Connection {
89-
match self {
90-
Self::Connection(conn, ..) => conn,
67+
impl SetSchema {
68+
fn new(schema: &str) -> Self {
69+
Self {
70+
schema: schema.into(),
71+
}
72+
}
73+
}
9174

92-
#[cfg(test)]
93-
Self::Guard(guard) => &guard,
75+
impl r2d2::CustomizeConnection<Connection, postgres::Error> for SetSchema {
76+
fn on_acquire(&self, conn: &mut Connection) -> Result<(), postgres::Error> {
77+
if self.schema != DEFAULT_SCHEMA {
78+
conn.execute(
79+
&format!("SET search_path TO {}, {};", self.schema, DEFAULT_SCHEMA),
80+
&[],
81+
)?;
9482
}
83+
Ok(())
9584
}
9685
}
9786

src/test/mod.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod fakes;
22

3+
use crate::db::{Pool, PoolConnection};
34
use crate::storage::s3::TestS3;
45
use crate::web::Server;
56
use crate::Config;
@@ -11,10 +12,7 @@ use reqwest::{
1112
blocking::{Client, RequestBuilder},
1213
Method,
1314
};
14-
use std::{
15-
panic,
16-
sync::{Arc, Mutex, MutexGuard},
17-
};
15+
use std::{panic, sync::Arc};
1816

1917
pub(crate) fn wrapper(f: impl FnOnce(&TestEnvironment) -> Result<(), Error>) {
2018
let _ = dotenv::dotenv();
@@ -123,7 +121,13 @@ impl TestEnvironment {
123121
}
124122

125123
fn base_config(&self) -> Config {
126-
Config::from_env().expect("failed to get base config")
124+
let mut config = Config::from_env().expect("failed to get base config");
125+
126+
// Use less connections for each test compared to production.
127+
config.max_pool_size = 2;
128+
config.min_pool_idle = 0;
129+
130+
config
127131
}
128132

129133
pub(crate) fn override_config(&self, f: impl FnOnce(&mut Config)) {
@@ -157,7 +161,7 @@ impl TestEnvironment {
157161
}
158162

159163
pub(crate) struct TestDatabase {
160-
conn: Arc<Mutex<Connection>>,
164+
pool: Pool,
161165
schema: String,
162166
}
163167

@@ -178,13 +182,15 @@ impl TestDatabase {
178182
crate::db::migrate(None, &conn)?;
179183

180184
Ok(TestDatabase {
181-
conn: Arc::new(Mutex::new(conn)),
185+
pool: Pool::new_with_schema(config, &schema)?,
182186
schema,
183187
})
184188
}
185189

186-
pub(crate) fn conn(&self) -> MutexGuard<Connection> {
187-
self.conn.lock().expect("failed to lock the connection")
190+
pub(crate) fn conn(&self) -> PoolConnection {
191+
self.pool
192+
.get()
193+
.expect("failed to get a connection out of the pool")
188194
}
189195

190196
pub(crate) fn fake_release(&self) -> fakes::FakeRelease {
@@ -212,8 +218,8 @@ pub(crate) struct TestFrontend {
212218
impl TestFrontend {
213219
fn new(db: &TestDatabase, config: Arc<Config>) -> Self {
214220
Self {
215-
server: Server::start_test(db.conn.clone(), config)
216-
.expect("failed to start the server"),
221+
server: Server::start(Some("127.0.0.1:0"), false, db.pool.clone(), config)
222+
.expect("failed to start the web server"),
217223
client: Client::new(),
218224
}
219225
}

src/web/mod.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ use std::net::SocketAddr;
7979
use std::sync::Arc;
8080
use std::{env, fmt, path::PathBuf, time::Duration};
8181

82-
#[cfg(test)]
83-
use std::sync::Mutex;
84-
8582
/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
8683
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 30 * 12; // 12 months
8784
const STYLE_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));
@@ -397,21 +394,6 @@ impl Server {
397394
Ok(server)
398395
}
399396

400-
#[cfg(test)]
401-
pub(crate) fn start_test(
402-
conn: Arc<Mutex<Connection>>,
403-
config: Arc<Config>,
404-
) -> Result<Self, Error> {
405-
let templates = TemplateData::new(&conn.lock().unwrap())?;
406-
407-
Ok(Self::start_inner(
408-
"127.0.0.1:0",
409-
Pool::new_simple(conn.clone()),
410-
config,
411-
Arc::new(templates),
412-
))
413-
}
414-
415397
fn start_inner(
416398
addr: &str,
417399
pool: Pool,

0 commit comments

Comments
 (0)