feat: save snapshot to sqlite db (#2718)

* chore: snapshot

* chore: impl sqlite snapshot

* feat: snapshot config

* feat: update patch

* ci: fix tauri ci

* ci: add cache path

* chore: save snapshot

* chore: update patch

* ci: fix s fmt
This commit is contained in:
Nathan.fooo 2023-06-06 16:03:29 +08:00 committed by GitHub
parent f2dd58a4f1
commit bf121623ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 323 additions and 98 deletions

View File

@ -36,9 +36,10 @@ jobs:
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
with: with:
prefix-key: 'ubuntu-latest' prefix-key: 'ubuntu-latest-tauri'
workspaces: | workspaces: |
frontend/rust-lib frontend/rust-lib
frontend/appflowy_tauri/src-tauri
- name: install dependencies (windows only) - name: install dependencies (windows only)
if: matrix.platform == 'windows-latest' if: matrix.platform == 'windows-latest'
@ -51,7 +52,7 @@ jobs:
npm install -g pnpm@${{ env.PNPM_VERSION }} npm install -g pnpm@${{ env.PNPM_VERSION }}
- name: install dependencies (ubuntu only) - name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-20.04' if: matrix.platform == 'ubuntu-latest'
working-directory: frontend working-directory: frontend
run: | run: |
sudo apt-get update sudo apt-get update

View File

@ -34,12 +34,12 @@ default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"] custom-protocol = ["tauri/custom-protocol"]
[patch.crates-io] [patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
#collab = { path = "../../AppFlowy-Collab/collab" } #collab = { path = "../../AppFlowy-Collab/collab" }
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" } #collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }

View File

@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]] [[package]]
name = "appflowy-integrate" name = "appflowy-integrate"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -887,7 +887,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -905,7 +905,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-client-ws" name = "collab-client-ws"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab-sync", "collab-sync",
@ -923,7 +923,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -949,7 +949,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-derive" name = "collab-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -961,7 +961,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -978,7 +978,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -997,7 +997,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-persistence" name = "collab-persistence"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"bincode", "bincode",
"chrono", "chrono",
@ -1017,7 +1017,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1036,6 +1036,7 @@ dependencies = [
"rusoto_credential", "rusoto_credential",
"serde", "serde",
"serde_json", "serde_json",
"similar 2.2.1",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-retry", "tokio-retry",
@ -1047,7 +1048,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-sync" name = "collab-sync"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5c519e#5c519e988e93f6fbc7d98ce4373c5ff30a547fb1"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab", "collab",
@ -1568,7 +1569,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"similar", "similar 1.3.0",
"syn 1.0.109", "syn 1.0.109",
"tera", "tera",
"toml", "toml",
@ -1598,6 +1599,7 @@ dependencies = [
"appflowy-integrate", "appflowy-integrate",
"bytes", "bytes",
"console-subscriber", "console-subscriber",
"diesel",
"flowy-config", "flowy-config",
"flowy-database2", "flowy-database2",
"flowy-document2", "flowy-document2",
@ -1619,6 +1621,7 @@ dependencies = [
"serde_repr", "serde_repr",
"tokio", "tokio",
"tracing", "tracing",
"uuid",
] ]
[[package]] [[package]]
@ -4218,6 +4221,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
[[package]]
name = "similar"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.10" version = "0.3.10"

View File

@ -33,11 +33,11 @@ opt-level = 3
incremental = false incremental = false
[patch.crates-io] [patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "cbc2e0" } appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5c519e" }
#collab = { path = "../AppFlowy-Collab/collab" } #collab = { path = "../AppFlowy-Collab/collab" }
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" } #collab-folder = { path = "../AppFlowy-Collab/collab-folder" }

View File

@ -12,13 +12,15 @@ flowy-user = { path = "../flowy-user" }
flowy-net = { path = "../flowy-net" } flowy-net = { path = "../flowy-net" }
flowy-folder2 = { path = "../flowy-folder2" } flowy-folder2 = { path = "../flowy-folder2" }
flowy-database2 = { path = "../flowy-database2" } flowy-database2 = { path = "../flowy-database2" }
flowy-sqlite = { path = "../flowy-sqlite", optional = true } flowy-sqlite = { path = "../flowy-sqlite" }
flowy-document2 = { path = "../flowy-document2" } flowy-document2 = { path = "../flowy-document2" }
flowy-error = { path = "../flowy-error" } flowy-error = { path = "../flowy-error" }
flowy-task = { path = "../flowy-task" } flowy-task = { path = "../flowy-task" }
flowy-server = { path = "../flowy-server" } flowy-server = { path = "../flowy-server" }
flowy-config = { path = "../flowy-config" } flowy-config = { path = "../flowy-config" }
appflowy-integrate = { version = "0.1.0" } appflowy-integrate = { version = "0.1.0" }
diesel = { version = "1.4.8", features = ["sqlite"] }
uuid = { version = "1.3.3", features = ["v4"] }
tracing = { version = "0.1", features = ["log"] } tracing = { version = "0.1", features = ["log"] }
futures-core = { version = "0.3", default-features = false } futures-core = { version = "0.3", default-features = false }
@ -56,7 +58,6 @@ ts = [
"flowy-config/ts", "flowy-config/ts",
] ]
rev-sqlite = [ rev-sqlite = [
"flowy-sqlite",
"flowy-user/rev-sqlite", "flowy-user/rev-sqlite",
] ]
openssl_vendored = ["flowy-sqlite/openssl_vendored"] openssl_vendored = ["flowy-sqlite/openssl_vendored"]

View File

@ -0,0 +1,184 @@
use std::sync::Arc;
use appflowy_integrate::{
calculate_snapshot_diff, try_encode_snapshot, CollabSnapshot, MutexCollab, PersistenceError,
Snapshot, SnapshotDB,
};
use diesel::SqliteConnection;
use flowy_error::FlowyError;
use flowy_sqlite::{
insert_or_ignore_into,
prelude::*,
schema::{collab_snapshot, collab_snapshot::dsl},
};
use flowy_user::services::UserSession;
use lib_infra::util::timestamp;
pub struct SnapshotDBImpl(pub Arc<UserSession>);
impl SnapshotDB for SnapshotDBImpl {
fn get_snapshots(&self, _uid: i64, object_id: &str) -> Vec<CollabSnapshot> {
self
.0
.db_pool()
.and_then(|pool| Ok(pool.get()?))
.and_then(|conn| {
CollabSnapshotTableSql::get_all_snapshots(object_id, &conn)
.and_then(|rows| Ok(rows.into_iter().map(|row| row.into()).collect()))
})
.unwrap_or_else(|_| vec![])
}
fn create_snapshot(
&self,
uid: i64,
object_id: &str,
snapshot: Snapshot,
collab: Arc<MutexCollab>,
) -> Result<(), PersistenceError> {
let object_id = object_id.to_string();
let weak_pool = Arc::downgrade(
&self
.0
.db_pool()
.map_err(|e| PersistenceError::Internal(Box::new(e)))?,
);
let _ = tokio::task::spawn_blocking(move || {
if let Some(pool) = weak_pool.upgrade() {
let conn = pool
.get()
.map_err(|e| PersistenceError::Internal(Box::new(e)))?;
// Try to acquire a txn lock, if failed, it means there is a txn running, so we just ignore this snapshot
let result = try_encode_snapshot(
&collab
.lock()
.try_transaction()
.map_err(|e| PersistenceError::Internal(Box::new(e)))?,
snapshot,
);
match result.and_then(|new_snapshot_data| {
let desc = match CollabSnapshotTableSql::get_latest_snapshot(&object_id, &conn) {
None => Ok("".to_string()),
Some(old_snapshot) => {
calculate_snapshot_diff(uid, &object_id, &old_snapshot.data, &new_snapshot_data)
},
}
.map_err(|e| PersistenceError::InvalidData(format!("{:?}", e)))?;
// Save the snapshot to disk
CollabSnapshotTableSql::create(
CollabSnapshotRow {
id: uuid::Uuid::new_v4().to_string(),
object_id: object_id.clone(),
desc,
timestamp: timestamp(),
data: new_snapshot_data,
},
&conn,
)
.map_err(|e| PersistenceError::Internal(Box::new(e)))
}) {
Ok(_) => {},
Err(e) => tracing::error!("create snapshot error: {:?}", e),
}
}
Ok::<(), PersistenceError>(())
});
Ok(())
}
}
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
#[table_name = "collab_snapshot"]
struct CollabSnapshotRow {
id: String,
object_id: String,
desc: String,
timestamp: i64,
data: Vec<u8>,
}
impl From<CollabSnapshotRow> for CollabSnapshot {
fn from(table: CollabSnapshotRow) -> Self {
Self {
data: table.data,
created_at: table.timestamp,
}
}
}
struct CollabSnapshotTableSql;
impl CollabSnapshotTableSql {
fn create(row: CollabSnapshotRow, conn: &SqliteConnection) -> Result<(), FlowyError> {
// Batch insert: https://diesel.rs/guides/all-about-inserts.html
let values = (
dsl::id.eq(row.id),
dsl::object_id.eq(row.object_id),
dsl::desc.eq(row.desc),
dsl::data.eq(row.data),
dsl::timestamp.eq(row.timestamp),
);
let _ = insert_or_ignore_into(dsl::collab_snapshot)
.values(values)
.execute(conn)?;
Ok(())
}
fn get_all_snapshots(
object_id: &str,
conn: &SqliteConnection,
) -> Result<Vec<CollabSnapshotRow>, FlowyError> {
let sql = dsl::collab_snapshot
.filter(dsl::object_id.eq(object_id))
.into_boxed();
let rows = sql
.order(dsl::timestamp.asc())
.load::<CollabSnapshotRow>(conn)?;
Ok(rows)
}
fn get_latest_snapshot(object_id: &str, conn: &SqliteConnection) -> Option<CollabSnapshotRow> {
let sql = dsl::collab_snapshot
.filter(dsl::object_id.eq(object_id))
.into_boxed();
sql
.order(dsl::timestamp.desc())
.first::<CollabSnapshotRow>(conn)
.ok()
}
#[allow(dead_code)]
fn delete(
object_id: &str,
snapshot_ids: Option<Vec<String>>,
conn: &SqliteConnection,
) -> Result<(), FlowyError> {
let mut sql = diesel::delete(dsl::collab_snapshot).into_boxed();
sql = sql.filter(dsl::object_id.eq(object_id));
if let Some(snapshot_ids) = snapshot_ids {
tracing::trace!(
"[{}] Delete snapshot: {}:{:?}",
std::any::type_name::<Self>(),
object_id,
snapshot_ids
);
sql = sql.filter(dsl::id.eq_any(snapshot_ids));
}
let affected_row = sql.execute(conn)?;
tracing::trace!(
"[{}] Delete {} rows",
std::any::type_name::<Self>(),
affected_row
);
Ok(())
}
}

View File

@ -1,7 +1,9 @@
pub use collab_deps::*;
pub use database_deps::*; pub use database_deps::*;
pub use document2_deps::*; pub use document2_deps::*;
pub use folder2_deps::*; pub use folder2_deps::*;
mod collab_deps;
mod document2_deps; mod document2_deps;
mod folder2_deps; mod folder2_deps;
mod util; mod util;

View File

@ -10,8 +10,8 @@ use std::{
}; };
use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, CloudStorageType}; use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, CloudStorageType};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tracing::debug;
use flowy_database2::DatabaseManager2; use flowy_database2::DatabaseManager2;
use flowy_document2::manager::DocumentManager as DocumentManager2; use flowy_document2::manager::DocumentManager as DocumentManager2;
@ -27,7 +27,6 @@ use lib_dispatch::runtime::tokio_default_runtime;
use lib_infra::future::{to_fut, Fut}; use lib_infra::future::{to_fut, Fut};
use module::make_plugins; use module::make_plugins;
pub use module::*; pub use module::*;
use tracing::debug;
use crate::deps_resolve::*; use crate::deps_resolve::*;
use crate::integrate::server::{AppFlowyServerProvider, ServerProviderType}; use crate::integrate::server::{AppFlowyServerProvider, ServerProviderType};
@ -141,15 +140,23 @@ impl AppFlowyCore {
runtime.spawn(TaskRunner::run(task_dispatcher.clone())); runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
let server_provider = Arc::new(AppFlowyServerProvider::new()); let server_provider = Arc::new(AppFlowyServerProvider::new());
let (
user_session,
folder_manager,
server_provider,
database_manager,
document_manager2,
collab_builder,
) = runtime.block_on(async {
let user_session = mk_user_session(&config, server_provider.clone());
/// The shared collab builder is used to build the [Collab] instance. The plugins will be loaded /// The shared collab builder is used to build the [Collab] instance. The plugins will be loaded
/// on demand based on the [CollabPluginConfig]. /// on demand based on the [CollabPluginConfig].
let collab_builder = Arc::new(AppFlowyCollabBuilder::new( let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
server_provider.provider_type().into(), server_provider.provider_type().into(),
Some(Arc::new(SnapshotDBImpl(user_session.clone()))),
)); ));
let (user_session, folder_manager, server_provider, database_manager, document_manager2) =
runtime.block_on(async {
let user_session = mk_user_session(&config, server_provider.clone());
let database_manager2 = Database2DepsResolver::resolve( let database_manager2 = Database2DepsResolver::resolve(
user_session.clone(), user_session.clone(),
task_dispatcher.clone(), task_dispatcher.clone(),
@ -178,6 +185,7 @@ impl AppFlowyCore {
server_provider, server_provider,
database_manager2, database_manager2,
document_manager2, document_manager2,
collab_builder,
) )
}); });

View File

@ -48,11 +48,12 @@ impl DatabaseManager2 {
} }
pub async fn initialize(&self, user_id: i64) -> FlowyResult<()> { pub async fn initialize(&self, user_id: i64) -> FlowyResult<()> {
let config = CollabPersistenceConfig::new().snapshot_per_update(10);
let db = self.user.collab_db()?; let db = self.user.collab_db()?;
*self.user_database.lock() = Some(InnerUserDatabase::new( *self.user_database.lock() = Some(InnerUserDatabase::new(
user_id, user_id,
db, db,
CollabPersistenceConfig::default(), config,
UserDatabaseCollabBuilderImpl(self.collab_builder.clone()), UserDatabaseCollabBuilderImpl(self.collab_builder.clone()),
)); ));
// do nothing // do nothing
@ -269,16 +270,6 @@ unsafe impl Send for UserDatabase {}
struct UserDatabaseCollabBuilderImpl(Arc<AppFlowyCollabBuilder>); struct UserDatabaseCollabBuilderImpl(Arc<AppFlowyCollabBuilder>);
impl DatabaseCollabBuilder for UserDatabaseCollabBuilderImpl { impl DatabaseCollabBuilder for UserDatabaseCollabBuilderImpl {
fn build(
&self,
uid: i64,
object_id: &str,
object_name: &str,
db: Arc<RocksCollabDB>,
) -> Arc<MutexCollab> {
self.0.build(uid, object_id, object_name, db)
}
fn build_with_config( fn build_with_config(
&self, &self,
uid: i64, uid: i64,

View File

@ -6,4 +6,5 @@ pub mod filter;
pub mod group; pub mod group;
pub mod setting; pub mod setting;
pub mod share; pub mod share;
pub mod snapshot;
pub mod sort; pub mod sort;

View File

@ -49,7 +49,7 @@ impl DocumentManager {
let uid = self.user.user_id()?; let uid = self.user.user_id()?;
let db = self.user.collab_db()?; let db = self.user.collab_db()?;
let collab = self.collab_builder.build(uid, &doc_id, "document", db); let collab = self.collab_builder.build(uid, &doc_id, "document", db);
let data = data.unwrap_or_else(|| default_document_data()); let data = data.unwrap_or_else(default_document_data);
let document = Arc::new(Document::create_with_data(collab, data)?); let document = Arc::new(Document::create_with_data(collab, data)?);
Ok(document) Ok(document)
} }

View File

@ -50,6 +50,6 @@ pub fn db() -> Arc<RocksCollabDB> {
} }
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> { pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
let builder = AppFlowyCollabBuilder::new(CloudStorageType::Local); let builder = AppFlowyCollabBuilder::new(CloudStorageType::Local, None);
Arc::new(builder) Arc::new(builder)
} }

View File

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE collab_snapshot;

View File

@ -0,0 +1,8 @@
-- Your SQL goes here
CREATE TABLE collab_snapshot (
id TEXT NOT NULL PRIMARY KEY DEFAULT '',
object_id TEXT NOT NULL DEFAULT '',
desc TEXT NOT NULL DEFAULT '',
timestamp BIGINT NOT NULL DEFAULT 0,
data BLOB NOT NULL DEFAULT (x'')
);

View File

@ -1,31 +1,35 @@
#[macro_use]
pub extern crate diesel;
#[macro_use]
pub extern crate diesel_derives;
#[macro_use]
extern crate diesel_migrations;
use std::{fmt::Debug, io, path::Path};
pub use diesel::*; pub use diesel::*;
pub use diesel_derives::*; pub use diesel_derives::*;
use diesel_migrations::*; use diesel_migrations::*;
use std::{fmt::Debug, io, path::Path};
pub mod kv;
mod sqlite;
use crate::sqlite::PoolConfig; use crate::sqlite::PoolConfig;
pub use crate::sqlite::{ConnectionPool, DBConnection, Database}; pub use crate::sqlite::{ConnectionPool, DBConnection, Database};
pub mod kv;
mod sqlite;
pub mod schema; pub mod schema;
#[macro_use] #[macro_use]
pub mod macros; pub mod macros;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_derives;
#[macro_use]
extern crate diesel_migrations;
pub type Error = diesel::result::Error; pub type Error = diesel::result::Error;
pub mod prelude { pub mod prelude {
pub use super::UserDatabaseConnection;
pub use crate::*;
pub use diesel::SqliteConnection; pub use diesel::SqliteConnection;
pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl}; pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl};
pub use crate::*;
pub use super::UserDatabaseConnection;
} }
embed_migrations!("../flowy-sqlite/migrations/"); embed_migrations!("../flowy-sqlite/migrations/");

View File

@ -1,5 +1,15 @@
// @generated automatically by Diesel CLI. // @generated automatically by Diesel CLI.
diesel::table! {
collab_snapshot (id) {
id -> Text,
object_id -> Text,
desc -> Text,
timestamp -> BigInt,
data -> Binary,
}
}
diesel::table! { diesel::table! {
user_table (id) { user_table (id) {
id -> Text, id -> Text,
@ -11,3 +21,5 @@ diesel::table! {
email -> Text, email -> Text,
} }
} }
diesel::allow_tables_to_appear_in_same_query!(collab_snapshot, user_table,);

View File

@ -54,7 +54,7 @@ async fn create_view_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace; let current_workspace = test.get_current_workspace().await.workspace;
let view = test let view = test
.create_view(&current_workspace.id, format!("My first view")) .create_view(&current_workspace.id, "My first view".to_string())
.await; .await;
assert_eq!(view.parent_view_id, current_workspace.id); assert_eq!(view.parent_view_id, current_workspace.id);
assert_eq!(view.name, "My first view"); assert_eq!(view.name, "My first view");
@ -66,7 +66,7 @@ async fn delete_view_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace; let current_workspace = test.get_current_workspace().await.workspace;
let view = test let view = test
.create_view(&current_workspace.id, format!("My first view")) .create_view(&current_workspace.id, "My first view".to_string())
.await; .await;
test.delete_view(&view.id).await; test.delete_view(&view.id).await;
@ -89,7 +89,7 @@ async fn put_back_trash_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace; let current_workspace = test.get_current_workspace().await.workspace;
let view = test let view = test
.create_view(&current_workspace.id, format!("My first view")) .create_view(&current_workspace.id, "My first view".to_string())
.await; .await;
test.delete_view(&view.id).await; test.delete_view(&view.id).await;
@ -132,7 +132,7 @@ async fn delete_view_permanently_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace; let current_workspace = test.get_current_workspace().await.workspace;
let view = test let view = test
.create_view(&current_workspace.id, format!("My first view")) .create_view(&current_workspace.id, "My first view".to_string())
.await; .await;
let payload = RepeatedViewIdPB { let payload = RepeatedViewIdPB {
items: vec![view.id.clone()], items: vec![view.id.clone()],

View File

@ -50,7 +50,7 @@ impl UserDB {
Ok(pool) Ok(pool)
} }
fn open_kv_db_if_need(&self, user_id: i64) -> Result<Arc<RocksCollabDB>, FlowyError> { fn open_collab_db_if_need(&self, user_id: i64) -> Result<Arc<RocksCollabDB>, FlowyError> {
if let Some(kv) = COLLAB_DB_MAP.read().get(&user_id) { if let Some(kv) = COLLAB_DB_MAP.read().get(&user_id) {
return Ok(kv.clone()); return Ok(kv.clone());
} }
@ -65,8 +65,9 @@ impl UserDB {
let mut dir = PathBuf::new(); let mut dir = PathBuf::new();
dir.push(&self.db_dir); dir.push(&self.db_dir);
dir.push(user_id.to_string()); dir.push(user_id.to_string());
dir.push("collab_db");
tracing::trace!("open kv db {} at path: {:?}", user_id, dir); tracing::trace!("open collab db {} at path: {:?}", user_id, dir);
let db = RocksCollabDB::open(dir).map_err(|err| FlowyError::internal().context(err))?; let db = RocksCollabDB::open(dir).map_err(|err| FlowyError::internal().context(err))?;
let db = Arc::new(db); let db = Arc::new(db);
write_guard.insert(user_id.to_owned(), db.clone()); write_guard.insert(user_id.to_owned(), db.clone());
@ -94,9 +95,9 @@ impl UserDB {
Ok(pool) Ok(pool)
} }
pub(crate) fn get_kv_db(&self, user_id: i64) -> Result<Arc<RocksCollabDB>, FlowyError> { pub(crate) fn get_collab_db(&self, user_id: i64) -> Result<Arc<RocksCollabDB>, FlowyError> {
let kv_db = self.open_kv_db_if_need(user_id)?; let collab_db = self.open_collab_db_if_need(user_id)?;
Ok(kv_db) Ok(collab_db)
} }
} }

View File

@ -96,7 +96,7 @@ impl UserSession {
pub fn get_collab_db(&self) -> Result<Arc<RocksCollabDB>, FlowyError> { pub fn get_collab_db(&self) -> Result<Arc<RocksCollabDB>, FlowyError> {
let user_id = self.get_session()?.user_id; let user_id = self.get_session()?.user_id;
self.database.get_kv_db(user_id) self.database.get_collab_db(user_id)
} }
#[tracing::instrument(level = "debug", skip(self, params))] #[tracing::instrument(level = "debug", skip(self, params))]