From d3d10d54b18981ba6d530bc027bd409f031b8eb5 Mon Sep 17 00:00:00 2001 From: Linh Tran Tuan Date: Wed, 12 Aug 2020 16:06:49 +0900 Subject: [PATCH] Adding backup engine info (#454) --- src/backup.rs | 85 ++++++++++++++++++++++++++++++++++++++++-- src/slice_transform.rs | 7 +--- tests/test_backup.rs | 8 ++++ 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/backup.rs b/src/backup.rs index 1b3f0aa..f84e0e9 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -15,10 +15,25 @@ use crate::{ffi, Error, DB}; -use libc::c_int; +use libc::{c_int, c_uchar}; use std::ffi::CString; use std::path::Path; +/// Represents information of a backup including timestamp of the backup +/// and the size (please note that sum of all backups' sizes is bigger than the actual +/// size of the backup directory because some data is shared by multiple backups). +/// Backups are identified by their always-increasing IDs. +pub struct BackupEngineInfo { + /// Timestamp of the backup + pub timestamp: i64, + /// ID of the backup + pub backup_id: u32, + /// Size of the backup + pub size: u64, + /// Number of files related to the backup + pub num_files: u32, +} + pub struct BackupEngine { inner: *mut ffi::rocksdb_backup_engine_t, } @@ -58,10 +73,28 @@ impl BackupEngine { Ok(BackupEngine { inner: be }) } + /// Captures the state of the database in the latest backup. + /// + /// Note: no flush before backup is performed. User might want to + /// use `create_new_backup_flush` instead. pub fn create_new_backup(&mut self, db: &DB) -> Result<(), Error> { + self.create_new_backup_flush(db, false) + } + + /// Captures the state of the database in the latest backup. + /// + /// Set flush_before_backup=true to avoid losing unflushed key/value + /// pairs from the memtable. + pub fn create_new_backup_flush( + &mut self, + db: &DB, + flush_before_backup: bool, + ) -> Result<(), Error> { unsafe { - ffi_try!(ffi::rocksdb_backup_engine_create_new_backup( - self.inner, db.inner, + ffi_try!(ffi::rocksdb_backup_engine_create_new_backup_flush( + self.inner, + db.inner, + flush_before_backup as c_uchar, )); Ok(()) } @@ -137,6 +170,52 @@ impl BackupEngine { } Ok(()) } + + /// Checks that each file exists and that the size of the file matches our + /// expectations. it does not check file checksum. + /// + /// If this BackupEngine created the backup, it compares the files' current + /// sizes against the number of bytes written to them during creation. + /// Otherwise, it compares the files' current sizes against their sizes when + /// the BackupEngine was opened. + pub fn verify_backup(&self, backup_id: u32) -> Result<(), Error> { + unsafe { + ffi_try!(ffi::rocksdb_backup_engine_verify_backup( + self.inner, backup_id, + )); + } + Ok(()) + } + + /// Get a list of all backups together with information on timestamp of the backup + /// and the size (please note that sum of all backups' sizes is bigger than the actual + /// size of the backup directory because some data is shared by multiple backups). + /// Backups are identified by their always-increasing IDs. + /// + /// You can perform this function safely, even with other BackupEngine performing + /// backups on the same directory + pub fn get_backup_info(&self) -> Vec { + unsafe { + let i = ffi::rocksdb_backup_engine_get_backup_info(self.inner); + + let n = ffi::rocksdb_backup_engine_info_count(i); + + let mut info = Vec::with_capacity(n as usize); + for index in 0..n { + info.push(BackupEngineInfo { + timestamp: ffi::rocksdb_backup_engine_info_timestamp(i, index), + backup_id: ffi::rocksdb_backup_engine_info_backup_id(i, index), + size: ffi::rocksdb_backup_engine_info_size(i, index), + num_files: ffi::rocksdb_backup_engine_info_number_files(i, index), + }) + } + + // destroy backup info object + ffi::rocksdb_backup_engine_info_destroy(i); + + info + } + } } impl BackupEngineOptions { diff --git a/src/slice_transform.rs b/src/slice_transform.rs index 396012f..38c1f77 100644 --- a/src/slice_transform.rs +++ b/src/slice_transform.rs @@ -111,9 +111,6 @@ pub unsafe extern "C" fn in_domain_callback( ) -> u8 { let cb = &mut *(raw_cb as *mut TransformCallback); let key = slice::from_raw_parts(raw_key as *const u8, key_len as usize); - if let Some(in_domain) = cb.in_domain_fn { - in_domain(key) as u8 - } else { - 0xff - } + cb.in_domain_fn + .map_or(0xff, |in_domain| in_domain(key) as u8) } diff --git a/tests/test_backup.rs b/tests/test_backup.rs index 0e42c56..c3e4885 100644 --- a/tests/test_backup.rs +++ b/tests/test_backup.rs @@ -38,6 +38,14 @@ fn backup_restore() { let mut backup_engine = BackupEngine::open(&backup_opts, &backup_path).unwrap(); assert!(backup_engine.create_new_backup(&db).is_ok()); + // check backup info + let info = backup_engine.get_backup_info(); + assert!(!info.is_empty()); + info.iter().for_each(|i| { + assert!(backup_engine.verify_backup(i.backup_id).is_ok()); + assert!(i.size > 0); + }); + let mut restore_option = RestoreOptions::default(); restore_option.set_keep_log_files(false); // true to keep log files let restore_status = backup_engine.restore_from_latest_backup(