Add a simple daily backup module
This commit is contained in:
parent
b8a8af3979
commit
d22617538e
6 changed files with 133 additions and 24 deletions
55
backend/src/data/backup.rs
Normal file
55
backend/src/data/backup.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use chrono::{NaiveTime, TimeDelta};
|
||||
use tracing::{Level, event};
|
||||
|
||||
use super::db;
|
||||
|
||||
/// This function starts a backup process that runs at a specified time of day forever.
|
||||
/// It creates a backup of the database at the specified directory.
|
||||
/// The backup file is named with the date and time at the time of the backup.
|
||||
pub fn start<P>(
|
||||
directory: P,
|
||||
db_connection: db::Connection,
|
||||
time_of_day: NaiveTime,
|
||||
) -> tokio::task::JoinHandle<()>
|
||||
where
|
||||
P: AsRef<std::path::Path> + Send + Sync + 'static,
|
||||
{
|
||||
if !directory.as_ref().is_dir() {
|
||||
panic!(
|
||||
"Path must be a directory: {}",
|
||||
directory
|
||||
.as_ref()
|
||||
.to_str()
|
||||
.unwrap_or("<Unable to convert directory to string>")
|
||||
);
|
||||
}
|
||||
|
||||
// Can also user tokio::spawn_blocking if needed.
|
||||
tokio::task::spawn(async move {
|
||||
loop {
|
||||
let mut time_to_wait = time_of_day - chrono::Local::now().time();
|
||||
if time_to_wait < TimeDelta::zero() {
|
||||
time_to_wait += TimeDelta::days(1);
|
||||
}
|
||||
event!(Level::DEBUG, "Backup in {}s", time_to_wait.num_seconds());
|
||||
tokio::time::sleep(time_to_wait.to_std().unwrap()).await;
|
||||
|
||||
let path = directory.as_ref().join(format!(
|
||||
"recipes_backup_{}.sqlite",
|
||||
chrono::Local::now().format("%Y-%m-%d_%H%M%S")
|
||||
));
|
||||
|
||||
event!(
|
||||
Level::INFO,
|
||||
"Starting backup process to {}...",
|
||||
path.display()
|
||||
);
|
||||
|
||||
if let Err(error) = db_connection.backup(path).await {
|
||||
event!(Level::ERROR, "Error when backing up database: {}", error);
|
||||
}
|
||||
|
||||
event!(Level::INFO, "Backup done");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -8,10 +8,10 @@ use std::{
|
|||
};
|
||||
|
||||
use sqlx::{
|
||||
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions, SqliteSynchronous},
|
||||
Pool, Sqlite, Transaction,
|
||||
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions, SqliteSynchronous},
|
||||
};
|
||||
use tracing::{event, Level};
|
||||
use tracing::{Level, event};
|
||||
|
||||
use crate::consts;
|
||||
|
||||
|
|
@ -93,6 +93,18 @@ impl Connection {
|
|||
Ok(connection)
|
||||
}
|
||||
|
||||
pub async fn backup<P>(&self, file: P) -> Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
sqlx::query("VACUUM INTO $1")
|
||||
.bind(file.as_ref().to_str().unwrap())
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map(|_| ()) // Ignore the result.
|
||||
.map_err(DBError::from)
|
||||
}
|
||||
|
||||
async fn tx(&self) -> Result<Transaction<Sqlite>> {
|
||||
self.pool.begin().await.map_err(DBError::from)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod backup;
|
||||
pub mod db;
|
||||
pub mod model;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue