describe the delta interaction between client and server

This commit is contained in:
appflowy 2021-10-05 10:19:43 +08:00
parent c872226b44
commit d0111e30dc
7 changed files with 90 additions and 33 deletions

View File

@ -94,14 +94,18 @@ async fn delta_sync_with_server_push_delta() {
// ◀─────────────────┤ start ws connection
// │ │
// ◀─────────────────┤ notify with rev: 1
// │ │
// ┌───────────────────┐ │ │ ┌──────────────────────────┐
// │ops: ["123"] rev: 3│ ├────Push Rev─────▶ │ops: ["abc", "123"] rev: 4│
// └───────────────────┘ │ │ └──────────────────────────┘
// ┌──────────────────────────┐ │ │ ┌────────────────────┐
// │ops: ["abc", "123"] rev: 4│ ◀────Push Rev─────┤ │ops: ["abc"] rev: 4 │
// └──────────────────────────┘ │ │ └────────────────────┘
// │ │
// ┌───────────────────┐ │ │
// │ops: ["123"] rev: 3│ ├────Push Rev─────▶ transform
// └───────────────────┘ │ │ ┌──────────────────────────┐
// │ │ │ops: ["abc", "123"] rev: 4│
// │ │ └──────────────────────────┘
// │ │ ┌────────────────────────────────┐
// compose ◀────Push Rev─────┤ │ops: ["abc", "retain 3"] rev: 4 │
// │ │ └────────────────────────────────┘
// ┌──────────────────────────┐ │
// │ops: ["abc", "123"] rev: 4│ │
// └──────────────────────────┘ │
// │ │
#[actix_rt::test]
async fn delta_sync_while_local_rev_less_than_server_rev() {
let test = DocumentTest::new().await;
@ -120,6 +124,31 @@ async fn delta_sync_while_local_rev_less_than_server_rev() {
.await;
}
#[rustfmt::skip]
// ┌─────────┐ ┌─────────┐
// │ Server │ │ Client │
// └─────────┘ └─────────┘
// ┌───────────────────┐ │ │
// │ops: ["123"] rev: 1│ │ │
// └───────────────────┘ │ │
// ◀── http request ─┤ Open doc
// │ │
// │ │ ┌───────────────┐
// ├──http response──┼──▶│ops: [123] rev:│
// │ │ └───────────────┘
// │ │ ┌──────────────────────────────────┐
// │ │ │ops: ["123","abc", "efg"] rev: 3 │
// │ │ └──────────────────────────────────┘
// ◀─────────────────┤ start ws connection
// │ │
// ◀─────────────────┤ notify with rev: 3
// │ │
// ├────Pull Rev─────▶
// │ │ ┌──────────────────────────────────┐
// compose ◀────Push Rev─────┤ │ops: ["retain 3", "abcefg"] rev: 3│
// ┌──────────────────────────────────┐│ │ └──────────────────────────────────┘
// │ops: ["123","abc", "efg"] rev: 3 ││ │
// └──────────────────────────────────┘│ │
#[actix_rt::test]
async fn delta_sync_while_local_rev_greater_than_server_rev() {
let test = DocumentTest::new().await;

View File

@ -114,7 +114,7 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
let doc_id = context.read().doc_id.clone();
match script {
DocScript::ConnectWs => {
sleep(Duration::from_millis(300)).await;
// sleep(Duration::from_millis(300)).await;
let user_session = context.read().user_session.clone();
let token = user_session.token().unwrap();
let _ = user_session.start_ws_connection(&token).await.unwrap();
@ -126,13 +126,12 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
context.read().client_edit_context().insert(index, s).await.unwrap();
},
DocScript::AssertClient(s) => {
sleep(Duration::from_millis(300)).await;
sleep(Duration::from_millis(100)).await;
let json = context.read().client_edit_context().doc_json().await.unwrap();
assert_eq(s, &json);
},
DocScript::AssertServer(s) => {
sleep(Duration::from_millis(300)).await;
sleep(Duration::from_millis(100)).await;
let pg_pool = context.read().pool.clone();
let doc_manager = context.read().doc_manager.clone();
let edit_doc = doc_manager.get(&doc_id, pg_pool).await.unwrap().unwrap();

View File

@ -7,11 +7,11 @@ edition = "2018"
[lib]
name = "dart_ffi"
# this value will change depending on the target os
# for iOS it would be `cdylib`
# for Macos it would be `cdylib`
# for iOS it would be `rlib`
# for Macos it would be `rlib`
# for android it would be `c-dylib`
# default cdylib
crate-type = ["cdylib"]
# default rlib
crate-type = ["rlib"]
[dependencies]

View File

@ -127,3 +127,14 @@ pub struct RevisionRange {
#[pb(index = 3)]
pub to_rev_id: i64,
}
impl RevisionRange {
pub fn len(&self) -> i64 {
debug_assert!(self.to_rev_id >= self.from_rev_id);
if self.to_rev_id >= self.from_rev_id {
self.to_rev_id - self.from_rev_id
} else {
0
}
}
}

View File

@ -260,7 +260,7 @@ impl ClientEditDoc {
},
WsDataType::PullRev => {
let range = RevisionRange::try_from(bytes)?;
let revision = self.rev_manager.send_revisions(range).await?;
let revision = self.rev_manager.construct_revisions(range).await?;
self.ws.send(revision.into());
},
WsDataType::NewDocUser => {},

View File

@ -65,7 +65,7 @@ impl RevisionManager {
pub fn update_rev_id(&self, rev_id: i64) { self.rev_id_counter.set(rev_id); }
pub async fn send_revisions(&self, range: RevisionRange) -> Result<Revision, DocError> {
pub async fn construct_revisions(&self, range: RevisionRange) -> Result<Revision, DocError> {
debug_assert!(&range.doc_id == &self.doc_id);
let (ret, rx) = oneshot::channel();
let sender = self.rev_store.clone();

View File

@ -5,7 +5,7 @@ use crate::{
sql_tables::{RevState, RevTableSql},
};
use async_stream::stream;
use dashmap::DashMap;
use dashmap::{mapref::one::Ref, DashMap};
use flowy_database::ConnectionPool;
use flowy_ot::core::{Attributes, Delta, OperationTransformable};
use futures::{stream::StreamExt, TryFutureExt};
@ -85,7 +85,7 @@ impl RevisionStoreActor {
self.handle_revision_acked(rev_id).await;
},
RevisionCmd::GetRevisions { range, ret } => {
let result = revs_in_range(&self.doc_id, self.persistence.clone(), range).await;
let result = self.revs_in_range(range).await;
let _ = ret.send(result);
},
RevisionCmd::DocumentDelta { ret } => {
@ -150,6 +150,37 @@ impl RevisionStoreActor {
}
}));
}
async fn revs_in_range(&self, range: RevisionRange) -> DocResult<Vec<Revision>> {
let iter_range = (range.from_rev_id..=range.to_rev_id);
let revs = iter_range
.flat_map(|rev_id| {
//
match self.revs.get(&rev_id) {
None => None,
Some(rev) => Some((&*(*rev)).clone()),
}
})
.collect::<Vec<Revision>>();
debug_assert!(revs.len() == range.len() as usize);
if revs.len() == range.len() as usize {
Ok(revs)
} else {
let doc_id = self.doc_id.clone();
let persistence = self.persistence.clone();
let result = spawn_blocking(move || {
let conn = &*persistence.pool.get().map_err(internal_error)?;
let revisions = persistence.rev_sql.read_rev_tables_with_range(&doc_id, range, conn)?;
Ok(revisions)
})
.await
.map_err(internal_error)?;
result
}
}
}
async fn fetch_document(
@ -216,19 +247,6 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc<Persistence>) -> DocRes
.map_err(internal_error)?
}
async fn revs_in_range(doc_id: &str, persistence: Arc<Persistence>, range: RevisionRange) -> DocResult<Vec<Revision>> {
let doc_id = doc_id.to_owned();
let result = spawn_blocking(move || {
let conn = &*persistence.pool.get().map_err(internal_error)?;
let revisions = persistence.rev_sql.read_rev_tables_with_range(&doc_id, range, conn)?;
Ok(revisions)
})
.await
.map_err(internal_error)?;
result
}
struct Persistence {
rev_sql: Arc<RevTableSql>,
pool: Arc<ConnectionPool>,