2022-01-11 05:34:45 +00:00
|
|
|
use flowy_database::{schema::user_table, DBConnection, Database};
|
|
|
|
use flowy_error::FlowyError;
|
|
|
|
use flowy_user_data_model::entities::{SignInResponse, SignUpResponse, UpdateUserParams, UserProfile};
|
2021-07-09 15:31:44 +00:00
|
|
|
use lazy_static::lazy_static;
|
2021-11-19 06:38:11 +00:00
|
|
|
use lib_sqlite::ConnectionPool;
|
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};
|
2021-09-03 08:43:03 +00:00
|
|
|
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! {
|
2021-07-10 08:27:20 +00:00
|
|
|
static ref DB: RwLock<Option<Database>> = RwLock::new(None);
|
2021-07-09 15:31:44 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 08:00:11 +00:00
|
|
|
pub struct UserDB {
|
2021-07-09 15:31:44 +00:00
|
|
|
db_dir: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UserDB {
|
2022-01-19 08:00:11 +00:00
|
|
|
pub fn new(db_dir: &str) -> Self {
|
2021-10-01 11:39:08 +00:00
|
|
|
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> {
|
2021-07-18 15:56:36 +00:00
|
|
|
if user_id.is_empty() {
|
2021-12-14 10:04:51 +00:00
|
|
|
return Err(FlowyError::internal().context("user id is empty"));
|
2021-07-18 15:56:36 +00:00
|
|
|
}
|
2021-07-18 01:03:21 +00:00
|
|
|
|
2021-11-03 07:37:38 +00:00
|
|
|
tracing::info!("open user db {}", user_id);
|
2021-07-10 08:27:20 +00:00
|
|
|
let dir = format!("{}/{}", self.db_dir, user_id);
|
2021-07-11 13:54:55 +00:00
|
|
|
let db = flowy_database::init(&dir).map_err(|e| {
|
2021-09-03 08:43:03 +00:00
|
|
|
log::error!("init user db failed, {:?}, user_id: {}", e, user_id);
|
2021-12-14 10:04:51 +00:00
|
|
|
FlowyError::internal().context(e)
|
2021-07-11 13:54:55 +00:00
|
|
|
})?;
|
|
|
|
|
2021-09-03 08:43:03 +00:00
|
|
|
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")),
|
2021-09-03 08:43:03 +00:00
|
|
|
Some(mut write_guard) => {
|
|
|
|
write_guard.insert(user_id.to_owned(), db);
|
|
|
|
Ok(())
|
2022-01-23 04:14:00 +00:00
|
|
|
}
|
2021-09-03 08:43:03 +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> {
|
2021-09-03 08:43:03 +00:00
|
|
|
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")),
|
2021-09-03 08:43:03 +00:00
|
|
|
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-09-03 08:43:03 +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> {
|
2021-09-03 08:43:03 +00:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2021-09-03 08:43:03 +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")),
|
2021-09-03 08:43:03 +00:00
|
|
|
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
|
|
|
}
|
2021-09-03 08:43:03 +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-11 07:33:19 +00:00
|
|
|
}
|
2021-07-18 01:03:21 +00:00
|
|
|
|
2021-09-03 08:43:03 +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) {
|
2021-09-03 08:43:03 +00:00
|
|
|
let mut record = INIT_RECORD.lock();
|
|
|
|
record.insert(user_id.to_owned(), is_init);
|
2021-07-11 07:33:19 +00:00
|
|
|
}
|
|
|
|
|
2021-07-18 01:03:21 +00:00
|
|
|
fn is_user_db_init(user_id: &str) -> bool {
|
2021-09-03 08:43:03 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2021-07-11 07:33:19 +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 {
|
|
|
|
fn from(resp: SignUpResponse) -> Self { UserTable::new(resp.user_id, resp.name, resp.email, resp.token) }
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::convert::From<SignInResponse> for UserTable {
|
|
|
|
fn from(resp: SignInResponse) -> Self { UserTable::new(resp.user_id, resp.name, resp.email, resp.token) }
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-10 16:07:37 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|