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::env::Env;
17use crate::{db::DBInner, ffi, ffi_util::to_cpath, DBCommon, Error, ThreadMode};
18
19use libc::c_uchar;
20use std::ffi::CString;
21use std::path::Path;
22
23/// Represents information of a backup including timestamp of the backup
24/// and the size (please note that sum of all backups' sizes is bigger than the actual
25/// size of the backup directory because some data is shared by multiple backups).
26/// Backups are identified by their always-increasing IDs.
27pub struct BackupEngineInfo {
28    /// Timestamp of the backup
29    pub timestamp: i64,
30    /// ID of the backup
31    pub backup_id: u32,
32    /// Size of the backup
33    pub size: u64,
34    /// Number of files related to the backup
35    pub num_files: u32,
36}
37
38pub struct BackupEngine {
39    inner: *mut ffi::rocksdb_backup_engine_t,
40    _outlive: Env,
41}
42
43pub struct BackupEngineOptions {
44    inner: *mut ffi::rocksdb_backup_engine_options_t,
45}
46
47pub struct RestoreOptions {
48    inner: *mut ffi::rocksdb_restore_options_t,
49}
50
51impl BackupEngine {
52    /// Open a backup engine with the specified options and RocksDB Env.
53    pub fn open(opts: &BackupEngineOptions, env: &Env) -> Result<Self, Error> {
54        let be: *mut ffi::rocksdb_backup_engine_t;
55        unsafe {
56            be = ffi_try!(ffi::rocksdb_backup_engine_open_opts(
57                opts.inner,
58                env.0.inner
59            ));
60        }
61
62        if be.is_null() {
63            return Err(Error::new("Could not initialize backup engine.".to_owned()));
64        }
65
66        Ok(Self {
67            inner: be,
68            _outlive: env.clone(),
69        })
70    }
71
72    /// Captures the state of the database in the latest backup.
73    ///
74    /// Note: no flush before backup is performed. User might want to
75    /// use `create_new_backup_flush` instead.
76    pub fn create_new_backup<T: ThreadMode, D: DBInner>(
77        &mut self,
78        db: &DBCommon<T, D>,
79    ) -> Result<(), Error> {
80        self.create_new_backup_flush(db, false)
81    }
82
83    /// Captures the state of the database in the latest backup.
84    ///
85    /// Set flush_before_backup=true to avoid losing unflushed key/value
86    /// pairs from the memtable.
87    pub fn create_new_backup_flush<T: ThreadMode, D: DBInner>(
88        &mut self,
89        db: &DBCommon<T, D>,
90        flush_before_backup: bool,
91    ) -> Result<(), Error> {
92        unsafe {
93            ffi_try!(ffi::rocksdb_backup_engine_create_new_backup_flush(
94                self.inner,
95                db.inner.inner(),
96                c_uchar::from(flush_before_backup),
97            ));
98            Ok(())
99        }
100    }
101
102    pub fn purge_old_backups(&mut self, num_backups_to_keep: usize) -> Result<(), Error> {
103        unsafe {
104            ffi_try!(ffi::rocksdb_backup_engine_purge_old_backups(
105                self.inner,
106                num_backups_to_keep as u32,
107            ));
108            Ok(())
109        }
110    }
111
112    /// Restore from the latest backup
113    ///
114    /// # Arguments
115    ///
116    /// * `db_dir` - A path to the database directory
117    /// * `wal_dir` - A path to the wal directory
118    /// * `opts` - Restore options
119    ///
120    /// # Examples
121    ///
122    /// ```ignore
123    /// use rocksdb::backup::{BackupEngine, BackupEngineOptions};
124    /// let backup_opts = BackupEngineOptions::default();
125    /// let mut backup_engine = BackupEngine::open(&backup_opts, &backup_path).unwrap();
126    /// let mut restore_option = rocksdb::backup::RestoreOptions::default();
127    /// restore_option.set_keep_log_files(true); /// true to keep log files
128    /// if let Err(e) = backup_engine.restore_from_latest_backup(&db_path, &wal_dir, &restore_option) {
129    ///     error!("Failed to restore from the backup. Error:{:?}", e);
130    ///     return Err(e.to_string());
131    ///  }
132    /// ```
133
134    pub fn restore_from_latest_backup<D: AsRef<Path>, W: AsRef<Path>>(
135        &mut self,
136        db_dir: D,
137        wal_dir: W,
138        opts: &RestoreOptions,
139    ) -> Result<(), Error> {
140        let c_db_dir = to_cpath(db_dir)?;
141        let c_wal_dir = to_cpath(wal_dir)?;
142
143        unsafe {
144            ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_latest_backup(
145                self.inner,
146                c_db_dir.as_ptr(),
147                c_wal_dir.as_ptr(),
148                opts.inner,
149            ));
150        }
151        Ok(())
152    }
153
154    /// Restore from a specified backup
155    ///
156    /// The specified backup id should be passed in as an additional parameter.
157    pub fn restore_from_backup<D: AsRef<Path>, W: AsRef<Path>>(
158        &mut self,
159        db_dir: D,
160        wal_dir: W,
161        opts: &RestoreOptions,
162        backup_id: u32,
163    ) -> Result<(), Error> {
164        let c_db_dir = to_cpath(db_dir)?;
165        let c_wal_dir = to_cpath(wal_dir)?;
166
167        unsafe {
168            ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_backup(
169                self.inner,
170                c_db_dir.as_ptr(),
171                c_wal_dir.as_ptr(),
172                opts.inner,
173                backup_id,
174            ));
175        }
176        Ok(())
177    }
178
179    /// Checks that each file exists and that the size of the file matches our
180    /// expectations. it does not check file checksum.
181    ///
182    /// If this BackupEngine created the backup, it compares the files' current
183    /// sizes against the number of bytes written to them during creation.
184    /// Otherwise, it compares the files' current sizes against their sizes when
185    /// the BackupEngine was opened.
186    pub fn verify_backup(&self, backup_id: u32) -> Result<(), Error> {
187        unsafe {
188            ffi_try!(ffi::rocksdb_backup_engine_verify_backup(
189                self.inner, backup_id,
190            ));
191        }
192        Ok(())
193    }
194
195    /// Get a list of all backups together with information on timestamp of the backup
196    /// and the size (please note that sum of all backups' sizes is bigger than the actual
197    /// size of the backup directory because some data is shared by multiple backups).
198    /// Backups are identified by their always-increasing IDs.
199    ///
200    /// You can perform this function safely, even with other BackupEngine performing
201    /// backups on the same directory
202    pub fn get_backup_info(&self) -> Vec<BackupEngineInfo> {
203        unsafe {
204            let i = ffi::rocksdb_backup_engine_get_backup_info(self.inner);
205
206            let n = ffi::rocksdb_backup_engine_info_count(i);
207
208            let mut info = Vec::with_capacity(n as usize);
209            for index in 0..n {
210                info.push(BackupEngineInfo {
211                    timestamp: ffi::rocksdb_backup_engine_info_timestamp(i, index),
212                    backup_id: ffi::rocksdb_backup_engine_info_backup_id(i, index),
213                    size: ffi::rocksdb_backup_engine_info_size(i, index),
214                    num_files: ffi::rocksdb_backup_engine_info_number_files(i, index),
215                });
216            }
217
218            // destroy backup info object
219            ffi::rocksdb_backup_engine_info_destroy(i);
220
221            info
222        }
223    }
224}
225
226impl BackupEngineOptions {
227    /// Initializes `BackupEngineOptions` with the directory to be used for storing/accessing the
228    /// backup files.
229    pub fn new<P: AsRef<Path>>(backup_dir: P) -> Result<Self, Error> {
230        let backup_dir = backup_dir.as_ref();
231        let c_backup_dir = CString::new(backup_dir.to_string_lossy().as_bytes()).map_err(|_| {
232            Error::new(
233                "Failed to convert backup_dir to CString \
234                     when constructing BackupEngineOptions"
235                    .to_owned(),
236            )
237        })?;
238
239        unsafe {
240            let opts = ffi::rocksdb_backup_engine_options_create(c_backup_dir.as_ptr());
241            assert!(!opts.is_null(), "Could not create RocksDB backup options");
242
243            Ok(Self { inner: opts })
244        }
245    }
246
247    /// Sets the number of operations (such as file copies or file checksums) that `RocksDB` may
248    /// perform in parallel when executing a backup or restore.
249    ///
250    /// Default: 1
251    pub fn set_max_background_operations(&mut self, max_background_operations: i32) {
252        unsafe {
253            ffi::rocksdb_backup_engine_options_set_max_background_operations(
254                self.inner,
255                max_background_operations,
256            );
257        }
258    }
259}
260
261impl RestoreOptions {
262    /// Sets `keep_log_files`. If true, restore won't overwrite the existing log files in wal_dir.
263    /// It will also move all log files from archive directory to wal_dir. Use this option in
264    /// combination with BackupEngineOptions::backup_log_files = false for persisting in-memory
265    /// databases.
266    ///
267    /// Default: false
268    pub fn set_keep_log_files(&mut self, keep_log_files: bool) {
269        unsafe {
270            ffi::rocksdb_restore_options_set_keep_log_files(self.inner, i32::from(keep_log_files));
271        }
272    }
273}
274
275impl Default for RestoreOptions {
276    fn default() -> Self {
277        unsafe {
278            let opts = ffi::rocksdb_restore_options_create();
279            assert!(!opts.is_null(), "Could not create RocksDB restore options");
280
281            Self { inner: opts }
282        }
283    }
284}
285
286impl Drop for BackupEngine {
287    fn drop(&mut self) {
288        unsafe {
289            ffi::rocksdb_backup_engine_close(self.inner);
290        }
291    }
292}
293
294impl Drop for BackupEngineOptions {
295    fn drop(&mut self) {
296        unsafe {
297            ffi::rocksdb_backup_engine_options_destroy(self.inner);
298        }
299    }
300}
301
302impl Drop for RestoreOptions {
303    fn drop(&mut self) {
304        unsafe {
305            ffi::rocksdb_restore_options_destroy(self.inner);
306        }
307    }
308}