|
@@ -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<()> {
|
|
|
|
- if self.revs.contains_key(&revision.rev_id) {
|
|
|
|
|
|
+ pub async fn handle_new_revision(&self, revision: Revision) -> DocResult<()> {
|
|
|
|
+ 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);
|
|
|
|
- let _receiver = operation.receiver();
|
|
|
|
- self.revs.insert(revision.rev_id, operation);
|
|
|
|
|
|
+ self.pending_revs_sender.send(PendingRevisionMsg::Revision {
|
|
|
|
+ revision: revision.clone(),
|
|
|
|
+ });
|
|
|
|
+ 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) {
|
|
|
|
- match self.revs.get_mut(rev_id.as_ref()) {
|
|
|
|
|
|
+ pub async fn handle_revision_acked(&self, rev_id: RevId) {
|
|
|
|
+ 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.iter().map(|kv| kv.key().clone()).collect::<Vec<i64>>();
|
|
|
|
- let revisions = revs
|
|
|
|
|
|
+ let ids = revs_map.iter().map(|kv| kv.key().clone()).collect::<Vec<i64>>();
|
|
|
|
+ let revisions_state = revs_map
|
|
.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
|