mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
use diesel as sqlite orm & setup db init schedule
This commit is contained in:
2
.idea/appflowy_client.iml
generated
2
.idea/appflowy_client.iml
generated
@ -16,6 +16,8 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/scripts/flowy-tool/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/scripts/flowy-tool/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-test/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-test/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-user/tests" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-user/tests" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-db/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-infra/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.pub" />
|
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.pub" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.dart_tool" />
|
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.dart_tool" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/build" />
|
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/build" />
|
||||||
|
@ -8,6 +8,8 @@ members = [
|
|||||||
"flowy-ast",
|
"flowy-ast",
|
||||||
"flowy-derive",
|
"flowy-derive",
|
||||||
"flowy-test",
|
"flowy-test",
|
||||||
|
"flowy-infra",
|
||||||
|
"flowy-db",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
1
rust-lib/flowy-db/.env
Normal file
1
rust-lib/flowy-db/.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
DATABASE_URL=/tmp/database.sql
|
12
rust-lib/flowy-db/Cargo.toml
Normal file
12
rust-lib/flowy-db/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "flowy-db"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
diesel = {version = "1.4.7", features = ["sqlite"]}
|
||||||
|
diesel_derives = {version = "1.4.1", features = ["sqlite"]}
|
||||||
|
diesel_migrations = {version = "1.4.0", features = ["sqlite"]}
|
||||||
|
flowy-infra = {path = "../flowy-infra"}
|
5
rust-lib/flowy-db/diesel.toml
Normal file
5
rust-lib/flowy-db/diesel.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# For documentation on how to configure this file,
|
||||||
|
# see diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
|
[print_schema]
|
||||||
|
file = "src/schema.rs"
|
0
rust-lib/flowy-db/migrations/.gitkeep
Normal file
0
rust-lib/flowy-db/migrations/.gitkeep
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE user_table;
|
@ -0,0 +1,8 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
|
||||||
|
CREATE TABLE user_table (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL DEFAULT '',
|
||||||
|
password TEXT NOT NULL DEFAULT '',
|
||||||
|
email TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
17
rust-lib/flowy-db/src/database.rs
Normal file
17
rust-lib/flowy-db/src/database.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use crate::errors::FlowyDBError;
|
||||||
|
use diesel_migrations::*;
|
||||||
|
use flowy_infra::sqlite::*;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
embed_migrations!("../flowy-db/migrations/");
|
||||||
|
pub const DB_NAME: &str = "flowy-database.db";
|
||||||
|
|
||||||
|
pub fn init(storage_path: &str) -> Result<DataBase, FlowyDBError> {
|
||||||
|
if !Path::new(storage_path).exists() {
|
||||||
|
std::fs::create_dir_all(storage_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pool_config = PoolConfig::default();
|
||||||
|
let database = DataBase::new(storage_path, DB_NAME, pool_config)?;
|
||||||
|
Ok(database)
|
||||||
|
}
|
16
rust-lib/flowy-db/src/errors.rs
Normal file
16
rust-lib/flowy-db/src/errors.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use flowy_infra::Error;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FlowyDBError {
|
||||||
|
InitError(String),
|
||||||
|
IOError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<flowy_infra::Error> for FlowyDBError {
|
||||||
|
fn from(error: flowy_infra::Error) -> Self { FlowyDBError::InitError(format!("{:?}", error)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<io::Error> for FlowyDBError {
|
||||||
|
fn from(error: io::Error) -> Self { FlowyDBError::IOError(format!("{:?}", error)) }
|
||||||
|
}
|
15
rust-lib/flowy-db/src/lib.rs
Normal file
15
rust-lib/flowy-db/src/lib.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
mod database;
|
||||||
|
mod errors;
|
||||||
|
mod schema;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate diesel;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate diesel_derives;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate diesel_migrations;
|
||||||
|
|
||||||
|
pub use flowy_infra::sqlite::DataBase;
|
||||||
|
|
||||||
|
pub use database::init;
|
||||||
|
pub use errors::*;
|
8
rust-lib/flowy-db/src/schema.rs
Normal file
8
rust-lib/flowy-db/src/schema.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
table! {
|
||||||
|
user_table (id) {
|
||||||
|
id -> Text,
|
||||||
|
name -> Text,
|
||||||
|
password -> Text,
|
||||||
|
email -> Text,
|
||||||
|
}
|
||||||
|
}
|
16
rust-lib/flowy-infra/Cargo.toml
Normal file
16
rust-lib/flowy-infra/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "flowy-infra"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
r2d2 = "0.8.9"
|
||||||
|
diesel = {version = "1.4.7", features = ["sqlite"]}
|
||||||
|
diesel_derives = {version = "1.4.1", features = ["sqlite"]}
|
||||||
|
diesel_migrations = {version = "1.4.0", features = ["sqlite"]}
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
scheduled-thread-pool = "0.2.5"
|
||||||
|
error-chain = "=0.12.0"
|
||||||
|
log = "0.4.11"
|
22
rust-lib/flowy-infra/src/errors.rs
Normal file
22
rust-lib/flowy-infra/src/errors.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use error_chain::{
|
||||||
|
error_chain,
|
||||||
|
error_chain_processing,
|
||||||
|
impl_error_chain_kind,
|
||||||
|
impl_error_chain_processed,
|
||||||
|
impl_extract_backtrace,
|
||||||
|
};
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
UnknownMigrationExists(v: String) {
|
||||||
|
display("unknown migration version: '{}'", v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreign_links {
|
||||||
|
R2D2(::r2d2::Error);
|
||||||
|
Migrations(::diesel_migrations::RunMigrationsError);
|
||||||
|
Diesel(::diesel::result::Error);
|
||||||
|
Connection(::diesel::ConnectionError);
|
||||||
|
Io(::std::io::Error);
|
||||||
|
}
|
||||||
|
}
|
13
rust-lib/flowy-infra/src/lib.rs
Normal file
13
rust-lib/flowy-infra/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#[allow(deprecated, clippy::large_enum_variant)]
|
||||||
|
mod errors;
|
||||||
|
pub mod sqlite;
|
||||||
|
|
||||||
|
pub use errors::{Error, ErrorKind, Result};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(2 + 2, 4);
|
||||||
|
}
|
||||||
|
}
|
36
rust-lib/flowy-infra/src/sqlite/database.rs
Normal file
36
rust-lib/flowy-infra/src/sqlite/database.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use crate::{
|
||||||
|
errors::*,
|
||||||
|
sqlite::pool::{ConnectionManager, ConnectionPool, PoolConfig},
|
||||||
|
};
|
||||||
|
use r2d2::PooledConnection;
|
||||||
|
|
||||||
|
pub struct DataBase {
|
||||||
|
uri: String,
|
||||||
|
pool: ConnectionPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataBase {
|
||||||
|
pub fn new(dir: &str, name: &str, pool_config: PoolConfig) -> Result<Self> {
|
||||||
|
let uri = db_file_uri(dir, name);
|
||||||
|
let pool = ConnectionPool::new(pool_config, &uri)?;
|
||||||
|
Ok(Self { uri, pool })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_uri(&self) -> &str { &self.uri }
|
||||||
|
|
||||||
|
pub fn get_conn(&self) -> Result<PooledConnection<ConnectionManager>> {
|
||||||
|
let conn = self.pool.get()?;
|
||||||
|
Ok(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn db_file_uri(dir: &str, name: &str) -> String {
|
||||||
|
use std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
|
let mut uri = dir.to_owned();
|
||||||
|
if !uri.ends_with(MAIN_SEPARATOR) {
|
||||||
|
uri.push(MAIN_SEPARATOR);
|
||||||
|
}
|
||||||
|
uri.push_str(name);
|
||||||
|
uri
|
||||||
|
}
|
5
rust-lib/flowy-infra/src/sqlite/mod.rs
Normal file
5
rust-lib/flowy-infra/src/sqlite/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod database;
|
||||||
|
mod pool;
|
||||||
|
|
||||||
|
pub use database::*;
|
||||||
|
pub use pool::*;
|
134
rust-lib/flowy-infra/src/sqlite/pool.rs
Normal file
134
rust-lib/flowy-infra/src/sqlite/pool.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use diesel::{connection::Connection, SqliteConnection};
|
||||||
|
use r2d2::{ManageConnection, Pool};
|
||||||
|
use scheduled_thread_pool::ScheduledThreadPool;
|
||||||
|
use std::{
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref DB_POOL: Arc<ScheduledThreadPool> = Arc::new(
|
||||||
|
ScheduledThreadPool::with_name("db-pool-{}:", 4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConnectionPool {
|
||||||
|
pub(crate) inner: Pool<ConnectionManager>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for ConnectionPool {
|
||||||
|
type Target = Pool<ConnectionManager>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionPool {
|
||||||
|
pub fn new<T>(config: PoolConfig, uri: T) -> Result<Self>
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
let manager = ConnectionManager::new(uri);
|
||||||
|
let thread_pool = DB_POOL.clone();
|
||||||
|
let config = Arc::new(config);
|
||||||
|
|
||||||
|
let pool = r2d2::Pool::builder()
|
||||||
|
.thread_pool(thread_pool)
|
||||||
|
.min_idle(Some(config.min_idle))
|
||||||
|
.max_size(config.max_size)
|
||||||
|
.max_lifetime(None)
|
||||||
|
.connection_timeout(config.connection_timeout)
|
||||||
|
.idle_timeout(Some(config.idle_timeout))
|
||||||
|
.build_unchecked(manager);
|
||||||
|
Ok(ConnectionPool { inner: pool })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct ConnCounter(Arc<ConnCounterInner>);
|
||||||
|
|
||||||
|
impl std::ops::Deref for ConnCounter {
|
||||||
|
type Target = ConnCounterInner;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &*self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct ConnCounterInner {
|
||||||
|
max_number: AtomicUsize,
|
||||||
|
current_number: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnCounterInner {
|
||||||
|
pub fn get_max_num(&self) -> usize { self.max_number.load(SeqCst) }
|
||||||
|
|
||||||
|
pub fn reset(&self) {
|
||||||
|
// reset max_number to current_number
|
||||||
|
let _ = self
|
||||||
|
.max_number
|
||||||
|
.fetch_update(SeqCst, SeqCst, |_| Some(self.current_number.load(SeqCst)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type OnExecFunc = Box<dyn Fn() -> Box<dyn Fn(&SqliteConnection, &str)> + Send + Sync>;
|
||||||
|
|
||||||
|
pub struct PoolConfig {
|
||||||
|
min_idle: u32,
|
||||||
|
max_size: u32,
|
||||||
|
connection_timeout: Duration,
|
||||||
|
idle_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PoolConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
min_idle: 1,
|
||||||
|
max_size: 10,
|
||||||
|
connection_timeout: Duration::from_secs(10),
|
||||||
|
idle_timeout: Duration::from_secs(5 * 60),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PoolConfig {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn min_idle(mut self, min_idle: u32) -> Self {
|
||||||
|
self.min_idle = min_idle;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn max_size(mut self, max_size: u32) -> Self {
|
||||||
|
self.max_size = max_size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConnectionManager {
|
||||||
|
db_uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManageConnection for ConnectionManager {
|
||||||
|
type Connection = SqliteConnection;
|
||||||
|
type Error = crate::Error;
|
||||||
|
|
||||||
|
fn connect(&self) -> Result<Self::Connection> {
|
||||||
|
if !std::path::PathBuf::from(&self.db_uri).exists() {
|
||||||
|
log::error!("db file not exists");
|
||||||
|
}
|
||||||
|
Ok(SqliteConnection::establish(&self.db_uri)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid(&self, conn: &mut Self::Connection) -> Result<()> {
|
||||||
|
Ok(conn.execute("SELECT 1").map(|_| ())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_broken(&self, _conn: &mut Self::Connection) -> bool { false }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionManager {
|
||||||
|
pub fn new<S: Into<String>>(uri: S) -> Self { ConnectionManager { db_uri: uri.into() } }
|
||||||
|
}
|
@ -10,6 +10,8 @@ derive_more = {version = "0.99", features = ["display"]}
|
|||||||
flowy-dispatch = { path = "../flowy-dispatch" }
|
flowy-dispatch = { path = "../flowy-dispatch" }
|
||||||
flowy-log = { path = "../flowy-log" }
|
flowy-log = { path = "../flowy-log" }
|
||||||
flowy-derive = { path = "../flowy-derive" }
|
flowy-derive = { path = "../flowy-derive" }
|
||||||
|
flowy-db = { path = "../flowy-db" }
|
||||||
|
|
||||||
tracing = { version = "0.1", features = ["log"] }
|
tracing = { version = "0.1", features = ["log"] }
|
||||||
bytes = "1.0"
|
bytes = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
26
rust-lib/flowy-user/src/domain/database.rs
Normal file
26
rust-lib/flowy-user/src/domain/database.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use crate::errors::UserError;
|
||||||
|
use flowy_db::DataBase;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref DB: RwLock<Option<DataBase>> = RwLock::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DB_INIT: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
pub fn init_user_db(dir: &str) -> Result<(), UserError> {
|
||||||
|
let database = flowy_db::init(dir)?;
|
||||||
|
*(DB.write()?) = Some(database);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn init_db_test() { init_user_db(".").unwrap(); }
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
pub use user::*;
|
pub use user::*;
|
||||||
|
|
||||||
|
mod database;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
15
rust-lib/flowy-user/src/errors.rs
Normal file
15
rust-lib/flowy-user/src/errors.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use std::sync::PoisonError;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UserError {
|
||||||
|
DBInitFail(String),
|
||||||
|
PoisonError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<flowy_db::FlowyDBError> for UserError {
|
||||||
|
fn from(error: flowy_db::FlowyDBError) -> Self { UserError::DBInitFail(format!("{:?}", error)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::convert::From<PoisonError<T>> for UserError {
|
||||||
|
fn from(error: PoisonError<T>) -> Self { UserError::PoisonError(format!("{:?}", error)) }
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
mod domain;
|
mod domain;
|
||||||
mod error;
|
mod errors;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
4
scripts/install_diesel.sh
Executable file
4
scripts/install_diesel.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
brew install sqlite3
|
||||||
|
cargo install diesel_cli --no-default-features --features sqlite
|
Reference in New Issue
Block a user