AppFlowy/frontend/rust-lib/flowy-user/src/services/database.rs

174 lines
5.3 KiB
Rust
Raw Normal View History

2022-03-19 08:23:34 +00:00
use flowy_database::ConnectionPool;
2022-01-11 05:34:45 +00:00
use flowy_database::{schema::user_table, DBConnection, Database};
2022-03-19 08:23:34 +00:00
use flowy_error::{ErrorCode, FlowyError};
2022-01-11 05:34:45 +00:00
use flowy_user_data_model::entities::{SignInResponse, SignUpResponse, UpdateUserParams, UserProfile};
2021-07-09 15:31:44 +00:00
use lazy_static::lazy_static;
2021-07-18 01:03:21 +00:00
use once_cell::sync::Lazy;
2021-09-04 07:12:53 +00:00
use parking_lot::{Mutex, RwLock};
use std::{collections::HashMap, sync::Arc, time::Duration};
2022-01-11 05:34:45 +00:00
2021-07-09 15:31:44 +00:00
lazy_static! {
static ref DB: RwLock<Option<Database>> = RwLock::new(None);
2021-07-09 15:31:44 +00:00
}
pub struct UserDB {
2021-07-09 15:31:44 +00:00
db_dir: String,
}
impl UserDB {
pub fn new(db_dir: &str) -> Self {
Self {
db_dir: db_dir.to_owned(),
}
}
2021-07-09 15:31:44 +00:00
2021-12-14 10:04:51 +00:00
fn open_user_db(&self, user_id: &str) -> Result<(), FlowyError> {
if user_id.is_empty() {
2022-03-19 08:23:34 +00:00
return Err(ErrorCode::UserIdIsEmpty.into());
}
2021-07-18 01:03:21 +00:00
2021-11-03 07:37:38 +00:00
tracing::info!("open user db {}", user_id);
let dir = format!("{}/{}", self.db_dir, user_id);
let db = flowy_database::init(&dir).map_err(|e| {
log::error!("init user db failed, {:?}, user_id: {}", e, user_id);
2021-12-14 10:04:51 +00:00
FlowyError::internal().context(e)
})?;
match DB_MAP.try_write_for(Duration::from_millis(300)) {
2021-12-14 10:04:51 +00:00
None => Err(FlowyError::internal().context("Acquire write lock to save user db failed")),
Some(mut write_guard) => {
write_guard.insert(user_id.to_owned(), db);
Ok(())
2022-01-23 04:14:00 +00:00
}
}
2021-07-09 15:31:44 +00:00
}
2021-12-14 10:04:51 +00:00
pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), FlowyError> {
match DB_MAP.try_write_for(Duration::from_millis(300)) {
2021-12-14 10:04:51 +00:00
None => Err(FlowyError::internal().context("Acquire write lock to close user db failed")),
Some(mut write_guard) => {
set_user_db_init(false, user_id);
write_guard.remove(user_id);
Ok(())
2022-01-23 04:14:00 +00:00
}
}
2021-07-09 15:31:44 +00:00
}
2021-12-14 10:04:51 +00:00
pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, FlowyError> {
2021-08-31 15:01:46 +00:00
let conn = self.get_pool(user_id)?.get()?;
Ok(conn)
}
2021-12-14 10:04:51 +00:00
pub(crate) fn get_pool(&self, user_id: &str) -> Result<Arc<ConnectionPool>, FlowyError> {
// Opti: INIT_LOCK try to lock the INIT_RECORD accesses. Because the write guard
// can not nested in the read guard that will cause the deadlock.
match INIT_LOCK.try_lock_for(Duration::from_millis(300)) {
None => log::error!("get_pool fail"),
Some(_) => {
if !is_user_db_init(user_id) {
let _ = self.open_user_db(user_id)?;
set_user_db_init(true, user_id);
}
2022-01-23 04:14:00 +00:00
}
2021-07-09 15:31:44 +00:00
}
match DB_MAP.try_read_for(Duration::from_millis(300)) {
2021-12-14 10:04:51 +00:00
None => Err(FlowyError::internal().context("Acquire read lock to read user db failed")),
Some(read_guard) => match read_guard.get(user_id) {
2021-12-14 10:04:51 +00:00
None => {
Err(FlowyError::internal().context("Get connection failed. The database is not initialization"))
2022-01-23 04:14:00 +00:00
}
Some(database) => Ok(database.get_pool()),
},
2021-07-09 15:31:44 +00:00
}
}
}
2021-07-18 01:03:21 +00:00
lazy_static! {
static ref DB_MAP: RwLock<HashMap<String, Database>> = RwLock::new(HashMap::new());
}
2021-07-18 01:03:21 +00:00
static INIT_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
static INIT_RECORD: Lazy<Mutex<HashMap<String, bool>>> = Lazy::new(|| Mutex::new(HashMap::new()));
2021-07-18 01:03:21 +00:00
fn set_user_db_init(is_init: bool, user_id: &str) {
let mut record = INIT_RECORD.lock();
record.insert(user_id.to_owned(), is_init);
}
2021-07-18 01:03:21 +00:00
fn is_user_db_init(user_id: &str) -> bool {
match INIT_RECORD.lock().get(user_id) {
2021-07-18 01:03:21 +00:00
None => false,
2021-11-27 11:19:41 +00:00
Some(flag) => *flag,
2021-07-18 01:03:21 +00:00
}
}
2022-01-11 05:34:45 +00:00
#[derive(Clone, Default, Queryable, Identifiable, Insertable)]
#[table_name = "user_table"]
pub struct UserTable {
pub(crate) id: String,
pub(crate) name: String,
pub(crate) token: String,
pub(crate) email: String,
pub(crate) workspace: String, // deprecated
}
impl UserTable {
pub fn new(id: String, name: String, email: String, token: String) -> Self {
Self {
id,
name,
email,
token,
workspace: "".to_owned(),
}
}
pub fn set_workspace(mut self, workspace: String) -> Self {
self.workspace = workspace;
self
}
}
impl std::convert::From<SignUpResponse> for UserTable {
2022-01-24 09:35:58 +00:00
fn from(resp: SignUpResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
2022-01-11 05:34:45 +00:00
}
impl std::convert::From<SignInResponse> for UserTable {
2022-01-24 09:35:58 +00:00
fn from(resp: SignInResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
2022-01-11 05:34:45 +00:00
}
impl std::convert::From<UserTable> for UserProfile {
fn from(table: UserTable) -> Self {
UserProfile {
id: table.id,
email: table.email,
name: table.name,
token: table.token,
}
}
}
2022-01-11 05:34:45 +00:00
#[derive(AsChangeset, Identifiable, Default, Debug)]
#[table_name = "user_table"]
pub struct UserTableChangeset {
pub id: String,
pub workspace: Option<String>, // deprecated
pub name: Option<String>,
pub email: Option<String>,
}
impl UserTableChangeset {
pub fn new(params: UpdateUserParams) -> Self {
UserTableChangeset {
id: params.id,
workspace: None,
name: params.name,
email: params.email,
}
2021-07-09 15:31:44 +00:00
}
}