electrs_rocksdb/
backup.rs

1// Copyright 2016 Alex Regueiro
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16use crate::{db::DBInner, ffi, ffi_util::to_cpath, DBCommon, Error, ThreadMode};
17
18use libc::{c_int, c_uchar};
19use std::path::Path;
20
21/// Represents information of a backup including timestamp of the backup
22/// and the size (please note that sum of all backups' sizes is bigger than the actual
23/// size of the backup directory because some data is shared by multiple backups).
24/// Backups are identified by their always-increasing IDs.
25pub struct BackupEngineInfo {
26    /// Timestamp of the backup
27    pub timestamp: i64,
28    /// ID of the backup
29    pub backup_id: u32,
30    /// Size of the backup
31    pub size: u64,
32    /// Number of files related to the backup
33    pub num_files: u32,
34}
35
36pub struct BackupEngine {
37    inner: *mut ffi::rocksdb_backup_engine_t,
38}
39
40pub struct BackupEngineOptions {
41    inner: *mut ffi::rocksdb_options_t,
42}
43
44pub struct RestoreOptions {
45    inner: *mut ffi::rocksdb_restore_options_t,
46}
47
48impl BackupEngine {
49    /// Open a backup engine with the specified options.
50    pub fn open<P: AsRef<Path>>(opts: &BackupEngineOptions, path: P) -> Result<Self, Error> {
51        let cpath = to_cpath(path)?;
52
53        let be: *mut ffi::rocksdb_backup_engine_t;
54        unsafe {
55            be = ffi_try!(ffi::rocksdb_backup_engine_open(opts.inner, cpath.as_ptr()));
56        }
57
58        if be.is_null() {
59            return Err(Error::new("Could not initialize backup engine.".to_owned()));
60        }
61
62        Ok(Self { inner: be })
63    }
64
65    /// Captures the state of the database in the latest backup.
66    ///
67    /// Note: no flush before backup is performed. User might want to
68    /// use `create_new_backup_flush` instead.
69    pub fn create_new_backup<T: ThreadMode, D: DBInner>(
70        &mut self,
71        db: &DBCommon<T, D>,
72    ) -> Result<(), Error> {
73        self.create_new_backup_flush(db, false)
74    }
75
76    /// Captures the state of the database in the latest backup.
77    ///
78    /// Set flush_before_backup=true to avoid losing unflushed key/value
79    /// pairs from the memtable.
80    pub fn create_new_backup_flush<T: ThreadMode, D: DBInner>(
81        &mut self,
82        db: &DBCommon<T, D>,
83        flush_before_backup: bool,
84    ) -> Result<(), Error> {
85        unsafe {
86            ffi_try!(ffi::rocksdb_backup_engine_create_new_backup_flush(
87                self.inner,
88                db.inner.inner(),
89                c_uchar::from(flush_before_backup),
90            ));
91            Ok(())
92        }
93    }
94
95    pub fn purge_old_backups(&mut self, num_backups_to_keep: usize) -> Result<(), Error> {
96        unsafe {
97            ffi_try!(ffi::rocksdb_backup_engine_purge_old_backups(
98                self.inner,
99                num_backups_to_keep as u32,
100            ));
101            Ok(())
102        }
103    }
104
105    /// Restore from the latest backup
106    ///
107    /// # Arguments
108    ///
109    /// * `db_dir` - A path to the database directory
110    /// * `wal_dir` - A path to the wal directory
111    /// * `opts` - Restore options
112    ///
113    /// # Examples
114    ///
115    /// ```ignore
116    /// use rocksdb::backup::{BackupEngine, BackupEngineOptions};
117    /// let backup_opts = BackupEngineOptions::default();
118    /// let mut backup_engine = BackupEngine::open(&backup_opts, &backup_path).unwrap();
119    /// let mut restore_option = rocksdb::backup::RestoreOptions::default();
120    /// restore_option.set_keep_log_files(true); /// true to keep log files
121    /// if let Err(e) = backup_engine.restore_from_latest_backup(&db_path, &wal_dir, &restore_option) {
122    ///     error!("Failed to restore from the backup. Error:{:?}", e);
123    ///     return Err(e.to_string());
124    ///  }
125    /// ```
126
127    pub fn restore_from_latest_backup<D: AsRef<Path>, W: AsRef<Path>>(
128        &mut self,
129        db_dir: D,
130        wal_dir: W,
131        opts: &RestoreOptions,
132    ) -> Result<(), Error> {
133        let c_db_dir = to_cpath(db_dir)?;
134        let c_wal_dir = to_cpath(wal_dir)?;
135
136        unsafe {
137            ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_latest_backup(
138                self.inner,
139                c_db_dir.as_ptr(),
140                c_wal_dir.as_ptr(),
141                opts.inner,
142            ));
143        }
144        Ok(())
145    }
146
147    /// Restore from a specified backup
148    ///
149    /// The specified backup id should be passed in as an additional parameter.
150    pub fn restore_from_backup<D: AsRef<Path>, W: AsRef<Path>>(
151        &mut self,
152        db_dir: D,
153        wal_dir: W,
154        opts: &RestoreOptions,
155        backup_id: u32,
156    ) -> Result<(), Error> {
157        let c_db_dir = to_cpath(db_dir)?;
158        let c_wal_dir = to_cpath(wal_dir)?;
159
160        unsafe {
161            ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_backup(
162                self.inner,
163                c_db_dir.as_ptr(),
164                c_wal_dir.as_ptr(),
165                opts.inner,
166                backup_id,
167            ));
168        }
169        Ok(())
170    }
171
172    /// Checks that each file exists and that the size of the file matches our
173    /// expectations. it does not check file checksum.
174    ///
175    /// If this BackupEngine created the backup, it compares the files' current
176    /// sizes against the number of bytes written to them during creation.
177    /// Otherwise, it compares the files' current sizes against their sizes when
178    /// the BackupEngine was opened.
179    pub fn verify_backup(&self, backup_id: u32) -> Result<(), Error> {
180        unsafe {
181            ffi_try!(ffi::rocksdb_backup_engine_verify_backup(
182                self.inner, backup_id,
183            ));
184        }
185        Ok(())
186    }
187
188    /// Get a list of all backups together with information on timestamp of the backup
189    /// and the size (please note that sum of all backups' sizes is bigger than the actual
190    /// size of the backup directory because some data is shared by multiple backups).
191    /// Backups are identified by their always-increasing IDs.
192    ///
193    /// You can perform this function safely, even with other BackupEngine performing
194    /// backups on the same directory
195    pub fn get_backup_info(&self) -> Vec<BackupEngineInfo> {
196        unsafe {
197            let i = ffi::rocksdb_backup_engine_get_backup_info(self.inner);
198
199            let n = ffi::rocksdb_backup_engine_info_count(i);
200
201            let mut info = Vec::with_capacity(n as usize);
202            for index in 0..n {
203                info.push(BackupEngineInfo {
204                    timestamp: ffi::rocksdb_backup_engine_info_timestamp(i, index),
205                    backup_id: ffi::rocksdb_backup_engine_info_backup_id(i, index),
206                    size: ffi::rocksdb_backup_engine_info_size(i, index),
207                    num_files: ffi::rocksdb_backup_engine_info_number_files(i, index),
208                });
209            }
210
211            // destroy backup info object
212            ffi::rocksdb_backup_engine_info_destroy(i);
213
214            info
215        }
216    }
217}
218
219impl BackupEngineOptions {
220    //
221}
222
223impl RestoreOptions {
224    pub fn set_keep_log_files(&mut self, keep_log_files: bool) {
225        unsafe {
226            ffi::rocksdb_restore_options_set_keep_log_files(
227                self.inner,
228                c_int::from(keep_log_files),
229            );
230        }
231    }
232}
233
234impl Default for BackupEngineOptions {
235    fn default() -> Self {
236        unsafe {
237            let opts = ffi::rocksdb_options_create();
238            assert!(!opts.is_null(), "Could not create RocksDB backup options");
239
240            Self { inner: opts }
241        }
242    }
243}
244
245impl Default for RestoreOptions {
246    fn default() -> Self {
247        unsafe {
248            let opts = ffi::rocksdb_restore_options_create();
249            assert!(!opts.is_null(), "Could not create RocksDB restore options");
250
251            Self { inner: opts }
252        }
253    }
254}
255
256impl Drop for BackupEngine {
257    fn drop(&mut self) {
258        unsafe {
259            ffi::rocksdb_backup_engine_close(self.inner);
260        }
261    }
262}
263
264impl Drop for BackupEngineOptions {
265    fn drop(&mut self) {
266        unsafe {
267            ffi::rocksdb_options_destroy(self.inner);
268        }
269    }
270}
271
272impl Drop for RestoreOptions {
273    fn drop(&mut self) {
274        unsafe {
275            ffi::rocksdb_restore_options_destroy(self.inner);
276        }
277    }
278}