mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
mv kv to flowy-database
This commit is contained in:
223
frontend/rust-lib/flowy-database/src/kv/kv.rs
Normal file
223
frontend/rust-lib/flowy-database/src/kv/kv.rs
Normal file
@ -0,0 +1,223 @@
|
||||
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};
|
||||
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());
|
||||
}
|
||||
|
||||
pub struct KV {
|
||||
database: Option<Database>,
|
||||
cache: HashMap<String, KeyValue>,
|
||||
}
|
||||
|
||||
impl KV {
|
||||
fn new() -> Self {
|
||||
KV {
|
||||
database: None,
|
||||
cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(value: KeyValue) -> Result<(), String> {
|
||||
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> {
|
||||
log::debug!("remove key: {}", key);
|
||||
match KV_HOLDER.write() {
|
||||
Ok(mut guard) => {
|
||||
guard.cache.remove(key);
|
||||
},
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn update_cache(value: KeyValue) {
|
||||
match KV_HOLDER.write() {
|
||||
Ok(mut guard) => {
|
||||
guard.cache.insert(value.key.clone(), value);
|
||||
},
|
||||
Err(e) => log::error!("Require write lock failed: {:?}", e),
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_get_func {
|
||||
(
|
||||
$func_name:ident,
|
||||
$get_method:ident=>$target:ident
|
||||
) => {
|
||||
impl KV {
|
||||
#[allow(dead_code)]
|
||||
pub fn $func_name(k: &str) -> Option<$target> {
|
||||
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) => {
|
||||
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);
|
||||
match KV::set(item) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
},
|
||||
Err(e) => {
|
||||
let msg = format!("KVStore get connection failed: {:?}", e);
|
||||
log::error!("{:?}", msg);
|
||||
Err(msg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
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();
|
||||
}
|
||||
|
||||
KV::init(dir).unwrap();
|
||||
|
||||
KV::set_str("1", "hello".to_string());
|
||||
assert_eq!(KV::get_str("1").unwrap(), "hello");
|
||||
|
||||
assert_eq!(KV::get_str("2"), None);
|
||||
|
||||
KV::set_bool("1", true);
|
||||
assert_eq!(KV::get_bool("1").unwrap(), true);
|
||||
|
||||
assert_eq!(KV::get_bool("2"), None);
|
||||
}
|
||||
}
|
6
frontend/rust-lib/flowy-database/src/kv/mod.rs
Normal file
6
frontend/rust-lib/flowy-database/src/kv/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
mod kv;
|
||||
mod schema;
|
||||
|
||||
pub use kv::*;
|
20
frontend/rust-lib/flowy-database/src/kv/schema.rs
Normal file
20
frontend/rust-lib/flowy-database/src/kv/schema.rs
Normal file
@ -0,0 +1,20 @@
|
||||
#[allow(dead_code)]
|
||||
pub const KV_SQL: &str = r#"
|
||||
CREATE TABLE IF NOT EXISTS kv_table (
|
||||
key TEXT NOT NULL PRIMARY KEY,
|
||||
str_value TEXT,
|
||||
int_value BIGINT,
|
||||
float_value DOUBLE,
|
||||
bool_value BOOLEAN
|
||||
);
|
||||
"#;
|
||||
|
||||
table! {
|
||||
kv_table (key) {
|
||||
key -> Text,
|
||||
str_value -> Nullable<Text>,
|
||||
int_value -> Nullable<BigInt>,
|
||||
float_value -> Nullable<Double>,
|
||||
bool_value -> Nullable<Bool>,
|
||||
}
|
||||
}
|
@ -1,3 +1,12 @@
|
||||
pub use diesel::*;
|
||||
pub use diesel_derives::*;
|
||||
use diesel_migrations::*;
|
||||
use std::{fmt::Debug, io, path::Path};
|
||||
pub mod kv;
|
||||
|
||||
use lib_sqlite::PoolConfig;
|
||||
pub use lib_sqlite::{ConnectionPool, DBConnection, Database};
|
||||
|
||||
pub mod schema;
|
||||
|
||||
#[macro_use]
|
||||
@ -5,25 +14,17 @@ pub mod macros;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
pub use diesel::*;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel_derives;
|
||||
pub use diesel_derives::*;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
|
||||
pub use lib_sqlite::{ConnectionPool, DBConnection, Database};
|
||||
pub type Error = diesel::result::Error;
|
||||
|
||||
use diesel_migrations::*;
|
||||
use lib_sqlite::PoolConfig;
|
||||
use std::{fmt::Debug, io, path::Path};
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::UserDatabaseConnection;
|
||||
pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl};
|
||||
|
||||
pub use super::UserDatabaseConnection;
|
||||
}
|
||||
|
||||
embed_migrations!("../flowy-database/migrations/");
|
||||
|
Reference in New Issue
Block a user