Abstract HashMap into HTable (#146)

It is likely that we'll change the HashMap implementation in the future,
hence its best to hide away the HashMap to make sure we can easily
replace it.
next
Sayan Nandan 3 years ago committed by GitHub
parent 6ab73958f2
commit 74893c275e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,114 @@
/*
* Created on Sun May 09 2021
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2021, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
pub use std::collections::hash_map::Entry;
use std::collections::hash_map::Keys;
use std::collections::hash_map::Values;
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::FromIterator;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HTable<K, V>
where
K: Eq + Hash,
{
inner: HashMap<K, V>,
}
impl<K, V> HTable<K, V>
where
K: Eq + Hash,
{
pub fn new() -> Self {
HTable {
inner: HashMap::new(),
}
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn remove<Q>(&mut self, key: &Q) -> Option<(K, V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.remove_entry(key)
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.contains_key(key)
}
pub fn clear(&mut self) {
self.inner.clear()
}
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get(key)
}
pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
self.inner.entry(key)
}
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.inner.insert(k, v)
}
pub fn keys(&self) -> Keys<'_, K, V> {
self.inner.keys()
}
pub fn values(&self) -> Values<'_, K, V> {
self.inner.values()
}
}
impl<K: Eq + Hash, V> IntoIterator for HTable<K, V> {
type Item = (K, V);
type IntoIter = std::collections::hash_map::IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<K, V> FromIterator<(K, V)> for HTable<K, V>
where
K: Eq + Hash,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (K, V)>,
{
HTable {
inner: HashMap::from_iter(iter),
}
}
}

@ -40,9 +40,10 @@ use libsky::TResult;
use parking_lot::RwLock;
use parking_lot::RwLockReadGuard;
use parking_lot::RwLockWriteGuard;
use std::collections::HashMap;
use crate::coredb::htable::HTable;
use std::sync::Arc;
use tokio;
pub mod htable;
use tokio::sync::Notify;
#[macro_export]
@ -167,13 +168,13 @@ impl Shared {
}
}
/// The `Coretable` holds all the key-value pairs in a `HashMap`
/// The `Coretable` holds all the key-value pairs in a `HTable`
/// and the `terminate` field, which when set to true will cause all other
/// background tasks to terminate
#[derive(Debug)]
pub struct Coretable {
/// The core table contain key-value pairs
coremap: HashMap<String, Data>,
coremap: HTable<String, Data>,
/// The termination signal flag
pub terminate: bool,
/// Whether the database is poisoned or not
@ -184,12 +185,12 @@ pub struct Coretable {
}
impl Coretable {
/// Get a reference to the inner `HashMap`
pub const fn get_ref<'a>(&'a self) -> &'a HashMap<String, Data> {
/// Get a reference to the inner `HTable`
pub const fn get_ref<'a>(&'a self) -> &'a HTable<String, Data> {
&self.coremap
}
/// Get a **mutable** reference to the inner `HashMap`
pub fn get_mut_ref<'a>(&'a mut self) -> &'a mut HashMap<String, Data> {
/// Get a **mutable** reference to the inner `HTable`
pub fn get_mut_ref<'a>(&'a mut self) -> &'a mut HTable<String, Data> {
&mut self.coremap
}
}
@ -338,7 +339,7 @@ impl CoreDB {
shared: Arc::new(Shared {
bgsave_task: Notify::new(),
table: RwLock::new(Coretable {
coremap: HashMap::<String, Data>::new(),
coremap: HTable::<String, Data>::new(),
terminate: false,
poisoned: false,
}),
@ -380,12 +381,12 @@ impl CoreDB {
}
#[cfg(test)]
/// Get a deep copy of the `HashMap`
/// Get a deep copy of the `HTable`
///
/// **⚠ Do note**: This is super inefficient since it performs an actual
/// clone of the `HashMap` and doesn't do any `Arc`-business! This function
/// clone of the `HTable` and doesn't do any `Arc`-business! This function
/// can be used by test functions and the server, but **use with caution!**
pub fn get_hashmap_deep_clone(&self) -> HashMap<String, Data> {
pub fn get_HTable_deep_clone(&self) -> HTable<String, Data> {
(*self.acquire_read().get_ref()).clone()
}

@ -32,7 +32,7 @@ use crate::diskstore::snapshot::{DIR_OLD_SNAPSHOT, DIR_SNAPSHOT};
use bincode;
use bytes::Bytes;
use libsky::TResult;
use std::collections::HashMap;
use crate::coredb::htable::HTable;
use std::fs;
use std::io::{ErrorKind, Write};
use std::iter::FromIterator;
@ -54,7 +54,7 @@ lazy_static::lazy_static! {
pub static ref OLD_PATH: PathBuf = PathBuf::from("./data.bin");
}
fn get_snapshot(path: String) -> TResult<Option<HashMap<String, Data>>> {
fn get_snapshot(path: String) -> TResult<Option<HTable<String, Data>>> {
// the path just has the snapshot name, let's improve that
let mut snap_location = PathBuf::from(DIR_SNAPSHOT);
snap_location.push(&path);
@ -93,8 +93,8 @@ fn get_snapshot(path: String) -> TResult<Option<HashMap<String, Data>>> {
}
/// Try to get the saved data from disk. This returns `None`, if the `data/data.bin` wasn't found
/// otherwise the `data/data.bin` file is deserialized and parsed into a `HashMap`
pub fn get_saved(path: Option<String>) -> TResult<Option<HashMap<String, Data>>> {
/// otherwise the `data/data.bin` file is deserialized and parsed into a `HTable`
pub fn get_saved(path: Option<String>) -> TResult<Option<HTable<String, Data>>> {
if let Some(path) = path {
get_snapshot(path)
} else {
@ -139,12 +139,12 @@ pub fn get_saved(path: Option<String>) -> TResult<Option<HashMap<String, Data>>>
}
#[cfg(test)]
pub fn test_deserialize(file: Vec<u8>) -> TResult<HashMap<String, Data>> {
pub fn test_deserialize(file: Vec<u8>) -> TResult<HTable<String, Data>> {
deserialize(file)
}
fn deserialize(file: Vec<u8>) -> TResult<HashMap<String, Data>> {
fn deserialize(file: Vec<u8>) -> TResult<HTable<String, Data>> {
let parsed: DiskStoreFromDisk = bincode::deserialize(&file)?;
let parsed: HashMap<String, Data> = HashMap::from_iter(
let parsed: HTable<String, Data> = HTable::from_iter(
parsed
.0
.into_iter()
@ -161,20 +161,20 @@ fn deserialize(file: Vec<u8>) -> TResult<HashMap<String, Data>> {
///
/// This functions takes the entire in-memory table and writes it to the disk,
/// more specifically, the `data/data.bin` file
pub fn flush_data(file: &mut flock::FileLock, data: &HashMap<String, Data>) -> TResult<()> {
pub fn flush_data(file: &mut flock::FileLock, data: &HTable<String, Data>) -> TResult<()> {
let encoded = serialize(&data)?;
file.write(&encoded)?;
Ok(())
}
pub fn write_to_disk(file: &PathBuf, data: &HashMap<String, Data>) -> TResult<()> {
pub fn write_to_disk(file: &PathBuf, data: &HTable<String, Data>) -> TResult<()> {
let mut file = fs::File::create(&file)?;
let encoded = serialize(&data)?;
file.write_all(&encoded)?;
Ok(())
}
fn serialize(data: &HashMap<String, Data>) -> TResult<Vec<u8>> {
fn serialize(data: &HTable<String, Data>) -> TResult<Vec<u8>> {
let ds: DiskStoreFromMemory = (
data.keys().into_iter().collect(),
data.values().map(|val| val.get_inner_ref()).collect(),

@ -246,7 +246,7 @@ fn test_snapshot() {
let _ = snapengine.mksnap();
let current = snapengine.get_snapshots().next().unwrap();
let read_hmap = diskstore::test_deserialize(fs::read(PathBuf::from(current)).unwrap()).unwrap();
let dbhmap = db.get_hashmap_deep_clone();
let dbhmap = db.get_HTable_deep_clone();
assert_eq!(read_hmap, dbhmap);
snapengine.clearall().unwrap();
fs::remove_dir_all(ourdir).unwrap();

@ -49,7 +49,7 @@
use bincode;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::coredb::htable::HTable;
use std::error::Error;
use std::fs;
use std::io::prelude::*;
@ -60,7 +60,7 @@ pub trait IntoBinaryData: Serialize {
}
}
impl<'a> IntoBinaryData for &'a HashMap<String, Vec<u8>> {}
impl<'a> IntoBinaryData for &'a HTable<String, Vec<u8>> {}
#[derive(Serialize, Deserialize, Debug)]
/// The `PartMap` is a partition map which contains metadata about multiple partitions stored in a
@ -114,8 +114,8 @@ impl IntoIterator for PartMap {
///
/// This function creates two files: `snapstore.bin` and `snapstore.partmap`; the former is the data file
/// and the latter one is the partition map, or simply put, the partition metadata file. This function
/// accepts a `HashMap` of `HashMap`s with the key being the name of the partition.
pub fn flush_multi_ns<T>(ns: HashMap<&str, T>) -> Result<(), Box<dyn Error>>
/// accepts a `HTable` of `HTable`s with the key being the name of the partition.
pub fn flush_multi_ns<T>(ns: HTable<&str, T>) -> Result<(), Box<dyn Error>>
where
T: IntoBinaryData,
{
@ -157,16 +157,16 @@ where
/// 1. You should have a data file called 'snapstore.bin'
/// 2. You should have a partition map or partition metadata file called 'snapstore.partmap'
///
/// Once these requirements are met, the file will return a `HashMap` of named partitions which
/// Once these requirements are met, the file will return a `HTable` of named partitions which
/// can be used as required
pub fn unflush_multi_ns() -> Result<HashMap<String, HashMap<String, Vec<u8>>>, Box<dyn Error>> {
pub fn unflush_multi_ns() -> Result<HTable<String, HTable<String, Vec<u8>>>, Box<dyn Error>> {
// Try to read the partition map
let pmap: PartMap = bincode::deserialize(&fs::read("snapstore.partmap")?)?;
// Now read the data file
let mut file = fs::File::open("snapstore.bin")?;
// Get an iterator over the namespace data from the partition map
let mut map = pmap.into_iter();
let mut hmaps: HashMap<String, HashMap<String, Vec<u8>>> = HashMap::new();
let mut hmaps: HTable<String, HTable<String, Vec<u8>>> = HTable::new();
while let Some(partition) = map.next() {
// Create an empty buffer which will read precisely `len()` bytes from the file
let mut exact_op = vec![0; partition.len()];
@ -174,7 +174,7 @@ pub fn unflush_multi_ns() -> Result<HashMap<String, HashMap<String, Vec<u8>>>, B
file.read_exact(&mut exact_op)?;
// Deserialize this chunk
let tmp_map = bincode::deserialize(&exact_op)?;
// Insert the deserialized equivalent into our `HashMap` of `HashMap`s
// Insert the deserialized equivalent into our `HTable` of `HTable`s
hmaps.insert(partition.name, tmp_map);
}
Ok(hmaps)
@ -182,17 +182,17 @@ pub fn unflush_multi_ns() -> Result<HashMap<String, HashMap<String, Vec<u8>>>, B
#[test]
fn test_flush_multi_ns() {
let mut nsa = HashMap::new();
let mut nsa = HTable::new();
nsa.insert("my".to_owned(), "ohmy".to_owned().into_bytes());
nsa.insert("fly".to_owned(), "moondust".to_owned().into_bytes());
let mut nsb = HashMap::new();
let mut nsb = HTable::new();
nsb.insert("make".to_owned(), "melody".to_owned().into_bytes());
nsb.insert("aurora".to_owned(), "shower".to_owned().into_bytes());
let mut hm = HashMap::new();
let mut hm = HTable::new();
hm.insert("nsa", &nsa);
hm.insert("nsb", &nsb);
let _ = flush_multi_ns(hm).unwrap();
let mut hm_eq = HashMap::new();
let mut hm_eq = HTable::new();
hm_eq.insert("nsa".to_owned(), nsa);
hm_eq.insert("nsb".to_owned(), nsb);
let unflushed = unflush_multi_ns().unwrap();

@ -28,8 +28,7 @@ use crate::coredb::{self};
use crate::dbnet::connection::prelude::*;
use crate::protocol::responses;
use crate::resp::GroupBegin;
use std::collections::hash_map::Entry;
use crate::coredb::htable::Entry;
/// Run an `MSET` query
pub async fn mset<T, Strm>(

@ -28,8 +28,7 @@ use crate::coredb::{self};
use crate::dbnet::connection::prelude::*;
use crate::protocol::responses;
use crate::resp::GroupBegin;
use std::collections::hash_map::Entry;
use crate::coredb::htable::Entry;
/// Run an `MUPDATE` query
pub async fn mupdate<T, Strm>(

@ -31,8 +31,7 @@ use crate::coredb::{self};
use crate::dbnet::connection::prelude::*;
use crate::protocol::responses;
use coredb::Data;
use std::collections::hash_map::Entry;
use crate::coredb::htable::Entry;
use std::hint::unreachable_unchecked;
/// Run a `SET` query

@ -27,12 +27,11 @@
//! # `UPDATE` queries
//! This module provides functions to work with `UPDATE` queries
//!
use crate::coredb::htable::Entry;
use crate::coredb::{self};
use crate::dbnet::connection::prelude::*;
use crate::protocol::responses;
use coredb::Data;
use std::collections::hash_map::Entry;
use std::hint::unreachable_unchecked;
/// Run an `UPDATE` query

Loading…
Cancel
Save