Skip to content

Commit

Permalink
first changes to support second PostgreSQL user account for read-only…
Browse files Browse the repository at this point in the history
… operatoins
  • Loading branch information
RocketDerp committed Aug 10, 2023
1 parent cdd20b1 commit d20e4c9
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 3 deletions.
9 changes: 9 additions & 0 deletions crates/api_common/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::sync::Arc;
#[derive(Clone)]
pub struct LemmyContext {
pool: ActualDbPool,
read_pool: ActualDbPool,
client: Arc<ClientWithMiddleware>,
secret: Arc<Secret>,
rate_limit_cell: RateLimitCell,
Expand All @@ -20,23 +21,31 @@ pub struct LemmyContext {
impl LemmyContext {
pub fn create(
pool: ActualDbPool,
read_pool: ActualDbPool,
client: ClientWithMiddleware,
secret: Secret,
rate_limit_cell: RateLimitCell,
) -> LemmyContext {
LemmyContext {
pool,
read_pool,
client: Arc::new(client),
secret: Arc::new(secret),
rate_limit_cell,
}
}
pub fn read_pool(&self) -> DbPool<'_> {
DbPool::Pool(&self.read_pool)
}
pub fn pool(&self) -> DbPool<'_> {
DbPool::Pool(&self.pool)
}
pub fn inner_pool(&self) -> &ActualDbPool {
&self.pool
}
pub fn inner_read_pool(&self) -> &ActualDbPool {
&self.read_pool
}
pub fn client(&self) -> &ClientWithMiddleware {
&self.client
}
Expand Down
49 changes: 48 additions & 1 deletion crates/db_schema/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ use url::Url;

const FETCH_LIMIT_DEFAULT: i64 = 10;
pub const FETCH_LIMIT_MAX: i64 = 50;
const POOL_TIMEOUT: Option<Duration> = Some(Duration::from_secs(5));
// ToDo: split out the various pool timeout values
const POOL_TIMEOUT: Option<Duration> = Some(Duration::from_secs(12));
const POOL_READ_TIMEOUT: Option<Duration> = Some(Duration::from_secs(9));

pub type ActualDbPool = Pool<AsyncPgConnection>;

Expand Down Expand Up @@ -153,6 +155,10 @@ pub fn get_database_url_from_env() -> Result<String, VarError> {
env::var("LEMMY_DATABASE_URL")
}

pub fn get_database_read_url_from_env() -> Result<String, VarError> {
env::var("LEMMY_DATABASE_READ_URL")
}

pub fn fuzzy_search(q: &str) -> String {
let replaced = q.replace('%', "\\%").replace('_', "\\_").replace(' ', "%");
format!("%{replaced}%")
Expand Down Expand Up @@ -268,6 +274,31 @@ async fn build_db_pool_settings_opt(
Ok(pool)
}

async fn build_db_read_pool_settings_opt(
settings: Option<&Settings>,
) -> Result<ActualDbPool, LemmyError> {
let db_url = get_database_read_url(settings);
let pool_size = settings.map(|s| s.database.read_pool_size).unwrap_or(12);
// We only support TLS with sslmode=require currently
let tls_enabled = db_url.contains("sslmode=require");
let manager = if tls_enabled {
// diesel-async does not support any TLS connections out of the box, so we need to manually
// provide a setup function which handles creating the connection
AsyncDieselConnectionManager::<AsyncPgConnection>::new_with_setup(&db_url, establish_connection)
} else {
AsyncDieselConnectionManager::<AsyncPgConnection>::new(&db_url)
};
let pool = Pool::builder(manager)
.max_size(pool_size)
.wait_timeout(POOL_READ_TIMEOUT)
.create_timeout(POOL_READ_TIMEOUT)
.recycle_timeout(POOL_READ_TIMEOUT)
.runtime(Runtime::Tokio1)
.build()?;

Ok(pool)
}

fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
let fut = async {
let rustls_config = rustls::ClientConfig::builder()
Expand Down Expand Up @@ -323,6 +354,10 @@ pub async fn build_db_pool(settings: &Settings) -> Result<ActualDbPool, LemmyErr
build_db_pool_settings_opt(Some(settings)).await
}

pub async fn build_db_read_pool(settings: &Settings) -> Result<ActualDbPool, LemmyError> {
build_db_read_pool_settings_opt(Some(settings)).await
}

pub async fn build_db_pool_for_tests() -> ActualDbPool {
build_db_pool_settings_opt(None)
.await
Expand All @@ -340,6 +375,18 @@ pub fn get_database_url(settings: Option<&Settings>) -> String {
}
}

pub fn get_database_read_url(settings: Option<&Settings>) -> String {
// The env var should override anything in the settings config
match get_database_read_url_from_env() {
Ok(url) => url,
Err(e) => match settings {
Some(settings) => settings.get_database_read_url(),
// do not panic and fall-back to single URL?
None => panic!("Failed to read database URL from env var LEMMY_DATABASE_READ_URL: {e}"),
},
}
}

pub fn naive_now() -> NaiveDateTime {
chrono::prelude::Utc::now().naive_utc()
}
Expand Down
21 changes: 21 additions & 0 deletions crates/utils/src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ impl Settings {
Ok(config)
}

// can a second user be added and connection pool?
// that way PostgreSQL activity can be followed by user?
// This would also allow caching servers and replicated servres to be used
// for read-only performance-sensitive purposes.
// What is the name of that MIT caching PostgreSQL proxy server app?
pub fn get_database_url(&self) -> String {
match &self.database.connection {
DatabaseConnection::Uri { uri } => uri.clone(),
Expand All @@ -60,6 +65,22 @@ impl Settings {
}
}

pub fn get_database_read_url(&self) -> String {
match &self.database.read_connection {
DatabaseConnection::Uri { uri } => uri.clone(),
DatabaseConnection::Parts(parts) => {
format!(
"postgres://{}:{}@{}:{}/{}",
utf8_percent_encode(&parts.read_user, NON_ALPHANUMERIC),
utf8_percent_encode(&parts.read_password, NON_ALPHANUMERIC),
parts.host,
parts.port,
utf8_percent_encode(&parts.database, NON_ALPHANUMERIC),
)
}
}
}

fn get_config_location() -> String {
env::var("LEMMY_CONFIG_LOCATION").unwrap_or_else(|_| DEFAULT_CONFIG_FILE.to_string())
}
Expand Down
9 changes: 9 additions & 0 deletions crates/utils/src/settings/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,13 @@ pub struct PictrsConfig {
pub struct DatabaseConfig {
#[serde(flatten, default)]
pub connection: DatabaseConnection,
pub read_connection: DatabaseConnection,

/// Maximum number of active sql connections
#[default(5)]
pub pool_size: usize,
#[default(12)]
pub read_pool_size: usize,
}

#[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
Expand Down Expand Up @@ -114,6 +117,12 @@ pub struct DatabaseConnectionParts {
/// Password to connect to postgres
#[default("password")]
pub password: String,
/// Username to connect to postgres
#[default("lemmy_read0")]
pub(super) read_user: String,
/// Password to connect to postgres
#[default("readpassword")]
pub read_password: String,
#[default("localhost")]
/// Host where postgres is running
pub host: String,
Expand Down
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use lemmy_apub::{
};
use lemmy_db_schema::{
source::secret::Secret,
utils::{build_db_pool, get_database_url, run_migrations},
utils::{build_db_pool, build_db_read_pool, get_database_url, run_migrations},
};
use lemmy_routes::{feeds, images, nodeinfo, webfinger};
use lemmy_utils::{
Expand Down Expand Up @@ -76,9 +76,12 @@ pub async fn start_lemmy_server() -> Result<(), LemmyError> {
let db_url = get_database_url(Some(&settings));
run_migrations(&db_url);

// Set up the connection pool
// Set up the database connection pool
let pool = build_db_pool(&settings).await?;

// Set up the database read user connection pool
let read_pool = build_db_read_pool(&settings).await?;

// Run the Code-required migrations
run_advanced_migrations(&mut (&pool).into(), &settings).await?;

Expand Down Expand Up @@ -128,6 +131,7 @@ pub async fn start_lemmy_server() -> Result<(), LemmyError> {

let context = LemmyContext::create(
pool.clone(),
read_pool.clone(),
client.clone(),
secret.clone(),
rate_limit_cell.clone(),
Expand Down

0 comments on commit d20e4c9

Please sign in to comment.