-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example showing one way of storing out of band values
- Loading branch information
Showing
1 changed file
with
120 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use redb::{ | ||
Database, Error, ReadableTable, RedbKey, RedbValue, Table, TableDefinition, WriteTransaction, | ||
}; | ||
use std::fs::{File, OpenOptions}; | ||
use std::io::{Read, Seek, SeekFrom, Write}; | ||
use std::marker::PhantomData; | ||
|
||
const TABLE: TableDefinition<u64, &str> = TableDefinition::new("my_data"); | ||
|
||
struct SpecialValuesDb { | ||
database: Database, | ||
file: File, | ||
} | ||
|
||
impl SpecialValuesDb { | ||
fn new() -> Self { | ||
SpecialValuesDb { | ||
database: Database::create("index.redb").unwrap(), | ||
file: OpenOptions::new() | ||
.write(true) | ||
.create(true) | ||
.read(true) | ||
.open("values.dat") | ||
.unwrap(), | ||
} | ||
} | ||
|
||
fn begin_txn(&mut self) -> SpecialValuesTransaction { | ||
SpecialValuesTransaction { | ||
inner: self.database.begin_write().unwrap(), | ||
file: &mut self.file, | ||
} | ||
} | ||
} | ||
|
||
struct SpecialValuesTransaction<'db> { | ||
inner: WriteTransaction<'db>, | ||
file: &'db mut File, | ||
} | ||
|
||
impl<'db> SpecialValuesTransaction<'db> { | ||
fn open_table<'txn, K: RedbKey + 'static, V: RedbValue + 'static>( | ||
&'txn mut self, | ||
table: TableDefinition<K, V>, | ||
) -> SpecialValuesTable<'db, 'txn, K, V> { | ||
let def: TableDefinition<K, (u64, u64)> = TableDefinition::new(table.name()); | ||
SpecialValuesTable { | ||
inner: self.inner.open_table(def).unwrap(), | ||
file: self.file, | ||
_value_type: Default::default(), | ||
} | ||
} | ||
|
||
fn commit(self) { | ||
self.file.sync_all().unwrap(); | ||
self.inner.commit().unwrap(); | ||
} | ||
} | ||
|
||
struct SpecialValuesTable<'db, 'txn, K: RedbKey + 'static, V: RedbValue + 'static> { | ||
inner: Table<'db, 'txn, K, (u64, u64)>, | ||
file: &'txn mut File, | ||
_value_type: PhantomData<V>, | ||
} | ||
|
||
impl<'db, 'txn, K: RedbKey + 'static, V: RedbValue + 'static> SpecialValuesTable<'db, 'txn, K, V> { | ||
fn insert(&mut self, key: K::SelfType<'_>, value: V::SelfType<'_>) { | ||
// Append to end of file | ||
let offset = self.file.seek(SeekFrom::End(0)).unwrap(); | ||
let value = V::as_bytes(&value); | ||
self.file.write_all(value.as_ref()).unwrap(); | ||
self.inner | ||
.insert(key, (offset, value.as_ref().len() as u64)) | ||
.unwrap(); | ||
} | ||
|
||
fn get(&mut self, key: K::SelfType<'_>) -> ValueAccessor<V> { | ||
let (offset, length) = self.inner.get(key).unwrap().unwrap().value(); | ||
self.file.seek(SeekFrom::Start(offset)).unwrap(); | ||
let mut data = vec![0u8; length as usize]; | ||
self.file.read_exact(data.as_mut_slice()).unwrap(); | ||
ValueAccessor { | ||
data, | ||
_value_type: Default::default(), | ||
} | ||
} | ||
} | ||
|
||
struct ValueAccessor<V: RedbValue + 'static> { | ||
data: Vec<u8>, | ||
_value_type: PhantomData<V>, | ||
} | ||
|
||
impl<V: RedbValue + 'static> ValueAccessor<V> { | ||
fn value(&self) -> V::SelfType<'_> { | ||
V::from_bytes(&self.data) | ||
} | ||
} | ||
|
||
/// redb is not designed to support very large values, or values with special requirements (such as alignment or mutability). | ||
/// There's a hard limit of slightly less than 4GiB per value, and performance is likely to be poor when mutating values above a few megabytes. | ||
/// Additionally, because redb is copy-on-write, mutating a value in-place is not possible, and therefore mutating large values is slow. | ||
/// Storing values with alignment requirements is also not supported. | ||
/// | ||
/// This example demonstrates one way to handle such values, via a sidecar file. | ||
fn main() -> Result<(), Error> { | ||
let mut db = SpecialValuesDb::new(); | ||
let mut txn = db.begin_txn(); | ||
{ | ||
let mut table = txn.open_table(TABLE); | ||
table.insert(0, "hello world"); | ||
} | ||
txn.commit(); | ||
|
||
let mut txn = db.begin_txn(); | ||
let mut table = txn.open_table(TABLE); | ||
assert_eq!(table.get(0).value(), "hello world"); | ||
|
||
Ok(()) | ||
} |