diff --git a/frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/down.sql b/frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/down.sql new file mode 100644 index 0000000000..8d9f64d2a7 --- /dev/null +++ b/frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE rev_history; \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/up.sql b/frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/up.sql new file mode 100644 index 0000000000..23ada9a2e8 --- /dev/null +++ b/frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here +CREATE TABLE rev_history ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + object_id TEXT NOT NULL DEFAULT '', + start_rev_id BIGINT NOT NULL DEFAULT 0, + end_rev_id BIGINT NOT NULL DEFAULT 0, + data BLOB NOT NULL DEFAULT (x'') +); \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/src/schema.rs b/frontend/rust-lib/flowy-database/src/schema.rs index 8fddd037e0..b57ff46875 100644 --- a/frontend/rust-lib/flowy-database/src/schema.rs +++ b/frontend/rust-lib/flowy-database/src/schema.rs @@ -57,6 +57,16 @@ table! { } } +table! { + rev_history (id) { + id -> Integer, + object_id -> Text, + start_rev_id -> BigInt, + end_rev_id -> BigInt, + data -> Binary, + } +} + table! { rev_table (id) { id -> Integer, @@ -124,6 +134,7 @@ allow_tables_to_appear_in_same_query!( grid_meta_rev_table, grid_rev_table, kv_table, + rev_history, rev_table, trash_table, user_table, diff --git a/frontend/rust-lib/flowy-revision/src/history/mod.rs b/frontend/rust-lib/flowy-revision/src/history/mod.rs new file mode 100644 index 0000000000..be42f6f9be --- /dev/null +++ b/frontend/rust-lib/flowy-revision/src/history/mod.rs @@ -0,0 +1,4 @@ +mod persistence; +mod rev_history; + +pub use rev_history::*; diff --git a/frontend/rust-lib/flowy-revision/src/history/persistence.rs b/frontend/rust-lib/flowy-revision/src/history/persistence.rs new file mode 100644 index 0000000000..c548e04940 --- /dev/null +++ b/frontend/rust-lib/flowy-revision/src/history/persistence.rs @@ -0,0 +1,22 @@ +use crate::history::RevisionHistoryDiskCache; +use flowy_error::FlowyError; +use flowy_sync::entities::revision::Revision; + +pub struct SQLiteRevisionHistoryPersistence {} + +impl SQLiteRevisionHistoryPersistence { + pub fn new() -> Self { + Self {} + } +} + +impl RevisionHistoryDiskCache for SQLiteRevisionHistoryPersistence { + type Error = FlowyError; + + fn save_revision(&self, revision: Revision) -> Result<(), Self::Error> { + todo!() + } +} + +struct RevisionHistorySql(); +impl RevisionHistorySql {} diff --git a/frontend/rust-lib/flowy-revision/src/history/rev_history.rs b/frontend/rust-lib/flowy-revision/src/history/rev_history.rs new file mode 100644 index 0000000000..8c0b10e071 --- /dev/null +++ b/frontend/rust-lib/flowy-revision/src/history/rev_history.rs @@ -0,0 +1,78 @@ +use crate::history::persistence::SQLiteRevisionHistoryPersistence; +use flowy_error::FlowyError; +use flowy_sync::entities::revision::Revision; +use std::fmt::Debug; +use std::sync::Arc; +use tokio::sync::RwLock; + +pub trait RevisionHistoryDiskCache: Send + Sync { + type Error: Debug; + + fn save_revision(&self, revision: Revision) -> Result<(), Self::Error>; +} + +pub struct RevisionHistory { + config: RevisionHistoryConfig, + checkpoint: Arc>, + disk_cache: Arc>, +} + +impl RevisionHistory { + pub fn new(config: RevisionHistoryConfig) -> Self { + let disk_cache = Arc::new(SQLiteRevisionHistoryPersistence::new()); + let cloned_disk_cache = disk_cache.clone(); + let checkpoint = HistoryCheckpoint::from_config(&config, move |revision| { + let _ = cloned_disk_cache.save_revision(revision); + }); + let checkpoint = Arc::new(RwLock::new(checkpoint)); + + Self { + config, + checkpoint, + disk_cache, + } + } + + pub async fn save_revision(&self, revision: &Revision) { + self.checkpoint.write().await.add_revision(revision); + } +} + +pub struct RevisionHistoryConfig { + check_when_close: bool, + check_interval: i64, +} + +impl std::default::Default for RevisionHistoryConfig { + fn default() -> Self { + Self { + check_when_close: true, + check_interval: 19, + } + } +} + +struct HistoryCheckpoint { + interval: i64, + revisions: Vec, + on_check: Box, +} + +impl HistoryCheckpoint { + fn from_config(config: &RevisionHistoryConfig, on_check: F) -> Self + where + F: Fn(Revision) + Send + Sync + 'static, + { + Self { + interval: config.check_interval, + revisions: vec![], + on_check: Box::new(on_check), + } + } + + fn check(&mut self) -> Revision { + todo!() + } + + fn add_revision(&mut self, revision: &Revision) {} +} diff --git a/frontend/rust-lib/flowy-revision/src/lib.rs b/frontend/rust-lib/flowy-revision/src/lib.rs index 05e60c00e0..6c708afab2 100644 --- a/frontend/rust-lib/flowy-revision/src/lib.rs +++ b/frontend/rust-lib/flowy-revision/src/lib.rs @@ -1,5 +1,6 @@ mod cache; mod conflict_resolve; +mod history; mod rev_manager; mod rev_persistence; mod ws_manager; diff --git a/frontend/rust-lib/flowy-revision/src/rev_manager.rs b/frontend/rust-lib/flowy-revision/src/rev_manager.rs index 68d8db6f27..15f37af456 100644 --- a/frontend/rust-lib/flowy-revision/src/rev_manager.rs +++ b/frontend/rust-lib/flowy-revision/src/rev_manager.rs @@ -1,4 +1,5 @@ use crate::disk::RevisionState; +use crate::history::{RevisionHistory, RevisionHistoryConfig}; use crate::{RevisionPersistence, WSDataProviderDataSource}; use bytes::Bytes; use flowy_error::{FlowyError, FlowyResult}; @@ -45,7 +46,7 @@ pub struct RevisionManager { user_id: String, rev_id_counter: RevIdCounter, rev_persistence: Arc, - + rev_history: Arc, #[cfg(feature = "flowy_unit_test")] rev_ack_notifier: tokio::sync::broadcast::Sender, } @@ -53,6 +54,8 @@ pub struct RevisionManager { impl RevisionManager { pub fn new(user_id: &str, object_id: &str, rev_persistence: Arc) -> Self { let rev_id_counter = RevIdCounter::new(0); + let rev_history_config = RevisionHistoryConfig::default(); + let rev_history = Arc::new(RevisionHistory::new(rev_history_config)); #[cfg(feature = "flowy_unit_test")] let (revision_ack_notifier, _) = tokio::sync::broadcast::channel(1); @@ -61,6 +64,7 @@ impl RevisionManager { user_id: user_id.to_owned(), rev_id_counter, rev_persistence, + rev_history, #[cfg(feature = "flowy_unit_test")] rev_ack_notifier: revision_ack_notifier, diff --git a/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs index 25ee1f5471..68c182bcc0 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs @@ -61,7 +61,7 @@ impl GridBlockMetaPad { &mut self, row: RowMeta, start_row_id: Option, - ) -> CollaborateResult> { + ) -> CollaborateResult> { self.modify(|rows| { if let Some(start_row_id) = start_row_id { if !start_row_id.is_empty() { @@ -77,7 +77,10 @@ impl GridBlockMetaPad { }) } - pub fn delete_rows(&mut self, row_ids: Vec>) -> CollaborateResult> { + pub fn delete_rows( + &mut self, + row_ids: Vec>, + ) -> CollaborateResult> { self.modify(|rows| { rows.retain(|row| !row_ids.contains(&Cow::Borrowed(&row.id))); Ok(Some(())) @@ -141,7 +144,10 @@ impl GridBlockMetaPad { .map(|index| index as i32) } - pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult> { + pub fn update_row( + &mut self, + changeset: RowMetaChangeset, + ) -> CollaborateResult> { let row_id = changeset.row_id.clone(); self.modify_row(&row_id, |row| { let mut is_changed = None; @@ -166,7 +172,12 @@ impl GridBlockMetaPad { }) } - pub fn move_row(&mut self, row_id: &str, from: usize, to: usize) -> CollaborateResult> { + pub fn move_row( + &mut self, + row_id: &str, + from: usize, + to: usize, + ) -> CollaborateResult> { self.modify(|row_metas| { if let Some(position) = row_metas.iter().position(|row_meta| row_meta.id == row_id) { debug_assert_eq!(from, position); @@ -179,7 +190,7 @@ impl GridBlockMetaPad { }) } - pub fn modify(&mut self, f: F) -> CollaborateResult> + pub fn modify(&mut self, f: F) -> CollaborateResult> where F: for<'a> FnOnce(&'a mut Vec>) -> CollaborateResult>, { @@ -198,14 +209,14 @@ impl GridBlockMetaPad { // self.delta.to_str().unwrap_or_else(|_| "".to_string()) // ); self.delta = self.delta.compose(&delta)?; - Ok(Some(GridBlockMetaChange { delta, md5: self.md5() })) + Ok(Some(GridBlockMetaDeltaChangeset { delta, md5: self.md5() })) } } } } } - fn modify_row(&mut self, row_id: &str, f: F) -> CollaborateResult> + fn modify_row(&mut self, row_id: &str, f: F) -> CollaborateResult> where F: FnOnce(&mut RowMeta) -> CollaborateResult>, { @@ -233,7 +244,7 @@ impl GridBlockMetaPad { } } -pub struct GridBlockMetaChange { +pub struct GridBlockMetaDeltaChangeset { pub delta: GridBlockMetaDelta, /// md5: the md5 of the grid after applying the change. pub md5: String,