use crate::Error;
use lambda_http::tracing::warn;
use or_panic::prelude::*;
use sqlx::{PgPool, postgres::PgPoolOptions};
use std::{env, time::Duration};
use tokio::sync::OnceCell;
static DB_POOL: OnceCell<PgPool> = OnceCell::const_new();
#[derive(Clone, Copy)]
pub struct Database {
pool: &'static PgPool,
}
impl Database {
pub async fn try_new() -> Result<Database, Error> {
let pool = Database::get_or_init_cnx_pool().await?;
Ok(Database { pool })
}
pub async fn get_or_init_cnx_pool() -> Result<&'static PgPool, Error> {
if let Some(pool) = DB_POOL.get() {
return Ok(pool);
}
warn!("initiating new database connection pool (`DB_POOL` was empty).");
let db_url = env::var("DB_URL").map_err(|_| {
Error::InvalidEnvironmentConfig("missing the required `DB_URL` environment variable.")
})?;
let db_max_cnx = env::var("DB_MAX_CONNS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10);
let pool = PgPoolOptions::new()
.max_connections(db_max_cnx)
.acquire_timeout(Duration::from_secs(5))
.connect(&db_url)
.await?;
if let Some(pool) = DB_POOL.get() {
return Ok(pool);
}
let _ = DB_POOL.set(pool);
let db_pool = DB_POOL
.get()
.or_panic("`DB_POOL` should never NOT be initialized here");
Ok(db_pool)
}
pub fn pool(&self) -> &PgPool {
self.pool
}
pub async fn ping(&self) -> Result<(), Error> {
sqlx::query("SELECT 1").execute(self.pool()).await?;
Ok(())
}
pub async fn begin(&self) -> Result<sqlx::Transaction<'_, sqlx::Postgres>, Error> {
Ok(self.pool().begin().await?)
}
}