mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
f2dd58a4f1
commit
bf121623ae
5
.github/workflows/tauri_ci.yaml
vendored
5
.github/workflows/tauri_ci.yaml
vendored
@ -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
|
||||||
|
@ -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" }
|
||||||
|
31
frontend/rust-lib/Cargo.lock
generated
31
frontend/rust-lib/Cargo.lock
generated
@ -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"
|
||||||
|
@ -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" }
|
||||||
|
@ -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"]
|
||||||
|
184
frontend/rust-lib/flowy-core/src/deps_resolve/collab_deps.rs
Normal file
184
frontend/rust-lib/flowy-core/src/deps_resolve/collab_deps.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE collab_snapshot;
|
@ -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'')
|
||||||
|
);
|
@ -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/");
|
||||||
|
@ -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,);
|
||||||
|
@ -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(¤t_workspace.id, format!("My first view"))
|
.create_view(¤t_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(¤t_workspace.id, format!("My first view"))
|
.create_view(¤t_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(¤t_workspace.id, format!("My first view"))
|
.create_view(¤t_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(¤t_workspace.id, format!("My first view"))
|
.create_view(¤t_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()],
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user