From d28eba2ff5ef41a656136e04fb16291308f6722a Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sat, 26 Nov 2016 23:40:27 +0000 Subject: [PATCH] Added basic support for backups. --- librocksdb-sys/src/test.rs | 5 +- src/backup.rs | 100 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 49 +++++++++++++++++- src/rocksdb.rs | 67 +++++-------------------- src/rocksdb_options.rs | 7 ++- 5 files changed, 164 insertions(+), 64 deletions(-) create mode 100644 src/backup.rs diff --git a/librocksdb-sys/src/test.rs b/librocksdb-sys/src/test.rs index 6d3aa79..d7718a4 100644 --- a/librocksdb-sys/src/test.rs +++ b/librocksdb-sys/src/test.rs @@ -41,11 +41,10 @@ fn internal() { let rustpath = "_rust_rocksdb_internaltest"; let cpath = CString::new(rustpath).unwrap(); - let cpath_ptr = cpath.as_ptr(); let mut err: *mut c_char = ptr::null_mut(); let err_ptr: *mut *mut c_char = &mut err; - let db = rocksdb_open(opts, cpath_ptr as *const _, err_ptr); + let db = rocksdb_open(opts, cpath.as_ptr() as *const _, err_ptr); if !err.is_null() { println!("failed to open rocksdb: {}", error_message(err)); } @@ -80,7 +79,7 @@ fn internal() { rocksdb_readoptions_destroy(readopts); assert!(err.is_null()); rocksdb_close(db); - rocksdb_destroy_db(opts, cpath_ptr as *const _, err_ptr); + rocksdb_destroy_db(opts, cpath.as_ptr() as *const _, err_ptr); assert!(err.is_null()); } } diff --git a/src/backup.rs b/src/backup.rs new file mode 100644 index 0000000..13572a4 --- /dev/null +++ b/src/backup.rs @@ -0,0 +1,100 @@ +// Copyright 2016 Alex Regueiro +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +use std::ffi::CString; +use std::path::{Path}; + +use libc::{uint32_t}; + +use {DB, Error}; +use ffi; + +pub struct BackupEngine { + inner: *mut ffi::rocksdb_backup_engine_t, +} + +pub struct BackupEngineOptions { + inner: *mut ffi::rocksdb_options_t, +} + +impl BackupEngine { + /// Open a backup engine with the specified options. + pub fn open>(opts: &BackupEngineOptions, path: P) -> Result { + let path = path.as_ref(); + let cpath = match CString::new(path.to_string_lossy().as_bytes()) { + Ok(c) => c, + Err(_) => { + return Err(Error::new("Failed to convert path to CString \ + when opening backup engine" + .to_owned())) + } + }; + + let be: *mut ffi::rocksdb_backup_engine_t; + unsafe { + be = ffi_try!(ffi::rocksdb_backup_engine_open(opts.inner, cpath.as_ptr())) + } + + if be.is_null() { + return Err(Error::new("Could not initialize backup engine.".to_owned())); + } + + Ok(BackupEngine { + inner: be, + }) + } + + fn create_new_backup(&mut self, db: &DB) -> Result<(), Error> { + unsafe { + ffi_try!(ffi::rocksdb_backup_engine_create_new_backup(self.inner, db.inner)); + Ok(()) + } + } + + fn purge_old_backups(&mut self, num_backups_to_keep: usize) -> Result<(), Error> { + unsafe { + ffi_try!(ffi::rocksdb_backup_engine_purge_old_backups(self.inner, num_backups_to_keep as uint32_t)); + Ok(()) + } + } +} + +impl Default for BackupEngineOptions { + fn default() -> BackupEngineOptions { + unsafe { + let opts = ffi::rocksdb_options_create(); + if opts.is_null() { + panic!("Could not create backup options".to_owned()); + } + BackupEngineOptions { inner: opts } + } + } +} + +impl Drop for BackupEngine { + fn drop(&mut self) { + unsafe { + ffi::rocksdb_backup_engine_close(self.inner); + } + } +} + +impl Drop for BackupEngineOptions { + fn drop(&mut self) { + unsafe { + ffi::rocksdb_options_destroy(self.inner); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 0854799..9596ff2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,15 +37,60 @@ extern crate librocksdb_sys as ffi; #[macro_use] mod ffi_util; +pub mod backup; mod comparator; pub mod merge_operator; mod rocksdb; mod rocksdb_options; -pub use rocksdb::{DB, DBCompactionStyle, DBCompressionType, DBIterator, DBRecoveryMode, DBVector, - Direction, Error, IteratorMode, Snapshot, WriteBatch, new_bloom_filter}; +use std::collections::BTreeMap; +use std::error; +use std::fmt; +use std::path::{PathBuf}; pub use merge_operator::MergeOperands; +pub use rocksdb::{DBCompactionStyle, DBCompressionType, DBIterator, DBRecoveryMode, DBVector, + Direction, IteratorMode, Snapshot, WriteBatch, new_bloom_filter}; + +/// A RocksDB database. +pub struct DB { + inner: *mut ffi::rocksdb_t, + cfs: BTreeMap, + path: PathBuf, +} + +#[derive(Debug, PartialEq)] +pub struct Error { + message: String, +} + +impl Error { + fn new(message: String) -> Error { + Error { message: message } + } + + pub fn to_string(self) -> String { + self.into() + } +} + +impl From for String { + fn from(e: Error) -> String { + e.message + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + &self.message + } +} + +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.message.fmt(formatter) + } +} /// For configuring block-based file storage. pub struct BlockBasedOptions { diff --git a/src/rocksdb.rs b/src/rocksdb.rs index 320716d..d529868 100644 --- a/src/rocksdb.rs +++ b/src/rocksdb.rs @@ -14,19 +14,18 @@ // use std::collections::BTreeMap; -use std::error; use std::ffi::CString; use std::fmt; use std::fs; use std::ops::Deref; -use std::path::{Path, PathBuf}; +use std::path::{Path}; use std::ptr; use std::slice; use std::str; use libc::{self, c_char, c_int, c_uchar, c_void, size_t}; -use {Options, WriteOptions}; +use {DB, Error, Options, WriteOptions}; use ffi; pub fn new_bloom_filter(bits: c_int) -> *mut ffi::rocksdb_filterpolicy_t { @@ -37,13 +36,6 @@ pub fn new_cache(capacity: size_t) -> *mut ffi::rocksdb_cache_t { unsafe { ffi::rocksdb_cache_create_lru(capacity) } } -/// A RocksDB database. -pub struct DB { - inner: *mut ffi::rocksdb_t, - cfs: BTreeMap, - path: PathBuf, -} - unsafe impl Send for DB {} unsafe impl Sync for DB {} @@ -151,39 +143,6 @@ pub enum Direction { pub type KVBytes = (Box<[u8]>, Box<[u8]>); -#[derive(Debug, PartialEq)] -pub struct Error { - message: String, -} - -impl Error { - fn new(message: String) -> Error { - Error { message: message } - } - - pub fn to_string(self) -> String { - self.into() - } -} - -impl From for String { - fn from(e: Error) -> String { - e.message - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - &self.message - } -} - -impl fmt::Display for Error { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.message.fmt(formatter) - } -} - impl Iterator for DBIterator { type Item = KVBytes; @@ -357,23 +316,21 @@ impl DB { /// /// # Panics /// - /// * Panics if the column family doesn't exist + /// * Panics if the column family doesn't exist. pub fn open_cf>(opts: &Options, path: P, cfs: &[&str]) -> Result { let path = path.as_ref(); - let cpath = match CString::new(path.to_string_lossy().as_bytes()) { Ok(c) => c, Err(_) => { return Err(Error::new("Failed to convert path to CString \ - when opening rocksdb" + when opening DB." .to_owned())) } }; - let cpath_ptr = cpath.as_ptr(); if let Err(e) = fs::create_dir_all(&path) { - return Err(Error::new(format!("Failed to create rocksdb \ - directory: {:?}", + return Err(Error::new(format!("Failed to create RocksDB\ + directory: `{:?}`.", e))); } @@ -382,7 +339,7 @@ impl DB { if cfs.len() == 0 { unsafe { - db = ffi_try!(ffi::rocksdb_open(opts.inner, cpath_ptr as *const _)); + db = ffi_try!(ffi::rocksdb_open(opts.inner, cpath.as_ptr() as *const _)); } } else { let mut cfs_v = cfs.to_vec(); @@ -409,7 +366,7 @@ impl DB { unsafe { db = ffi_try!(ffi::rocksdb_open_column_families(opts.inner, - cpath_ptr as *const _, + cpath.as_ptr() as *const _, cfs_v.len() as c_int, cfnames.as_ptr() as *const _, cfopts.as_ptr(), @@ -479,10 +436,10 @@ impl DB { pub fn get_opt(&self, key: &[u8], readopts: &ReadOptions) -> Result, Error> { if readopts.inner.is_null() { - return Err(Error::new("Unable to create rocksdb read options. \ + return Err(Error::new("Unable to create RocksDB read options. \ This is a fairly trivial call, and its \ failure may be indicative of a \ - mis-compiled or mis-loaded rocksdb \ + mis-compiled or mis-loaded RocksDB \ library." .to_owned())); } @@ -513,10 +470,10 @@ impl DB { readopts: &ReadOptions) -> Result, Error> { if readopts.inner.is_null() { - return Err(Error::new("Unable to create rocksdb read options. \ + return Err(Error::new("Unable to create RocksDB read options. \ This is a fairly trivial call, and its \ failure may be indicative of a \ - mis-compiled or mis-loaded rocksdb \ + mis-compiled or mis-loaded RocksDB \ library." .to_owned())); } diff --git a/src/rocksdb_options.rs b/src/rocksdb_options.rs index c36a866..f134e4e 100644 --- a/src/rocksdb_options.rs +++ b/src/rocksdb_options.rs @@ -88,7 +88,7 @@ impl Default for BlockBasedOptions { fn default() -> BlockBasedOptions { let block_opts = unsafe { ffi::rocksdb_block_based_options_create() }; if block_opts.is_null() { - panic!("Could not create rocksdb block based options".to_owned()); + panic!("Could not create RocksDB block based options"); } BlockBasedOptions { inner: block_opts } } @@ -828,14 +828,13 @@ impl Default for Options { unsafe { let opts = ffi::rocksdb_options_create(); if opts.is_null() { - panic!("Could not create rocksdb options".to_owned()); + panic!("Could not create RocksDB options"); } Options { inner: opts } } } } - impl WriteOptions { pub fn new() -> WriteOptions { WriteOptions::default() @@ -858,7 +857,7 @@ impl Default for WriteOptions { fn default() -> WriteOptions { let write_opts = unsafe { ffi::rocksdb_writeoptions_create() }; if write_opts.is_null() { - panic!("Could not create rocksdb write options".to_owned()); + panic!("Could not create RocksDB write options"); } WriteOptions { inner: write_opts } }