Skip to content

Commit

Permalink
Lots of volatile database
Browse files Browse the repository at this point in the history
  • Loading branch information
maxfierrog committed Nov 1, 2024
1 parent 6c203d5 commit 77365ad
Show file tree
Hide file tree
Showing 9 changed files with 613 additions and 95 deletions.
2 changes: 1 addition & 1 deletion doc
Submodule doc updated from 2f08ba to 0843ed
16 changes: 12 additions & 4 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
use anyhow::Result;
use bitvec::prelude::{BitSlice, Msb0};

use std::path::Path;
use std::{
path::Path,
sync::{RwLockReadGuard, RwLockWriteGuard},
};

use crate::{model::State, solver::RecordType};

Expand Down Expand Up @@ -105,10 +108,15 @@ pub trait Persistent {
/// a database should be optimized for inter-table operations. In fact, this
/// interface's semantics are such that its implementations optimize performance
/// for cases of sequential operations on a single table.
pub trait Tabular {
pub trait Tabular<T> {
fn create_table(&self, id: &str, schema: Schema) -> Result<()>;
fn select_table(&self, id: &str) -> Result<()>;
fn delete_table(&self, id: &str) -> Result<()>;
fn delete_table(&mut self, id: &str) -> Result<()>;

fn get_table(&mut self, id: &str) -> Result<Option<RwLockReadGuard<T>>>;
fn get_table_mut(
&mut self,
id: &str,
) -> Result<Option<RwLockWriteGuard<T>>>;
}

/// Allows a database implementation to read raw data from a record buffer.
Expand Down
38 changes: 37 additions & 1 deletion src/database/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
//! #### Authorship
//! - Max Fierro, 2/24/2024 ([email protected])
use anyhow::anyhow;
use anyhow::Result;

use std::sync::Mutex;

use crate::database::error::DatabaseError;
use crate::database::Attribute;
use crate::database::Datatype;
Expand All @@ -33,7 +36,13 @@ pub struct SchemaIterator<'a> {
index: usize,
}

/* BUILDER IMPLEMENTATION */
/// Thread-safe generator for sequential database keys.
#[derive(Default)]
pub struct KeySequencer {
sequence_key: Mutex<u64>,
}

/* SCHEMA BUILDER IMPLEMENTATION */

impl SchemaBuilder {
/// Returns a new instance of a `SchemaBuilder`, which can be used to
Expand Down Expand Up @@ -123,6 +132,33 @@ impl SchemaBuilder {
}
}

/* KEY SEQUENCER IMPLEMENTATION */

impl KeySequencer {
/// Returns a new instance of `KeySequencer`, which can be used to generate
/// sequential database keys in a thread-safe manner. The first generated
/// key will be `initial`.
pub fn new(initial: u64) -> Self {
Self {
sequence_key: Mutex::new(initial),
}
}

/// Returns the next sequential database key in a thread-safe manner.
pub fn next(&self) -> Result<u64> {
let mut key = self
.sequence_key
.lock()
.map_err(|_| anyhow!("Key sequencer lock poisoned."))?;

{
let cur = *key;
*key += 1;
Ok(cur)
}
}
}

/* UTILITY IMPLEMENTATIONS */

impl ToString for Datatype {
Expand Down
3 changes: 3 additions & 0 deletions src/database/volatile/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! # Volatile Database Error
//!
//! TODO
148 changes: 59 additions & 89 deletions src/database/volatile/mod.rs
Original file line number Diff line number Diff line change
@@ -1,115 +1,85 @@
//! # Volatile Database
//!
//! This module provides a trivial database implementation backed by a volatile
//! in-memory hashmap.
//!
//! #### Authorship
//! - Max Fierro, 2/24/2024 ([email protected])
//! - Casey Stanford, 4/10/2024 ([email protected])
//! in-memory data structure arrangement.
use anyhow::Result;
use bitvec::{order::Msb0, prelude::*, slice::BitSlice, store::BitStore};

use std::collections::HashMap;
use std::sync::Arc;

use crate::{
database::{KVStore, Record, Schema, Tabular},
model::State,
solver::record::rem,
};
use crate::database::util::KeySequencer;
use resource::ResourceManager;
use transaction::TransactionManager;

/// [`KVStore`] implementation backed by an in-memory [`HashMap`].
/// Constrained by the space available in memory, growing at O(n) with the number of entries.
pub struct Database {
memory: HashMap<State, BitVec<u8, Msb0>>,
}
/* RE-EXPORTS */

impl Database {
pub fn initialize() -> Self {
Self {
memory: HashMap::new(),
}
}
}
pub use resource::Request;
pub use resource::Resource;
pub use transaction::Transaction;

impl KVStore for Database {
fn put<R: Record>(&mut self, key: State, value: &R) {
let new = BitVec::from(value.raw()).clone();
self.memory.insert(key, new);
}
/* MODULES */

fn get(&self, key: State) -> Option<&BitSlice<u8, Msb0>> {
if let Some(vect) = self.memory.get(&key) {
return Some(&vect[..]);
} else {
return None;
}
}
mod transaction;
mod resource;

fn del(&mut self, key: State) {
self.memory.remove(&key);
}
/* DEFINITIONS */

type SequenceKey = u64;
type TransactionID = SequenceKey;
type ResourceID = SequenceKey;

pub struct Database {
transaction_manager: Arc<TransactionManager>,
resource_manager: Arc<ResourceManager>,
sequencer: Arc<Sequencer>,
}

impl Tabular for Database {
fn create_table(&self, id: &str, schema: Schema) -> Result<()> {
todo!()
}
#[derive(Default)]
struct Sequencer {
transaction: KeySequencer,
resource: KeySequencer,
}

/* IMPLEMENTATION */

fn select_table(&self, id: &str) -> Result<()> {
todo!()
impl Sequencer {
pub fn next_transaction(&self) -> Result<TransactionID> {
self.transaction.next()
}

fn delete_table(&self, id: &str) -> Result<()> {
todo!()
pub fn next_resource(&self) -> Result<TransactionID> {
self.transaction.next()
}
}

#[cfg(test)]
mod tests {

use crate::database::volatile::tests::rem::RecordBuffer;

use super::*;

/// This test:
/// - Creates an example state test_state and Record test_rec.
/// - Checks that that test_state is initially not mapped in the database.
/// - Puts test_rec in the database, with test_state as its key.
/// - Checks that test_state now maps to test_rec.
#[test]
fn put_data_and_get_it() {
let mut db: Database = Database::initialize();
let test_state: State = 7;
assert!(db.get(test_state).is_none());
let test_rec: RecordBuffer = RecordBuffer::new().unwrap();
db.put(test_state, &test_rec);
if let Some(result_rec) = db.get(test_state) {
assert_eq!(result_rec, test_rec.raw());
} else {
assert_eq!(1, 0);
impl Database {
fn new() -> Self {
let sequencer = Arc::new(Sequencer::default());
let resource_manager = ResourceManager::new(sequencer.clone());
let transaction_manager = TransactionManager::new(
resource_manager.clone(),
sequencer.clone(),
);

Self {
transaction_manager,
resource_manager,
sequencer,
}
}

/// This test
/// - Creates an example state test_state and Record test_rec.
/// - Puts test_rec in the database, with test_state as its key.
/// - Deletes test_state and any associated Record.
/// - Checks that test_state now, again, maps to nothing.
/// - Puts test_rec BACK in the database, and confirms that test_state now maps to it once again.
#[test]
fn put_remove_and_put() {
let mut db: Database = Database::initialize();
let test_state: State = 7;
let test_rec: RecordBuffer = RecordBuffer::new().unwrap();
db.put(test_state, &test_rec);
db.del(test_state);
assert!(db.get(test_state).is_none());
db.put(test_state, &test_rec);
if let Some(result_rec) = db.get(test_state) {
assert_eq!(result_rec, test_rec.raw());
} else {
assert_eq!(1, 0);
fn create_transaction(&self, request: Request) -> Result<Arc<Transaction>> {
let transaction = self
.resource_manager
.initialize_transaction(
request,
self.transaction_manager.clone(),
)?;

{
self.transaction_manager
.add_transaction(transaction.clone());
Ok(transaction)
}
}
}
Loading

0 comments on commit 77365ad

Please sign in to comment.