|
@@ -1,90 +1,34 @@
|
|
|
+use super::edit_doc::EditDoc;
|
|
|
use crate::service::{doc::read_doc, util::parse_from_bytes, ws::WsBizHandler};
|
|
|
use actix_web::web::Data;
|
|
|
use bytes::Bytes;
|
|
|
-use dashmap::{mapref::one::Ref, DashMap};
|
|
|
use flowy_document::{
|
|
|
- protobuf::{Doc, QueryDocParams, Revision, WsDataType, WsDocumentData},
|
|
|
+ protobuf::{QueryDocParams, Revision, WsDataType, WsDocumentData},
|
|
|
services::doc::Document,
|
|
|
};
|
|
|
-use flowy_net::errors::{internal_error, ServerError};
|
|
|
-use flowy_ot::core::Delta;
|
|
|
-use parking_lot::{RawRwLock, RwLock};
|
|
|
+use flowy_net::errors::ServerError;
|
|
|
+use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
|
|
use protobuf::Message;
|
|
|
use sqlx::PgPool;
|
|
|
-use std::sync::Arc;
|
|
|
-
|
|
|
-#[rustfmt::skip]
|
|
|
-//
|
|
|
-// Frontend │ Backend
|
|
|
-//
|
|
|
-// ┌──────────┐ ┌──────────┐ │ ┌─────────┐ ┌───────────────┐
|
|
|
-// │ user 1 │───────▶│WsManager │───────────▶│ws_client│───────────▶│DocWsBizHandler│
|
|
|
-// └──────────┘ └──────────┘ │ └─────────┘ └───────────────┘
|
|
|
-//
|
|
|
-// WsDocumentData────▶WsMessage ────▶ Message ─────▶WsMessage ─────▶WsDocumentData
|
|
|
+use std::{collections::HashMap, sync::Arc};
|
|
|
|
|
|
pub struct DocWsBizHandler {
|
|
|
inner: Arc<Inner>,
|
|
|
}
|
|
|
|
|
|
-struct Inner {
|
|
|
- pg_pool: Data<PgPool>,
|
|
|
- edited_docs: DashMap<String, Arc<RwLock<EditedDoc>>>,
|
|
|
-}
|
|
|
-
|
|
|
impl DocWsBizHandler {
|
|
|
pub fn new(pg_pool: Data<PgPool>) -> Self {
|
|
|
Self {
|
|
|
- inner: Arc::new(Inner {
|
|
|
- edited_docs: DashMap::new(),
|
|
|
- pg_pool,
|
|
|
- }),
|
|
|
+ inner: Arc::new(Inner::new(pg_pool)),
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-async fn handle_document_data(inner: Arc<Inner>, data: Bytes) -> Result<(), ServerError> {
|
|
|
- let document_data: WsDocumentData = parse_from_bytes(&data)?;
|
|
|
- match document_data.ty {
|
|
|
- WsDataType::Command => {},
|
|
|
- WsDataType::Delta => {
|
|
|
- let revision: Revision = parse_from_bytes(&document_data.data).unwrap();
|
|
|
- let edited_doc = get_edit_doc(inner, &revision.doc_id).await?;
|
|
|
- let _ = edited_doc.write().apply_revision(revision)?;
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-
|
|
|
-async fn get_edit_doc(
|
|
|
- inner: Arc<Inner>,
|
|
|
- doc_id: &str,
|
|
|
-) -> Result<Arc<RwLock<EditedDoc>>, ServerError> {
|
|
|
- let pg_pool = inner.pg_pool.clone();
|
|
|
-
|
|
|
- if let Some(doc) = inner.edited_docs.get(doc_id) {
|
|
|
- return Ok(doc.clone());
|
|
|
- }
|
|
|
-
|
|
|
- let params = QueryDocParams {
|
|
|
- doc_id: doc_id.to_string(),
|
|
|
- ..Default::default()
|
|
|
- };
|
|
|
-
|
|
|
- let doc = read_doc(pg_pool.get_ref(), params).await?;
|
|
|
- let edited_doc = Arc::new(RwLock::new(EditedDoc::new(doc)?));
|
|
|
- inner
|
|
|
- .edited_docs
|
|
|
- .insert(doc_id.to_string(), edited_doc.clone());
|
|
|
- Ok(edited_doc)
|
|
|
-}
|
|
|
-
|
|
|
impl WsBizHandler for DocWsBizHandler {
|
|
|
fn receive_data(&self, data: Bytes) {
|
|
|
let inner = self.inner.clone();
|
|
|
- actix_rt::spawn(async {
|
|
|
- let result = handle_document_data(inner, data).await;
|
|
|
+ actix_rt::spawn(async move {
|
|
|
+ let result = inner.handle(data).await;
|
|
|
match result {
|
|
|
Ok(_) => {},
|
|
|
Err(e) => log::error!("WsBizHandler handle data error: {:?}", e),
|
|
@@ -93,34 +37,53 @@ impl WsBizHandler for DocWsBizHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-struct EditedDoc {
|
|
|
- doc_id: String,
|
|
|
- document: Document,
|
|
|
+struct Inner {
|
|
|
+ pg_pool: Data<PgPool>,
|
|
|
+ edit_docs: RwLock<HashMap<String, Arc<EditDoc>>>,
|
|
|
}
|
|
|
|
|
|
-impl EditedDoc {
|
|
|
- fn new(doc: Doc) -> Result<Self, ServerError> {
|
|
|
- let delta = Delta::from_bytes(doc.data).map_err(internal_error)?;
|
|
|
- let document = Document::from_delta(delta);
|
|
|
- Ok(Self {
|
|
|
- doc_id: doc.id.clone(),
|
|
|
- document,
|
|
|
- })
|
|
|
+impl Inner {
|
|
|
+ fn new(pg_pool: Data<PgPool>) -> Self {
|
|
|
+ Self {
|
|
|
+ pg_pool,
|
|
|
+ edit_docs: RwLock::new(HashMap::new()),
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- fn apply_revision(&mut self, revision: Revision) -> Result<(), ServerError> {
|
|
|
- let delta = Delta::from_bytes(revision.delta).map_err(internal_error)?;
|
|
|
- let _ = self
|
|
|
- .document
|
|
|
- .apply_delta(delta.clone())
|
|
|
- .map_err(internal_error)?;
|
|
|
-
|
|
|
- let json = self.document.to_json();
|
|
|
- let md5 = format!("{:x}", md5::compute(json));
|
|
|
- if md5 != revision.md5 {
|
|
|
- log::error!("Document conflict after apply delta {}", delta)
|
|
|
+ async fn handle(&self, data: Bytes) -> Result<(), ServerError> {
|
|
|
+ let document_data: WsDocumentData = parse_from_bytes(&data)?;
|
|
|
+
|
|
|
+ match document_data.ty {
|
|
|
+ WsDataType::Command => {},
|
|
|
+ WsDataType::Delta => {
|
|
|
+ let revision: Revision = parse_from_bytes(&document_data.data)?;
|
|
|
+ let edited_doc = self.get_edit_doc(&revision.doc_id).await?;
|
|
|
+ tokio::spawn(async move {
|
|
|
+ edited_doc.apply_revision(revision).await.unwrap();
|
|
|
+ });
|
|
|
+ },
|
|
|
}
|
|
|
|
|
|
Ok(())
|
|
|
}
|
|
|
+
|
|
|
+ async fn get_edit_doc(&self, doc_id: &str) -> Result<Arc<EditDoc>, ServerError> {
|
|
|
+ // Opti: using lock free map instead?
|
|
|
+ let edit_docs = self.edit_docs.upgradable_read();
|
|
|
+ if let Some(doc) = edit_docs.get(doc_id) {
|
|
|
+ return Ok(doc.clone());
|
|
|
+ } else {
|
|
|
+ let mut edit_docs = RwLockUpgradableReadGuard::upgrade(edit_docs);
|
|
|
+ let pg_pool = self.pg_pool.clone();
|
|
|
+ let params = QueryDocParams {
|
|
|
+ doc_id: doc_id.to_string(),
|
|
|
+ ..Default::default()
|
|
|
+ };
|
|
|
+
|
|
|
+ let doc = read_doc(pg_pool.get_ref(), params).await?;
|
|
|
+ let edit_doc = Arc::new(EditDoc::new(doc, self.pg_pool.clone())?);
|
|
|
+ edit_docs.insert(doc_id.to_string(), edit_doc.clone());
|
|
|
+ Ok(edit_doc)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|