make load_or_create *only* load_or_create

Extracted the other logic to its current singular callsite for now.

The load_or_create function finally does nothing other than load or
create the database (and do some related checks, which is fine). This
paves the way for more/better database surgery tooling.
This commit is contained in:
Charles Hall 2024-09-24 17:44:33 -07:00
parent e9caf228b3
commit c2c6083277
No known key found for this signature in database
GPG key ID: 7B8E0645816E07CF
3 changed files with 56 additions and 52 deletions

View file

@ -40,11 +40,14 @@ use crate::{
ruma_wrapper::{Ar, Ra},
server_server, well_known,
},
config,
config::{Config, ListenComponent, ListenTransport},
config::{self, Config, ListenComponent, ListenTransport},
database::KeyValueDatabase,
error, observability, services, utils,
utils::error::{Error, Result},
error, observability, services,
utils::{
self,
error::{Error, Result},
},
Services, SERVICES,
};
pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> {
@ -70,8 +73,27 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> {
.expect("should be able to increase the soft limit to the hard limit");
info!("Loading database");
let db = KeyValueDatabase::load_or_create(config, reload_handles)
.map_err(Error::DatabaseError)?;
let db = Box::leak(Box::new(
KeyValueDatabase::load_or_create(&config)
.map_err(Error::DatabaseError)?,
));
// This is the first and only time we initialize the SERVICE static
*SERVICES.write().unwrap() = Some(Box::leak(Box::new(
Services::build(db, config, reload_handles)
.map_err(Error::InitializeServices)?,
)));
// Matrix resource ownership is based on the server name; changing it
// requires recreating the database from scratch. This check needs to be
// done before background tasks are started to avoid data races.
if services().users.count().map(|x| x > 0).map_err(Error::NonZeroUsers)? {
let admin_bot = services().globals.admin_bot_user_id.as_ref();
if !services().users.exists(admin_bot).map_err(Error::AdminBotExists)? {
return Err(Error::Renamed);
}
}
db.apply_migrations().await.map_err(Error::DatabaseError)?;
info!("Starting background tasks");

View file

@ -24,7 +24,6 @@ use tracing::{debug, error, info, info_span, warn, Instrument};
use crate::{
config::DatabaseBackend,
observability::FilterReloadHandles,
service::{
media::MediaFileKey,
rooms::{
@ -33,7 +32,7 @@ use crate::{
timeline::PduCount,
},
},
services, utils, Config, Error, PduEvent, Result, Services, SERVICES,
services, utils, Config, Error, PduEvent, Result,
};
pub(crate) struct KeyValueDatabase {
@ -319,11 +318,8 @@ impl KeyValueDatabase {
allow(unreachable_code)
)]
#[allow(clippy::too_many_lines)]
pub(crate) fn load_or_create(
config: Config,
reload_handles: FilterReloadHandles,
) -> Result<&'static KeyValueDatabase> {
Self::check_db_setup(&config)?;
pub(crate) fn load_or_create(config: &Config) -> Result<KeyValueDatabase> {
Self::check_db_setup(config)?;
if !Path::new(&config.database.path).exists() {
fs::create_dir_all(&config.database.path).map_err(|_| {
@ -339,21 +335,19 @@ impl KeyValueDatabase {
not(any(feature = "rocksdb", feature = "sqlite")),
allow(unused_variables)
)]
let builder: Arc<dyn KeyValueDatabaseEngine> = match config
.database
.backend
{
#[cfg(feature = "sqlite")]
DatabaseBackend::Sqlite => {
Arc::new(Arc::<abstraction::sqlite::Engine>::open(&config)?)
}
#[cfg(feature = "rocksdb")]
DatabaseBackend::Rocksdb => {
Arc::new(Arc::<abstraction::rocksdb::Engine>::open(&config)?)
}
};
let builder: Arc<dyn KeyValueDatabaseEngine> =
match config.database.backend {
#[cfg(feature = "sqlite")]
DatabaseBackend::Sqlite => {
Arc::new(Arc::<abstraction::sqlite::Engine>::open(config)?)
}
#[cfg(feature = "rocksdb")]
DatabaseBackend::Rocksdb => {
Arc::new(Arc::<abstraction::rocksdb::Engine>::open(config)?)
}
};
let db_raw = Box::new(Self {
let db = Self {
db: builder.clone(),
userid_password: builder.open_tree("userid_password")?,
userid_displayname: builder.open_tree("userid_displayname")?,
@ -527,31 +521,7 @@ impl KeyValueDatabase {
our_real_users_cache: RwLock::new(HashMap::new()),
appservice_in_room_cache: RwLock::new(HashMap::new()),
lasttimelinecount_cache: Mutex::new(HashMap::new()),
});
let db = Box::leak(db_raw);
let services_raw =
Box::new(Services::build(db, config, reload_handles)?);
// This is the first and only time we initialize the SERVICE static
*SERVICES.write().unwrap() = Some(Box::leak(services_raw));
// Matrix resource ownership is based on the server name; changing it
// requires recreating the database from scratch.
if services().users.count()? > 0 {
let admin_bot = services().globals.admin_bot_user_id.as_ref();
if !services().users.exists(admin_bot)? {
error!(
user_id = %admin_bot,
"The admin bot does not exist and the database is not new",
);
return Err(Error::bad_database(
"Cannot reuse an existing database after changing the \
server name, please delete the old one first.",
));
}
}
};
Ok(db)
}

View file

@ -61,6 +61,18 @@ pub(crate) enum ServeCommand {
#[error("failed to serve requests")]
Serve(#[from] Serve),
#[error("failed to initialize services")]
InitializeServices(#[source] crate::utils::error::Error),
#[error("failed to check if there are any users")]
NonZeroUsers(#[source] crate::utils::error::Error),
#[error("failed to check if the admin bot exists")]
AdminBotExists(#[source] crate::utils::error::Error),
#[error("`server_name` in the database and config file differ")]
Renamed,
}
/// Observability initialization errors