From f6ade11eb2a4fc00b20fbd609fdb963218a7a403 Mon Sep 17 00:00:00 2001
From: appflowy <annie@appflowy.io>
Date: Tue, 7 Jun 2022 16:38:00 +0800
Subject: [PATCH] chore: config revision history

---
 .../down.sql                                  |  2 +
 .../2022-06-07-071750_revision-history/up.sql |  8 ++
 .../rust-lib/flowy-database/src/schema.rs     | 11 +++
 .../flowy-revision/src/history/mod.rs         |  4 +
 .../flowy-revision/src/history/persistence.rs | 22 ++++++
 .../flowy-revision/src/history/rev_history.rs | 78 +++++++++++++++++++
 frontend/rust-lib/flowy-revision/src/lib.rs   |  1 +
 .../flowy-revision/src/rev_manager.rs         |  6 +-
 .../src/client_grid/grid_block_meta_pad.rs    | 27 +++++--
 9 files changed, 150 insertions(+), 9 deletions(-)
 create mode 100644 frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/down.sql
 create mode 100644 frontend/rust-lib/flowy-database/migrations/2022-06-07-071750_revision-history/up.sql
 create mode 100644 frontend/rust-lib/flowy-revision/src/history/mod.rs
 create mode 100644 frontend/rust-lib/flowy-revision/src/history/persistence.rs
 create mode 100644 frontend/rust-lib/flowy-revision/src/history/rev_history.rs

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<RwLock<HistoryCheckpoint>>,
+    disk_cache: Arc<dyn RevisionHistoryDiskCache<Error = FlowyError>>,
+}
+
+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<Revision>,
+    on_check: Box<dyn Fn(Revision) + Send + Sync + 'static>,
+}
+
+impl HistoryCheckpoint {
+    fn from_config<F>(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<RevisionPersistence>,
-
+    rev_history: Arc<RevisionHistory>,
     #[cfg(feature = "flowy_unit_test")]
     rev_ack_notifier: tokio::sync::broadcast::Sender<i64>,
 }
@@ -53,6 +54,8 @@ pub struct RevisionManager {
 impl RevisionManager {
     pub fn new(user_id: &str, object_id: &str, rev_persistence: Arc<RevisionPersistence>) -> 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<String>,
-    ) -> CollaborateResult<Option<GridBlockMetaChange>> {
+    ) -> CollaborateResult<Option<GridBlockMetaDeltaChangeset>> {
         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<Cow<'_, String>>) -> CollaborateResult<Option<GridBlockMetaChange>> {
+    pub fn delete_rows(
+        &mut self,
+        row_ids: Vec<Cow<'_, String>>,
+    ) -> CollaborateResult<Option<GridBlockMetaDeltaChangeset>> {
         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<Option<GridBlockMetaChange>> {
+    pub fn update_row(
+        &mut self,
+        changeset: RowMetaChangeset,
+    ) -> CollaborateResult<Option<GridBlockMetaDeltaChangeset>> {
         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<Option<GridBlockMetaChange>> {
+    pub fn move_row(
+        &mut self,
+        row_id: &str,
+        from: usize,
+        to: usize,
+    ) -> CollaborateResult<Option<GridBlockMetaDeltaChangeset>> {
         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<F>(&mut self, f: F) -> CollaborateResult<Option<GridBlockMetaChange>>
+    pub fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<GridBlockMetaDeltaChangeset>>
     where
         F: for<'a> FnOnce(&'a mut Vec<Arc<RowMeta>>) -> CollaborateResult<Option<()>>,
     {
@@ -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<F>(&mut self, row_id: &str, f: F) -> CollaborateResult<Option<GridBlockMetaChange>>
+    fn modify_row<F>(&mut self, row_id: &str, f: F) -> CollaborateResult<Option<GridBlockMetaDeltaChangeset>>
     where
         F: FnOnce(&mut RowMeta) -> CollaborateResult<Option<()>>,
     {
@@ -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,