tuwunel_database/engine/
backup.rs1use std::{ffi::OsString, path::PathBuf};
2
3use rocksdb::backup::{BackupEngine, BackupEngineOptions};
4use tuwunel_core::{
5 Err, Result, error, implement, info, utils::time::rfc2822_from_seconds, warn,
6};
7
8use super::Engine;
9use crate::util::map_err;
10
11#[implement(Engine)]
12#[tracing::instrument(skip(self))]
13pub fn backup(&self) -> Result {
14 let mut engine = self.backup_engine()?;
15 let config = &self.ctx.server.config;
16 if config.database_backups_to_keep > 0 {
17 let flush = !self.is_read_only();
18 engine
19 .create_new_backup_flush(&self.db, flush)
20 .map_err(map_err)?;
21
22 let engine_info = engine.get_backup_info();
23 let info = &engine_info
24 .last()
25 .expect("backup engine info is not empty");
26 info!(
27 "Created database backup #{} using {} bytes in {} files",
28 info.backup_id, info.size, info.num_files,
29 );
30 }
31
32 if config.database_backups_to_keep >= 0 {
33 let keep = u32::try_from(config.database_backups_to_keep)?;
34 if let Err(e) = engine.purge_old_backups(keep.try_into()?) {
35 error!("Failed to purge old backup: {e:?}");
36 }
37 }
38
39 if config.database_backups_to_keep == 0 {
40 warn!("Configuration item `database_backups_to_keep` is set to 0.");
41 }
42
43 Ok(())
44}
45
46#[implement(Engine)]
47pub fn backup_list(&self) -> Result<impl Iterator<Item = String> + Send> {
48 let info = self.backup_engine()?.get_backup_info();
49
50 if info.is_empty() {
51 return Err!("No backups found.");
52 }
53
54 let list = info.into_iter().map(|info| {
55 format!(
56 "#{} {}: {} bytes, {} files",
57 info.backup_id,
58 rfc2822_from_seconds(info.timestamp),
59 info.size,
60 info.num_files,
61 )
62 });
63
64 Ok(list)
65}
66
67#[implement(Engine)]
68pub fn backup_count(&self) -> Result<usize> {
69 let info = self.backup_engine()?.get_backup_info();
70
71 Ok(info.len())
72}
73
74#[implement(Engine)]
75fn backup_engine(&self) -> Result<BackupEngine> {
76 let path = self.backup_path()?;
77 let options = BackupEngineOptions::new(path).map_err(map_err)?;
78 BackupEngine::open(&options, &*self.ctx.env.lock()?).map_err(map_err)
79}
80
81#[implement(Engine)]
82fn backup_path(&self) -> Result<OsString> {
83 let path = self
84 .ctx
85 .server
86 .config
87 .database_backup_path
88 .clone()
89 .map(PathBuf::into_os_string)
90 .unwrap_or_default();
91
92 if path.is_empty() {
93 return Err!(Config("database_backup_path", "Configure path to enable backups"));
94 }
95
96 Ok(path)
97}