AppFlowy/frontend/rust-lib/flowy-database/src/kv/kv.rs

224 lines
5.8 KiB
Rust
Raw Normal View History

use crate::kv::schema::{kv_table, kv_table::dsl, KV_SQL};
use ::diesel::{query_dsl::*, ExpressionMethods};
use diesel::{Connection, SqliteConnection};
use lazy_static::lazy_static;
use lib_sqlite::{DBConnection, Database, PoolConfig};
2021-11-09 07:32:57 +00:00
use std::{collections::HashMap, path::Path, sync::RwLock};
const DB_NAME: &str = "kv.db";
lazy_static! {
static ref KV_HOLDER: RwLock<KV> = RwLock::new(KV::new());
}
2021-09-03 04:44:48 +00:00
pub struct KV {
database: Option<Database>,
cache: HashMap<String, KeyValue>,
}
2021-09-03 04:44:48 +00:00
impl KV {
fn new() -> Self {
KV {
database: None,
cache: HashMap::new(),
}
}
fn set(value: KeyValue) -> Result<(), String> {
2021-12-08 13:51:06 +00:00
log::trace!("[KV]: set value: {:?}", value);
update_cache(value.clone());
let _ = diesel::replace_into(kv_table::table)
.values(&value)
.execute(&*(get_connection()?))
.map_err(|e| format!("KV set error: {:?}", e))?;
Ok(())
}
fn get(key: &str) -> Result<KeyValue, String> {
if let Some(value) = read_cache(key) {
return Ok(value);
}
let conn = get_connection()?;
let value = dsl::kv_table
.filter(kv_table::key.eq(key))
.first::<KeyValue>(&*conn)
.map_err(|e| format!("KV get error: {:?}", e))?;
update_cache(value.clone());
Ok(value)
}
#[allow(dead_code)]
pub fn remove(key: &str) -> Result<(), String> {
2021-11-09 10:21:34 +00:00
log::debug!("remove key: {}", key);
match KV_HOLDER.write() {
Ok(mut guard) => {
guard.cache.remove(key);
2022-01-23 04:14:00 +00:00
}
Err(e) => log::error!("Require write lock failed: {:?}", e),
};
let conn = get_connection()?;
let sql = dsl::kv_table.filter(kv_table::key.eq(key));
let _ = diesel::delete(sql)
.execute(&*conn)
.map_err(|e| format!("KV remove error: {:?}", e))?;
Ok(())
}
pub fn init(root: &str) -> Result<(), String> {
if !Path::new(root).exists() {
return Err(format!("Init KVStore failed. {} not exists", root));
}
let pool_config = PoolConfig::default();
let database = Database::new(root, DB_NAME, pool_config).unwrap();
let conn = database.get_connection().unwrap();
SqliteConnection::execute(&*conn, KV_SQL).unwrap();
let mut store = KV_HOLDER
.write()
.map_err(|e| format!("KVStore write failed: {:?}", e))?;
store.database = Some(database);
Ok(())
}
}
fn read_cache(key: &str) -> Option<KeyValue> {
match KV_HOLDER.read() {
Ok(guard) => guard.cache.get(key).cloned(),
Err(e) => {
log::error!("Require read lock failed: {:?}", e);
None
2022-01-23 04:14:00 +00:00
}
}
}
fn update_cache(value: KeyValue) {
match KV_HOLDER.write() {
Ok(mut guard) => {
guard.cache.insert(value.key.clone(), value);
2022-01-23 04:14:00 +00:00
}
Err(e) => log::error!("Require write lock failed: {:?}", e),
};
}
macro_rules! impl_get_func {
(
$func_name:ident,
$get_method:ident=>$target:ident
) => {
2021-09-03 04:44:48 +00:00
impl KV {
#[allow(dead_code)]
pub fn $func_name(k: &str) -> Option<$target> {
2021-09-03 04:44:48 +00:00
match KV::get(k) {
Ok(item) => item.$get_method,
Err(_) => None,
}
}
}
};
}
macro_rules! impl_set_func {
($func_name:ident,$set_method:ident,$key_type:ident) => {
2021-09-03 04:44:48 +00:00
impl KV {
#[allow(dead_code)]
pub fn $func_name(key: &str, value: $key_type) {
let mut item = KeyValue::new(key);
item.$set_method = Some(value);
2021-09-03 04:44:48 +00:00
match KV::set(item) {
2022-01-23 04:14:00 +00:00
Ok(_) => {}
Err(e) => {
log::error!("{:?}", e)
2022-01-23 04:14:00 +00:00
}
};
}
}
};
}
impl_set_func!(set_str, str_value, String);
impl_set_func!(set_bool, bool_value, bool);
impl_set_func!(set_int, int_value, i64);
impl_set_func!(set_float, float_value, f64);
impl_get_func!(get_str,str_value=>String);
impl_get_func!(get_int,int_value=>i64);
impl_get_func!(get_float,float_value=>f64);
impl_get_func!(get_bool,bool_value=>bool);
fn get_connection() -> Result<DBConnection, String> {
match KV_HOLDER.read() {
Ok(store) => {
let conn = store
.database
.as_ref()
.expect("KVStore is not init")
.get_connection()
.map_err(|e| format!("KVStore error: {:?}", e))?;
Ok(conn)
2022-01-23 04:14:00 +00:00
}
Err(e) => {
let msg = format!("KVStore get connection failed: {:?}", e);
log::error!("{:?}", msg);
Err(msg)
2022-01-23 04:14:00 +00:00
}
}
}
2021-12-12 13:18:23 +00:00
#[derive(Clone, Debug, Default, Queryable, Identifiable, Insertable, AsChangeset)]
#[table_name = "kv_table"]
#[primary_key(key)]
pub struct KeyValue {
pub key: String,
pub str_value: Option<String>,
pub int_value: Option<i64>,
pub float_value: Option<f64>,
pub bool_value: Option<bool>,
}
impl KeyValue {
pub fn new(key: &str) -> Self {
KeyValue {
key: key.to_string(),
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
2021-09-03 04:44:48 +00:00
use crate::kv::KV;
#[test]
fn kv_store_test() {
let dir = "./temp/";
if !std::path::Path::new(dir).exists() {
std::fs::create_dir_all(dir).unwrap();
}
2021-09-03 04:44:48 +00:00
KV::init(dir).unwrap();
2021-09-03 04:44:48 +00:00
KV::set_str("1", "hello".to_string());
assert_eq!(KV::get_str("1").unwrap(), "hello");
2021-09-03 04:44:48 +00:00
assert_eq!(KV::get_str("2"), None);
2021-09-03 04:44:48 +00:00
KV::set_bool("1", true);
assert_eq!(KV::get_bool("1").unwrap(), true);
2021-09-03 04:44:48 +00:00
assert_eq!(KV::get_bool("2"), None);
}
}