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}