|
@@ -1,310 +1,283 @@
|
|
|
-use crate::core::{
|
|
|
- web_socket::{DocumentWSSinkDataProvider, DocumentWSSteamConsumer, HttpWebSocketManager},
|
|
|
- DocumentRevisionManager,
|
|
|
- DocumentWSReceiver,
|
|
|
- DocumentWebSocket,
|
|
|
- EditorCommand,
|
|
|
- TransformDeltas,
|
|
|
+use crate::{
|
|
|
+ core::SYNC_INTERVAL_IN_MILLIS,
|
|
|
+ ws_receivers::{DocumentWSReceiver, DocumentWebSocket},
|
|
|
};
|
|
|
+use async_stream::stream;
|
|
|
use bytes::Bytes;
|
|
|
-use flowy_collaboration::{
|
|
|
- entities::{
|
|
|
- revision::{RepeatedRevision, Revision, RevisionRange},
|
|
|
- ws::{DocumentClientWSData, NewDocumentUser},
|
|
|
- },
|
|
|
- errors::CollaborateResult,
|
|
|
+use flowy_collaboration::entities::{
|
|
|
+ revision::{RevId, RevisionRange},
|
|
|
+ ws::{DocumentClientWSData, DocumentServerWSData, DocumentServerWSDataType, NewDocumentUser},
|
|
|
};
|
|
|
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
|
|
+use futures::stream::StreamExt;
|
|
|
use lib_infra::future::FutureResult;
|
|
|
-
|
|
|
-use flowy_collaboration::entities::ws::DocumentServerWSDataType;
|
|
|
-
|
|
|
use lib_ws::WSConnectState;
|
|
|
-use std::{collections::VecDeque, convert::TryFrom, sync::Arc};
|
|
|
-use tokio::sync::{broadcast, mpsc::UnboundedSender, oneshot, RwLock};
|
|
|
-
|
|
|
-pub(crate) trait DocumentWebSocketManager: Send + Sync {
|
|
|
- fn stop(&self);
|
|
|
- fn receiver(&self) -> Arc<dyn DocumentWSReceiver>;
|
|
|
-}
|
|
|
+use std::{convert::TryFrom, sync::Arc};
|
|
|
+use tokio::{
|
|
|
+ sync::{
|
|
|
+ broadcast,
|
|
|
+ mpsc,
|
|
|
+ mpsc::{UnboundedReceiver, UnboundedSender},
|
|
|
+ },
|
|
|
+ task::spawn_blocking,
|
|
|
+ time::{interval, Duration},
|
|
|
+};
|
|
|
|
|
|
-pub(crate) async fn make_document_ws_manager(
|
|
|
+pub struct DocumentWebSocketManager {
|
|
|
doc_id: String,
|
|
|
- user_id: String,
|
|
|
- edit_cmd_tx: UnboundedSender<EditorCommand>,
|
|
|
- rev_manager: Arc<DocumentRevisionManager>,
|
|
|
+ data_provider: Arc<dyn DocumentWSSinkDataProvider>,
|
|
|
+ stream_consumer: Arc<dyn DocumentWSSteamConsumer>,
|
|
|
ws_conn: Arc<dyn DocumentWebSocket>,
|
|
|
-) -> Arc<dyn DocumentWebSocketManager> {
|
|
|
- // if cfg!(feature = "http_server") {
|
|
|
- // let shared_sink =
|
|
|
- // Arc::new(SharedWSSinkDataProvider::new(rev_manager.clone()));
|
|
|
- // let ws_stream_consumer = Arc::new(DocumentWebSocketSteamConsumerAdapter {
|
|
|
- // doc_id: doc_id.clone(),
|
|
|
- // edit_cmd_tx,
|
|
|
- // rev_manager: rev_manager.clone(),
|
|
|
- // shared_sink: shared_sink.clone(),
|
|
|
- // });
|
|
|
- // let data_provider =
|
|
|
- // Arc::new(DocumentWSSinkDataProviderAdapter(shared_sink));
|
|
|
- // let ws_manager = Arc::new(HttpWebSocketManager::new(
|
|
|
- // &doc_id,
|
|
|
- // ws_conn,
|
|
|
- // data_provider,
|
|
|
- // ws_stream_consumer,
|
|
|
- // ));
|
|
|
- // listen_document_ws_state(&user_id, &doc_id, ws_manager.scribe_state(),
|
|
|
- // rev_manager); Arc::new(ws_manager)
|
|
|
- // } else {
|
|
|
- // Arc::new(Arc::new(LocalWebSocketManager {}))
|
|
|
- // }
|
|
|
- let shared_sink = Arc::new(SharedWSSinkDataProvider::new(rev_manager.clone()));
|
|
|
- let ws_stream_consumer = Arc::new(DocumentWebSocketSteamConsumerAdapter {
|
|
|
- doc_id: doc_id.clone(),
|
|
|
- edit_cmd_tx,
|
|
|
- rev_manager: rev_manager.clone(),
|
|
|
- shared_sink: shared_sink.clone(),
|
|
|
- });
|
|
|
- let data_provider = Arc::new(DocumentWSSinkDataProviderAdapter(shared_sink));
|
|
|
- let ws_manager = Arc::new(HttpWebSocketManager::new(
|
|
|
- &doc_id,
|
|
|
- ws_conn,
|
|
|
- data_provider,
|
|
|
- ws_stream_consumer,
|
|
|
- ));
|
|
|
- listen_document_ws_state(&user_id, &doc_id, ws_manager.scribe_state(), rev_manager);
|
|
|
- Arc::new(ws_manager)
|
|
|
-}
|
|
|
-
|
|
|
-fn listen_document_ws_state(
|
|
|
- _user_id: &str,
|
|
|
- _doc_id: &str,
|
|
|
- mut subscriber: broadcast::Receiver<WSConnectState>,
|
|
|
- _rev_manager: Arc<DocumentRevisionManager>,
|
|
|
-) {
|
|
|
- tokio::spawn(async move {
|
|
|
- while let Ok(state) = subscriber.recv().await {
|
|
|
- match state {
|
|
|
- WSConnectState::Init => {},
|
|
|
- WSConnectState::Connecting => {},
|
|
|
- WSConnectState::Connected => {},
|
|
|
- WSConnectState::Disconnected => {},
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
+ ws_msg_tx: UnboundedSender<DocumentServerWSData>,
|
|
|
+ ws_msg_rx: Option<UnboundedReceiver<DocumentServerWSData>>,
|
|
|
+ stop_sync_tx: SinkStopTx,
|
|
|
+ state: broadcast::Sender<WSConnectState>,
|
|
|
}
|
|
|
|
|
|
-pub(crate) struct DocumentWebSocketSteamConsumerAdapter {
|
|
|
- pub(crate) doc_id: String,
|
|
|
- pub(crate) edit_cmd_tx: UnboundedSender<EditorCommand>,
|
|
|
- pub(crate) rev_manager: Arc<DocumentRevisionManager>,
|
|
|
- pub(crate) shared_sink: Arc<SharedWSSinkDataProvider>,
|
|
|
-}
|
|
|
+impl DocumentWebSocketManager {
|
|
|
+ pub(crate) fn new(
|
|
|
+ doc_id: &str,
|
|
|
+ ws_conn: Arc<dyn DocumentWebSocket>,
|
|
|
+ data_provider: Arc<dyn DocumentWSSinkDataProvider>,
|
|
|
+ stream_consumer: Arc<dyn DocumentWSSteamConsumer>,
|
|
|
+ ) -> Self {
|
|
|
+ let (ws_msg_tx, ws_msg_rx) = mpsc::unbounded_channel();
|
|
|
+ let (stop_sync_tx, _) = tokio::sync::broadcast::channel(2);
|
|
|
+ let doc_id = doc_id.to_string();
|
|
|
+ let (state, _) = broadcast::channel(2);
|
|
|
+ let mut manager = DocumentWebSocketManager {
|
|
|
+ doc_id,
|
|
|
+ data_provider,
|
|
|
+ stream_consumer,
|
|
|
+ ws_conn,
|
|
|
+ ws_msg_tx,
|
|
|
+ ws_msg_rx: Some(ws_msg_rx),
|
|
|
+ stop_sync_tx,
|
|
|
+ state,
|
|
|
+ };
|
|
|
+ manager.run();
|
|
|
+ manager
|
|
|
+ }
|
|
|
|
|
|
-impl DocumentWSSteamConsumer for DocumentWebSocketSteamConsumerAdapter {
|
|
|
- fn receive_push_revision(&self, bytes: Bytes) -> FutureResult<(), FlowyError> {
|
|
|
- let rev_manager = self.rev_manager.clone();
|
|
|
- let edit_cmd_tx = self.edit_cmd_tx.clone();
|
|
|
- let shared_sink = self.shared_sink.clone();
|
|
|
- let doc_id = self.doc_id.clone();
|
|
|
- FutureResult::new(async move {
|
|
|
- if let Some(server_composed_revision) = handle_remote_revision(edit_cmd_tx, rev_manager, bytes).await? {
|
|
|
- let data = DocumentClientWSData::from_revisions(&doc_id, vec![server_composed_revision]);
|
|
|
- shared_sink.push_back(data).await;
|
|
|
- }
|
|
|
- Ok(())
|
|
|
- })
|
|
|
+ fn run(&mut self) {
|
|
|
+ let ws_msg_rx = self.ws_msg_rx.take().expect("Only take once");
|
|
|
+ let sink = DocumentWSSink::new(
|
|
|
+ &self.doc_id,
|
|
|
+ self.data_provider.clone(),
|
|
|
+ self.ws_conn.clone(),
|
|
|
+ self.stop_sync_tx.subscribe(),
|
|
|
+ );
|
|
|
+ let stream = DocumentWSStream::new(
|
|
|
+ &self.doc_id,
|
|
|
+ self.stream_consumer.clone(),
|
|
|
+ ws_msg_rx,
|
|
|
+ self.stop_sync_tx.subscribe(),
|
|
|
+ );
|
|
|
+ tokio::spawn(sink.run());
|
|
|
+ tokio::spawn(stream.run());
|
|
|
}
|
|
|
|
|
|
- fn receive_ack(&self, id: String, ty: DocumentServerWSDataType) -> FutureResult<(), FlowyError> {
|
|
|
- let shared_sink = self.shared_sink.clone();
|
|
|
- FutureResult::new(async move { shared_sink.ack(id, ty).await })
|
|
|
+ pub fn scribe_state(&self) -> broadcast::Receiver<WSConnectState> { self.state.subscribe() }
|
|
|
+
|
|
|
+ pub(crate) fn stop(&self) {
|
|
|
+ if self.stop_sync_tx.send(()).is_ok() {
|
|
|
+ tracing::debug!("{} stop sync", self.doc_id)
|
|
|
+ }
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- fn receive_new_user_connect(&self, _new_user: NewDocumentUser) -> FutureResult<(), FlowyError> {
|
|
|
- // the _new_user will be used later
|
|
|
- FutureResult::new(async move { Ok(()) })
|
|
|
+impl DocumentWSReceiver for DocumentWebSocketManager {
|
|
|
+ fn receive_ws_data(&self, doc_data: DocumentServerWSData) {
|
|
|
+ match self.ws_msg_tx.send(doc_data) {
|
|
|
+ Ok(_) => {},
|
|
|
+ Err(e) => tracing::error!("❌ Propagate ws message failed. {}", e),
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- fn pull_revisions_in_range(&self, range: RevisionRange) -> FutureResult<(), FlowyError> {
|
|
|
- let rev_manager = self.rev_manager.clone();
|
|
|
- let shared_sink = self.shared_sink.clone();
|
|
|
- let doc_id = self.doc_id.clone();
|
|
|
- FutureResult::new(async move {
|
|
|
- let revisions = rev_manager.get_revisions_in_range(range).await?;
|
|
|
- let data = DocumentClientWSData::from_revisions(&doc_id, revisions);
|
|
|
- shared_sink.push_back(data).await;
|
|
|
- Ok(())
|
|
|
- })
|
|
|
+ fn connect_state_changed(&self, state: &WSConnectState) {
|
|
|
+ match self.state.send(state.clone()) {
|
|
|
+ Ok(_) => {},
|
|
|
+ Err(e) => tracing::error!("{}", e),
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-pub(crate) struct DocumentWSSinkDataProviderAdapter(pub(crate) Arc<SharedWSSinkDataProvider>);
|
|
|
-impl DocumentWSSinkDataProvider for DocumentWSSinkDataProviderAdapter {
|
|
|
- fn next(&self) -> FutureResult<Option<DocumentClientWSData>, FlowyError> {
|
|
|
- let shared_sink = self.0.clone();
|
|
|
- FutureResult::new(async move { shared_sink.next().await })
|
|
|
- }
|
|
|
+impl std::ops::Drop for DocumentWebSocketManager {
|
|
|
+ fn drop(&mut self) { tracing::debug!("{} HttpWebSocketManager was drop", self.doc_id) }
|
|
|
}
|
|
|
|
|
|
-async fn transform_pushed_revisions(
|
|
|
- revisions: Vec<Revision>,
|
|
|
- edit_cmd: &UnboundedSender<EditorCommand>,
|
|
|
-) -> FlowyResult<TransformDeltas> {
|
|
|
- let (ret, rx) = oneshot::channel::<CollaborateResult<TransformDeltas>>();
|
|
|
- let _ = edit_cmd.send(EditorCommand::TransformRevision { revisions, ret });
|
|
|
- Ok(rx.await.map_err(internal_error)??)
|
|
|
+pub trait DocumentWSSteamConsumer: Send + Sync {
|
|
|
+ fn receive_push_revision(&self, bytes: Bytes) -> FutureResult<(), FlowyError>;
|
|
|
+ fn receive_ack(&self, id: String, ty: DocumentServerWSDataType) -> FutureResult<(), FlowyError>;
|
|
|
+ fn receive_new_user_connect(&self, new_user: NewDocumentUser) -> FutureResult<(), FlowyError>;
|
|
|
+ fn pull_revisions_in_range(&self, range: RevisionRange) -> FutureResult<(), FlowyError>;
|
|
|
}
|
|
|
|
|
|
-#[tracing::instrument(level = "debug", skip(edit_cmd_tx, rev_manager, bytes))]
|
|
|
-pub(crate) async fn handle_remote_revision(
|
|
|
- edit_cmd_tx: UnboundedSender<EditorCommand>,
|
|
|
- rev_manager: Arc<DocumentRevisionManager>,
|
|
|
- bytes: Bytes,
|
|
|
-) -> FlowyResult<Option<Revision>> {
|
|
|
- let mut revisions = RepeatedRevision::try_from(bytes)?.into_inner();
|
|
|
- if revisions.is_empty() {
|
|
|
- return Ok(None);
|
|
|
- }
|
|
|
+pub struct DocumentWSStream {
|
|
|
+ doc_id: String,
|
|
|
+ consumer: Arc<dyn DocumentWSSteamConsumer>,
|
|
|
+ ws_msg_rx: Option<mpsc::UnboundedReceiver<DocumentServerWSData>>,
|
|
|
+ stop_rx: Option<SinkStopRx>,
|
|
|
+}
|
|
|
|
|
|
- let first_revision = revisions.first().unwrap();
|
|
|
- if let Some(local_revision) = rev_manager.get_revision(first_revision.rev_id).await {
|
|
|
- if local_revision.md5 == first_revision.md5 {
|
|
|
- // The local revision is equal to the pushed revision. Just ignore it.
|
|
|
- revisions = revisions.split_off(1);
|
|
|
- if revisions.is_empty() {
|
|
|
- return Ok(None);
|
|
|
- }
|
|
|
- } else {
|
|
|
- return Ok(None);
|
|
|
+impl DocumentWSStream {
|
|
|
+ pub fn new(
|
|
|
+ doc_id: &str,
|
|
|
+ consumer: Arc<dyn DocumentWSSteamConsumer>,
|
|
|
+ ws_msg_rx: mpsc::UnboundedReceiver<DocumentServerWSData>,
|
|
|
+ stop_rx: SinkStopRx,
|
|
|
+ ) -> Self {
|
|
|
+ DocumentWSStream {
|
|
|
+ doc_id: doc_id.to_owned(),
|
|
|
+ consumer,
|
|
|
+ ws_msg_rx: Some(ws_msg_rx),
|
|
|
+ stop_rx: Some(stop_rx),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- let TransformDeltas {
|
|
|
- client_prime,
|
|
|
- server_prime,
|
|
|
- } = transform_pushed_revisions(revisions.clone(), &edit_cmd_tx).await?;
|
|
|
+ pub async fn run(mut self) {
|
|
|
+ let mut receiver = self.ws_msg_rx.take().expect("Only take once");
|
|
|
+ let mut stop_rx = self.stop_rx.take().expect("Only take once");
|
|
|
+ let doc_id = self.doc_id.clone();
|
|
|
+ let stream = stream! {
|
|
|
+ loop {
|
|
|
+ tokio::select! {
|
|
|
+ result = receiver.recv() => {
|
|
|
+ match result {
|
|
|
+ Some(msg) => {
|
|
|
+ yield msg
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ tracing::debug!("[DocumentStream:{}] loop exit", doc_id);
|
|
|
+ break;
|
|
|
+ },
|
|
|
+ }
|
|
|
+ },
|
|
|
+ _ = stop_rx.recv() => {
|
|
|
+ tracing::debug!("[DocumentStream:{}] loop exit", doc_id);
|
|
|
+ break
|
|
|
+ },
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- match server_prime {
|
|
|
- None => {
|
|
|
- // The server_prime is None means the client local revisions conflict with the
|
|
|
- // server, and it needs to override the client delta.
|
|
|
- let (ret, rx) = oneshot::channel();
|
|
|
- let _ = edit_cmd_tx.send(EditorCommand::OverrideDelta {
|
|
|
- revisions,
|
|
|
- delta: client_prime,
|
|
|
- ret,
|
|
|
- });
|
|
|
- let _ = rx.await.map_err(internal_error)??;
|
|
|
- Ok(None)
|
|
|
- },
|
|
|
- Some(server_prime) => {
|
|
|
- let (ret, rx) = oneshot::channel();
|
|
|
- let _ = edit_cmd_tx.send(EditorCommand::ComposeRemoteDelta {
|
|
|
- revisions,
|
|
|
- client_delta: client_prime,
|
|
|
- server_delta: server_prime,
|
|
|
- ret,
|
|
|
- });
|
|
|
- Ok(rx.await.map_err(internal_error)??)
|
|
|
- },
|
|
|
+ stream
|
|
|
+ .for_each(|msg| async {
|
|
|
+ match self.handle_message(msg).await {
|
|
|
+ Ok(_) => {},
|
|
|
+ Err(e) => log::error!("[DocumentStream:{}] error: {}", self.doc_id, e),
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .await;
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-#[derive(Clone)]
|
|
|
-enum SourceType {
|
|
|
- Shared,
|
|
|
- Revision,
|
|
|
-}
|
|
|
|
|
|
-#[derive(Clone)]
|
|
|
-pub(crate) struct SharedWSSinkDataProvider {
|
|
|
- shared: Arc<RwLock<VecDeque<DocumentClientWSData>>>,
|
|
|
- rev_manager: Arc<DocumentRevisionManager>,
|
|
|
- source_ty: Arc<RwLock<SourceType>>,
|
|
|
-}
|
|
|
+ async fn handle_message(&self, msg: DocumentServerWSData) -> FlowyResult<()> {
|
|
|
+ let DocumentServerWSData { doc_id: _, ty, data } = msg;
|
|
|
+ let bytes = spawn_blocking(move || Bytes::from(data))
|
|
|
+ .await
|
|
|
+ .map_err(internal_error)?;
|
|
|
|
|
|
-impl SharedWSSinkDataProvider {
|
|
|
- pub(crate) fn new(rev_manager: Arc<DocumentRevisionManager>) -> Self {
|
|
|
- SharedWSSinkDataProvider {
|
|
|
- shared: Arc::new(RwLock::new(VecDeque::new())),
|
|
|
- rev_manager,
|
|
|
- source_ty: Arc::new(RwLock::new(SourceType::Shared)),
|
|
|
+ tracing::trace!("[DocumentStream]: new message: {:?}", ty);
|
|
|
+ match ty {
|
|
|
+ DocumentServerWSDataType::ServerPushRev => {
|
|
|
+ let _ = self.consumer.receive_push_revision(bytes).await?;
|
|
|
+ },
|
|
|
+ DocumentServerWSDataType::ServerPullRev => {
|
|
|
+ let range = RevisionRange::try_from(bytes)?;
|
|
|
+ let _ = self.consumer.pull_revisions_in_range(range).await?;
|
|
|
+ },
|
|
|
+ DocumentServerWSDataType::ServerAck => {
|
|
|
+ let rev_id = RevId::try_from(bytes).unwrap().value;
|
|
|
+ let _ = self.consumer.receive_ack(rev_id.to_string(), ty).await;
|
|
|
+ },
|
|
|
+ DocumentServerWSDataType::UserConnect => {
|
|
|
+ let new_user = NewDocumentUser::try_from(bytes)?;
|
|
|
+ let _ = self.consumer.receive_new_user_connect(new_user).await;
|
|
|
+ // Notify the user that someone has connected to this document
|
|
|
+ },
|
|
|
}
|
|
|
+ Ok(())
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- #[allow(dead_code)]
|
|
|
- pub(crate) async fn push_front(&self, data: DocumentClientWSData) { self.shared.write().await.push_front(data); }
|
|
|
+pub type Tick = ();
|
|
|
+pub type SinkStopRx = broadcast::Receiver<()>;
|
|
|
+pub type SinkStopTx = broadcast::Sender<()>;
|
|
|
|
|
|
- async fn push_back(&self, data: DocumentClientWSData) { self.shared.write().await.push_back(data); }
|
|
|
+pub trait DocumentWSSinkDataProvider: Send + Sync {
|
|
|
+ fn next(&self) -> FutureResult<Option<DocumentClientWSData>, FlowyError>;
|
|
|
+}
|
|
|
|
|
|
- async fn next(&self) -> FlowyResult<Option<DocumentClientWSData>> {
|
|
|
- let source_ty = self.source_ty.read().await.clone();
|
|
|
- match source_ty {
|
|
|
- SourceType::Shared => match self.shared.read().await.front() {
|
|
|
- None => {
|
|
|
- *self.source_ty.write().await = SourceType::Revision;
|
|
|
- Ok(None)
|
|
|
- },
|
|
|
- Some(data) => {
|
|
|
- tracing::debug!("[SharedWSSinkDataProvider]: {}:{:?}", data.doc_id, data.ty);
|
|
|
- Ok(Some(data.clone()))
|
|
|
- },
|
|
|
- },
|
|
|
- SourceType::Revision => {
|
|
|
- if !self.shared.read().await.is_empty() {
|
|
|
- *self.source_ty.write().await = SourceType::Shared;
|
|
|
- return Ok(None);
|
|
|
- }
|
|
|
+pub struct DocumentWSSink {
|
|
|
+ provider: Arc<dyn DocumentWSSinkDataProvider>,
|
|
|
+ ws_sender: Arc<dyn DocumentWebSocket>,
|
|
|
+ stop_rx: Option<SinkStopRx>,
|
|
|
+ doc_id: String,
|
|
|
+}
|
|
|
|
|
|
- match self.rev_manager.next_sync_revision().await? {
|
|
|
- Some(rev) => {
|
|
|
- let doc_id = rev.doc_id.clone();
|
|
|
- Ok(Some(DocumentClientWSData::from_revisions(&doc_id, vec![rev])))
|
|
|
- },
|
|
|
- None => {
|
|
|
- //
|
|
|
- let doc_id = self.rev_manager.doc_id.clone();
|
|
|
- let latest_rev_id = self.rev_manager.rev_id();
|
|
|
- Ok(Some(DocumentClientWSData::ping(&doc_id, latest_rev_id)))
|
|
|
- },
|
|
|
- }
|
|
|
- },
|
|
|
+impl DocumentWSSink {
|
|
|
+ pub fn new(
|
|
|
+ doc_id: &str,
|
|
|
+ provider: Arc<dyn DocumentWSSinkDataProvider>,
|
|
|
+ ws_sender: Arc<dyn DocumentWebSocket>,
|
|
|
+ stop_rx: SinkStopRx,
|
|
|
+ ) -> Self {
|
|
|
+ Self {
|
|
|
+ provider,
|
|
|
+ ws_sender,
|
|
|
+ stop_rx: Some(stop_rx),
|
|
|
+ doc_id: doc_id.to_owned(),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async fn ack(&self, id: String, _ty: DocumentServerWSDataType) -> FlowyResult<()> {
|
|
|
- // let _ = self.rev_manager.ack_revision(id).await?;
|
|
|
- let source_ty = self.source_ty.read().await.clone();
|
|
|
- match source_ty {
|
|
|
- SourceType::Shared => {
|
|
|
- let should_pop = match self.shared.read().await.front() {
|
|
|
- None => false,
|
|
|
- Some(val) => {
|
|
|
- let expected_id = val.id();
|
|
|
- if expected_id == id {
|
|
|
- true
|
|
|
- } else {
|
|
|
- tracing::error!("The front element's {} is not equal to the {}", expected_id, id);
|
|
|
- false
|
|
|
+ pub async fn run(mut self) {
|
|
|
+ let (tx, mut rx) = mpsc::unbounded_channel();
|
|
|
+ let mut stop_rx = self.stop_rx.take().expect("Only take once");
|
|
|
+ let doc_id = self.doc_id.clone();
|
|
|
+ tokio::spawn(tick(tx));
|
|
|
+ let stream = stream! {
|
|
|
+ loop {
|
|
|
+ tokio::select! {
|
|
|
+ result = rx.recv() => {
|
|
|
+ match result {
|
|
|
+ Some(msg) => yield msg,
|
|
|
+ None => break,
|
|
|
}
|
|
|
},
|
|
|
+ _ = stop_rx.recv() => {
|
|
|
+ tracing::debug!("[DocumentSink:{}] loop exit", doc_id);
|
|
|
+ break
|
|
|
+ },
|
|
|
};
|
|
|
- if should_pop {
|
|
|
- let _ = self.shared.write().await.pop_front();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ stream
|
|
|
+ .for_each(|_| async {
|
|
|
+ match self.send_next_revision().await {
|
|
|
+ Ok(_) => {},
|
|
|
+ Err(e) => log::error!("[DocumentSink] send failed, {:?}", e),
|
|
|
}
|
|
|
+ })
|
|
|
+ .await;
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn send_next_revision(&self) -> FlowyResult<()> {
|
|
|
+ match self.provider.next().await? {
|
|
|
+ None => {
|
|
|
+ tracing::trace!("Finish synchronizing revisions");
|
|
|
+ Ok(())
|
|
|
},
|
|
|
- SourceType::Revision => {
|
|
|
- match id.parse::<i64>() {
|
|
|
- Ok(rev_id) => {
|
|
|
- let _ = self.rev_manager.ack_revision(rev_id).await?;
|
|
|
- },
|
|
|
- Err(e) => {
|
|
|
- tracing::error!("Parse rev_id from {} failed. {}", id, e);
|
|
|
- },
|
|
|
- };
|
|
|
+ Some(data) => {
|
|
|
+ tracing::trace!("[DocumentSink] send: {}:{}-{:?}", data.doc_id, data.id(), data.ty);
|
|
|
+ self.ws_sender.send(data)
|
|
|
},
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- Ok(())
|
|
|
+async fn tick(sender: mpsc::UnboundedSender<Tick>) {
|
|
|
+ let mut interval = interval(Duration::from_millis(SYNC_INTERVAL_IN_MILLIS));
|
|
|
+ while sender.send(()).is_ok() {
|
|
|
+ interval.tick().await;
|
|
|
}
|
|
|
}
|