Implement safe mode backing store

Signed-off-by: Victor Porof <victor.porof@gmail.com>
without.crypto
Victor Porof 5 years ago
parent 1d9c11f9f4
commit 4a955a70a0
  1. 4
      Cargo.toml
  2. 9
      src/backend.rs
  3. 42
      src/backend/impl_safe.rs
  4. 69
      src/backend/impl_safe/cursor.rs
  5. 116
      src/backend/impl_safe/database.rs
  6. 186
      src/backend/impl_safe/environment.rs
  7. 65
      src/backend/impl_safe/error.rs
  8. 128
      src/backend/impl_safe/flags.rs
  9. 35
      src/backend/impl_safe/info.rs
  10. 24
      src/backend/impl_safe/iter.rs
  11. 39
      src/backend/impl_safe/stat.rs
  12. 159
      src/backend/impl_safe/transaction.rs
  13. 17
      src/error.rs

@ -14,8 +14,9 @@ categories = ["database"]
exclude = ["/tests/envs/*"]
[features]
default = []
default = ["with-safe-mode"]
backtrace = ["failure/backtrace", "failure/std"]
with-safe-mode = ["log", "uuid/v4", "uuid/serde", "serde/derive", "serde/rc"]
with-asan = ["lmdb-rkv/with-asan"]
with-fuzzer = ["lmdb-rkv/with-fuzzer"]
with-fuzzer-no-link = ["lmdb-rkv/with-fuzzer-no-link"]
@ -27,6 +28,7 @@ bitflags = "1"
byteorder = "1"
lazy_static = "1.0"
lmdb-rkv = "0.12.3"
log = { version = "0.4", optional = true }
ordered-float = "1.0"
uuid = "0.7"
serde = "1.0"

@ -10,6 +10,7 @@
mod common;
mod impl_lmdb;
mod impl_safe;
mod traits;
pub use common::*;
@ -22,3 +23,11 @@ pub use impl_lmdb::ErrorImpl as LmdbError;
pub use impl_lmdb::RoCursorImpl as LmdbRoCursor;
pub use impl_lmdb::RoTransactionImpl as LmdbRoTransaction;
pub use impl_lmdb::RwTransactionImpl as LmdbRwTransaction;
pub use impl_safe::DatabaseImpl as SafeModeDatabase;
pub use impl_safe::EnvironmentBuilderImpl as SafeMode;
pub use impl_safe::EnvironmentImpl as SafeModeEnvironment;
pub use impl_safe::ErrorImpl as SafeModeError;
pub use impl_safe::RoCursorImpl as SafeModeRoCursor;
pub use impl_safe::RoTransactionImpl as SafeModeRoTransaction;
pub use impl_safe::RwTransactionImpl as SafeModeRwTransaction;

@ -0,0 +1,42 @@
// Copyright 2018-2019 Mozilla
//
// 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.
mod cursor;
mod database;
mod environment;
mod error;
mod flags;
mod info;
mod iter;
mod stat;
mod transaction;
pub use cursor::{
RoCursorImpl,
RwCursorImpl,
};
pub use database::DatabaseImpl;
pub use environment::{
EnvironmentBuilderImpl,
EnvironmentImpl,
};
pub use error::ErrorImpl;
pub use flags::{
DatabaseFlagsImpl,
EnvironmentFlagsImpl,
WriteFlagsImpl,
};
pub use info::InfoImpl;
pub use iter::IterImpl;
pub use stat::StatImpl;
pub use transaction::{
RoTransactionImpl,
RwTransactionImpl,
};

@ -0,0 +1,69 @@
// Copyright 2018-2019 Mozilla
//
// 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 super::{
database::Snapshot,
IterImpl,
};
use crate::backend::traits::BackendRoCursor;
#[derive(Debug)]
pub struct RoCursorImpl<'env>(pub(crate) &'env Snapshot);
impl<'env> BackendRoCursor<'env> for RoCursorImpl<'env> {
type Iter = IterImpl<'env>;
fn iter(&mut self) -> Self::Iter {
IterImpl(Box::new(self.0.iter()))
}
fn iter_from<K>(&mut self, key: K) -> Self::Iter
where
K: AsRef<[u8]>,
{
// FIXME: Don't allocate.
let key = key.as_ref().to_vec();
IterImpl(Box::new(self.0.iter().skip_while(move |&(k, _)| k != &key[..])))
}
fn iter_dup_of<K>(&mut self, key: K) -> Self::Iter
where
K: AsRef<[u8]>,
{
// FIXME: Don't allocate.
let key = key.as_ref().to_vec();
IterImpl(Box::new(self.0.iter().filter(move |&(k, _)| k == &key[..])))
}
}
#[derive(Debug)]
pub struct RwCursorImpl<'env>(&'env mut Snapshot);
impl<'env> BackendRoCursor<'env> for RwCursorImpl<'env> {
type Iter = IterImpl<'env>;
fn iter(&mut self) -> Self::Iter {
unimplemented!()
}
fn iter_from<K>(&mut self, _key: K) -> Self::Iter
where
K: AsRef<[u8]>,
{
unimplemented!()
}
fn iter_dup_of<K>(&mut self, _key: K) -> Self::Iter
where
K: AsRef<[u8]>,
{
unimplemented!()
}
}

@ -0,0 +1,116 @@
// Copyright 2018-2019 Mozilla
//
// 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::collections::{
BTreeSet,
HashMap,
};
use std::sync::{
Arc,
RwLock,
};
use serde_derive::{
Deserialize,
Serialize,
};
use uuid::Uuid;
use super::{
DatabaseFlagsImpl,
ErrorImpl,
};
use crate::backend::traits::BackendDatabase;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseImpl {
id: Uuid,
flags: DatabaseFlagsImpl,
snapshot: Arc<RwLock<Snapshot>>,
}
impl DatabaseImpl {
pub(crate) fn new(flags: Option<DatabaseFlagsImpl>, snapshot: Option<Snapshot>) -> DatabaseImpl {
DatabaseImpl {
id: Uuid::new_v4(),
flags: flags.unwrap_or_else(DatabaseFlagsImpl::default),
snapshot: Arc::new(RwLock::new(snapshot.unwrap_or_else(Snapshot::new))),
}
}
pub(crate) fn id(&self) -> &Uuid {
&self.id
}
pub(crate) fn flags(&self) -> &DatabaseFlagsImpl {
&self.flags
}
pub(crate) fn snapshot(&self) -> Result<Snapshot, ErrorImpl> {
let snapshot = self.snapshot.read().map_err(|_| ErrorImpl::TxnPoisonError)?;
Ok(snapshot.clone())
}
pub(crate) fn replace(&mut self, value: Snapshot) -> Result<Snapshot, ErrorImpl> {
let mut snapshot = self.snapshot.write().map_err(|_| ErrorImpl::TxnPoisonError)?;
Ok(std::mem::replace(&mut snapshot, value))
}
}
impl BackendDatabase for DatabaseImpl {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Snapshot {
map: HashMap<Box<[u8]>, BTreeSet<Box<[u8]>>>,
}
impl Snapshot {
pub(crate) fn new() -> Snapshot {
Snapshot {
map: HashMap::new(),
}
}
pub(crate) fn get(&self, key: &[u8]) -> Option<&[u8]> {
self.map.get(key).and_then(|v| v.iter().next()).map(|v| v.as_ref())
}
pub(crate) fn put_one(&mut self, key: &[u8], value: &[u8]) {
let values = self.map.entry(Box::from(key)).or_insert_with(BTreeSet::new);
values.clear();
values.insert(Box::from(value));
}
pub(crate) fn put_dup(&mut self, key: &[u8], value: &[u8]) {
let values = self.map.entry(Box::from(key)).or_insert_with(BTreeSet::new);
values.insert(Box::from(value));
}
pub(crate) fn del_exact(&mut self, key: &[u8], value: &[u8]) -> Option<()> {
let values = self.map.entry(Box::from(key)).or_insert_with(BTreeSet::new);
let was_removed = values.remove(value);
Some(()).filter(|_| was_removed)
}
pub(crate) fn del_all(&mut self, key: &[u8]) -> Option<()> {
let values = self.map.entry(Box::from(key)).or_insert_with(BTreeSet::new);
let was_empty = values.is_empty();
values.clear();
Some(()).filter(|_| !was_empty)
}
pub(crate) fn clear(&mut self) {
self.map.clear();
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (&[u8], &[u8])> {
self.map.iter().flat_map(|(key, values)| values.iter().map(move |value| (key.as_ref(), value.as_ref())))
}
}

@ -0,0 +1,186 @@
// Copyright 2018-2019 Mozilla
//
// 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::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::path::{
Path,
PathBuf,
};
use std::sync::{
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
};
use log::warn;
use super::{
DatabaseFlagsImpl,
DatabaseImpl,
EnvironmentFlagsImpl,
ErrorImpl,
InfoImpl,
RoTransactionImpl,
RwTransactionImpl,
StatImpl,
};
use crate::backend::traits::{
BackendEnvironment,
BackendEnvironmentBuilder,
};
const DEFAULT_DB_FILENAME: &str = "data.safe.bin";
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct EnvironmentBuilderImpl {
flags: EnvironmentFlagsImpl,
}
impl<'env> BackendEnvironmentBuilder<'env> for EnvironmentBuilderImpl {
type Error = ErrorImpl;
type Environment = EnvironmentImpl;
type Flags = EnvironmentFlagsImpl;
fn new() -> EnvironmentBuilderImpl {
EnvironmentBuilderImpl {
flags: EnvironmentFlagsImpl::empty(),
}
}
fn set_flags<T>(&mut self, flags: T) -> &mut Self
where
T: Into<Self::Flags>,
{
self.flags = flags.into();
self
}
fn set_max_readers(&mut self, max_readers: u32) -> &mut Self {
warn!("Ignoring `set_max_readers({})`", max_readers);
self
}
fn set_max_dbs(&mut self, max_dbs: u32) -> &mut Self {
warn!("Ignoring `set_max_dbs({})`", max_dbs);
self
}
fn set_map_size(&mut self, size: usize) -> &mut Self {
warn!("Ignoring `set_map_size({})`", size);
self
}
fn open(&self, path: &Path) -> Result<Self::Environment, Self::Error> {
let mut env = EnvironmentImpl::new(path, self.flags)?;
env.read_from_disk()?;
Ok(env)
}
}
#[derive(Debug)]
pub struct EnvironmentImpl {
path: PathBuf,
dbs: RwLock<HashMap<Option<String>, DatabaseImpl>>,
}
impl EnvironmentImpl {
pub(crate) fn new(path: &Path, _flags: EnvironmentFlagsImpl) -> Result<EnvironmentImpl, ErrorImpl> {
Ok(EnvironmentImpl {
path: path.to_path_buf(),
dbs: RwLock::new(HashMap::new()),
})
}
pub(crate) fn read_from_disk(&mut self) -> Result<(), ErrorImpl> {
let mut path = Cow::from(&self.path);
if fs::metadata(&path)?.is_dir() {
path.to_mut().push(DEFAULT_DB_FILENAME);
};
if fs::metadata(&path).is_err() {
fs::write(&path, bincode::serialize(&self.dbs)?)?;
};
let serialized = fs::read(&path)?;
self.dbs = bincode::deserialize(&serialized)?;
Ok(())
}
pub(crate) fn write_to_disk(&self) -> Result<(), ErrorImpl> {
let mut path = Cow::from(&self.path);
if fs::metadata(&path)?.is_dir() {
path.to_mut().push(DEFAULT_DB_FILENAME);
};
fs::write(&path, bincode::serialize(&self.dbs)?)?;
Ok(())
}
pub(crate) fn dbs(&self) -> Result<RwLockReadGuard<HashMap<Option<String>, DatabaseImpl>>, ErrorImpl> {
self.dbs.read().map_err(|_| ErrorImpl::DbPoisonError)
}
pub(crate) fn dbs_mut(&self) -> Result<RwLockWriteGuard<HashMap<Option<String>, DatabaseImpl>>, ErrorImpl> {
self.dbs.write().map_err(|_| ErrorImpl::DbPoisonError)
}
}
impl<'env> BackendEnvironment<'env> for EnvironmentImpl {
type Error = ErrorImpl;
type Database = DatabaseImpl;
type Flags = DatabaseFlagsImpl;
type Stat = StatImpl;
type Info = InfoImpl;
type RoTransaction = RoTransactionImpl<'env>;
type RwTransaction = RwTransactionImpl<'env>;
fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error> {
// TOOD: don't reallocate `name`.
let dbs = self.dbs.read().map_err(|_| ErrorImpl::DbPoisonError)?;
let db = dbs.get(&name.map(String::from)).ok_or(ErrorImpl::DbNotFoundError)?.clone();
Ok(db)
}
fn create_db(&self, name: Option<&str>, flags: Self::Flags) -> Result<Self::Database, Self::Error> {
// TOOD: don't reallocate `name`.
let mut dbs = self.dbs.write().map_err(|_| ErrorImpl::DbPoisonError)?;
let db = dbs.entry(name.map(String::from)).or_insert_with(|| DatabaseImpl::new(Some(flags), None)).clone();
Ok(db)
}
fn begin_ro_txn(&'env self) -> Result<Self::RoTransaction, Self::Error> {
RoTransactionImpl::new(self)
}
fn begin_rw_txn(&'env self) -> Result<Self::RwTransaction, Self::Error> {
RwTransactionImpl::new(self)
}
fn sync(&self, force: bool) -> Result<(), Self::Error> {
warn!("Ignoring `force={}`", force);
self.write_to_disk()
}
fn stat(&self) -> Result<Self::Stat, Self::Error> {
Ok(StatImpl)
}
fn info(&self) -> Result<Self::Info, Self::Error> {
Ok(InfoImpl)
}
fn freelist(&self) -> Result<usize, Self::Error> {
unimplemented!()
}
fn set_map_size(&self, size: usize) -> Result<(), Self::Error> {
warn!("Ignoring `set_map_size({})`", size);
Ok(())
}
}

@ -0,0 +1,65 @@
// Copyright 2018-2019 Mozilla
//
// 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::fmt;
use std::io;
use bincode::Error as BincodeError;
use crate::backend::traits::BackendError;
use crate::error::StoreError;
#[derive(Debug)]
pub enum ErrorImpl {
KeyValuePairNotFound,
DbPoisonError,
DbNotFoundError,
DbIsForeignError,
TxnPoisonError,
IoError(io::Error),
BincodeError(BincodeError),
}
impl BackendError for ErrorImpl {}
impl fmt::Display for ErrorImpl {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
ErrorImpl::KeyValuePairNotFound => write!(fmt, "KeyValuePairNotFound (safe mode)"),
ErrorImpl::DbPoisonError => write!(fmt, "DbPoisonError (safe mode)"),
ErrorImpl::DbNotFoundError => write!(fmt, "DbNotFoundError (safe mode)"),
ErrorImpl::DbIsForeignError => write!(fmt, "DbIsForeignError (safe mode)"),
ErrorImpl::TxnPoisonError => write!(fmt, "TxnPoisonError (safe mode)"),
ErrorImpl::IoError(e) => e.fmt(fmt),
ErrorImpl::BincodeError(e) => e.fmt(fmt),
}
}
}
impl Into<StoreError> for ErrorImpl {
fn into(self) -> StoreError {
match self {
ErrorImpl::KeyValuePairNotFound => StoreError::KeyValuePairNotFound,
_ => StoreError::SafeModeError(self),
}
}
}
impl From<io::Error> for ErrorImpl {
fn from(e: io::Error) -> ErrorImpl {
ErrorImpl::IoError(e)
}
}
impl From<BincodeError> for ErrorImpl {
fn from(e: BincodeError) -> ErrorImpl {
ErrorImpl::BincodeError(e)
}
}

@ -0,0 +1,128 @@
// Copyright 2018-2019 Mozilla
//
// 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 bitflags::bitflags;
use serde_derive::{
Deserialize,
Serialize,
};
use crate::backend::common::{
DatabaseFlags,
EnvironmentFlags,
WriteFlags,
};
use crate::backend::traits::{
BackendDatabaseFlags,
BackendEnvironmentFlags,
BackendFlags,
BackendWriteFlags,
};
bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct EnvironmentFlagsImpl: u32 {
const NIL = 0b0000_0000;
}
}
impl BackendFlags for EnvironmentFlagsImpl {
fn empty() -> EnvironmentFlagsImpl {
EnvironmentFlagsImpl::empty()
}
}
impl BackendEnvironmentFlags for EnvironmentFlagsImpl {
fn set(&mut self, flag: EnvironmentFlags, value: bool) {
self.set(flag.into(), value)
}
}
impl Into<EnvironmentFlagsImpl> for EnvironmentFlags {
fn into(self) -> EnvironmentFlagsImpl {
match self {
EnvironmentFlags::FIXED_MAP => unimplemented!(),
EnvironmentFlags::NO_SUB_DIR => unimplemented!(),
EnvironmentFlags::WRITE_MAP => unimplemented!(),
EnvironmentFlags::READ_ONLY => unimplemented!(),
EnvironmentFlags::NO_META_SYNC => unimplemented!(),
EnvironmentFlags::NO_SYNC => unimplemented!(),
EnvironmentFlags::MAP_ASYNC => unimplemented!(),
EnvironmentFlags::NO_TLS => unimplemented!(),
EnvironmentFlags::NO_LOCK => unimplemented!(),
EnvironmentFlags::NO_READAHEAD => unimplemented!(),
EnvironmentFlags::NO_MEM_INIT => unimplemented!(),
}
}
}
bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct DatabaseFlagsImpl: u32 {
const DUP_SORT = 0b0000_0001;
const INTEGER_KEY = 0b0000_0010;
}
}
impl BackendFlags for DatabaseFlagsImpl {
fn empty() -> DatabaseFlagsImpl {
DatabaseFlagsImpl::empty()
}
}
impl BackendDatabaseFlags for DatabaseFlagsImpl {
fn set(&mut self, flag: DatabaseFlags, value: bool) {
self.set(flag.into(), value)
}
}
impl Into<DatabaseFlagsImpl> for DatabaseFlags {
fn into(self) -> DatabaseFlagsImpl {
match self {
DatabaseFlags::REVERSE_KEY => unimplemented!(),
DatabaseFlags::DUP_SORT => DatabaseFlagsImpl::DUP_SORT,
DatabaseFlags::INTEGER_KEY => DatabaseFlagsImpl::INTEGER_KEY,
DatabaseFlags::DUP_FIXED => unimplemented!(),
DatabaseFlags::INTEGER_DUP => unimplemented!(),
DatabaseFlags::REVERSE_DUP => unimplemented!(),
}
}
}
bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct WriteFlagsImpl: u32 {
const NIL = 0b0000_0000;
}
}
impl BackendFlags for WriteFlagsImpl {
fn empty() -> WriteFlagsImpl {
WriteFlagsImpl::empty()
}
}
impl BackendWriteFlags for WriteFlagsImpl {
fn set(&mut self, flag: WriteFlags, value: bool) {
self.set(flag.into(), value)
}
}
impl Into<WriteFlagsImpl> for WriteFlags {
fn into(self) -> WriteFlagsImpl {
match self {
WriteFlags::NO_OVERWRITE => unimplemented!(),
WriteFlags::NO_DUP_DATA => unimplemented!(),
WriteFlags::CURRENT => unimplemented!(),
WriteFlags::APPEND => unimplemented!(),
WriteFlags::APPEND_DUP => unimplemented!(),
}
}
}

@ -0,0 +1,35 @@
// Copyright 2018-2019 Mozilla
//
// 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 crate::backend::traits::BackendInfo;
pub struct InfoImpl;
impl BackendInfo for InfoImpl {
fn map_size(&self) -> usize {
unimplemented!()
}
fn last_pgno(&self) -> usize {
unimplemented!()
}
fn last_txnid(&self) -> usize {
unimplemented!()
}
fn max_readers(&self) -> usize {
unimplemented!()
}
fn num_readers(&self) -> usize {
unimplemented!()
}
}

@ -0,0 +1,24 @@
// Copyright 2018-2019 Mozilla
//
// 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 super::ErrorImpl;
use crate::backend::traits::BackendIter;
// FIXME: Use generics instead.
pub struct IterImpl<'env>(pub(crate) Box<dyn Iterator<Item = (&'env [u8], &'env [u8])> + 'env>);
impl<'env> BackendIter<'env> for IterImpl<'env> {
type Error = ErrorImpl;
#[allow(clippy::type_complexity)]
fn next(&mut self) -> Option<Result<(&'env [u8], &'env [u8]), Self::Error>> {
self.0.next().map(Ok)
}
}

@ -0,0 +1,39 @@
// Copyright 2018-2019 Mozilla
//
// 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 crate::backend::traits::BackendStat;
pub struct StatImpl;
impl BackendStat for StatImpl {
fn page_size(&self) -> usize {
unimplemented!()
}
fn depth(&self) -> usize {
unimplemented!()
}
fn branch_pages(&self) -> usize {
unimplemented!()
}
fn leaf_pages(&self) -> usize {
unimplemented!()
}
fn overflow_pages(&self) -> usize {
unimplemented!()
}
fn entries(&self) -> usize {
unimplemented!()
}
}

@ -0,0 +1,159 @@
// Copyright 2018-2019 Mozilla
//
// 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::collections::HashMap;
use uuid::Uuid;
use super::{
database::Snapshot,
DatabaseFlagsImpl,
DatabaseImpl,
EnvironmentImpl,
ErrorImpl,
RoCursorImpl,
WriteFlagsImpl,
};
use crate::backend::traits::{
BackendRoCursorTransaction,
BackendRoTransaction,
BackendRwCursorTransaction,
BackendRwTransaction,
};
#[derive(Debug)]
pub struct RoTransactionImpl<'env> {
env: &'env EnvironmentImpl,
snapshots: HashMap<Uuid, Result<Snapshot, ErrorImpl>>,
}
impl<'env> RoTransactionImpl<'env> {
pub(crate) fn new(env: &'env EnvironmentImpl) -> Result<RoTransactionImpl<'env>, ErrorImpl> {
let snapshots = env.dbs()?.iter().map(|(_, db)| (*db.id(), db.snapshot())).collect();
Ok(RoTransactionImpl {
env,
snapshots,
})
}
}
impl<'env> BackendRoTransaction for RoTransactionImpl<'env> {
type Error = ErrorImpl;
type Database = DatabaseImpl;
type Flags = WriteFlagsImpl;
fn get(&self, db: &Self::Database, key: &[u8]) -> Result<&[u8], Self::Error> {
let snapshot = self.snapshots.get(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_ref().map_err(|_| ErrorImpl::TxnPoisonError)?;
data.get(key).ok_or_else(|| ErrorImpl::KeyValuePairNotFound)
}
fn abort(self) {
// noop
}
}
impl<'env> BackendRoCursorTransaction<'env> for RoTransactionImpl<'env> {
type RoCursor = RoCursorImpl<'env>;
fn open_ro_cursor(&'env self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
let snapshot = self.snapshots.get(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_ref().map_err(|_| ErrorImpl::TxnPoisonError)?;
Ok(RoCursorImpl(data))
}
}
#[derive(Debug)]
pub struct RwTransactionImpl<'env> {
env: &'env EnvironmentImpl,
snapshots: HashMap<Uuid, Result<Snapshot, ErrorImpl>>,
}
impl<'env> RwTransactionImpl<'env> {
pub(crate) fn new(env: &'env EnvironmentImpl) -> Result<RwTransactionImpl<'env>, ErrorImpl> {
let snapshots = env.dbs()?.iter().map(|(_, db)| (*db.id(), db.snapshot())).collect();
Ok(RwTransactionImpl {
env,
snapshots,
})
}
}
impl<'env> BackendRwTransaction for RwTransactionImpl<'env> {
type Error = ErrorImpl;
type Database = DatabaseImpl;
type Flags = WriteFlagsImpl;
fn get(&self, db: &Self::Database, key: &[u8]) -> Result<&[u8], Self::Error> {
let snapshot = self.snapshots.get(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_ref().map_err(|_| ErrorImpl::TxnPoisonError)?;
data.get(key).ok_or_else(|| ErrorImpl::KeyValuePairNotFound)
}
fn put(&mut self, db: &Self::Database, key: &[u8], value: &[u8], _flags: Self::Flags) -> Result<(), Self::Error> {
let snapshot = self.snapshots.get_mut(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_mut().map_err(|_| ErrorImpl::TxnPoisonError)?;
if db.flags().contains(DatabaseFlagsImpl::DUP_SORT) {
data.put_dup(key, value);
} else {
data.put_one(key, value);
}
Ok(())
}
fn del(&mut self, db: &Self::Database, key: &[u8], value: Option<&[u8]>) -> Result<(), Self::Error> {
let snapshot = self.snapshots.get_mut(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_mut().map_err(|_| ErrorImpl::TxnPoisonError)?;
let deleted = match (value, db.flags()) {
(Some(value), flags) if flags.contains(DatabaseFlagsImpl::DUP_SORT) => data.del_exact(key, value),
_ => data.del_all(key),
};
Ok(deleted.ok_or_else(|| ErrorImpl::KeyValuePairNotFound)?)
}
fn clear_db(&mut self, db: &Self::Database) -> Result<(), Self::Error> {
let snapshot = self.snapshots.get_mut(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_mut().map_err(|_| ErrorImpl::TxnPoisonError)?;
data.clear();
Ok(())
}
fn commit(self) -> Result<(), Self::Error> {
let mut dbs = self.env.dbs_mut()?;
for (id, snapshot) in self.snapshots {
match dbs.iter_mut().find(|(_, db)| db.id() == &id) {
Some((_, db)) => {
db.replace(snapshot?)?;
},
None => {
unreachable!();
},
}
}
drop(dbs);
self.env.write_to_disk()
}
fn abort(self) {
// noop
}
}
impl<'env> BackendRwCursorTransaction<'env> for RwTransactionImpl<'env> {
type RoCursor = RoCursorImpl<'env>;
fn open_ro_cursor(&'env self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
let snapshot = self.snapshots.get(db.id()).ok_or_else(|| ErrorImpl::DbIsForeignError)?;
let data = snapshot.as_ref().map_err(|_| ErrorImpl::TxnPoisonError)?;
Ok(RoCursorImpl(data))
}
}

@ -17,6 +17,7 @@ use std::thread::ThreadId;
use failure::Fail;
pub use crate::backend::SafeModeError;
use crate::value::Type;
#[derive(Debug, Fail)]
@ -69,9 +70,12 @@ pub enum StoreError {
#[fail(display = "data error: {:?}", _0)]
DataError(DataError),
#[fail(display = "lmdb error: {}", _0)]
#[fail(display = "lmdb backend error: {}", _0)]
LmdbError(lmdb::Error),
#[fail(display = "safe mode backend error: {}", _0)]
SafeModeError(SafeModeError),
#[fail(display = "read transaction already exists in thread {:?}", _0)]
ReadTransactionAlreadyExists(ThreadId),
@ -136,9 +140,12 @@ pub enum MigrateError {
#[fail(display = "invalid page number")]
InvalidPageNum,
#[fail(display = "lmdb error: {}", _0)]
#[fail(display = "lmdb backend error: {}", _0)]
LmdbError(lmdb::Error),
#[fail(display = "safe mode backend error: {}", _0)]
SafeModeError(SafeModeError),
#[fail(display = "string conversion error")]
StringConversionError,
@ -193,3 +200,9 @@ impl From<lmdb::Error> for MigrateError {
MigrateError::LmdbError(e)
}
}
impl From<SafeModeError> for MigrateError {
fn from(e: SafeModeError) -> MigrateError {
MigrateError::SafeModeError(e)
}
}

Loading…
Cancel
Save