mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-24 22:57:12 -04:00
send revsion in sequence
This commit is contained in:
parent
f84fb6f87f
commit
97ac22ec02
12 changed files with 316 additions and 213 deletions
|
@ -48,8 +48,6 @@ impl ServerEditDoc {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn document_json(&self) -> String { self.document.read().to_json() }
|
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
level = "debug",
|
level = "debug",
|
||||||
skip(self, user),
|
skip(self, user),
|
||||||
|
@ -64,14 +62,14 @@ impl ServerEditDoc {
|
||||||
match cur_rev_id.cmp(&rev_id) {
|
match cur_rev_id.cmp(&rev_id) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
user.socket
|
user.socket
|
||||||
.do_send(mk_pull_rev_ws_message(&self.doc_id, next(cur_rev_id), rev_id))
|
.do_send(mk_pull_message(&self.doc_id, next(cur_rev_id), rev_id))
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
},
|
},
|
||||||
Ordering::Equal => {},
|
Ordering::Equal => {},
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
let doc_delta = self.document.read().delta().clone();
|
let doc_delta = self.document.read().delta().clone();
|
||||||
let cli_revision = self.mk_revision(rev_id, doc_delta);
|
let cli_revision = self.mk_revision(rev_id, doc_delta);
|
||||||
let ws_cli_revision = mk_push_rev_ws_message(&self.doc_id, cli_revision);
|
let ws_cli_revision = mk_push_message(&self.doc_id, cli_revision);
|
||||||
user.socket.do_send(ws_cli_revision).map_err(internal_error)?;
|
user.socket.do_send(ws_cli_revision).map_err(internal_error)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -94,38 +92,42 @@ impl ServerEditDoc {
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
pg_pool: Data<PgPool>,
|
pg_pool: Data<PgPool>,
|
||||||
) -> Result<(), ServerError> {
|
) -> Result<(), ServerError> {
|
||||||
// Opti: find out another way to keep the user socket available.
|
|
||||||
self.users.insert(user.id(), user.clone());
|
self.users.insert(user.id(), user.clone());
|
||||||
let cur_rev_id = self.rev_id.load(SeqCst);
|
let cur_rev_id = self.rev_id.load(SeqCst);
|
||||||
match cur_rev_id.cmp(&revision.rev_id) {
|
match cur_rev_id.cmp(&revision.rev_id) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let next_rev_id = next(cur_rev_id);
|
let next_rev_id = next(cur_rev_id);
|
||||||
if next_rev_id == revision.base_rev_id || cur_rev_id == revision.base_rev_id {
|
if cur_rev_id == revision.base_rev_id || next_rev_id == revision.base_rev_id {
|
||||||
|
// The rev is in the right order, just compose it.
|
||||||
let _ = self.compose_revision(&revision, pg_pool).await?;
|
let _ = self.compose_revision(&revision, pg_pool).await?;
|
||||||
user.socket
|
user.socket
|
||||||
.do_send(mk_acked_ws_message(&revision))
|
.do_send(mk_acked_message(&revision))
|
||||||
|
.map_err(internal_error)?;
|
||||||
|
} else {
|
||||||
|
// The server document is outdated, pull the missing revision from the client.
|
||||||
|
user.socket
|
||||||
|
.do_send(mk_pull_message(&self.doc_id, next_rev_id, revision.rev_id))
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The server document is outdated, try to get the missing revision from the
|
|
||||||
// client.
|
|
||||||
user.socket
|
|
||||||
.do_send(mk_pull_rev_ws_message(&self.doc_id, next_rev_id, revision.rev_id))
|
|
||||||
.map_err(internal_error)?;
|
|
||||||
},
|
},
|
||||||
Ordering::Equal => {},
|
Ordering::Equal => {
|
||||||
|
// Do nothing
|
||||||
|
},
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
// The client document is outdated. Transform the client revision delta and then
|
// The client document is outdated. Transform the client revision delta and then
|
||||||
// send the prime delta to the client. Client should compose the this prime
|
// send the prime delta to the client. Client should compose the this prime
|
||||||
// delta.
|
// delta.
|
||||||
let cli_revision = self.transform_revision(&revision)?;
|
let cli_revision = self.transform_revision(&revision)?;
|
||||||
let ws_cli_revision = mk_push_rev_ws_message(&self.doc_id, cli_revision);
|
user.socket
|
||||||
user.socket.do_send(ws_cli_revision).map_err(internal_error)?;
|
.do_send(mk_push_message(&self.doc_id, cli_revision))
|
||||||
|
.map_err(internal_error)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn document_json(&self) -> String { self.document.read().to_json() }
|
||||||
|
|
||||||
async fn compose_revision(&self, revision: &Revision, pg_pool: Data<PgPool>) -> Result<(), ServerError> {
|
async fn compose_revision(&self, revision: &Revision, pg_pool: Data<PgPool>) -> Result<(), ServerError> {
|
||||||
let delta = Delta::from_bytes(&revision.delta_data).map_err(internal_error)?;
|
let delta = Delta::from_bytes(&revision.delta_data).map_err(internal_error)?;
|
||||||
let _ = self.compose_delta(delta)?;
|
let _ = self.compose_delta(delta)?;
|
||||||
|
@ -197,7 +199,7 @@ impl ServerEditDoc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_push_rev_ws_message(doc_id: &str, revision: Revision) -> WsMessageAdaptor {
|
fn mk_push_message(doc_id: &str, revision: Revision) -> WsMessageAdaptor {
|
||||||
let bytes = revision.write_to_bytes().unwrap();
|
let bytes = revision.write_to_bytes().unwrap();
|
||||||
let data = WsDocumentData {
|
let data = WsDocumentData {
|
||||||
doc_id: doc_id.to_string(),
|
doc_id: doc_id.to_string(),
|
||||||
|
@ -207,7 +209,7 @@ fn mk_push_rev_ws_message(doc_id: &str, revision: Revision) -> WsMessageAdaptor
|
||||||
mk_ws_message(data)
|
mk_ws_message(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_pull_rev_ws_message(doc_id: &str, from_rev_id: i64, to_rev_id: i64) -> WsMessageAdaptor {
|
fn mk_pull_message(doc_id: &str, from_rev_id: i64, to_rev_id: i64) -> WsMessageAdaptor {
|
||||||
let range = RevisionRange {
|
let range = RevisionRange {
|
||||||
doc_id: doc_id.to_string(),
|
doc_id: doc_id.to_string(),
|
||||||
start: from_rev_id,
|
start: from_rev_id,
|
||||||
|
@ -224,7 +226,7 @@ fn mk_pull_rev_ws_message(doc_id: &str, from_rev_id: i64, to_rev_id: i64) -> WsM
|
||||||
mk_ws_message(data)
|
mk_ws_message(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_acked_ws_message(revision: &Revision) -> WsMessageAdaptor {
|
fn mk_acked_message(revision: &Revision) -> WsMessageAdaptor {
|
||||||
// let mut wtr = vec![];
|
// let mut wtr = vec![];
|
||||||
// let _ = wtr.write_i64::<BigEndian>(revision.rev_id);
|
// let _ = wtr.write_i64::<BigEndian>(revision.rev_id);
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,20 @@ async fn delta_sync_while_editing() {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn delta_sync_multi_revs() {
|
||||||
|
let test = DocumentTest::new().await;
|
||||||
|
test.run_scripts(vec![
|
||||||
|
DocScript::ConnectWs,
|
||||||
|
DocScript::OpenDoc,
|
||||||
|
DocScript::InsertText(0, "abc"),
|
||||||
|
DocScript::InsertText(3, "123"),
|
||||||
|
DocScript::InsertText(6, "efg"),
|
||||||
|
DocScript::InsertText(9, "456"),
|
||||||
|
])
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn delta_sync_while_editing_with_attribute() {
|
async fn delta_sync_while_editing_with_attribute() {
|
||||||
let test = DocumentTest::new().await;
|
let test = DocumentTest::new().await;
|
||||||
|
|
|
@ -41,6 +41,7 @@ md5 = "0.7.0"
|
||||||
byteorder = {version = "1.3.4"}
|
byteorder = {version = "1.3.4"}
|
||||||
async-stream = "0.3.2"
|
async-stream = "0.3.2"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
|
pin-project = "1.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
flowy-test = { path = "../flowy-test" }
|
flowy-test = { path = "../flowy-test" }
|
||||||
|
|
|
@ -80,6 +80,10 @@ pub struct Revision {
|
||||||
pub ty: RevType,
|
pub ty: RevType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Revision {
|
||||||
|
pub fn is_empty(&self) -> bool { self.base_rev_id == self.rev_id }
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Revision {
|
impl std::fmt::Debug for Revision {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
let _ = f.write_fmt(format_args!("doc_id {}, ", self.doc_id))?;
|
let _ = f.write_fmt(format_args!("doc_id {}, ", self.doc_id))?;
|
||||||
|
@ -108,7 +112,10 @@ impl Revision {
|
||||||
let delta_data = delta.as_ref().to_vec();
|
let delta_data = delta.as_ref().to_vec();
|
||||||
let base_rev_id = base_rev_id.into();
|
let base_rev_id = base_rev_id.into();
|
||||||
let rev_id = rev_id.into();
|
let rev_id = rev_id.into();
|
||||||
debug_assert!(base_rev_id != rev_id);
|
|
||||||
|
if base_rev_id != 0 {
|
||||||
|
debug_assert!(base_rev_id != rev_id);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base_rev_id,
|
base_rev_id,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(vecdeque_binary_search)]
|
||||||
|
|
||||||
pub mod entities;
|
pub mod entities;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
cache::DocCache,
|
cache::DocCache,
|
||||||
doc::{
|
doc::{
|
||||||
edit::{ClientEditDoc, EditDocWsHandler},
|
edit::{ClientEditDoc, EditDocWsHandler},
|
||||||
revision::{DocRevision, RevisionServer},
|
revision::RevisionServer,
|
||||||
},
|
},
|
||||||
server::Server,
|
server::Server,
|
||||||
ws::WsDocumentManager,
|
ws::WsDocumentManager,
|
||||||
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use flowy_database::ConnectionPool;
|
use flowy_database::ConnectionPool;
|
||||||
use flowy_infra::future::{wrap_future, FnFuture, ResultFuture};
|
use flowy_infra::future::{wrap_future, FnFuture, ResultFuture};
|
||||||
use flowy_ot::core::Delta;
|
|
||||||
use tokio::time::{interval, Duration};
|
use tokio::time::{interval, Duration};
|
||||||
|
|
||||||
pub(crate) struct DocController {
|
pub(crate) struct DocController {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
entities::{
|
entities::{
|
||||||
doc::{Doc, DocDelta, RevId, RevType, Revision, RevisionRange},
|
doc::{DocDelta, RevId, RevType, Revision, RevisionRange},
|
||||||
ws::{WsDataType, WsDocumentData},
|
ws::{WsDataType, WsDocumentData},
|
||||||
},
|
},
|
||||||
errors::{internal_error, DocError, DocResult},
|
errors::{internal_error, DocError, DocResult},
|
||||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
message::{DocumentMsg, TransformDeltas},
|
message::{DocumentMsg, TransformDeltas},
|
||||||
model::OpenDocAction,
|
model::OpenDocAction,
|
||||||
},
|
},
|
||||||
revision::{DocRevision, RevisionCmd, RevisionManager, RevisionServer, RevisionStoreActor},
|
revision::{DocRevision, RevisionManager, RevisionServer, RevisionStoreActor},
|
||||||
UndoResult,
|
UndoResult,
|
||||||
},
|
},
|
||||||
ws::{DocumentWebSocket, WsDocumentHandler},
|
ws::{DocumentWebSocket, WsDocumentHandler},
|
||||||
|
@ -45,12 +45,14 @@ impl ClientEditDoc {
|
||||||
server: Arc<dyn RevisionServer>,
|
server: Arc<dyn RevisionServer>,
|
||||||
user: Arc<dyn DocumentUser>,
|
user: Arc<dyn DocumentUser>,
|
||||||
) -> DocResult<Self> {
|
) -> DocResult<Self> {
|
||||||
let rev_store = spawn_rev_store_actor(doc_id, pool.clone(), server.clone());
|
let (sender, mut receiver) = mpsc::channel(1);
|
||||||
let doc = load_document(rev_store.clone()).await?;
|
let mut rev_manager = RevisionManager::new(doc_id, pool.clone(), server.clone(), sender);
|
||||||
let delta = doc.delta()?;
|
spawn_rev_receiver(receiver, ws.clone());
|
||||||
let rev_manager = Arc::new(RevisionManager::new(doc_id, doc.rev_id.into(), rev_store));
|
|
||||||
|
let delta = rev_manager.load_document().await?;
|
||||||
let document = spawn_doc_edit_actor(doc_id, delta, pool.clone());
|
let document = spawn_doc_edit_actor(doc_id, delta, pool.clone());
|
||||||
let doc_id = doc_id.to_string();
|
let doc_id = doc_id.to_string();
|
||||||
|
let rev_manager = Arc::new(rev_manager);
|
||||||
let edit_doc = Self {
|
let edit_doc = Self {
|
||||||
doc_id,
|
doc_id,
|
||||||
rev_manager,
|
rev_manager,
|
||||||
|
@ -72,7 +74,7 @@ impl ClientEditDoc {
|
||||||
};
|
};
|
||||||
let _ = self.document.send(msg);
|
let _ = self.document.send(msg);
|
||||||
let delta = rx.await.map_err(internal_error)??;
|
let delta = rx.await.map_err(internal_error)??;
|
||||||
let rev_id = self.mk_revision(delta).await?;
|
let rev_id = self.save_revision(delta).await?;
|
||||||
save_document(self.document.clone(), rev_id.into()).await
|
save_document(self.document.clone(), rev_id.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +83,7 @@ impl ClientEditDoc {
|
||||||
let msg = DocumentMsg::Delete { interval, ret };
|
let msg = DocumentMsg::Delete { interval, ret };
|
||||||
let _ = self.document.send(msg);
|
let _ = self.document.send(msg);
|
||||||
let delta = rx.await.map_err(internal_error)??;
|
let delta = rx.await.map_err(internal_error)??;
|
||||||
let _ = self.mk_revision(delta).await?;
|
let _ = self.save_revision(delta).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +96,7 @@ impl ClientEditDoc {
|
||||||
};
|
};
|
||||||
let _ = self.document.send(msg);
|
let _ = self.document.send(msg);
|
||||||
let delta = rx.await.map_err(internal_error)??;
|
let delta = rx.await.map_err(internal_error)??;
|
||||||
let _ = self.mk_revision(delta).await?;
|
let _ = self.save_revision(delta).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ impl ClientEditDoc {
|
||||||
};
|
};
|
||||||
let _ = self.document.send(msg);
|
let _ = self.document.send(msg);
|
||||||
let delta = rx.await.map_err(internal_error)??;
|
let delta = rx.await.map_err(internal_error)??;
|
||||||
let _ = self.mk_revision(delta).await?;
|
let _ = self.save_revision(delta).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ impl ClientEditDoc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, delta), fields(revision_delta = %delta.to_json(), send_state, base_rev_id, rev_id))]
|
#[tracing::instrument(level = "debug", skip(self, delta), fields(revision_delta = %delta.to_json(), send_state, base_rev_id, rev_id))]
|
||||||
async fn mk_revision(&self, delta: Delta) -> Result<RevId, DocError> {
|
async fn save_revision(&self, delta: Delta) -> Result<RevId, DocError> {
|
||||||
let delta_data = delta.to_bytes();
|
let delta_data = delta.to_bytes();
|
||||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id();
|
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id();
|
||||||
tracing::Span::current().record("base_rev_id", &base_rev_id);
|
tracing::Span::current().record("base_rev_id", &base_rev_id);
|
||||||
|
@ -161,15 +163,6 @@ impl ClientEditDoc {
|
||||||
let delta_data = delta_data.to_vec();
|
let delta_data = delta_data.to_vec();
|
||||||
let revision = Revision::new(base_rev_id, rev_id, delta_data, &self.doc_id, RevType::Local);
|
let revision = Revision::new(base_rev_id, rev_id, delta_data, &self.doc_id, RevType::Local);
|
||||||
let _ = self.rev_manager.add_revision(&revision).await?;
|
let _ = self.rev_manager.add_revision(&revision).await?;
|
||||||
match self.ws.send(revision.into()) {
|
|
||||||
Ok(_) => {
|
|
||||||
tracing::Span::current().record("send_state", &"success");
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
tracing::Span::current().record("send_state", &format!("failed: {:?}", e).as_str());
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(rev_id.into())
|
Ok(rev_id.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +177,7 @@ impl ClientEditDoc {
|
||||||
let _ = self.document.send(msg);
|
let _ = self.document.send(msg);
|
||||||
let _ = rx.await.map_err(internal_error)??;
|
let _ = rx.await.map_err(internal_error)??;
|
||||||
|
|
||||||
let rev_id = self.mk_revision(delta).await?;
|
let rev_id = self.save_revision(delta).await?;
|
||||||
save_document(self.document.clone(), rev_id).await
|
save_document(self.document.clone(), rev_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,14 +297,24 @@ impl WsDocumentHandler for EditDocWsHandler {
|
||||||
match state {
|
match state {
|
||||||
WsState::Init => {},
|
WsState::Init => {},
|
||||||
WsState::Connected(_) => self.0.notify_open_doc(),
|
WsState::Connected(_) => self.0.notify_open_doc(),
|
||||||
WsState::Disconnected(e) => {
|
WsState::Disconnected(_e) => {},
|
||||||
log::error!("websocket error: {:?}", e);
|
|
||||||
//
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_rev_receiver(mut receiver: mpsc::Receiver<Revision>, ws: Arc<dyn DocumentWebSocket>) {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
while let Some(revision) = receiver.recv().await {
|
||||||
|
match ws.send(revision.into()) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => log::error!("Send revision failed: {:?}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async fn save_document(document: UnboundedSender<DocumentMsg>, rev_id: RevId) -> DocResult<()> {
|
async fn save_document(document: UnboundedSender<DocumentMsg>, rev_id: RevId) -> DocResult<()> {
|
||||||
let (ret, rx) = oneshot::channel::<DocResult<()>>();
|
let (ret, rx) = oneshot::channel::<DocResult<()>>();
|
||||||
let _ = document.send(DocumentMsg::SaveDocument { rev_id, ret });
|
let _ = document.send(DocumentMsg::SaveDocument { rev_id, ret });
|
||||||
|
@ -319,27 +322,9 @@ async fn save_document(document: UnboundedSender<DocumentMsg>, rev_id: RevId) ->
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_rev_store_actor(
|
|
||||||
doc_id: &str,
|
|
||||||
pool: Arc<ConnectionPool>,
|
|
||||||
server: Arc<dyn RevisionServer>,
|
|
||||||
) -> mpsc::Sender<RevisionCmd> {
|
|
||||||
let (sender, receiver) = mpsc::channel::<RevisionCmd>(50);
|
|
||||||
let actor = RevisionStoreActor::new(doc_id, pool, receiver, server);
|
|
||||||
tokio::spawn(actor.run());
|
|
||||||
sender
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_doc_edit_actor(doc_id: &str, delta: Delta, pool: Arc<ConnectionPool>) -> UnboundedSender<DocumentMsg> {
|
fn spawn_doc_edit_actor(doc_id: &str, delta: Delta, pool: Arc<ConnectionPool>) -> UnboundedSender<DocumentMsg> {
|
||||||
let (sender, receiver) = mpsc::unbounded_channel::<DocumentMsg>();
|
let (sender, receiver) = mpsc::unbounded_channel::<DocumentMsg>();
|
||||||
let actor = DocumentActor::new(&doc_id, delta, pool.clone(), receiver);
|
let actor = DocumentActor::new(&doc_id, delta, pool.clone(), receiver);
|
||||||
tokio::spawn(actor.run());
|
tokio::spawn(actor.run());
|
||||||
sender
|
sender
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_document(sender: mpsc::Sender<RevisionCmd>) -> DocResult<Doc> {
|
|
||||||
let (ret, rx) = oneshot::channel();
|
|
||||||
let _ = sender.send(RevisionCmd::DocumentDelta { ret }).await;
|
|
||||||
let result = rx.await.map_err(internal_error)?;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
entities::doc::{Doc, RevId, RevType, Revision, RevisionRange},
|
entities::doc::{Doc, RevId, RevType, Revision, RevisionRange},
|
||||||
errors::{internal_error, DocError},
|
errors::{internal_error, DocError, DocResult},
|
||||||
services::{doc::revision::store_actor::RevisionCmd, util::RevIdCounter, ws::DocumentWebSocket},
|
services::{doc::revision::RevisionStoreActor, util::RevIdCounter, ws::DocumentWebSocket},
|
||||||
};
|
};
|
||||||
|
use flowy_database::ConnectionPool;
|
||||||
use flowy_infra::future::ResultFuture;
|
use flowy_infra::future::ResultFuture;
|
||||||
use flowy_ot::core::{Delta, OperationTransformable};
|
use flowy_ot::core::{Delta, OperationTransformable};
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
|
|
||||||
pub struct DocRevision {
|
pub struct DocRevision {
|
||||||
|
@ -20,12 +22,18 @@ pub trait RevisionServer: Send + Sync {
|
||||||
pub struct RevisionManager {
|
pub struct RevisionManager {
|
||||||
doc_id: String,
|
doc_id: String,
|
||||||
rev_id_counter: RevIdCounter,
|
rev_id_counter: RevIdCounter,
|
||||||
rev_store: mpsc::Sender<RevisionCmd>,
|
rev_store: Arc<RevisionStoreActor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionManager {
|
impl RevisionManager {
|
||||||
pub fn new(doc_id: &str, rev_id: RevId, rev_store: mpsc::Sender<RevisionCmd>) -> Self {
|
pub fn new(
|
||||||
let rev_id_counter = RevIdCounter::new(rev_id.into());
|
doc_id: &str,
|
||||||
|
pool: Arc<ConnectionPool>,
|
||||||
|
server: Arc<dyn RevisionServer>,
|
||||||
|
pending_rev_sender: mpsc::Sender<Revision>,
|
||||||
|
) -> Self {
|
||||||
|
let rev_store = Arc::new(RevisionStoreActor::new(doc_id, pool, server, pending_rev_sender));
|
||||||
|
let rev_id_counter = RevIdCounter::new(0);
|
||||||
Self {
|
Self {
|
||||||
doc_id: doc_id.to_string(),
|
doc_id: doc_id.to_string(),
|
||||||
rev_id_counter,
|
rev_id_counter,
|
||||||
|
@ -33,21 +41,21 @@ impl RevisionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn load_document(&mut self) -> DocResult<Delta> {
|
||||||
|
let doc = self.rev_store.fetch_document().await?;
|
||||||
|
self.set_rev_id(doc.rev_id);
|
||||||
|
Ok(doc.delta()?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn add_revision(&self, revision: &Revision) -> Result<(), DocError> {
|
pub async fn add_revision(&self, revision: &Revision) -> Result<(), DocError> {
|
||||||
let (ret, rx) = oneshot::channel();
|
let _ = self.rev_store.handle_new_revision(revision.clone()).await?;
|
||||||
let cmd = RevisionCmd::Revision {
|
Ok(())
|
||||||
revision: revision.clone(),
|
|
||||||
ret,
|
|
||||||
};
|
|
||||||
let _ = self.rev_store.send(cmd).await;
|
|
||||||
let result = rx.await.map_err(internal_error)?;
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ack_rev(&self, rev_id: RevId) -> Result<(), DocError> {
|
pub fn ack_rev(&self, rev_id: RevId) -> Result<(), DocError> {
|
||||||
let sender = self.rev_store.clone();
|
let rev_store = self.rev_store.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _ = sender.send(RevisionCmd::AckRevision { rev_id }).await;
|
rev_store.handle_revision_acked(rev_id).await;
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -64,14 +72,7 @@ impl RevisionManager {
|
||||||
|
|
||||||
pub async fn construct_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);
|
debug_assert!(&range.doc_id == &self.doc_id);
|
||||||
let (ret, rx) = oneshot::channel();
|
let revisions = self.rev_store.revs_in_range(range.clone()).await?;
|
||||||
let sender = self.rev_store.clone();
|
|
||||||
let cmd = RevisionCmd::GetRevisions {
|
|
||||||
range: range.clone(),
|
|
||||||
ret,
|
|
||||||
};
|
|
||||||
let _ = sender.send(cmd).await;
|
|
||||||
let revisions = rx.await.map_err(internal_error)??;
|
|
||||||
let mut new_delta = Delta::new();
|
let mut new_delta = Delta::new();
|
||||||
for revision in revisions {
|
for revision in revisions {
|
||||||
match Delta::from_bytes(revision.delta_data) {
|
match Delta::from_bytes(revision.delta_data) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod manager;
|
mod manager;
|
||||||
mod model;
|
mod model;
|
||||||
mod store_actor;
|
mod rev_store;
|
||||||
|
|
||||||
pub use manager::*;
|
pub use manager::*;
|
||||||
pub use store_actor::*;
|
pub use rev_store::*;
|
||||||
|
|
|
@ -2,43 +2,28 @@ use crate::{entities::doc::Revision, errors::DocResult, services::ws::DocumentWe
|
||||||
|
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
pub type Sender = oneshot::Sender<DocResult<()>>;
|
pub type PendingRevSender = oneshot::Sender<DocResult<()>>;
|
||||||
pub type Receiver = oneshot::Receiver<DocResult<()>>;
|
pub type PendingRevReceiver = oneshot::Receiver<DocResult<()>>;
|
||||||
|
|
||||||
pub struct RevisionOperation {
|
pub struct RevisionContext {
|
||||||
inner: Revision,
|
pub revision: Revision,
|
||||||
ret: Option<Sender>,
|
|
||||||
receiver: Option<Receiver>,
|
|
||||||
pub state: RevState,
|
pub state: RevState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionOperation {
|
impl RevisionContext {
|
||||||
pub fn new(revision: &Revision) -> Self {
|
pub fn new(revision: Revision) -> Self {
|
||||||
let (ret, receiver) = oneshot::channel::<DocResult<()>>();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
inner: revision.clone(),
|
revision,
|
||||||
ret: Some(ret),
|
|
||||||
receiver: Some(receiver),
|
|
||||||
state: RevState::Local,
|
state: RevState::Local,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn receiver(&mut self) -> Receiver { self.receiver.take().expect("Receiver should not be called twice") }
|
|
||||||
|
|
||||||
pub fn finish(&mut self) {
|
|
||||||
self.state = RevState::Acked;
|
|
||||||
match self.ret.take() {
|
|
||||||
None => {},
|
|
||||||
Some(ret) => {
|
|
||||||
let _ = ret.send(Ok(()));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for RevisionOperation {
|
pub(crate) struct PendingRevId {
|
||||||
type Target = Revision;
|
pub rev_id: i64,
|
||||||
|
pub sender: PendingRevSender,
|
||||||
fn deref(&self) -> &Self::Target { &self.inner }
|
}
|
||||||
|
|
||||||
|
impl PendingRevId {
|
||||||
|
pub(crate) fn new(rev_id: i64, sender: PendingRevSender) -> Self { Self { rev_id, sender } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,34 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
entities::doc::{revision_from_doc, Doc, RevId, RevType, Revision, RevisionRange},
|
entities::doc::{revision_from_doc, Doc, RevId, RevType, Revision, RevisionRange},
|
||||||
errors::{internal_error, DocError, DocResult},
|
errors::{internal_error, DocError, DocResult},
|
||||||
services::doc::revision::{model::RevisionOperation, DocRevision, RevisionServer},
|
services::doc::revision::{
|
||||||
|
model::{PendingRevId, PendingRevReceiver, RevisionContext},
|
||||||
|
RevisionServer,
|
||||||
|
},
|
||||||
sql_tables::{RevState, RevTableSql},
|
sql_tables::{RevState, RevTableSql},
|
||||||
};
|
};
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use dashmap::DashMap;
|
use dashmap::{mapref::one::Ref, DashMap, DashSet};
|
||||||
use flowy_database::ConnectionPool;
|
use flowy_database::ConnectionPool;
|
||||||
use flowy_ot::core::{Attributes, Delta, OperationTransformable};
|
use flowy_ot::core::{Delta, OperationTransformable};
|
||||||
use futures::{stream::StreamExt, TryFutureExt};
|
use futures::{stream::StreamExt, TryFutureExt};
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{mpsc, oneshot, RwLock},
|
sync::{mpsc, oneshot, RwLock, RwLockWriteGuard},
|
||||||
task::{spawn_blocking, JoinHandle},
|
task::{spawn_blocking, JoinHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum RevisionCmd {
|
|
||||||
Revision {
|
|
||||||
revision: Revision,
|
|
||||||
ret: oneshot::Sender<DocResult<()>>,
|
|
||||||
},
|
|
||||||
AckRevision {
|
|
||||||
rev_id: RevId,
|
|
||||||
},
|
|
||||||
GetRevisions {
|
|
||||||
range: RevisionRange,
|
|
||||||
ret: oneshot::Sender<DocResult<Vec<Revision>>>,
|
|
||||||
},
|
|
||||||
DocumentDelta {
|
|
||||||
ret: oneshot::Sender<DocResult<Doc>>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RevisionStoreActor {
|
pub struct RevisionStoreActor {
|
||||||
doc_id: String,
|
doc_id: String,
|
||||||
persistence: Arc<Persistence>,
|
persistence: Arc<Persistence>,
|
||||||
revs: Arc<DashMap<i64, RevisionOperation>>,
|
revs_map: Arc<DashMap<i64, RevisionContext>>,
|
||||||
|
pending_revs_sender: RevSender,
|
||||||
|
pending_revs: Arc<RwLock<VecDeque<PendingRevId>>>,
|
||||||
delay_save: RwLock<Option<JoinHandle<()>>>,
|
delay_save: RwLock<Option<JoinHandle<()>>>,
|
||||||
receiver: Option<mpsc::Receiver<RevisionCmd>>,
|
|
||||||
server: Arc<dyn RevisionServer>,
|
server: Arc<dyn RevisionServer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,74 +36,70 @@ impl RevisionStoreActor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
doc_id: &str,
|
doc_id: &str,
|
||||||
pool: Arc<ConnectionPool>,
|
pool: Arc<ConnectionPool>,
|
||||||
receiver: mpsc::Receiver<RevisionCmd>,
|
|
||||||
server: Arc<dyn RevisionServer>,
|
server: Arc<dyn RevisionServer>,
|
||||||
|
pending_rev_sender: mpsc::Sender<Revision>,
|
||||||
) -> RevisionStoreActor {
|
) -> RevisionStoreActor {
|
||||||
let persistence = Arc::new(Persistence::new(pool));
|
|
||||||
let revs = Arc::new(DashMap::new());
|
|
||||||
let doc_id = doc_id.to_owned();
|
let doc_id = doc_id.to_owned();
|
||||||
|
let persistence = Arc::new(Persistence::new(pool));
|
||||||
|
let revs_map = Arc::new(DashMap::new());
|
||||||
|
let (pending_revs_sender, receiver) = mpsc::unbounded_channel();
|
||||||
|
let pending_revs = Arc::new(RwLock::new(VecDeque::new()));
|
||||||
|
let pending = PendingRevision::new(
|
||||||
|
&doc_id,
|
||||||
|
receiver,
|
||||||
|
persistence.clone(),
|
||||||
|
revs_map.clone(),
|
||||||
|
pending_rev_sender,
|
||||||
|
pending_revs.clone(),
|
||||||
|
);
|
||||||
|
tokio::spawn(pending.run());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
doc_id,
|
doc_id,
|
||||||
persistence,
|
persistence,
|
||||||
revs,
|
revs_map,
|
||||||
|
pending_revs_sender,
|
||||||
|
pending_revs,
|
||||||
delay_save: RwLock::new(None),
|
delay_save: RwLock::new(None),
|
||||||
receiver: Some(receiver),
|
|
||||||
server,
|
server,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(mut self) {
|
|
||||||
let mut receiver = self.receiver.take().expect("Should only call once");
|
|
||||||
let stream = stream! {
|
|
||||||
loop {
|
|
||||||
match receiver.recv().await {
|
|
||||||
Some(msg) => yield msg,
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
stream.for_each(|msg| self.handle_message(msg)).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_message(&self, cmd: RevisionCmd) {
|
|
||||||
match cmd {
|
|
||||||
RevisionCmd::Revision { revision, ret } => {
|
|
||||||
let result = self.handle_new_revision(revision).await;
|
|
||||||
let _ = ret.send(result);
|
|
||||||
},
|
|
||||||
RevisionCmd::AckRevision { rev_id } => {
|
|
||||||
self.handle_revision_acked(rev_id).await;
|
|
||||||
},
|
|
||||||
RevisionCmd::GetRevisions { range, ret } => {
|
|
||||||
let result = self.revs_in_range(range).await;
|
|
||||||
let _ = ret.send(result);
|
|
||||||
},
|
|
||||||
RevisionCmd::DocumentDelta { ret } => {
|
|
||||||
let delta = self.fetch_document().await;
|
|
||||||
let _ = ret.send(delta);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, revision))]
|
#[tracing::instrument(level = "debug", skip(self, revision))]
|
||||||
async fn handle_new_revision(&self, revision: Revision) -> DocResult<()> {
|
pub async fn handle_new_revision(&self, revision: Revision) -> DocResult<()> {
|
||||||
if self.revs.contains_key(&revision.rev_id) {
|
if self.revs_map.contains_key(&revision.rev_id) {
|
||||||
return Err(DocError::duplicate_rev().context(format!("Duplicate revision id: {}", revision.rev_id)));
|
return Err(DocError::duplicate_rev().context(format!("Duplicate revision id: {}", revision.rev_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut operation = RevisionOperation::new(&revision);
|
self.pending_revs_sender.send(PendingRevisionMsg::Revision {
|
||||||
let _receiver = operation.receiver();
|
revision: revision.clone(),
|
||||||
self.revs.insert(revision.rev_id, operation);
|
});
|
||||||
|
self.revs_map.insert(revision.rev_id, RevisionContext::new(revision));
|
||||||
self.save_revisions().await;
|
self.save_revisions().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, rev_id))]
|
#[tracing::instrument(level = "debug", skip(self, rev_id))]
|
||||||
async fn handle_revision_acked(&self, rev_id: RevId) {
|
pub async fn handle_revision_acked(&self, rev_id: RevId) {
|
||||||
match self.revs.get_mut(rev_id.as_ref()) {
|
let rev_id = rev_id.value;
|
||||||
|
log::debug!("Receive revision acked: {}", rev_id);
|
||||||
|
match self.pending_revs.write().await.pop_front() {
|
||||||
None => {},
|
None => {},
|
||||||
Some(mut rev) => rev.value_mut().finish(),
|
Some(pending) => {
|
||||||
|
debug_assert!(pending.rev_id == rev_id);
|
||||||
|
if pending.rev_id != rev_id {
|
||||||
|
log::error!(
|
||||||
|
"Acked: expected rev_id: {:?}, but receive: {:?}",
|
||||||
|
pending.rev_id,
|
||||||
|
rev_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pending.sender.send(Ok(()));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
match self.revs_map.get_mut(&rev_id) {
|
||||||
|
None => {},
|
||||||
|
Some(mut rev) => rev.value_mut().state = RevState::Acked,
|
||||||
}
|
}
|
||||||
self.save_revisions().await;
|
self.save_revisions().await;
|
||||||
}
|
}
|
||||||
|
@ -122,42 +109,41 @@ impl RevisionStoreActor {
|
||||||
handler.abort();
|
handler.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.revs.is_empty() {
|
if self.revs_map.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let revs = self.revs.clone();
|
let revs_map = self.revs_map.clone();
|
||||||
let persistence = self.persistence.clone();
|
let persistence = self.persistence.clone();
|
||||||
|
|
||||||
*self.delay_save.write().await = Some(tokio::spawn(async move {
|
*self.delay_save.write().await = Some(tokio::spawn(async move {
|
||||||
tokio::time::sleep(Duration::from_millis(300)).await;
|
tokio::time::sleep(Duration::from_millis(300)).await;
|
||||||
|
let ids = revs_map.iter().map(|kv| kv.key().clone()).collect::<Vec<i64>>();
|
||||||
let ids = revs.iter().map(|kv| kv.key().clone()).collect::<Vec<i64>>();
|
let revisions_state = revs_map
|
||||||
let revisions = revs
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|kv| ((*kv.value()).clone(), kv.state))
|
.map(|kv| (kv.revision.clone(), kv.state))
|
||||||
.collect::<Vec<(Revision, RevState)>>();
|
.collect::<Vec<(Revision, RevState)>>();
|
||||||
|
|
||||||
// TODO: Ok to unwrap?
|
// TODO: Ok to unwrap?
|
||||||
let conn = &*persistence.pool.get().map_err(internal_error).unwrap();
|
let conn = &*persistence.pool.get().map_err(internal_error).unwrap();
|
||||||
let result = conn.immediate_transaction::<_, DocError, _>(|| {
|
let result = conn.immediate_transaction::<_, DocError, _>(|| {
|
||||||
let _ = persistence.rev_sql.create_rev_table(revisions, conn).unwrap();
|
let _ = persistence.rev_sql.create_rev_table(revisions_state, conn).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => revs.retain(|k, _| !ids.contains(k)),
|
Ok(_) => revs_map.retain(|k, _| !ids.contains(k)),
|
||||||
Err(e) => log::error!("Save revision failed: {:?}", e),
|
Err(e) => log::error!("Save revision failed: {:?}", e),
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revs_in_range(&self, range: RevisionRange) -> DocResult<Vec<Revision>> {
|
pub async fn revs_in_range(&self, range: RevisionRange) -> DocResult<Vec<Revision>> {
|
||||||
let revs = range
|
let revs = range
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|rev_id| match self.revs.get(&rev_id) {
|
.flat_map(|rev_id| match self.revs_map.get(&rev_id) {
|
||||||
None => None,
|
None => None,
|
||||||
Some(rev) => Some((&*(*rev)).clone()),
|
Some(rev) => Some(rev.revision.clone()),
|
||||||
})
|
})
|
||||||
.collect::<Vec<Revision>>();
|
.collect::<Vec<Revision>>();
|
||||||
|
|
||||||
|
@ -167,7 +153,7 @@ impl RevisionStoreActor {
|
||||||
let doc_id = self.doc_id.clone();
|
let doc_id = self.doc_id.clone();
|
||||||
let persistence = self.persistence.clone();
|
let persistence = self.persistence.clone();
|
||||||
let result = spawn_blocking(move || {
|
let result = spawn_blocking(move || {
|
||||||
let conn = &*persistence.pool.get().map_err(internal_error)?;
|
let conn = &*persistence.pool.get().map_err(internal_error).unwrap();
|
||||||
let revisions = persistence.rev_sql.read_rev_tables_with_range(&doc_id, range, conn)?;
|
let revisions = persistence.rev_sql.read_rev_tables_with_range(&doc_id, range, conn)?;
|
||||||
Ok(revisions)
|
Ok(revisions)
|
||||||
})
|
})
|
||||||
|
@ -178,7 +164,7 @@ impl RevisionStoreActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_document(&self) -> DocResult<Doc> {
|
pub async fn fetch_document(&self) -> DocResult<Doc> {
|
||||||
let result = fetch_from_local(&self.doc_id, self.persistence.clone()).await;
|
let result = fetch_from_local(&self.doc_id, self.persistence.clone()).await;
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
return result;
|
return result;
|
||||||
|
@ -186,7 +172,16 @@ impl RevisionStoreActor {
|
||||||
|
|
||||||
let doc = self.server.fetch_document_from_remote(&self.doc_id).await?;
|
let doc = self.server.fetch_document_from_remote(&self.doc_id).await?;
|
||||||
let revision = revision_from_doc(doc.clone(), RevType::Remote);
|
let revision = revision_from_doc(doc.clone(), RevType::Remote);
|
||||||
let _ = self.handle_new_revision(revision).await?;
|
let conn = &*self.persistence.pool.get().map_err(internal_error).unwrap();
|
||||||
|
let _ = conn.immediate_transaction::<_, DocError, _>(|| {
|
||||||
|
let _ = self
|
||||||
|
.persistence
|
||||||
|
.rev_sql
|
||||||
|
.create_rev_table(vec![(revision, RevState::Acked)], conn)
|
||||||
|
.unwrap();
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(doc)
|
Ok(doc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,6 +232,106 @@ impl Persistence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PendingRevisionMsg {
|
||||||
|
Revision { revision: Revision },
|
||||||
|
}
|
||||||
|
|
||||||
|
type RevSender = mpsc::UnboundedSender<PendingRevisionMsg>;
|
||||||
|
type RevReceiver = mpsc::UnboundedReceiver<PendingRevisionMsg>;
|
||||||
|
|
||||||
|
struct PendingRevision {
|
||||||
|
doc_id: String,
|
||||||
|
pending_revs: Arc<RwLock<VecDeque<PendingRevId>>>,
|
||||||
|
persistence: Arc<Persistence>,
|
||||||
|
revs_map: Arc<DashMap<i64, RevisionContext>>,
|
||||||
|
msg_receiver: Option<RevReceiver>,
|
||||||
|
next_rev: mpsc::Sender<Revision>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingRevision {
|
||||||
|
pub fn new(
|
||||||
|
doc_id: &str,
|
||||||
|
msg_receiver: RevReceiver,
|
||||||
|
persistence: Arc<Persistence>,
|
||||||
|
revs_map: Arc<DashMap<i64, RevisionContext>>,
|
||||||
|
next_rev: mpsc::Sender<Revision>,
|
||||||
|
pending_revs: Arc<RwLock<VecDeque<PendingRevId>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
doc_id: doc_id.to_owned(),
|
||||||
|
pending_revs,
|
||||||
|
msg_receiver: Some(msg_receiver),
|
||||||
|
persistence,
|
||||||
|
revs_map,
|
||||||
|
next_rev,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(mut self) {
|
||||||
|
let mut receiver = self.msg_receiver.take().expect("Should only call once");
|
||||||
|
let stream = stream! {
|
||||||
|
loop {
|
||||||
|
match receiver.recv().await {
|
||||||
|
Some(msg) => yield msg,
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stream
|
||||||
|
.for_each(|msg| async {
|
||||||
|
match self.handle_msg(msg).await {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => log::error!("{:?}", e),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_msg(&self, msg: PendingRevisionMsg) -> DocResult<()> {
|
||||||
|
match msg {
|
||||||
|
PendingRevisionMsg::Revision { revision } => self.handle_revision(revision).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_revision(&self, revision: Revision) -> DocResult<()> {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let pending_rev = PendingRevId {
|
||||||
|
rev_id: revision.rev_id,
|
||||||
|
sender,
|
||||||
|
};
|
||||||
|
self.pending_revs.write().await.push_back(pending_rev);
|
||||||
|
let _ = self.prepare_next_pending_rev(receiver).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prepare_next_pending_rev(&self, done: PendingRevReceiver) -> DocResult<()> {
|
||||||
|
let next_rev_notify = self.next_rev.clone();
|
||||||
|
let doc_id = self.doc_id.clone();
|
||||||
|
let _ = match self.pending_revs.read().await.front() {
|
||||||
|
None => Ok(()),
|
||||||
|
Some(pending) => match self.revs_map.get(&pending.rev_id) {
|
||||||
|
None => {
|
||||||
|
let conn = self.persistence.pool.get().map_err(internal_error)?;
|
||||||
|
let some = self
|
||||||
|
.persistence
|
||||||
|
.rev_sql
|
||||||
|
.read_rev_table(&doc_id, &pending.rev_id, &*conn)?;
|
||||||
|
match some {
|
||||||
|
Some(revision) => next_rev_notify.send(revision).await.map_err(internal_error),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(context) => next_rev_notify
|
||||||
|
.send(context.revision.clone())
|
||||||
|
.await
|
||||||
|
.map_err(internal_error),
|
||||||
|
},
|
||||||
|
}?;
|
||||||
|
let _ = tokio::time::timeout(Duration::from_millis(2000), done).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fn update_revisions(&self) {
|
// fn update_revisions(&self) {
|
||||||
// let rev_ids = self
|
// let rev_ids = self
|
||||||
// .revs
|
// .revs
|
|
@ -51,21 +51,16 @@ impl RevTableSql {
|
||||||
|
|
||||||
pub(crate) fn read_rev_tables(
|
pub(crate) fn read_rev_tables(
|
||||||
&self,
|
&self,
|
||||||
doc_id_s: &str,
|
did: &str,
|
||||||
rev_id_s: Option<i64>,
|
rid: Option<i64>,
|
||||||
conn: &SqliteConnection,
|
conn: &SqliteConnection,
|
||||||
) -> Result<Vec<Revision>, DocError> {
|
) -> Result<Vec<Revision>, DocError> {
|
||||||
let mut filter = dsl::rev_table
|
let mut filter = dsl::rev_table.filter(doc_id.eq(did)).order(rev_id.asc()).into_boxed();
|
||||||
.filter(doc_id.eq(doc_id_s))
|
if let Some(rid) = rid {
|
||||||
.order(rev_id.asc())
|
filter = filter.filter(rev_id.eq(rid))
|
||||||
.into_boxed();
|
|
||||||
|
|
||||||
if let Some(rev_id_s) = rev_id_s {
|
|
||||||
filter = filter.filter(rev_id.eq(rev_id_s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let rev_tables = filter.load::<RevTable>(conn)?;
|
let rev_tables = filter.load::<RevTable>(conn)?;
|
||||||
|
|
||||||
let revisions = rev_tables
|
let revisions = rev_tables
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|table| table.into())
|
.map(|table| table.into())
|
||||||
|
@ -73,6 +68,22 @@ impl RevTableSql {
|
||||||
Ok(revisions)
|
Ok(revisions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_rev_table(
|
||||||
|
&self,
|
||||||
|
did: &str,
|
||||||
|
rid: &i64,
|
||||||
|
conn: &SqliteConnection,
|
||||||
|
) -> Result<Option<Revision>, DocError> {
|
||||||
|
let filter = dsl::rev_table.filter(doc_id.eq(did)).filter(rev_id.eq(rid));
|
||||||
|
let result = filter.first::<RevTable>(conn);
|
||||||
|
|
||||||
|
if Err(diesel::NotFound) == result {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(result?.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn read_rev_tables_with_range(
|
pub(crate) fn read_rev_tables_with_range(
|
||||||
&self,
|
&self,
|
||||||
doc_id_s: &str,
|
doc_id_s: &str,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue