rust_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::{
18 DBCommon, Error, RateLimiterMode, ThreadMode,
19 db::DBInner,
20 ffi,
21 ffi_util::{raw_data, to_cpath},
22};
23
24use libc::{c_char, c_int, c_uchar, c_void};
25use std::ffi::CString;
26use std::ops::BitOr;
27use std::panic::{AssertUnwindSafe, catch_unwind};
28use std::path::{Path, PathBuf};
29use std::process;
30use std::slice;
31use std::sync::Arc;
32
33/// Represents information of a backup including timestamp of the backup
34/// and the size (please note that sum of all backups' sizes is bigger than the actual
35/// size of the backup directory because some data is shared by multiple backups).
36/// Backups are identified by their always-increasing IDs.
37pub struct BackupEngineInfo {
38 /// Timestamp of the backup
39 pub timestamp: i64,
40 /// ID of the backup
41 pub backup_id: u32,
42 /// Size of the backup
43 pub size: u64,
44 /// Number of files related to the backup
45 pub num_files: u32,
46 /// Application metadata passed to
47 /// [`BackupEngine::create_new_backup_with_metadata`].
48 ///
49 /// Empty when the backup carries no metadata. RocksDB stores the metadata
50 /// as a `std::string` and skips writing it when empty, so an absent value
51 /// and an empty value are indistinguishable on read back.
52 pub app_metadata: Vec<u8>,
53}
54
55pub struct BackupEngine {
56 inner: *mut ffi::rocksdb_backup_engine_t,
57 _outlive: Env,
58 // `BackupEngineOptions::backup_env` is stored in the engine as a raw
59 // `Env*` (see `rocksdb_backup_engine_options_set_env` in `db/c.cc`) and
60 // `BackupEngineImpl` keeps a by-value copy of the options, so that Env has
61 // to outlive the engine, not just the options it was set on.
62 _outlive_backup_env: Option<Env>,
63}
64
65pub struct BackupEngineOptions {
66 inner: *mut ffi::rocksdb_backup_engine_options_t,
67 // Kept alive because RocksDB only borrows the `Env*`. See
68 // `BackupEngineOptions::set_env`.
69 backup_env: Option<Env>,
70}
71
72pub struct RestoreOptions {
73 inner: *mut ffi::rocksdb_restore_options_t,
74}
75
76/// CPU priority for the background threads that copy files during a backup.
77///
78/// Mirrors `rocksdb::CpuPriority` in `include/rocksdb/port_defs.h`. The
79/// discriminants are the upstream values, which is what the C API expects.
80#[derive(Debug, Copy, Clone, PartialEq, Eq)]
81#[repr(i32)]
82pub enum CpuPriority {
83 KIdle = 0,
84 KLow = 1,
85 KNormal = 2,
86 KHigh = 3,
87}
88
89impl From<i32> for CpuPriority {
90 /// Values RocksDB does not define map to [`CpuPriority::KNormal`], which
91 /// is both the upstream default for `background_thread_cpu_priority` and
92 /// the priority the copy threads start at.
93 fn from(value: i32) -> Self {
94 match value {
95 0 => Self::KIdle,
96 1 => Self::KLow,
97 3 => Self::KHigh,
98 // 2 is kNormal, and so is anything unrecognised.
99 _ => Self::KNormal,
100 }
101 }
102}
103
104/// How much of the destination database a restore may reuse.
105///
106/// Mirrors `RestoreOptions::Mode` in
107/// `include/rocksdb/utilities/backup_engine.h`. The values look like flags but
108/// RocksDB only ever compares the field for equality, so the modes are
109/// exclusive and must not be combined.
110#[derive(Debug, Copy, Clone, PartialEq, Eq)]
111#[repr(i32)]
112pub enum RestoreMode {
113 /// Cheapest way to restore a healthy database. Files whose names already
114 /// encode the DB session id are kept, so an interrupted or partial copy
115 /// can be completed instead of recopied. This mode also cooperates with
116 /// [`CreateBackupOptions::set_exclude_files_callback`] by looking for
117 /// excluded files in the existing database directory.
118 ///
119 /// Only effective for files using a modern share files naming scheme, see
120 /// [`ShareFilesNaming`].
121 KKeepLatestDbSessionIdFiles = 1,
122 /// For a database suspected to be corrupt. Each existing file is checksummed
123 /// against the checksum recorded in the backup metadata, and only files that
124 /// fail the comparison are replaced.
125 KVerifyChecksum = 2,
126 /// Zero trust and least efficient. Deletes every file in the destination
127 /// and copies everything from the backup. This is the default.
128 KPurgeAllFiles = 0xffff,
129}
130
131impl From<i32> for RestoreMode {
132 /// Values RocksDB does not define map to [`RestoreMode::KPurgeAllFiles`],
133 /// the upstream default and the only mode that makes no assumptions about
134 /// the destination directory.
135 fn from(value: i32) -> Self {
136 match value {
137 1 => Self::KKeepLatestDbSessionIdFiles,
138 2 => Self::KVerifyChecksum,
139 // 0xffff is kPurgeAllFiles, and so is anything unrecognised.
140 _ => Self::KPurgeAllFiles,
141 }
142 }
143}
144
145/// Naming scheme for table and blob files in the `shared_checksum` directory.
146///
147/// Mirrors `BackupEngineOptions::ShareFilesNaming` in
148/// `include/rocksdb/utilities/backup_engine.h`. It is a bit mask with two
149/// parts: the low 16 bits select one naming scheme, the high bits carry
150/// independent flags. RocksDB masks the two apart with
151/// [`Self::MASK_NO_NAMING_FLAGS`] and [`Self::MASK_NAMING_FLAGS`].
152///
153/// Only takes effect when both `share_table_files` and
154/// `share_files_with_checksum` are true.
155#[derive(Copy, Clone, Debug, Eq, PartialEq)]
156pub struct ShareFilesNaming(c_int);
157
158impl ShareFilesNaming {
159 /// `kLegacyCrc32cAndFileSize`. Names files
160 /// `<file_number>_<crc32c>_<file_size>.sst` and `.blob`. Only recommended
161 /// for preserving old behaviour: the triple can collide at massive scale,
162 /// and picking the name requires reading the whole file to checksum it.
163 pub const LEGACY_CRC32C_AND_FILE_SIZE: Self = Self(1);
164
165 /// `kUseDbSessionId`. Names SST files `<file_number>_s<db_session_id>.sst`,
166 /// which is strongly unique and known without reading the file. Blob files
167 /// and SST files with no session id fall back to
168 /// [`Self::LEGACY_CRC32C_AND_FILE_SIZE`].
169 pub const USE_DB_SESSION_ID: Self = Self(2);
170
171 /// `kFlagIncludeFileSize`. Inserts `_<file_size>` before the extension when
172 /// the scheme does not already include it.
173 ///
174 /// This is bit 31. The C API passes the mask as an `int`, so a value
175 /// carrying this flag reads back negative from [`Self::bits`].
176 pub const FLAG_INCLUDE_FILE_SIZE: Self = Self(1 << 31);
177
178 /// `kMaskNoNamingFlags`. Selects the naming scheme bits.
179 pub const MASK_NO_NAMING_FLAGS: Self = Self(0xffff);
180
181 /// `kMaskNamingFlags`. Selects the flag bits.
182 pub const MASK_NAMING_FLAGS: Self = Self(!0xffff);
183
184 /// The RocksDB default, [`Self::USE_DB_SESSION_ID`] with
185 /// [`Self::FLAG_INCLUDE_FILE_SIZE`].
186 pub const DEFAULT: Self = Self(Self::USE_DB_SESSION_ID.0 | Self::FLAG_INCLUDE_FILE_SIZE.0);
187
188 /// Returns the raw mask as the C API represents it.
189 pub const fn bits(self) -> c_int {
190 self.0
191 }
192
193 /// Returns true when every bit set in `other` is also set in `self`.
194 pub const fn contains(self, other: Self) -> bool {
195 (self.0 & other.0) == other.0
196 }
197}
198
199impl BitOr for ShareFilesNaming {
200 type Output = Self;
201
202 fn bitor(self, rhs: Self) -> Self {
203 Self(self.0 | rhs.0)
204 }
205}
206
207type ProgressFn = dyn Fn() + Send + Sync + 'static;
208type ExcludeFilesFn = dyn Fn(&[u8]) -> bool + Send + Sync + 'static;
209
210/// Owns a progress closure. [`CreateBackupOptions`] hands the address of the
211/// holder to the C API as the opaque `state` pointer, so the holder is kept
212/// behind an [`Arc`] rather than a `Box`: the C side holds a raw pointer to it
213/// while this crate still owns it, which is exactly what `Box`'s uniqueness
214/// guarantee rules out. This mirrors how `Options::set_callback_logger` keeps
215/// its log callback alive.
216struct ProgressCallback {
217 callback: Box<ProgressFn>,
218}
219
220/// Owns an exclude files closure. See [`ProgressCallback`].
221struct ExcludeFilesCallback {
222 callback: Box<ExcludeFilesFn>,
223}
224
225/// Trampoline for `rocksdb_create_backup_options_progress_cb`.
226unsafe extern "C" fn progress_callback_trampoline(state: *mut c_void) {
227 // SAFETY: `state` is the address of the `ProgressCallback` that
228 // `set_progress_callback` registered. `CreateBackupOptions` keeps it alive
229 // until after `rocksdb_create_backup_options_destroy` has run, and a
230 // shared reference is enough because `ProgressFn` is a `dyn Fn`. RocksDB
231 // serialises progress reports behind a mutex but may report from any copy
232 // thread, so `&mut` here would alias.
233 let holder = unsafe { &*state.cast::<ProgressCallback>() };
234 // A Rust panic cannot unwind out of an `extern "C"` function, and RocksDB
235 // only knows how to turn a C++ exception into Status::Aborted.
236 if catch_unwind(AssertUnwindSafe(|| (holder.callback)())).is_err() {
237 process::abort();
238 }
239}
240
241/// Trampoline for `rocksdb_create_backup_options_exclude_files_cb`.
242unsafe extern "C" fn exclude_files_callback_trampoline(
243 state: *mut c_void,
244 relative_file: *const c_char,
245 relative_file_len: usize,
246) -> c_uchar {
247 // SAFETY: see `progress_callback_trampoline`.
248 let holder = unsafe { &*state.cast::<ExcludeFilesCallback>() };
249 let relative_file = if relative_file_len == 0 {
250 &[][..]
251 } else {
252 // SAFETY: `db/c.cc` passes `relative_file.data()` and
253 // `relative_file.size()` of a live `std::string`, and the closure only
254 // borrows it for the duration of this call.
255 unsafe { slice::from_raw_parts(relative_file.cast::<u8>(), relative_file_len) }
256 };
257 match catch_unwind(AssertUnwindSafe(|| (holder.callback)(relative_file))) {
258 Ok(true) => 1,
259 Ok(false) => 0,
260 Err(_) => process::abort(),
261 }
262}
263
264/// Options for a single call to [`BackupEngine::create_new_backup_with_options`]
265/// or [`BackupEngine::create_new_backup_with_metadata`].
266///
267/// Wraps `rocksdb::CreateBackupOptions`.
268///
269/// # Callback ownership
270///
271/// `rocksdb_create_backup_options_set_progress_callback` and
272/// `rocksdb_create_backup_options_set_exclude_files_callback` take a bare
273/// `void* state` with no destructor argument. `db/c.cc` stores it in
274/// `rocksdb_create_backup_options_t::progress_state` /
275/// `exclude_files_state` and copies it into a C++ lambda by value.
276/// `rocksdb_create_backup_options_destroy` is just `delete options`, so the C
277/// API never frees the state. This type therefore owns the closures: it frees
278/// them when it is dropped, and frees the previous one when a setter is called
279/// a second time.
280///
281/// The C++ lambda captures the state pointer by value rather than the options
282/// wrapper, so the state must stay alive for the whole backup, not merely
283/// until the FFI call returns. Backup creation is synchronous and borrows
284/// these options for its duration, so holding the closures in this type is
285/// enough.
286pub struct CreateBackupOptions {
287 inner: *mut ffi::rocksdb_create_backup_options_t,
288 progress_callback: Option<Arc<ProgressCallback>>,
289 exclude_files_callback: Option<Arc<ExcludeFilesCallback>>,
290}
291
292// BackupEngine is a simple pointer wrapper, so it's safe to send to another thread
293// since the underlying RocksDB backup engine is thread-safe.
294unsafe impl Send for BackupEngine {}
295
296impl BackupEngine {
297 /// Open a backup engine with the specified options and RocksDB Env.
298 pub fn open(opts: &BackupEngineOptions, env: &Env) -> Result<Self, Error> {
299 let be: *mut ffi::rocksdb_backup_engine_t;
300 unsafe {
301 be = ffi_try!(ffi::rocksdb_backup_engine_open_opts(
302 opts.inner,
303 env.0.inner
304 ));
305 }
306
307 if be.is_null() {
308 return Err(Error::new("Could not initialize backup engine.".to_owned()));
309 }
310
311 Ok(Self {
312 inner: be,
313 _outlive: env.clone(),
314 _outlive_backup_env: opts.backup_env.clone(),
315 })
316 }
317
318 /// Captures the state of the database in the latest backup.
319 ///
320 /// Note: no flush before backup is performed. User might want to
321 /// use `create_new_backup_flush` instead.
322 pub fn create_new_backup<T: ThreadMode, D: DBInner>(
323 &mut self,
324 db: &DBCommon<T, D>,
325 ) -> Result<(), Error> {
326 self.create_new_backup_flush(db, false)
327 }
328
329 /// Captures the state of the database in the latest backup.
330 ///
331 /// Set flush_before_backup=true to avoid losing unflushed key/value
332 /// pairs from the memtable.
333 pub fn create_new_backup_flush<T: ThreadMode, D: DBInner>(
334 &mut self,
335 db: &DBCommon<T, D>,
336 flush_before_backup: bool,
337 ) -> Result<(), Error> {
338 unsafe {
339 ffi_try!(ffi::rocksdb_backup_engine_create_new_backup_flush(
340 self.inner,
341 db.inner.inner(),
342 c_uchar::from(flush_before_backup),
343 ));
344 Ok(())
345 }
346 }
347
348 /// Captures the state of the database in the latest backup and returns the
349 /// id of the new backup.
350 ///
351 /// Unlike [`Self::create_new_backup_flush`] this exposes the full
352 /// `CreateBackupOptions` surface: flush behaviour, CPU priority of the copy
353 /// threads, and the progress and exclude files callbacks.
354 pub fn create_new_backup_with_options<T: ThreadMode, D: DBInner>(
355 &mut self,
356 db: &DBCommon<T, D>,
357 opts: &CreateBackupOptions,
358 ) -> Result<u32, Error> {
359 let mut backup_id: u32 = 0;
360 unsafe {
361 ffi_try!(ffi::rocksdb_backup_engine_create_new_backup_with_options(
362 self.inner,
363 db.inner.inner(),
364 opts.inner,
365 &raw mut backup_id,
366 ));
367 }
368 Ok(backup_id)
369 }
370
371 /// Same as [`Self::create_new_backup_with_options`] but also stores
372 /// application metadata with the backup, and returns the id of the new
373 /// backup.
374 ///
375 /// Read the metadata back with [`BackupEngineInfo::app_metadata`].
376 ///
377 /// `app_metadata` is passed with an explicit length and RocksDB stores it
378 /// hex encoded in the backup meta file, so any byte sequence is preserved,
379 /// including embedded NULs.
380 ///
381 /// RocksDB rejects metadata larger than 1 MiB with an
382 /// `Invalid argument: App metadata too large` error, and nothing is
383 /// written in that case.
384 pub fn create_new_backup_with_metadata<T: ThreadMode, D: DBInner>(
385 &mut self,
386 db: &DBCommon<T, D>,
387 opts: &CreateBackupOptions,
388 app_metadata: &[u8],
389 ) -> Result<u32, Error> {
390 // `db/c.cc` substitutes an empty string for a null pointer, which
391 // avoids handing it the dangling pointer an empty slice yields.
392 let (metadata_ptr, metadata_len) = if app_metadata.is_empty() {
393 (std::ptr::null(), 0)
394 } else {
395 (app_metadata.as_ptr().cast::<c_char>(), app_metadata.len())
396 };
397
398 let mut backup_id: u32 = 0;
399 unsafe {
400 ffi_try!(ffi::rocksdb_backup_engine_create_new_backup_with_metadata(
401 self.inner,
402 db.inner.inner(),
403 opts.inner,
404 metadata_ptr,
405 metadata_len,
406 &raw mut backup_id,
407 ));
408 }
409 Ok(backup_id)
410 }
411
412 pub fn purge_old_backups(&mut self, num_backups_to_keep: usize) -> Result<(), Error> {
413 unsafe {
414 ffi_try!(ffi::rocksdb_backup_engine_purge_old_backups(
415 self.inner,
416 num_backups_to_keep as u32,
417 ));
418 Ok(())
419 }
420 }
421
422 /// Restore from the latest backup
423 ///
424 /// # Arguments
425 ///
426 /// * `db_dir` - A path to the database directory
427 /// * `wal_dir` - A path to the wal directory
428 /// * `opts` - Restore options
429 ///
430 /// # Examples
431 ///
432 /// ```ignore
433 /// use rust_rocksdb::backup::{BackupEngine, BackupEngineOptions};
434 /// let backup_opts = BackupEngineOptions::default();
435 /// let mut backup_engine = BackupEngine::open(&backup_opts, &backup_path).unwrap();
436 /// let mut restore_option = rust_rocksdb::backup::RestoreOptions::default();
437 /// restore_option.set_keep_log_files(true); /// true to keep log files
438 /// if let Err(e) = backup_engine.restore_from_latest_backup(&db_path, &wal_dir, &restore_option) {
439 /// error!("Failed to restore from the backup. Error: {:?}", e);
440 /// return Err(e.to_string());
441 /// }
442 /// ```
443 pub fn restore_from_latest_backup<D: AsRef<Path>, W: AsRef<Path>>(
444 &mut self,
445 db_dir: D,
446 wal_dir: W,
447 opts: &RestoreOptions,
448 ) -> Result<(), Error> {
449 let c_db_dir = to_cpath(db_dir)?;
450 let c_wal_dir = to_cpath(wal_dir)?;
451
452 unsafe {
453 ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_latest_backup(
454 self.inner,
455 c_db_dir.as_ptr(),
456 c_wal_dir.as_ptr(),
457 opts.inner,
458 ));
459 }
460 Ok(())
461 }
462
463 /// Restore from a specified backup
464 ///
465 /// The specified backup id should be passed in as an additional parameter.
466 pub fn restore_from_backup<D: AsRef<Path>, W: AsRef<Path>>(
467 &mut self,
468 db_dir: D,
469 wal_dir: W,
470 opts: &RestoreOptions,
471 backup_id: u32,
472 ) -> Result<(), Error> {
473 let c_db_dir = to_cpath(db_dir)?;
474 let c_wal_dir = to_cpath(wal_dir)?;
475
476 unsafe {
477 ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_backup(
478 self.inner,
479 c_db_dir.as_ptr(),
480 c_wal_dir.as_ptr(),
481 opts.inner,
482 backup_id,
483 ));
484 }
485 Ok(())
486 }
487
488 /// Checks that each file exists and that the size of the file matches our
489 /// expectations. it does not check file checksum.
490 ///
491 /// If this BackupEngine created the backup, it compares the files' current
492 /// sizes against the number of bytes written to them during creation.
493 /// Otherwise, it compares the files' current sizes against their sizes when
494 /// the BackupEngine was opened.
495 pub fn verify_backup(&self, backup_id: u32) -> Result<(), Error> {
496 unsafe {
497 ffi_try!(ffi::rocksdb_backup_engine_verify_backup(
498 self.inner, backup_id,
499 ));
500 }
501 Ok(())
502 }
503
504 /// Get a list of all backups together with information on timestamp of the backup
505 /// and the size (please note that sum of all backups' sizes is bigger than the actual
506 /// size of the backup directory because some data is shared by multiple backups).
507 /// Backups are identified by their always-increasing IDs.
508 ///
509 /// You can perform this function safely, even with other BackupEngine performing
510 /// backups on the same directory
511 pub fn get_backup_info(&self) -> Vec<BackupEngineInfo> {
512 unsafe {
513 let i = ffi::rocksdb_backup_engine_get_backup_info(self.inner);
514
515 let n = ffi::rocksdb_backup_engine_info_count(i);
516
517 let mut info = Vec::with_capacity(n as usize);
518 for index in 0..n {
519 // `rocksdb_backup_engine_info_app_metadata` returns
520 // `BackupInfo::app_metadata.data()`, a pointer borrowed from
521 // the info object rather than a malloc'd copy, so it has to be
522 // copied before `rocksdb_backup_engine_info_destroy` below. A
523 // backup with no metadata yields an empty string, so the
524 // pointer stays valid and the length is 0.
525 let mut metadata_len: usize = 0;
526 let metadata_ptr =
527 ffi::rocksdb_backup_engine_info_app_metadata(i, index, &raw mut metadata_len);
528 let app_metadata = if metadata_len == 0 {
529 Vec::new()
530 } else {
531 raw_data(metadata_ptr, metadata_len).unwrap_or_default()
532 };
533
534 info.push(BackupEngineInfo {
535 timestamp: ffi::rocksdb_backup_engine_info_timestamp(i, index),
536 backup_id: ffi::rocksdb_backup_engine_info_backup_id(i, index),
537 size: ffi::rocksdb_backup_engine_info_size(i, index),
538 num_files: ffi::rocksdb_backup_engine_info_number_files(i, index),
539 app_metadata,
540 });
541 }
542
543 // destroy backup info object
544 ffi::rocksdb_backup_engine_info_destroy(i);
545
546 info
547 }
548 }
549
550 /// Aborts a backup that is currently running.
551 ///
552 /// This is one way. Once it is called on a backup engine, every later backup
553 /// request on that engine fails. The backup directory is left consistent and is
554 /// cleaned up by the next backup or garbage collection performed through a new
555 /// backup engine on the same directory.
556 pub fn stop_backup(&mut self) {
557 unsafe {
558 ffi::rocksdb_backup_engine_stop_backup(self.inner);
559 }
560 }
561}
562
563impl BackupEngineOptions {
564 /// Initializes `BackupEngineOptions` with the directory to be used for storing/accessing the
565 /// backup files.
566 pub fn new<P: AsRef<Path>>(backup_dir: P) -> Result<Self, Error> {
567 let backup_dir = backup_dir.as_ref();
568 let c_backup_dir = CString::new(backup_dir.to_string_lossy().as_bytes()).map_err(|_| {
569 Error::new(
570 "Failed to convert backup_dir to CString \
571 when constructing BackupEngineOptions"
572 .to_owned(),
573 )
574 })?;
575
576 unsafe {
577 let opts = ffi::rocksdb_backup_engine_options_create(c_backup_dir.as_ptr());
578 assert!(!opts.is_null(), "Could not create RocksDB backup options");
579
580 Ok(Self {
581 inner: opts,
582 backup_env: None,
583 })
584 }
585 }
586
587 /// Sets the number of operations (such as file copies or file checksums) that `RocksDB` may
588 /// perform in parallel when executing a backup or restore.
589 ///
590 /// Default: 1
591 pub fn set_max_background_operations(&mut self, max_background_operations: i32) {
592 unsafe {
593 ffi::rocksdb_backup_engine_options_set_max_background_operations(
594 self.inner,
595 max_background_operations,
596 );
597 }
598 }
599
600 /// Sets whether to use fsync(2) to sync file data and metadata to disk after every file write,
601 /// guaranteeing that backups will be consistent after a reboot or if machine crashes. Setting
602 /// it to false will speed things up a bit, but some (newer) backups might be inconsistent. In
603 /// most cases, everything should be fine, though.
604 ///
605 /// Default: true
606 ///
607 /// Documentation: <https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB#advanced-usage>
608 pub fn set_sync(&mut self, sync: bool) {
609 unsafe {
610 ffi::rocksdb_backup_engine_options_set_sync(self.inner, c_uchar::from(sync));
611 }
612 }
613
614 /// Returns the value of the `sync` option.
615 pub fn get_sync(&mut self) -> bool {
616 let val_u8 = unsafe { ffi::rocksdb_backup_engine_options_get_sync(self.inner) };
617 val_u8 != 0
618 }
619
620 /// (Experimental - subject to change or removal) When taking a backup and saving file
621 /// temperature info (minimum schema_version is 2), there are two potential sources of
622 /// truth for the placement of files into temperature tiers: (a) the current file
623 /// temperature reported by the FileSystem or (b) the expected file temperature recorded
624 /// in DB manifest. When this option is false (default), (b) overrides (a) if both are not
625 /// UNKNOWN. When true, (a) overrides (b) if both are not UNKNOWN. Regardless of this
626 /// setting, a known temperature overrides UNKNOWN.
627 pub fn set_current_temperatures_override_manifest(&mut self, val: bool) {
628 unsafe {
629 ffi::rocksdb_backup_engine_options_set_current_temperatures_override_manifest(
630 self.inner,
631 c_uchar::from(val),
632 );
633 }
634 }
635
636 /// Returns the value of the `current_temperatures_override_manifest` option.
637 pub fn get_current_temperatures_override_manifest(&self) -> bool {
638 unsafe {
639 ffi::rocksdb_backup_engine_options_get_current_temperatures_override_manifest(
640 self.inner,
641 ) != 0
642 }
643 }
644
645 /// Sets the `io_buffer_size` option.
646 pub fn set_io_buffer_size(&mut self, val: u64) {
647 unsafe {
648 ffi::rocksdb_backup_engine_options_set_io_buffer_size(self.inner, val);
649 }
650 }
651
652 /// Returns the value of the `io_buffer_size` option.
653 pub fn get_io_buffer_size(&self) -> u64 {
654 unsafe { ffi::rocksdb_backup_engine_options_get_io_buffer_size(self.inner) }
655 }
656
657 /// Major schema version to use when writing backup meta files 1 (default) - compatible
658 /// with very old versions of RocksDB. 2 - can be read by RocksDB versions >= 6.19.0.
659 /// Minimum schema version for
660 /// - (Experimental) saving and restoring file temperature metadata
661 pub fn set_schema_version(&mut self, val: c_int) {
662 unsafe {
663 ffi::rocksdb_backup_engine_options_set_schema_version(self.inner, val);
664 }
665 }
666
667 /// Returns the value of the `schema_version` option.
668 pub fn get_schema_version(&self) -> c_int {
669 unsafe { ffi::rocksdb_backup_engine_options_get_schema_version(self.inner) }
670 }
671
672 /// share_files_with_checksum supports table and blob files.
673 ///
674 /// Only used if share_table_files is set to true. Setting to false is DEPRECATED and
675 /// potentially dangerous because in that case BackupEngine can lose data if backing up
676 /// databases with distinct or divergent history, for example if restoring from a backup
677 /// other than the latest, writing to the DB, and creating another backup. Setting to true
678 /// (default) prevents these issues by ensuring that different table files (SSTs) and blob
679 /// files with the same number are treated as distinct. See
680 /// share_files_with_checksum_naming and ShareFilesNaming.
681 ///
682 /// Default: true
683 pub fn set_share_files_with_checksum(&mut self, val: bool) {
684 unsafe {
685 ffi::rocksdb_backup_engine_options_set_share_files_with_checksum(
686 self.inner,
687 c_uchar::from(val),
688 );
689 }
690 }
691
692 /// Returns the value of the `share_files_with_checksum` option.
693 pub fn get_share_files_with_checksum(&self) -> bool {
694 unsafe { ffi::rocksdb_backup_engine_options_get_share_files_with_checksum(self.inner) != 0 }
695 }
696
697 /// Returns the current `backup_log_files` setting.
698 ///
699 /// See [`Self::set_backup_log_files`] for what this controls.
700 pub fn get_backup_log_files(&self) -> bool {
701 unsafe { ffi::rocksdb_backup_engine_options_get_backup_log_files(self.inner) != 0 }
702 }
703
704 /// Returns the current `backup_rate_limit` setting.
705 ///
706 /// See [`Self::set_backup_rate_limit`] for what this controls.
707 pub fn get_backup_rate_limit(&self) -> u64 {
708 unsafe { ffi::rocksdb_backup_engine_options_get_backup_rate_limit(self.inner) }
709 }
710
711 /// Returns the current `callback_trigger_interval_size` setting.
712 ///
713 /// See [`Self::set_callback_trigger_interval_size`] for what this controls.
714 pub fn get_callback_trigger_interval_size(&self) -> u64 {
715 unsafe { ffi::rocksdb_backup_engine_options_get_callback_trigger_interval_size(self.inner) }
716 }
717
718 /// Returns the current `destroy_old_data` setting.
719 ///
720 /// See [`Self::set_destroy_old_data`] for what this controls.
721 pub fn get_destroy_old_data(&self) -> bool {
722 unsafe { ffi::rocksdb_backup_engine_options_get_destroy_old_data(self.inner) != 0 }
723 }
724
725 /// Returns the current `max_background_operations` setting.
726 ///
727 /// See [`Self::set_max_background_operations`] for what this controls.
728 pub fn get_max_background_operations(&self) -> c_int {
729 unsafe { ffi::rocksdb_backup_engine_options_get_max_background_operations(self.inner) }
730 }
731
732 /// Returns the current `max_valid_backups_to_open` setting.
733 ///
734 /// See [`Self::set_max_valid_backups_to_open`] for what this controls.
735 pub fn get_max_valid_backups_to_open(&self) -> c_int {
736 unsafe { ffi::rocksdb_backup_engine_options_get_max_valid_backups_to_open(self.inner) }
737 }
738
739 /// Returns the current `restore_rate_limit` setting.
740 ///
741 /// See [`Self::set_restore_rate_limit`] for what this controls.
742 pub fn get_restore_rate_limit(&self) -> u64 {
743 unsafe { ffi::rocksdb_backup_engine_options_get_restore_rate_limit(self.inner) }
744 }
745
746 /// Returns the current `share_table_files` setting.
747 ///
748 /// See [`Self::set_share_table_files`] for what this controls.
749 pub fn get_share_table_files(&self) -> bool {
750 unsafe { ffi::rocksdb_backup_engine_options_get_share_table_files(self.inner) != 0 }
751 }
752
753 /// If false, we won't backup log files. This option can be useful for backing up
754 /// in-memory databases where log file are persisted, but table files are in memory.
755 /// Default: true.
756 pub fn set_backup_log_files(&mut self, val: bool) {
757 unsafe {
758 ffi::rocksdb_backup_engine_options_set_backup_log_files(self.inner, c_uchar::from(val));
759 }
760 }
761
762 /// Max bytes that can be transferred in a second while creating a backup.
763 ///
764 /// If 0, go as fast as you can. This limit only applies to writes.
765 ///
766 /// Default: `0`
767 pub fn set_backup_rate_limit(&mut self, val: u64) {
768 unsafe {
769 ffi::rocksdb_backup_engine_options_set_backup_rate_limit(self.inner, val);
770 }
771 }
772
773 /// During backup user can get callback every time next callback_trigger_interval_size
774 /// bytes being copied. Default: 4194304.
775 pub fn set_callback_trigger_interval_size(&mut self, val: u64) {
776 unsafe {
777 ffi::rocksdb_backup_engine_options_set_callback_trigger_interval_size(self.inner, val);
778 }
779 }
780
781 /// If true, it will delete whatever backups there are already Default: false.
782 pub fn set_destroy_old_data(&mut self, val: bool) {
783 unsafe {
784 ffi::rocksdb_backup_engine_options_set_destroy_old_data(self.inner, c_uchar::from(val));
785 }
786 }
787
788 /// For BackupEngineReadOnly, Open() will open at most this many of the latest
789 /// non-corrupted backups.
790 ///
791 /// Note: this setting is ignored (behaves like INT_MAX) for any kind of writable
792 /// BackupEngine because it would inhibit accounting for shared files for proper backup
793 /// deletion, including purging any incompletely created backups on creation of a new
794 /// backup.
795 ///
796 /// Default: INT_MAX.
797 pub fn set_max_valid_backups_to_open(&mut self, val: c_int) {
798 unsafe {
799 ffi::rocksdb_backup_engine_options_set_max_valid_backups_to_open(self.inner, val);
800 }
801 }
802
803 /// Max bytes that can be transferred in a second during restore. If 0, go as fast as you
804 /// can This limit only applies to writes. To also limit reads, a rate limiter able to
805 /// also limit reads (e.g, its mode = kAllIo) have to be passed in through the option
806 /// "restore_rate_limiter" Default: 0.
807 pub fn set_restore_rate_limit(&mut self, val: u64) {
808 unsafe {
809 ffi::rocksdb_backup_engine_options_set_restore_rate_limit(self.inner, val);
810 }
811 }
812
813 /// Share_table_files supports table and blob files.
814 ///
815 /// If share_table_files == true, the backup directory will share table and blob files
816 /// among backups, to save space among backups of the same DB and to enable incremental
817 /// backups by only copying new files. If share_table_files == false, each backup will be
818 /// on its own and will not share any data with other backups.
819 ///
820 /// default: true.
821 pub fn set_share_table_files(&mut self, val: bool) {
822 unsafe {
823 ffi::rocksdb_backup_engine_options_set_share_table_files(
824 self.inner,
825 c_uchar::from(val),
826 );
827 }
828 }
829
830 /// Changes the directory backups are written to and read from, replacing
831 /// the one given to [`Self::new`].
832 ///
833 /// RocksDB copies the string, so the path is not borrowed. It cannot
834 /// contain an interior NUL.
835 pub fn set_backup_dir<P: AsRef<Path>>(&mut self, backup_dir: P) -> Result<(), Error> {
836 let c_backup_dir = to_cpath(backup_dir)?;
837 unsafe {
838 ffi::rocksdb_backup_engine_options_set_backup_dir(self.inner, c_backup_dir.as_ptr());
839 }
840 Ok(())
841 }
842
843 /// Returns the directory backups are written to and read from.
844 pub fn get_backup_dir(&self) -> PathBuf {
845 unsafe {
846 // The C API returns `backup_dir.data()`, borrowed from the options
847 // object rather than malloc'd, so it must be copied here and must
848 // not be freed.
849 let mut len: usize = 0;
850 let ptr = ffi::rocksdb_backup_engine_options_get_backup_dir(self.inner, &raw mut len);
851 if len == 0 {
852 return PathBuf::new();
853 }
854 path_from_bytes(slice::from_raw_parts(ptr.cast::<u8>(), len))
855 }
856 }
857
858 /// Sets the naming scheme used for table and blob files stored in the
859 /// `shared_checksum` directory.
860 ///
861 /// The value must select exactly one naming scheme from the low bits, see
862 /// [`ShareFilesNaming`]. Default: [`ShareFilesNaming::DEFAULT`].
863 ///
864 /// Changing this is downgrade safe, because RocksDB can read, restore and
865 /// delete backups whose files use a different scheme. It does mean that
866 /// adding more backups to the same directory can store a file a second
867 /// time under its new shared name.
868 pub fn set_share_files_with_checksum_naming(&mut self, val: ShareFilesNaming) {
869 unsafe {
870 ffi::rocksdb_backup_engine_options_set_share_files_with_checksum_naming(
871 self.inner,
872 val.bits(),
873 );
874 }
875 }
876
877 /// Returns the current `share_files_with_checksum_naming` setting.
878 pub fn get_share_files_with_checksum_naming(&self) -> ShareFilesNaming {
879 unsafe {
880 ShareFilesNaming(
881 ffi::rocksdb_backup_engine_options_get_share_files_with_checksum_naming(self.inner),
882 )
883 }
884 }
885
886 /// Sets the Env used for the backup directory itself. The database being
887 /// backed up keeps using its own Env.
888 ///
889 /// RocksDB stores a borrowed `Env*` and `BackupEngineImpl` copies the
890 /// options by value, so the Env has to outlive the engine. This type keeps
891 /// a handle, and [`BackupEngine::open`] takes another one, so the Env stays
892 /// alive for as long as either of them does.
893 pub fn set_env(&mut self, env: &Env) {
894 unsafe {
895 ffi::rocksdb_backup_engine_options_set_env(self.inner, env.0.inner);
896 }
897 self.backup_env = Some(env.clone());
898 }
899
900 /// Sets a rate limiter for backup I/O, which unlike
901 /// [`Self::set_backup_rate_limit`] can be tuned beyond a byte budget.
902 ///
903 /// The limiter created here defaults to limiting writes only. Use
904 /// [`Self::set_backup_rate_limiter_with_mode`] to limit reads as well.
905 ///
906 /// The C API holds the limiter in a `shared_ptr`, so it stays alive for as
907 /// long as the options and the engine need it and there is nothing for the
908 /// caller to keep.
909 pub fn set_backup_rate_limiter(
910 &mut self,
911 rate_bytes_per_sec: i64,
912 refill_period_us: i64,
913 fairness: i32,
914 ) {
915 unsafe {
916 let ratelimiter =
917 ffi::rocksdb_ratelimiter_create(rate_bytes_per_sec, refill_period_us, fairness);
918 ffi::rocksdb_backup_engine_options_set_backup_rate_limiter(self.inner, ratelimiter);
919 ffi::rocksdb_ratelimiter_destroy(ratelimiter);
920 }
921 }
922
923 /// Sets a rate limiter for backup I/O, choosing which operations count
924 /// against the limit and whether the limit is auto tuned.
925 ///
926 /// See [`crate::Options::set_ratelimiter_with_mode`] for what the
927 /// parameters mean.
928 pub fn set_backup_rate_limiter_with_mode(
929 &mut self,
930 rate_bytes_per_sec: i64,
931 refill_period_us: i64,
932 fairness: i32,
933 mode: RateLimiterMode,
934 auto_tuned: bool,
935 ) {
936 unsafe {
937 let ratelimiter = ffi::rocksdb_ratelimiter_create_with_mode(
938 rate_bytes_per_sec,
939 refill_period_us,
940 fairness,
941 mode as c_int,
942 auto_tuned,
943 );
944 ffi::rocksdb_backup_engine_options_set_backup_rate_limiter(self.inner, ratelimiter);
945 ffi::rocksdb_ratelimiter_destroy(ratelimiter);
946 }
947 }
948
949 /// Sets a rate limiter for restore I/O.
950 ///
951 /// The limiter created here defaults to limiting writes only, which is
952 /// what [`Self::set_restore_rate_limit`] already does. Use
953 /// [`Self::set_restore_rate_limiter_with_mode`] with
954 /// [`RateLimiterMode::KAllIo`] to also limit reads.
955 ///
956 /// Ownership works as described on [`Self::set_backup_rate_limiter`].
957 pub fn set_restore_rate_limiter(
958 &mut self,
959 rate_bytes_per_sec: i64,
960 refill_period_us: i64,
961 fairness: i32,
962 ) {
963 unsafe {
964 let ratelimiter =
965 ffi::rocksdb_ratelimiter_create(rate_bytes_per_sec, refill_period_us, fairness);
966 ffi::rocksdb_backup_engine_options_set_restore_rate_limiter(self.inner, ratelimiter);
967 ffi::rocksdb_ratelimiter_destroy(ratelimiter);
968 }
969 }
970
971 /// Sets a rate limiter for restore I/O, choosing which operations count
972 /// against the limit and whether the limit is auto tuned.
973 ///
974 /// See [`crate::Options::set_ratelimiter_with_mode`] for what the
975 /// parameters mean.
976 pub fn set_restore_rate_limiter_with_mode(
977 &mut self,
978 rate_bytes_per_sec: i64,
979 refill_period_us: i64,
980 fairness: i32,
981 mode: RateLimiterMode,
982 auto_tuned: bool,
983 ) {
984 unsafe {
985 let ratelimiter = ffi::rocksdb_ratelimiter_create_with_mode(
986 rate_bytes_per_sec,
987 refill_period_us,
988 fairness,
989 mode as c_int,
990 auto_tuned,
991 );
992 ffi::rocksdb_backup_engine_options_set_restore_rate_limiter(self.inner, ratelimiter);
993 ffi::rocksdb_ratelimiter_destroy(ratelimiter);
994 }
995 }
996}
997
998/// Converts bytes borrowed from a RocksDB `std::string` into a path.
999///
1000/// Unix paths are arbitrary bytes, so this is lossless there. Other platforms
1001/// have no byte level path constructor, so invalid UTF-8 is replaced.
1002#[cfg(unix)]
1003fn path_from_bytes(bytes: &[u8]) -> PathBuf {
1004 use std::os::unix::ffi::OsStrExt;
1005 PathBuf::from(std::ffi::OsStr::from_bytes(bytes))
1006}
1007
1008#[cfg(not(unix))]
1009fn path_from_bytes(bytes: &[u8]) -> PathBuf {
1010 PathBuf::from(String::from_utf8_lossy(bytes).into_owned())
1011}
1012
1013impl CreateBackupOptions {
1014 /// If true, flush the memtables before taking the backup, so that writes
1015 /// that never reached a WAL are not lost. A flush always happens when 2PC
1016 /// is enabled.
1017 ///
1018 /// Default: false
1019 pub fn set_flush_before_backup(&mut self, val: bool) {
1020 unsafe {
1021 ffi::rocksdb_create_backup_options_set_flush_before_backup(
1022 self.inner,
1023 c_uchar::from(val),
1024 );
1025 }
1026 }
1027
1028 /// Returns the current `flush_before_backup` setting.
1029 pub fn get_flush_before_backup(&self) -> bool {
1030 unsafe { ffi::rocksdb_create_backup_options_get_flush_before_backup(self.inner) != 0 }
1031 }
1032
1033 /// If true, flush all column families atomically, giving cross column
1034 /// family consistency without WAL files. Combined with
1035 /// `BackupEngineOptions::backup_log_files = false` this makes it safe to
1036 /// skip backing up WALs for a multi column family database.
1037 ///
1038 /// Only takes effect when `flush_before_backup` is also true.
1039 ///
1040 /// Default: false
1041 pub fn set_atomic_flush(&mut self, val: bool) {
1042 unsafe {
1043 ffi::rocksdb_create_backup_options_set_atomic_flush(self.inner, c_uchar::from(val));
1044 }
1045 }
1046
1047 /// Returns the current `atomic_flush` setting.
1048 pub fn get_atomic_flush(&self) -> bool {
1049 unsafe { ffi::rocksdb_create_backup_options_get_atomic_flush(self.inner) != 0 }
1050 }
1051
1052 /// If false, `background_thread_cpu_priority` is ignored. If true, the
1053 /// priority of the copy threads can be lowered. Raising it has no effect,
1054 /// since the threads start at [`CpuPriority::KNormal`].
1055 ///
1056 /// Default: false
1057 pub fn set_decrease_background_thread_cpu_priority(&mut self, val: bool) {
1058 unsafe {
1059 ffi::rocksdb_create_backup_options_set_decrease_background_thread_cpu_priority(
1060 self.inner,
1061 c_uchar::from(val),
1062 );
1063 }
1064 }
1065
1066 /// Returns the current `decrease_background_thread_cpu_priority` setting.
1067 pub fn get_decrease_background_thread_cpu_priority(&self) -> bool {
1068 unsafe {
1069 ffi::rocksdb_create_backup_options_get_decrease_background_thread_cpu_priority(
1070 self.inner,
1071 ) != 0
1072 }
1073 }
1074
1075 /// CPU priority for the threads that copy files during the backup. Only
1076 /// used when [`Self::set_decrease_background_thread_cpu_priority`] is true.
1077 ///
1078 /// Default: [`CpuPriority::KNormal`]
1079 pub fn set_background_thread_cpu_priority(&mut self, val: CpuPriority) {
1080 unsafe {
1081 ffi::rocksdb_create_backup_options_set_background_thread_cpu_priority(
1082 self.inner,
1083 val as c_int,
1084 );
1085 }
1086 }
1087
1088 /// Returns the current `background_thread_cpu_priority` setting.
1089 pub fn get_background_thread_cpu_priority(&self) -> CpuPriority {
1090 unsafe {
1091 CpuPriority::from(
1092 ffi::rocksdb_create_backup_options_get_background_thread_cpu_priority(self.inner),
1093 )
1094 }
1095 }
1096
1097 /// Registers a closure that RocksDB calls every
1098 /// `BackupEngineOptions::callback_trigger_interval_size` bytes copied.
1099 ///
1100 /// The closure is called from the copy threads, not the thread that
1101 /// started the backup. RocksDB serialises the calls behind a mutex, but
1102 /// every copy thread holds a copy of the callback at the same time, so it
1103 /// must be `Send + Sync`. `'static` is required because these options
1104 /// store the closure without borrowing from the caller.
1105 ///
1106 /// Calling this again replaces and frees the previously registered
1107 /// closure. See the type level docs for why this type owns it.
1108 ///
1109 /// A panic out of the closure aborts the process, because a Rust panic
1110 /// cannot unwind through the C boundary.
1111 pub fn set_progress_callback<F>(&mut self, callback: F)
1112 where
1113 F: Fn() + Send + Sync + 'static,
1114 {
1115 let holder = Arc::new(ProgressCallback {
1116 callback: Box::new(callback),
1117 });
1118 let state = std::ptr::from_ref::<ProgressCallback>(holder.as_ref())
1119 .cast::<c_void>()
1120 .cast_mut();
1121 unsafe {
1122 ffi::rocksdb_create_backup_options_set_progress_callback(
1123 self.inner,
1124 state,
1125 Some(progress_callback_trampoline),
1126 );
1127 }
1128 // The C side now points at the new holder, so dropping the old one
1129 // here cannot leave it with a dangling state pointer.
1130 self.progress_callback = Some(holder);
1131 }
1132
1133 /// Registers a closure that decides which shared files to leave out of the
1134 /// backup. It is called once per candidate file with the file name relative
1135 /// to the backup directory, and returning true excludes that file.
1136 ///
1137 /// This is an advanced feature. RocksDB trusts the caller to keep the
1138 /// excluded files somewhere the database can still be restored from, for
1139 /// example an alternate backup directory. Restoring such a backup needs
1140 /// those files supplied through `RestoreOptions::alternate_dirs`, which the
1141 /// C API does not expose, so from this crate the only recovery path is
1142 /// [`RestoreMode::KKeepLatestDbSessionIdFiles`], which opportunistically
1143 /// looks for the missing files in the existing database directory.
1144 ///
1145 /// Only shared files are ever offered to the closure, so this needs
1146 /// `share_table_files` and `share_files_with_checksum` to be true. It also
1147 /// needs `schema_version` to be at least 2, otherwise creating the backup
1148 /// fails with `Invalid argument: exclude_files_callback requires
1149 /// schema_version >= 2`.
1150 ///
1151 /// `db/c.cc` calls this on the thread driving the backup, but the bound
1152 /// matches [`Self::set_progress_callback`] so that both closures have the
1153 /// same requirements.
1154 ///
1155 /// Calling this again replaces and frees the previously registered
1156 /// closure. A panic out of the closure aborts the process.
1157 pub fn set_exclude_files_callback<F>(&mut self, callback: F)
1158 where
1159 F: Fn(&[u8]) -> bool + Send + Sync + 'static,
1160 {
1161 let holder = Arc::new(ExcludeFilesCallback {
1162 callback: Box::new(callback),
1163 });
1164 let state = std::ptr::from_ref::<ExcludeFilesCallback>(holder.as_ref())
1165 .cast::<c_void>()
1166 .cast_mut();
1167 unsafe {
1168 ffi::rocksdb_create_backup_options_set_exclude_files_callback(
1169 self.inner,
1170 state,
1171 Some(exclude_files_callback_trampoline),
1172 );
1173 }
1174 // See `set_progress_callback`.
1175 self.exclude_files_callback = Some(holder);
1176 }
1177}
1178
1179impl Default for CreateBackupOptions {
1180 fn default() -> Self {
1181 unsafe {
1182 let opts = ffi::rocksdb_create_backup_options_create();
1183 assert!(
1184 !opts.is_null(),
1185 "Could not create RocksDB create backup options"
1186 );
1187
1188 Self {
1189 inner: opts,
1190 progress_callback: None,
1191 exclude_files_callback: None,
1192 }
1193 }
1194 }
1195}
1196
1197impl RestoreOptions {
1198 /// Sets `keep_log_files`. If true, restore won't overwrite the existing log files in wal_dir.
1199 /// It will also move all log files from archive directory to wal_dir. Use this option in
1200 /// combination with BackupEngineOptions::backup_log_files = false for persisting in-memory
1201 /// databases.
1202 ///
1203 /// Default: false
1204 pub fn set_keep_log_files(&mut self, keep_log_files: bool) {
1205 unsafe {
1206 ffi::rocksdb_restore_options_set_keep_log_files(self.inner, i32::from(keep_log_files));
1207 }
1208 }
1209
1210 /// If true, restore won't overwrite the existing log files in wal_dir. It will also move
1211 /// all log files from archive directory to wal_dir. Use this option in combination with
1212 /// BackupEngineOptions::backup_log_files = false for persisting in-memory databases.
1213 /// Default: false
1214 pub fn get_keep_log_files(&self) -> bool {
1215 unsafe { ffi::rocksdb_restore_options_get_keep_log_files(self.inner) != 0 }
1216 }
1217
1218 /// Sets how much of the destination database the restore may reuse.
1219 ///
1220 /// Default: [`RestoreMode::KPurgeAllFiles`]
1221 pub fn set_mode(&mut self, mode: RestoreMode) {
1222 unsafe {
1223 ffi::rocksdb_restore_options_set_mode(self.inner, mode as c_int);
1224 }
1225 }
1226
1227 /// Returns the current restore mode.
1228 pub fn get_mode(&self) -> RestoreMode {
1229 unsafe { RestoreMode::from(ffi::rocksdb_restore_options_get_mode(self.inner)) }
1230 }
1231}
1232
1233impl Default for RestoreOptions {
1234 fn default() -> Self {
1235 unsafe {
1236 let opts = ffi::rocksdb_restore_options_create();
1237 assert!(!opts.is_null(), "Could not create RocksDB restore options");
1238
1239 Self { inner: opts }
1240 }
1241 }
1242}
1243
1244impl Drop for BackupEngine {
1245 fn drop(&mut self) {
1246 unsafe {
1247 ffi::rocksdb_backup_engine_close(self.inner);
1248 }
1249 }
1250}
1251
1252impl Drop for BackupEngineOptions {
1253 fn drop(&mut self) {
1254 unsafe {
1255 ffi::rocksdb_backup_engine_options_destroy(self.inner);
1256 }
1257 }
1258}
1259
1260impl Drop for CreateBackupOptions {
1261 fn drop(&mut self) {
1262 unsafe {
1263 ffi::rocksdb_create_backup_options_destroy(self.inner);
1264 }
1265 // The closure holders are fields, so they are dropped after this body
1266 // runs, which is the order that matters: the C++ lambdas holding their
1267 // addresses are gone by then.
1268 }
1269}
1270
1271impl Drop for RestoreOptions {
1272 fn drop(&mut self) {
1273 unsafe {
1274 ffi::rocksdb_restore_options_destroy(self.inner);
1275 }
1276 }
1277}
1278
1279#[cfg(test)]
1280mod tests {
1281 use super::BackupEngineOptions;
1282
1283 #[test]
1284 fn test_sync() {
1285 let dir = tempfile::Builder::new()
1286 .prefix("rocksdb-test-sync")
1287 .tempdir()
1288 .expect("Failed to create temporary path for db.");
1289
1290 let mut opts = BackupEngineOptions::new(dir.path()).unwrap();
1291 assert!(opts.get_sync());
1292 opts.set_sync(false);
1293 assert!(!opts.get_sync());
1294 }
1295}