simplify database backup interface related
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
56cc9318de
commit
200df676e9
3 changed files with 69 additions and 52 deletions
|
@ -1,6 +1,11 @@
|
||||||
use std::{fmt::Write, path::PathBuf, sync::Arc};
|
use std::{fmt::Write, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use conduwuit::{Err, Result, info, utils::time, warn};
|
use conduwuit::{
|
||||||
|
Err, Result, info,
|
||||||
|
utils::{stream::IterStream, time},
|
||||||
|
warn,
|
||||||
|
};
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
use crate::admin_command;
|
use crate::admin_command;
|
||||||
|
|
||||||
|
@ -81,33 +86,31 @@ pub(super) async fn clear_caches(&self) -> Result {
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn list_backups(&self) -> Result {
|
pub(super) async fn list_backups(&self) -> Result {
|
||||||
let result = self.services.db.db.backup_list()?;
|
self.services
|
||||||
|
.db
|
||||||
if result.is_empty() {
|
.db
|
||||||
return Err!("No backups found.");
|
.backup_list()?
|
||||||
}
|
.try_stream()
|
||||||
|
.try_for_each(|result| write!(self, "{result}"))
|
||||||
self.write_str(&result).await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn backup_database(&self) -> Result {
|
pub(super) async fn backup_database(&self) -> Result {
|
||||||
let db = Arc::clone(&self.services.db);
|
let db = Arc::clone(&self.services.db);
|
||||||
let mut result = self
|
let result = self
|
||||||
.services
|
.services
|
||||||
.server
|
.server
|
||||||
.runtime()
|
.runtime()
|
||||||
.spawn_blocking(move || match db.db.backup() {
|
.spawn_blocking(move || match db.db.backup() {
|
||||||
| Ok(()) => String::new(),
|
| Ok(()) => "Done".to_owned(),
|
||||||
| Err(e) => e.to_string(),
|
| Err(e) => format!("Failed: {e}"),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if result.is_empty() {
|
let count = self.services.db.db.backup_count()?;
|
||||||
result = self.services.db.db.backup_list()?;
|
self.write_str(&format!("{result}. Currently have {count} backups."))
|
||||||
}
|
.await
|
||||||
|
|
||||||
self.write_str(&result).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
|
|
|
@ -1,24 +1,16 @@
|
||||||
use std::fmt::Write;
|
use std::{ffi::OsString, path::PathBuf};
|
||||||
|
|
||||||
use conduwuit::{Result, error, implement, info, utils::time::rfc2822_from_seconds, warn};
|
use conduwuit::{Err, Result, error, implement, info, utils::time::rfc2822_from_seconds, warn};
|
||||||
use rocksdb::backup::{BackupEngine, BackupEngineOptions};
|
use rocksdb::backup::{BackupEngine, BackupEngineOptions};
|
||||||
|
|
||||||
use super::Engine;
|
use super::Engine;
|
||||||
use crate::{or_else, util::map_err};
|
use crate::util::map_err;
|
||||||
|
|
||||||
#[implement(Engine)]
|
#[implement(Engine)]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn backup(&self) -> Result {
|
pub fn backup(&self) -> Result {
|
||||||
let server = &self.ctx.server;
|
let mut engine = self.backup_engine()?;
|
||||||
let config = &server.config;
|
let config = &self.ctx.server.config;
|
||||||
let path = config.database_backup_path.as_ref();
|
|
||||||
if path.is_none() || path.is_some_and(|path| path.as_os_str().is_empty()) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let options =
|
|
||||||
BackupEngineOptions::new(path.expect("valid database backup path")).map_err(map_err)?;
|
|
||||||
let mut engine = BackupEngine::open(&options, &*self.ctx.env.lock()?).map_err(map_err)?;
|
|
||||||
if config.database_backups_to_keep > 0 {
|
if config.database_backups_to_keep > 0 {
|
||||||
let flush = !self.is_read_only();
|
let flush = !self.is_read_only();
|
||||||
engine
|
engine
|
||||||
|
@ -40,34 +32,62 @@ pub fn backup(&self) -> Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.database_backups_to_keep == 0 {
|
||||||
|
warn!("Configuration item `database_backups_to_keep` is set to 0.");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(Engine)]
|
#[implement(Engine)]
|
||||||
pub fn backup_list(&self) -> Result<String> {
|
pub fn backup_list(&self) -> Result<impl Iterator<Item = String> + Send> {
|
||||||
let server = &self.ctx.server;
|
let info = self.backup_engine()?.get_backup_info();
|
||||||
let config = &server.config;
|
|
||||||
let path = config.database_backup_path.as_ref();
|
if info.is_empty() {
|
||||||
if path.is_none() || path.is_some_and(|path| path.as_os_str().is_empty()) {
|
return Err!("No backups found.");
|
||||||
return Ok("Configure database_backup_path to enable backups, or the path specified is \
|
|
||||||
not valid"
|
|
||||||
.to_owned());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = String::new();
|
let list = info.into_iter().map(|info| {
|
||||||
let options =
|
format!(
|
||||||
BackupEngineOptions::new(path.expect("valid database backup path")).or_else(or_else)?;
|
|
||||||
let engine = BackupEngine::open(&options, &*self.ctx.env.lock()?).or_else(or_else)?;
|
|
||||||
for info in engine.get_backup_info() {
|
|
||||||
writeln!(
|
|
||||||
res,
|
|
||||||
"#{} {}: {} bytes, {} files",
|
"#{} {}: {} bytes, {} files",
|
||||||
info.backup_id,
|
info.backup_id,
|
||||||
rfc2822_from_seconds(info.timestamp),
|
rfc2822_from_seconds(info.timestamp),
|
||||||
info.size,
|
info.size,
|
||||||
info.num_files,
|
info.num_files,
|
||||||
)?;
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[implement(Engine)]
|
||||||
|
pub fn backup_count(&self) -> Result<usize> {
|
||||||
|
let info = self.backup_engine()?.get_backup_info();
|
||||||
|
|
||||||
|
Ok(info.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[implement(Engine)]
|
||||||
|
fn backup_engine(&self) -> Result<BackupEngine> {
|
||||||
|
let path = self.backup_path()?;
|
||||||
|
let options = BackupEngineOptions::new(path).map_err(map_err)?;
|
||||||
|
BackupEngine::open(&options, &*self.ctx.env.lock()?).map_err(map_err)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[implement(Engine)]
|
||||||
|
fn backup_path(&self) -> Result<OsString> {
|
||||||
|
let path = self
|
||||||
|
.ctx
|
||||||
|
.server
|
||||||
|
.config
|
||||||
|
.database_backup_path
|
||||||
|
.clone()
|
||||||
|
.map(PathBuf::into_os_string)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if path.is_empty() {
|
||||||
|
return Err!(Config("database_backup_path", "Configure path to enable backups"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,10 +72,4 @@ impl Data {
|
||||||
pub fn bump_database_version(&self, new_version: u64) {
|
pub fn bump_database_version(&self, new_version: u64) {
|
||||||
self.global.raw_put(b"version", new_version);
|
self.global.raw_put(b"version", new_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn backup(&self) -> Result { self.db.db.backup() }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn backup_list(&self) -> Result<String> { self.db.db.backup_list() }
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue