feat: allow database directory configuration

Adds configuration options for database directory, either on command
line through (--db dir-name) or the config.toml file.

Fixes: https://todo.sr.ht/~gheartsfield/nostr-rs-relay/13
This commit is contained in:
Greg Heartsfield 2021-12-31 11:51:57 -06:00
parent f7f12a7984
commit 5a19a8876f
6 changed files with 59 additions and 14 deletions

View File

@ -35,9 +35,7 @@ COPY --from=builder /nostr-rs-relay/target/release/nostr-rs-relay ${APP}/nostr-r
RUN chown -R $APP_USER:$APP_USER ${APP} RUN chown -R $APP_USER:$APP_USER ${APP}
USER $APP_USER USER $APP_USER
WORKDIR ${APP_DATA} WORKDIR ${APP}
ENV RUST_LOG=info ENV RUST_LOG=info
CMD ["./nostr-rs-relay --db $APP_DATA"]
CMD ["../nostr-rs-relay"]

View File

@ -1,4 +1,8 @@
# Nostr-rs-relay configuration # Nostr-rs-relay configuration
[database]
# Directory for SQLite files. Defaults to the current directory. Can
# also be specified with the "--db dirname" command line option.
data_directory = "data"
[network] [network]
# Bind to this network address # Bind to this network address
@ -18,15 +22,15 @@ port = 8080
messages_per_sec = 0 messages_per_sec = 0
# Maximum WebSocket message in bytes. Defaults to 128k. # Maximum WebSocket message in bytes. Defaults to 128k.
#max_ws_message_bytes = 131072 max_ws_message_bytes = 131072
# Maximum WebSocket frame size in bytes. Defaults to 128k. # Maximum WebSocket frame size in bytes. Defaults to 128k.
#max_ws_frame_bytes = 131072 max_ws_frame_bytes = 131072
# Broadcast buffer size, in number of events. This prevents slow # Broadcast buffer size, in number of events. This prevents slow
# readers from consuming memory. Defaults to 4096. # readers from consuming memory. Defaults to 4096.
#broadcast_buffer = 4096 broadcast_buffer = 4096
# Event persistence buffer size, in number of events. This provides # Event persistence buffer size, in number of events. This provides
# backpressure to senders if writes are slow. Defaults to 16. # backpressure to senders if writes are slow. Defaults to 16.
#event_persist_buffer = 16 event_persist_buffer = 16

View File

@ -8,6 +8,12 @@ lazy_static! {
pub static ref SETTINGS: RwLock<Settings> = RwLock::new(Settings::default()); pub static ref SETTINGS: RwLock<Settings> = RwLock::new(Settings::default());
} }
#[derive(Debug, Serialize, Deserialize)]
#[allow(unused)]
pub struct Database {
pub data_directory: String,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[allow(unused)] #[allow(unused)]
pub struct Network { pub struct Network {
@ -46,6 +52,7 @@ pub struct Limits {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[allow(unused)] #[allow(unused)]
pub struct Settings { pub struct Settings {
pub database: Database,
pub network: Network, pub network: Network,
pub limits: Limits, pub limits: Limits,
pub retention: Retention, pub retention: Retention,
@ -82,6 +89,9 @@ impl Settings {
impl Default for Settings { impl Default for Settings {
fn default() -> Self { fn default() -> Self {
Settings { Settings {
database: Database {
data_directory: ".".to_owned(),
},
network: Network { network: Network {
port: 8080, port: 8080,
address: "0.0.0.0".to_owned(), address: "0.0.0.0".to_owned(),

View File

@ -122,14 +122,18 @@ pub async fn db_writer(
mut shutdown: tokio::sync::broadcast::Receiver<()>, mut shutdown: tokio::sync::broadcast::Receiver<()>,
) -> tokio::task::JoinHandle<Result<()>> { ) -> tokio::task::JoinHandle<Result<()>> {
task::spawn_blocking(move || { task::spawn_blocking(move || {
// get database configuration settings
let config = SETTINGS.read().unwrap();
let db_dir = &config.database.data_directory;
let full_path = Path::new(db_dir).join(DB_FILE);
// create a connection
let mut conn = Connection::open_with_flags( let mut conn = Connection::open_with_flags(
Path::new(DB_FILE), &full_path,
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE, OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE,
)?; )?;
info!("opened database for writing"); info!("opened database {:?} for writing", full_path);
upgrade_db(&mut conn)?; upgrade_db(&mut conn)?;
// get rate limit settings // get rate limit settings
let config = SETTINGS.read().unwrap();
let rps_setting = config.limits.messages_per_sec; let rps_setting = config.limits.messages_per_sec;
let mut lim_opt = None; let mut lim_opt = None;
let clock = governor::clock::QuantaClock::default(); let clock = governor::clock::QuantaClock::default();
@ -373,9 +377,12 @@ pub async fn db_query(
mut abandon_query_rx: tokio::sync::oneshot::Receiver<()>, mut abandon_query_rx: tokio::sync::oneshot::Receiver<()>,
) { ) {
task::spawn_blocking(move || { task::spawn_blocking(move || {
let config = SETTINGS.read().unwrap();
let db_dir = &config.database.data_directory;
let full_path = Path::new(db_dir).join(DB_FILE);
let conn = let conn =
Connection::open_with_flags(Path::new(DB_FILE), OpenFlags::SQLITE_OPEN_READ_ONLY) Connection::open_with_flags(&full_path, OpenFlags::SQLITE_OPEN_READ_ONLY).unwrap();
.unwrap();
debug!("opened database for reading"); debug!("opened database for reading");
debug!("going to query for: {:?}", sub); debug!("going to query for: {:?}", sub);
// generate SQL query // generate SQL query

View File

@ -36,6 +36,8 @@ pub enum Error {
SqlError(rusqlite::Error), SqlError(rusqlite::Error),
#[error("Config error")] #[error("Config error")]
ConfigError(config::ConfigError), ConfigError(config::ConfigError),
#[error("Data directory does not exist")]
DatabaseDirError,
} }
impl From<rusqlite::Error> for Error { impl From<rusqlite::Error> for Error {

View File

@ -12,6 +12,8 @@ use nostr_rs_relay::protostream;
use nostr_rs_relay::protostream::NostrMessage::*; use nostr_rs_relay::protostream::NostrMessage::*;
use nostr_rs_relay::protostream::NostrResponse::*; use nostr_rs_relay::protostream::NostrResponse::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::path::Path;
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
use tokio::runtime::Builder; use tokio::runtime::Builder;
use tokio::sync::broadcast; use tokio::sync::broadcast;
@ -20,17 +22,39 @@ use tokio::sync::mpsc;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use tungstenite::protocol::WebSocketConfig; use tungstenite::protocol::WebSocketConfig;
fn db_from_args(args: Vec<String>) -> Option<String> {
if args.len() == 3 {
if args.get(1) == Some(&"--db".to_owned()) {
return args.get(2).map(|x| x.to_owned());
}
}
None
}
/// Start running a Nostr relay server. /// Start running a Nostr relay server.
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
// setup logger // setup logger
let _ = env_logger::try_init(); let _ = env_logger::try_init();
// get database directory from args
let args: Vec<String> = env::args().collect();
let db_dir: Option<String> = db_from_args(args);
info!("Using database: {:?}", db_dir);
{ {
let mut settings = config::SETTINGS.write().unwrap(); let mut settings = config::SETTINGS.write().unwrap();
// replace default settings with those read from config.toml // replace default settings with those read from config.toml
let c = config::Settings::new(); let mut c = config::Settings::new();
// update with database location
if let Some(db) = db_dir {
c.database.data_directory = db.to_owned();
}
*settings = c; *settings = c;
} }
let config = config::SETTINGS.read().unwrap(); let config = config::SETTINGS.read().unwrap();
// do some config validation.
if !Path::new(&config.database.data_directory).is_dir() {
error!("Database directory does not exist");
return Err(Error::DatabaseDirError);
}
debug!("config: {:?}", config); debug!("config: {:?}", config);
let addr = format!("{}:{}", config.network.address.trim(), config.network.port); let addr = format!("{}:{}", config.network.address.trim(), config.network.port);
// configure tokio runtime // configure tokio runtime