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:
parent
ae23a41445
commit
8bf355f956
@ -16,6 +16,8 @@
|
||||
<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-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/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/build" />
|
||||
|
@ -8,6 +8,8 @@ members = [
|
||||
"flowy-ast",
|
||||
"flowy-derive",
|
||||
"flowy-test",
|
||||
"flowy-infra",
|
||||
"flowy-db",
|
||||
]
|
||||
|
||||
[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-log = { path = "../flowy-log" }
|
||||
flowy-derive = { path = "../flowy-derive" }
|
||||
flowy-db = { path = "../flowy-db" }
|
||||
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
bytes = "1.0"
|
||||
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::*;
|
||||
|
||||
mod database;
|
||||
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 error;
|
||||
mod errors;
|
||||
pub mod event;
|
||||
mod handlers;
|
||||
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
|
Loading…
Reference in New Issue
Block a user