[BE] Fixes #39: Spawn a connection on every request rather than having a db pool

master
fernando 2024-01-30 16:58:24 -05:00
parent 778b0b9b95
commit 8fa36e00ec
9 changed files with 44 additions and 106 deletions

1
backend/.gitignore vendored
View File

@ -4,3 +4,4 @@ aulavirtual
scraps scraps
request-logs request-logs
.idea .idea
.directory

View File

@ -12,8 +12,8 @@ use rocket::{http::Status, serde::json::Json};
mod courses; mod courses;
pub use courses::disenroll_user_options;
pub use courses::disenroll_user; pub use courses::disenroll_user;
pub use courses::disenroll_user_options;
#[options("/classroom/user")] #[options("/classroom/user")]
pub fn create_user_options() -> Status { pub fn create_user_options() -> Status {

View File

@ -4,6 +4,7 @@ use log::{error, info};
use reqwest::Client; use reqwest::Client;
use rocket::http::Status; use rocket::http::Status;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use sqlx::Connection;
use crate::json_result::JsonResult; use crate::json_result::JsonResult;
use crate::model::person::{PersonCreate, PersonLink}; use crate::model::person::{PersonCreate, PersonLink};

View File

@ -1,7 +1,8 @@
use cors::Cors; use cors::Cors;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use sqlx::mysql::MySqlPoolOptions; use sqlx::Connection;
use sqlx::MySql; use sqlx::MySql;
use sqlx::MySqlConnection;
use sqlx::Pool; use sqlx::Pool;
use std::env; use std::env;
use std::time::Instant; use std::time::Instant;
@ -20,13 +21,13 @@ static DB: OnceCell<Pool<MySql>> = OnceCell::new();
/// Opens & returns a connection to the database /// Opens & returns a connection to the database
/// ///
/// We don't use a connection pool because it often times out, /// We don't use a connection pool because on some days, on the afternoon,
/// we don't have a lot of traffic and reiniting the pool would /// the connections die and the user has to wait for all 5 connections
/// require to change a lot of code in multiple places. /// in the pool to die and then wait for the new connections to be created.
pub async fn db() -> Result<&'static Pool<MySql>, String> { pub async fn db() -> Result<MySqlConnection, String> {
/* /*
Init DB and set it as a global variable Init DB and set it as a global variable
* / */
let db_url = match env::var("DATABASE_URL") { let db_url = match env::var("DATABASE_URL") {
Ok(url) => url, Ok(url) => url,
Err(_) => return Err("env DATABASE_URL not found".to_string()), Err(_) => return Err("env DATABASE_URL not found".to_string()),
@ -43,73 +44,6 @@ pub async fn db() -> Result<&'static Pool<MySql>, String> {
Err("Error connecting to DB".to_string()) Err("Error connecting to DB".to_string())
} }
} }
// */
let attempts = 3;
for _ in 0..attempts {
match DB.get() {
Some(db) => {
log::info!("DB active connections: {}", db.size());
log::info!("DB num_idle connections: {}", db.num_idle());
return Ok(db);
}
None => {
log::info!("DB not initialized, initializing from db()");
let _ = init_db().await;
}
}
}
log::error!("Failed to initialize DB after {} attempts", attempts);
Err("Failed to initialize DB".to_string())
}
pub async fn init_db() -> Result<(), String> {
/*
Init DB and set it as a global variable
*/
let db_url = match env::var("DATABASE_URL") {
Ok(url) => url,
Err(_) => return Err("env DATABASE_URL not found".to_string()),
};
let start = Instant::now();
let pool = MySqlPoolOptions::new()
.max_connections(5)
/*
On some afternoons for some god forsaken reason the idle connections
to the db stay alive, but stop responding.
When this happens, we must restart the server, or wait for all
the active connections to timeout.
Here are some measures to circumvent that:
*/
// Set the maximum wait time for connections to 10 seconds
// In practice, the slowest connections take 1.5 seconds to connect
.acquire_timeout(std::time::Duration::from_secs(10))
// Set the maximum idle time for connections to 10 minutes
.idle_timeout(std::time::Duration::from_secs(10 * 60))
.connect(db_url.as_str())
.await;
log::info!(
"DB Pool connection took: {:?} ms",
start.elapsed().as_millis()
);
match pool {
Ok(pool) => match DB.set(pool) {
Ok(_) => Ok(()),
Err(_) => {
log::error!("Failed to set DB global variable");
Err("Failed to set DB".to_string())
}
},
Err(e) => {
log::error!("Error connecting to DB: {}", e);
Err("Error connecting to DB".to_string())
}
}
} }
#[launch] #[launch]

View File

@ -35,7 +35,7 @@ impl Course {
} }
}; };
let results = sqlx::query!("SELECT * FROM course").fetch_all(db).await; let results = sqlx::query!("SELECT * FROM course").fetch_all(&mut db).await;
let results = match results { let results = match results {
Ok(res) => res, Ok(res) => res,
@ -71,7 +71,7 @@ impl Course {
"SELECT course_name FROM course WHERE course_id = ?", "SELECT course_name FROM course WHERE course_id = ?",
course_id course_id
) )
.fetch_one(db) .fetch_one(&mut db)
.await; .await;
match res { match res {

View File

@ -20,7 +20,7 @@ impl CustomLabel {
FROM custom_label FROM custom_label
"#, "#,
) )
.fetch_all(db) .fetch_all(&mut db)
.await; .await;
match result { match result {
@ -42,7 +42,7 @@ impl CustomLabel {
"SELECT custom_label_id FROM custom_label WHERE custom_label_value = ?", "SELECT custom_label_id FROM custom_label WHERE custom_label_value = ?",
value value
) )
.fetch_all(db) .fetch_all(&mut db)
.await; .await;
let result = match result { let result = match result {
@ -72,7 +72,7 @@ impl CustomLabel {
"INSERT INTO custom_label (custom_label_value) VALUES (?)", "INSERT INTO custom_label (custom_label_value) VALUES (?)",
value value
) )
.execute(db) .execute(&mut db)
.await; .await;
if let Err(err) = result { if let Err(err) = result {

View File

@ -36,14 +36,14 @@ pub struct Person {
impl Person { impl Person {
pub async fn get_by_dni(dni: i32) -> Result<Person, DBError> { pub async fn get_by_dni(dni: i32) -> Result<Person, DBError> {
let db = match db().await { let mut db = match db().await {
Ok(db) => db, Ok(db) => db,
Err(reason) => return Err(DBError::Str(reason)), Err(reason) => return Err(DBError::Str(reason)),
}; };
let start = Instant::now(); let start = Instant::now();
let result = sqlx::query_as!(Person, "SELECT * FROM person WHERE person_dni = ?", dni) let result = sqlx::query_as!(Person, "SELECT * FROM person WHERE person_dni = ?", dni)
.fetch_one(db) .fetch_one(&mut db)
.await; .await;
log::info!( log::info!(
"DB query (person by dni) took: {:?} ms", "DB query (person by dni) took: {:?} ms",
@ -82,7 +82,7 @@ impl PersonCreate {
self.person_paternal_surname, self.person_paternal_surname,
self.person_maternal_surname, self.person_maternal_surname,
) )
.execute(db) .execute(&mut db)
.await; .await;
match result { match result {
@ -116,7 +116,7 @@ impl PersonLink {
self.person_classroom_username, self.person_classroom_username,
self.person_id, self.person_id,
) )
.execute(db) .execute(&mut db)
.await; .await;
match res { match res {

View File

@ -83,7 +83,7 @@ impl RegisterCreate {
self.person_id, self.person_id,
self.course_id self.course_id
) )
.execute(db) .execute(&mut db)
.await; .await;
match result { match result {
@ -107,7 +107,7 @@ impl RegisterCreate {
WHERE register_course_id IN WHERE register_course_id IN
(SELECT course_id FROM course WHERE course_name LIKE 'Matpel%')", (SELECT course_id FROM course WHERE course_name LIKE 'Matpel%')",
) )
.fetch_one(db) .fetch_one(&mut db)
.await; .await;
match res { match res {
@ -126,7 +126,7 @@ impl RegisterCreate {
WHERE register_course_id=?", WHERE register_course_id=?",
course_id course_id
) )
.fetch_one(db) .fetch_one(&mut db)
.await; .await;
match res { match res {
@ -345,7 +345,7 @@ impl Register {
WHERE register_person_id = (SELECT person_id FROM person WHERE person_dni = ?)", WHERE register_person_id = (SELECT person_id FROM person WHERE person_dni = ?)",
dni dni
) )
.fetch_all(db) .fetch_all(&mut db)
.await; .await;
log::info!( log::info!(
"DB (get register by id) took: {:?} ms", "DB (get register by id) took: {:?} ms",
@ -381,7 +381,7 @@ impl Register {
let mut db = db().await?; let mut db = db().await?;
let res = sqlx::query!("DELETE FROM register WHERE register_id = ?", register_id) let res = sqlx::query!("DELETE FROM register WHERE register_id = ?", register_id)
.execute(db) .execute(&mut db)
.await; .await;
match res { match res {

View File

@ -3,7 +3,7 @@
--c-on-error: #690005; --c-on-error: #690005;
--c-error-container: #93000a; --c-error-container: #93000a;
--c-on-error-container: #ffdad6; --c-on-error-container: #ffdad6;
--c-outline-50: rgba(143, 144, 154, 0.5); --c-outline-50: rgba(143, 144, 154, 0.5);
} }
@ -32,20 +32,22 @@ body {
.progress { .progress {
animation: progress 1s infinite linear; animation: progress 1s infinite linear;
} }
.left-right { .left-right {
transform-origin: 0% 50%; transform-origin: 0% 50%;
} }
@keyframes progress {
0% { @keyframes progress {
transform: translateX(0) scaleX(0); 0% {
} transform: translateX(0) scaleX(0);
40% { }
transform: translateX(0) scaleX(0.4);
} 40% {
100% { transform: translateX(0) scaleX(0.4);
transform: translateX(100%) scaleX(0.5); }
}
} 100% {
transform: translateX(100%) scaleX(0.5);
}
}