Skip to content

Commit 28f8bf0

Browse files
author
Pietro Albini
authored
Merge pull request #759 from Kixiron/r2u2
2 parents 30fac17 + d0f0916 commit 28f8bf0

11 files changed

+102
-68
lines changed

src/db/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,19 @@ pub fn connect_db() -> Result<Connection, failure::Error> {
2828
pub(crate) fn create_pool() -> r2d2::Pool<r2d2_postgres::PostgresConnectionManager> {
2929
let db_url = env::var("CRATESFYI_DATABASE_URL")
3030
.expect("CRATESFYI_DATABASE_URL environment variable is not exists");
31+
let pool_size = env::var("DOCSRS_POOL_SIZE")
32+
.map(|s| {
33+
s.parse::<u32>()
34+
.expect("DOCSRS_POOL_SIZE must be an integer")
35+
})
36+
.unwrap_or(90);
37+
3138
let manager =
3239
r2d2_postgres::PostgresConnectionManager::new(&db_url[..], r2d2_postgres::TlsMode::None)
3340
.expect("Failed to create PostgresConnectionManager");
41+
3442
r2d2::Pool::builder()
43+
.max_size(pool_size)
3544
.build(manager)
3645
.expect("Failed to create r2d2 pool")
3746
}

src/web/builds.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
6565
let version = cexpect!(router.find("version"));
6666
let req_build_id: i32 = router.find("id").unwrap_or("0").parse().unwrap_or(0);
6767

68-
let conn = extension!(req, Pool).get();
68+
let conn = extension!(req, Pool).get()?;
6969
let limits = ctry!(Limits::for_crate(&conn, name));
7070

7171
let mut build_list: Vec<Build> = Vec::new();

src/web/crate_details.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> {
315315
let name = cexpect!(router.find("name"));
316316
let req_version = router.find("version");
317317

318-
let conn = extension!(req, Pool).get();
318+
let conn = extension!(req, Pool).get()?;
319319

320320
match match_version(&conn, &name, req_version).and_then(|m| m.assume_exact()) {
321321
Some(MatchSemver::Exact((version, _))) => {

src/web/file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub struct DatabaseFileHandler;
4747
impl Handler for DatabaseFileHandler {
4848
fn handle(&self, req: &mut Request) -> IronResult<Response> {
4949
let path = req.url.path().join("/");
50-
let conn = extension!(req, Pool).get();
50+
let conn = extension!(req, Pool).get()?;
5151
if let Some(file) = File::from_path(&conn, &path) {
5252
Ok(file.serve())
5353
} else {

src/web/metrics.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,22 @@ lazy_static::lazy_static! {
6565
&["route"]
6666
)
6767
.unwrap();
68+
69+
pub static ref FAILED_DB_CONNECTIONS: IntCounter = register_int_counter!(
70+
"docsrs_failed_db_connections",
71+
"Number of attempted and failed connections to the database"
72+
)
73+
.unwrap();
74+
75+
pub static ref CONCURRENT_DB_CONNECTIONS: IntGauge = register_int_gauge!(
76+
"docsrs_db_connections",
77+
"The number of currently used database connections"
78+
)
79+
.unwrap();
6880
}
6981

7082
pub fn metrics_handler(req: &mut Request) -> IronResult<Response> {
71-
let conn = extension!(req, Pool).get();
83+
let conn = extension!(req, Pool).get()?;
7284

7385
QUEUED_CRATES_COUNT.set(
7486
ctry!(conn.query("SELECT COUNT(*) FROM queue WHERE attempt < 5;", &[]))
@@ -81,6 +93,9 @@ pub fn metrics_handler(req: &mut Request) -> IronResult<Response> {
8193
.get(0),
8294
);
8395

96+
let pool = extension!(req, Pool);
97+
CONCURRENT_DB_CONNECTIONS.set(pool.connections() as i64);
98+
8499
let mut buffer = Vec::new();
85100
let families = prometheus::gather();
86101
ctry!(TextEncoder::new().encode(&families, &mut buffer));

src/web/mod.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,6 @@ const OPENSEARCH_XML: &[u8] = include_bytes!("opensearch.xml");
8484

8585
const DEFAULT_BIND: &str = "0.0.0.0:3000";
8686

87-
type PoolFactoryFn = dyn Fn() -> Pool + Send + Sync;
88-
type PoolFactory = Box<PoolFactoryFn>;
89-
9087
fn handlebars_engine() -> Result<HandlebarsEngine, SourceError> {
9188
// TODO: Use DocBuilderOptions for paths
9289
let mut hbse = HandlebarsEngine::new();
@@ -103,25 +100,25 @@ struct CratesfyiHandler {
103100
router_handler: Box<dyn Handler>,
104101
database_file_handler: Box<dyn Handler>,
105102
static_handler: Box<dyn Handler>,
106-
pool_factory: PoolFactory,
103+
pool: Pool,
107104
}
108105

109106
impl CratesfyiHandler {
110-
fn chain<H: Handler>(pool_factory: &PoolFactoryFn, base: H) -> Chain {
107+
fn chain<H: Handler>(pool: Pool, base: H) -> Chain {
111108
let hbse = handlebars_engine().expect("Failed to load handlebar templates");
112109

113110
let mut chain = Chain::new(base);
114-
chain.link_before(pool_factory());
111+
chain.link_before(pool);
115112
chain.link_after(hbse);
116113
chain
117114
}
118115

119-
fn new(pool_factory: PoolFactory) -> CratesfyiHandler {
116+
fn new(pool: Pool) -> CratesfyiHandler {
120117
let routes = routes::build_routes();
121118
let blacklisted_prefixes = routes.page_prefixes();
122119

123-
let shared_resources = Self::chain(&pool_factory, rustdoc::SharedResourceHandler);
124-
let router_chain = Self::chain(&pool_factory, routes.iron_router());
120+
let shared_resources = Self::chain(pool.clone(), rustdoc::SharedResourceHandler);
121+
let router_chain = Self::chain(pool.clone(), routes.iron_router());
125122
let prefix = PathBuf::from(
126123
env::var("CRATESFYI_PREFIX")
127124
.expect("the CRATESFYI_PREFIX environment variable is not set"),
@@ -138,7 +135,7 @@ impl CratesfyiHandler {
138135
Box::new(file::DatabaseFileHandler),
139136
)),
140137
static_handler: Box::new(static_handler),
141-
pool_factory,
138+
pool,
142139
}
143140
}
144141
}
@@ -191,7 +188,7 @@ impl Handler for CratesfyiHandler {
191188
debug!("Path not found: {}", DebugPath(&req.url));
192189
}
193190

194-
Self::chain(&self.pool_factory, err).handle(req)
191+
Self::chain(self.pool.clone(), err).handle(req)
195192
})
196193
}
197194
}
@@ -354,28 +351,26 @@ pub struct Server {
354351

355352
impl Server {
356353
pub fn start(addr: Option<&str>) -> Self {
357-
let server = Self::start_inner(addr.unwrap_or(DEFAULT_BIND), Box::new(Pool::new));
354+
let server = Self::start_inner(addr.unwrap_or(DEFAULT_BIND), Pool::new());
358355
info!("Running docs.rs web server on http://{}", server.addr());
359356
server
360357
}
361358

362359
#[cfg(test)]
363360
pub(crate) fn start_test(conn: Arc<Mutex<Connection>>) -> Self {
364-
Self::start_inner(
365-
"127.0.0.1:0",
366-
Box::new(move || Pool::new_simple(conn.clone())),
367-
)
361+
Self::start_inner("127.0.0.1:0", Pool::new_simple(conn.clone()))
368362
}
369363

370-
fn start_inner(addr: &str, pool_factory: PoolFactory) -> Self {
364+
fn start_inner(addr: &str, pool: Pool) -> Self {
371365
// poke all the metrics counters to instantiate and register them
372366
metrics::TOTAL_BUILDS.inc_by(0);
373367
metrics::SUCCESSFUL_BUILDS.inc_by(0);
374368
metrics::FAILED_BUILDS.inc_by(0);
375369
metrics::NON_LIBRARY_BUILDS.inc_by(0);
376370
metrics::UPLOADED_FILES_TOTAL.inc_by(0);
371+
metrics::FAILED_DB_CONNECTIONS.inc_by(0);
377372

378-
let cratesfyi = CratesfyiHandler::new(pool_factory);
373+
let cratesfyi = CratesfyiHandler::new(pool);
379374
let inner = Iron::new(cratesfyi)
380375
.http(addr)
381376
.unwrap_or_else(|_| panic!("Failed to bind to socket on {}", addr));

src/web/pool.rs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::db::create_pool;
2-
use iron::{typemap, BeforeMiddleware, IronResult, Request};
2+
use iron::{status::Status, typemap, BeforeMiddleware, IronError, IronResult, Request};
33
use postgres::Connection;
4+
use std::marker::PhantomData;
45

56
#[cfg(test)]
67
use std::sync::{Arc, Mutex, MutexGuard};
78

9+
#[derive(Debug, Clone)]
810
pub(crate) enum Pool {
911
R2D2(r2d2::Pool<r2d2_postgres::PostgresConnectionManager>),
1012
#[cfg(test)]
@@ -20,43 +22,55 @@ impl Pool {
2022
pub(crate) fn new_simple(conn: Arc<Mutex<Connection>>) -> Self {
2123
Pool::Simple(conn)
2224
}
23-
}
2425

25-
impl typemap::Key for Pool {
26-
type Value = PoolConnection;
27-
}
26+
pub(super) fn get<'a>(&'a self) -> IronResult<DerefConnection<'a>> {
27+
match self {
28+
Self::R2D2(conn) => {
29+
let conn = conn.get().map_err(|err| {
30+
log::error!("Error getting db connection: {:?}", err);
31+
super::metrics::FAILED_DB_CONNECTIONS.inc();
32+
33+
IronError::new(err, Status::InternalServerError)
34+
})?;
35+
36+
Ok(DerefConnection::Connection(conn, PhantomData))
37+
}
2838

29-
impl BeforeMiddleware for Pool {
30-
fn before(&self, req: &mut Request) -> IronResult<()> {
31-
req.extensions.insert::<Pool>(match self {
32-
Self::R2D2(pool) => PoolConnection::R2D2(pool.get().unwrap()),
3339
#[cfg(test)]
34-
Self::Simple(mutex) => PoolConnection::Simple(mutex.clone()),
35-
});
36-
Ok(())
40+
Self::Simple(mutex) => Ok(DerefConnection::Guard(
41+
mutex.lock().expect("failed to lock the connection"),
42+
)),
43+
}
3744
}
38-
}
39-
40-
pub(crate) enum PoolConnection {
41-
R2D2(r2d2::PooledConnection<r2d2_postgres::PostgresConnectionManager>),
42-
#[cfg(test)]
43-
Simple(Arc<Mutex<Connection>>),
44-
}
4545

46-
impl PoolConnection {
47-
pub(super) fn get(&self) -> DerefConnection<'_> {
46+
pub(crate) fn connections(&self) -> u32 {
4847
match self {
49-
Self::R2D2(conn) => DerefConnection::Connection(&conn),
48+
Self::R2D2(conn) => conn.state().connections,
49+
5050
#[cfg(test)]
51-
Self::Simple(mutex) => {
52-
DerefConnection::Guard(mutex.lock().expect("failed to lock the connection"))
53-
}
51+
Self::Simple(..) => 0,
5452
}
5553
}
5654
}
5755

56+
impl typemap::Key for Pool {
57+
type Value = Pool;
58+
}
59+
60+
impl BeforeMiddleware for Pool {
61+
fn before(&self, req: &mut Request) -> IronResult<()> {
62+
req.extensions.insert::<Pool>(self.clone());
63+
64+
Ok(())
65+
}
66+
}
67+
5868
pub(crate) enum DerefConnection<'a> {
59-
Connection(&'a Connection),
69+
Connection(
70+
r2d2::PooledConnection<r2d2_postgres::PostgresConnectionManager>,
71+
PhantomData<&'a ()>,
72+
),
73+
6074
#[cfg(test)]
6175
Guard(MutexGuard<'a, Connection>),
6276
}
@@ -66,7 +80,8 @@ impl<'a> std::ops::Deref for DerefConnection<'a> {
6680

6781
fn deref(&self) -> &Connection {
6882
match self {
69-
Self::Connection(conn) => conn,
83+
Self::Connection(conn, ..) => conn,
84+
7085
#[cfg(test)]
7186
Self::Guard(guard) => &guard,
7287
}

src/web/releases.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ fn get_search_results(
335335
}
336336

337337
pub fn home_page(req: &mut Request) -> IronResult<Response> {
338-
let conn = extension!(req, Pool).get();
338+
let conn = extension!(req, Pool).get()?;
339339
let packages = get_releases(&conn, 1, RELEASES_IN_HOME, Order::ReleaseTime);
340340
Page::new(packages)
341341
.set_true("show_search_form")
@@ -344,7 +344,7 @@ pub fn home_page(req: &mut Request) -> IronResult<Response> {
344344
}
345345

346346
pub fn releases_feed_handler(req: &mut Request) -> IronResult<Response> {
347-
let conn = extension!(req, Pool).get();
347+
let conn = extension!(req, Pool).get()?;
348348
let packages = get_releases(&conn, 1, RELEASES_IN_FEED, Order::ReleaseTime);
349349
let mut resp = ctry!(Page::new(packages).to_resp("releases_feed"));
350350
resp.headers.set(::iron::headers::ContentType(
@@ -391,7 +391,7 @@ pub fn recent_releases_handler(req: &mut Request) -> IronResult<Response> {
391391
.unwrap_or("1")
392392
.parse()
393393
.unwrap_or(1);
394-
let conn = extension!(req, Pool).get();
394+
let conn = extension!(req, Pool).get()?;
395395
let packages = get_releases(&conn, page_number, RELEASES_IN_RELEASES, Order::ReleaseTime);
396396
releases_handler(
397397
packages,
@@ -408,7 +408,7 @@ pub fn releases_by_stars_handler(req: &mut Request) -> IronResult<Response> {
408408
.unwrap_or("1")
409409
.parse()
410410
.unwrap_or(1);
411-
let conn = extension!(req, Pool).get();
411+
let conn = extension!(req, Pool).get()?;
412412
let packages = get_releases(&conn, page_number, RELEASES_IN_RELEASES, Order::GithubStars);
413413
releases_handler(
414414
packages,
@@ -425,7 +425,7 @@ pub fn releases_recent_failures_handler(req: &mut Request) -> IronResult<Respons
425425
.unwrap_or("1")
426426
.parse()
427427
.unwrap_or(1);
428-
let conn = extension!(req, Pool).get();
428+
let conn = extension!(req, Pool).get()?;
429429
let packages = get_releases(
430430
&conn,
431431
page_number,
@@ -447,7 +447,7 @@ pub fn releases_failures_by_stars_handler(req: &mut Request) -> IronResult<Respo
447447
.unwrap_or("1")
448448
.parse()
449449
.unwrap_or(1);
450-
let conn = extension!(req, Pool).get();
450+
let conn = extension!(req, Pool).get()?;
451451
let packages = get_releases(
452452
&conn,
453453
page_number,
@@ -468,7 +468,7 @@ pub fn author_handler(req: &mut Request) -> IronResult<Response> {
468468
// page number of releases
469469
let page_number: i64 = router.find("page").unwrap_or("1").parse().unwrap_or(1);
470470

471-
let conn = extension!(req, Pool).get();
471+
let conn = extension!(req, Pool).get()?;
472472

473473
#[allow(clippy::or_fun_call)]
474474
let author = ctry!(router
@@ -518,7 +518,7 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
518518
let params = ctry!(req.get::<Params>());
519519
let query = params.find(&["query"]);
520520

521-
let conn = extension!(req, Pool).get();
521+
let conn = extension!(req, Pool).get()?;
522522
if let Some(&Value::String(ref query)) = query {
523523
// check if I am feeling lucky button pressed and redirect user to crate page
524524
// if there is a match
@@ -618,7 +618,7 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
618618
}
619619

620620
pub fn activity_handler(req: &mut Request) -> IronResult<Response> {
621-
let conn = extension!(req, Pool).get();
621+
let conn = extension!(req, Pool).get()?;
622622
let release_activity_data: Json = ctry!(conn.query(
623623
"SELECT value FROM config WHERE name = 'release_activity'",
624624
&[]
@@ -635,7 +635,7 @@ pub fn activity_handler(req: &mut Request) -> IronResult<Response> {
635635
}
636636

637637
pub fn build_queue_handler(req: &mut Request) -> IronResult<Response> {
638-
let conn = extension!(req, Pool).get();
638+
let conn = extension!(req, Pool).get()?;
639639
let mut crates: Vec<(String, String, i32)> = Vec::new();
640640
for krate in &conn
641641
.query(

0 commit comments

Comments
 (0)