chore: bump collab commit (#417)

* chore: bump collab rev

* chore: bump collab commit

* chore: custome decode ack code
This commit is contained in:
Nathan.fooo 2024-03-25 16:14:49 +08:00 committed by GitHub
parent 51ecdd664e
commit d4e45efd6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 116 additions and 36 deletions

8
Cargo.lock generated
View File

@ -1388,7 +1388,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=409058aad0969c4d4429151317428a3d17f341d1#409058aad0969c4d4429151317428a3d17f341d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1412,7 +1412,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=409058aad0969c4d4429151317428a3d17f341d1#409058aad0969c4d4429151317428a3d17f341d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1431,7 +1431,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=409058aad0969c4d4429151317428a3d17f341d1#409058aad0969c4d4429151317428a3d17f341d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -1446,7 +1446,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=409058aad0969c4d4429151317428a3d17f341d1#409058aad0969c4d4429151317428a3d17f341d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -175,16 +175,21 @@ collab-rt = { path = "libs/collab-rt" }
lto = true lto = true
opt-level = 3 opt-level = 3
codegen-units = 1 codegen-units = 1
panic = 'unwind'
[profile.profiling] [profile.profiling]
inherits = "release" inherits = "release"
debug = true debug = true
panic = 'unwind'
[profile.dev]
panic = 'unwind'
[patch.crates-io] [patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "409058aad0969c4d4429151317428a3d17f341d1" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "409058aad0969c4d4429151317428a3d17f341d1" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "409058aad0969c4d4429151317428a3d17f341d1" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "409058aad0969c4d4429151317428a3d17f341d1" }
[features] [features]
custom_env= [] custom_env= []

View File

@ -6,7 +6,7 @@ use assert_json_diff::{
use bytes::Bytes; use bytes::Bytes;
use client_api::collab_sync::{SinkConfig, SyncObject, SyncPlugin}; use client_api::collab_sync::{SinkConfig, SyncObject, SyncPlugin};
use client_api::ws::{WSClient, WSClientConfig}; use client_api::ws::{WSClient, WSClientConfig};
use collab::core::collab::MutexCollab; use collab::core::collab::{DocStateSource, MutexCollab};
use collab::core::collab_plugin::EncodedCollab; use collab::core::collab_plugin::EncodedCollab;
use collab::core::collab_state::SyncState; use collab::core::collab_state::SyncState;
use collab::core::origin::{CollabClient, CollabOrigin}; use collab::core::origin::{CollabClient, CollabOrigin};
@ -184,7 +184,7 @@ impl TestClient {
Folder::from_collab_doc_state( Folder::from_collab_doc_state(
uid, uid,
CollabOrigin::Empty, CollabOrigin::Empty,
data.doc_state.to_vec(), DocStateSource::FromDocState(data.doc_state.to_vec()),
&workspace_id, &workspace_id,
vec![], vec![],
) )
@ -498,7 +498,7 @@ impl TestClient {
MutexCollab::new_with_doc_state( MutexCollab::new_with_doc_state(
origin.clone(), origin.clone(),
&object_id, &object_id,
data.doc_state.to_vec(), DocStateSource::FromDocState(data.doc_state.to_vec()),
vec![], vec![],
false, false,
) )
@ -575,7 +575,14 @@ impl TestClient {
let (sink, stream) = (handler.sink(), handler.stream()); let (sink, stream) = (handler.sink(), handler.stream());
let origin = CollabOrigin::Client(CollabClient::new(self.uid().await, self.device_id.clone())); let origin = CollabOrigin::Client(CollabClient::new(self.uid().await, self.device_id.clone()));
let collab = Arc::new( let collab = Arc::new(
MutexCollab::new_with_doc_state(origin.clone(), object_id, doc_state, vec![], false).unwrap(), MutexCollab::new_with_doc_state(
origin.clone(),
object_id,
DocStateSource::FromDocState(doc_state),
vec![],
false,
)
.unwrap(),
); );
let ws_connect_state = self.ws_client.subscribe_connect_state(); let ws_connect_state = self.ws_client.subscribe_connect_state();
@ -660,9 +667,9 @@ pub async fn assert_server_snapshot(
let json = Collab::new_with_doc_state( let json = Collab::new_with_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
&object_id, &object_id,
encoded_collab_v1.doc_state.to_vec(), DocStateSource::FromDocState(encoded_collab_v1.doc_state.to_vec()),
vec![], vec![],
false, false,
) )
.unwrap() .unwrap()
.to_json_value(); .to_json_value();
@ -718,7 +725,7 @@ pub async fn assert_server_collab(
let json = Collab::new_with_doc_state( let json = Collab::new_with_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
&object_id, &object_id,
data.doc_state.to_vec(), DocStateSource::FromDocState(data.doc_state.to_vec()),
vec![], vec![],
false, false,
) )
@ -839,7 +846,7 @@ pub async fn get_collab_json_from_server(
Collab::new_with_doc_state( Collab::new_with_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
object_id, object_id,
bytes.doc_state.to_vec(), DocStateSource::FromDocState(bytes.doc_state.to_vec()),
vec![], vec![],
false, false,
) )

View File

@ -31,7 +31,6 @@ impl AggregateMessageQueue {
pub async fn push(&self, msg: Vec<ClientCollabMessage>) { pub async fn push(&self, msg: Vec<ClientCollabMessage>) {
let mut queue_guard = self.queue.lock().await; let mut queue_guard = self.queue.lock().await;
let mut seen_ids_guard = self.seen_ids.lock().await; let mut seen_ids_guard = self.seen_ids.lock().await;
for msg in msg.into_iter() { for msg in msg.into_iter() {
if seen_ids_guard.insert(SeenId::from(&msg)) { if seen_ids_guard.insert(SeenId::from(&msg)) {
queue_guard.push(msg); queue_guard.push(msg);

View File

@ -1,5 +1,6 @@
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -12,8 +13,9 @@ use collab::preclude::updates::decoder::DecoderV1;
use collab::preclude::updates::encoder::{Encode, Encoder, EncoderV1}; use collab::preclude::updates::encoder::{Encode, Encoder, EncoderV1};
use collab_entity::CollabType; use collab_entity::CollabType;
use collab_rt_protocol::{Message, MessageReader, SyncMessage}; use collab_rt_protocol::{Message, MessageReader, SyncMessage};
use serde::{Deserialize, Serialize}; use serde::de::Visitor;
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde::{de, Deserialize, Deserializer, Serialize};
use serde_repr::Serialize_repr;
pub trait CollabSinkMessage: Clone + Send + Sync + 'static + Ord + Display { pub trait CollabSinkMessage: Clone + Send + Sync + 'static + Ord + Display {
fn payload_size(&self) -> usize; fn payload_size(&self) -> usize;
@ -454,13 +456,14 @@ impl Display for UpdateSync {
} }
} }
#[derive(Clone, Eq, PartialEq, Debug, Serialize_repr, Deserialize_repr, Hash)] #[derive(Clone, Eq, PartialEq, Debug, Serialize_repr, Hash)]
#[repr(u8)] #[repr(u8)]
pub enum AckCode { pub enum AckCode {
Success = 0, Success = 0,
CannotApplyUpdate = 1, CannotApplyUpdate = 1,
Retry = 2, Retry = 2,
Internal = 3, Internal = 3,
EncodeState = 4,
} }
/// ⚠️ ⚠️ ⚠Compatibility Warning: /// ⚠️ ⚠️ ⚠Compatibility Warning:
@ -476,8 +479,39 @@ pub struct CollabAck {
pub object_id: String, pub object_id: String,
pub source: AckSource, pub source: AckSource,
pub payload: Bytes, pub payload: Bytes,
#[serde(deserialize_with = "deserialize_ack_code")]
pub code: AckCode, pub code: AckCode,
} }
fn deserialize_ack_code<'de, D>(deserializer: D) -> Result<AckCode, D::Error>
where
D: Deserializer<'de>,
{
struct AckCodeVisitor;
impl<'de> Visitor<'de> for AckCodeVisitor {
type Value = AckCode;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("an integer between 0 and 4")
}
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
where
E: de::Error,
{
match value {
0 => Ok(AckCode::Success),
1 => Ok(AckCode::CannotApplyUpdate),
2 => Ok(AckCode::Retry),
3 => Ok(AckCode::Internal),
4 => Ok(AckCode::EncodeState),
_ => Ok(AckCode::Internal),
}
}
}
deserializer.deserialize_u8(AckCodeVisitor)
}
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)] #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)]
pub struct AckSource { pub struct AckSource {

View File

@ -251,6 +251,9 @@ pub enum Error {
#[error("{0}")] #[error("{0}")]
YrsApplyUpdate(String), YrsApplyUpdate(String),
#[error("{0}")]
YrsEncodeState(String),
#[error(transparent)] #[error(transparent)]
BinCodeSerde(#[from] bincode::Error), BinCodeSerde(#[from] bincode::Error),

View File

@ -1,8 +1,10 @@
use collab::core::awareness::{Awareness, AwarenessUpdate}; use collab::core::awareness::{Awareness, AwarenessUpdate};
use collab::core::collab::TransactionMutExt; use collab::core::collab::{TransactionExt, TransactionMutExt};
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab::core::transaction::TransactionRetry; use collab::core::transaction::TransactionRetry;
use collab::preclude::Collab; use collab::preclude::Collab;
use yrs::updates::decoder::Decode; use yrs::updates::decoder::Decode;
use yrs::updates::encoder::{Encode, Encoder}; use yrs::updates::encoder::{Encode, Encoder};
use yrs::{ReadTxn, StateVector, Transact, Update}; use yrs::{ReadTxn, StateVector, Transact, Update};
@ -70,7 +72,10 @@ pub trait CollabSyncProtocol {
.doc() .doc()
.try_transact() .try_transact()
.map_err(|err| Error::YrsTransaction(format!("fail to handle sync step1. error: {}", err)))? .map_err(|err| Error::YrsTransaction(format!("fail to handle sync step1. error: {}", err)))?
.encode_state_as_update_v1(&sv); .try_encode_state_as_update_v1(&sv)
.map_err(|err| {
Error::YrsEncodeState(format!("fail to encode state as update. error: {}", err))
})?;
Ok(Some( Ok(Some(
Message::Sync(SyncMessage::SyncStep2(update)).encode_v1(), Message::Sync(SyncMessage::SyncStep2(update)).encode_v1(),
)) ))
@ -91,6 +96,10 @@ pub trait CollabSyncProtocol {
txn txn
.try_apply_update(update) .try_apply_update(update)
.map_err(|err| Error::YrsApplyUpdate(format!("sync step2 apply update: {}", err)))?; .map_err(|err| Error::YrsApplyUpdate(format!("sync step2 apply update: {}", err)))?;
txn
.try_commit()
.map_err(|err| Error::YrsTransaction(format!("sync step2 transaction acquire: {}", err)))?;
Ok(None) Ok(None)
} }

View File

@ -330,6 +330,7 @@ fn ack_code_from_error(error: &Error) -> AckCode {
match error { match error {
Error::YrsTransaction(_) => AckCode::Retry, Error::YrsTransaction(_) => AckCode::Retry,
Error::YrsApplyUpdate(_) => AckCode::CannotApplyUpdate, Error::YrsApplyUpdate(_) => AckCode::CannotApplyUpdate,
Error::YrsEncodeState(_) => AckCode::EncodeState,
_ => AckCode::Internal, _ => AckCode::Internal,
} }
} }

View File

@ -7,7 +7,7 @@ use crate::RealtimeAccessControl;
use anyhow::anyhow; use anyhow::anyhow;
use collab::core::awareness::{AwarenessUpdate, Event}; use collab::core::awareness::{AwarenessUpdate, Event};
use collab::core::collab::TransactionMutExt; use collab::core::collab::{DocStateSource, TransactionMutExt};
use collab::core::collab_plugin::EncodedCollab; use collab::core::collab_plugin::EncodedCollab;
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab::core::transaction::DocTransactionExtension; use collab::core::transaction::DocTransactionExtension;
@ -273,7 +273,7 @@ where
if let Ok(collab) = Collab::new_with_doc_state( if let Ok(collab) = Collab::new_with_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
object_id, object_id,
encoded_collab.doc_state.to_vec(), DocStateSource::FromDocState(encoded_collab.doc_state.to_vec()),
vec![], vec![],
false, false,
) { ) {

View File

@ -1,4 +1,5 @@
use collab::core::awareness::Awareness; use collab::core::awareness::Awareness;
use collab::core::collab::TransactionExt;
use collab_rt_protocol::CollabSyncProtocol; use collab_rt_protocol::CollabSyncProtocol;
use collab_rt_protocol::{CustomMessage, Error, Message, SyncMessage}; use collab_rt_protocol::{CustomMessage, Error, Message, SyncMessage};
use yrs::updates::encoder::{Encode, Encoder, EncoderV1}; use yrs::updates::encoder::{Encode, Encoder, EncoderV1};
@ -12,8 +13,14 @@ impl CollabSyncProtocol for ServerSyncProtocol {
awareness: &Awareness, awareness: &Awareness,
sv: StateVector, sv: StateVector,
) -> Result<Option<Vec<u8>>, Error> { ) -> Result<Option<Vec<u8>>, Error> {
let txn = awareness.doc().transact(); let txn = awareness
let client_step2_update = txn.encode_state_as_update_v1(&sv); .doc()
.try_transact()
.map_err(|err| Error::YrsTransaction(format!("fail to handle sync step1. error: {}", err)))?;
let client_step2_update = txn.try_encode_state_as_update_v1(&sv).map_err(|err| {
Error::YrsEncodeState(format!("fail to encode state as update. error: {}", err))
})?;
// Retrieve the latest document state from the client after they return online from offline editing. // Retrieve the latest document state from the client after they return online from offline editing.
let server_step1_update = txn.state_vector(); let server_step1_update = txn.state_vector();

View File

@ -42,7 +42,11 @@ impl CollabCache {
) -> Result<EncodedCollab, AppError> { ) -> Result<EncodedCollab, AppError> {
self.total_attempts.fetch_add(1, Ordering::Relaxed); self.total_attempts.fetch_add(1, Ordering::Relaxed);
// Attempt to retrieve encoded collab from memory cache, falling back to disk cache if necessary. // Attempt to retrieve encoded collab from memory cache, falling back to disk cache if necessary.
if let Some(encoded_collab) = self.mem_cache.get_encode_collab(&params.object_id).await { if let Some(encoded_collab) = self
.mem_cache
.get_encode_collab_from_mem(&params.object_id)
.await
{
event!( event!(
Level::DEBUG, Level::DEBUG,
"Get encoded collab:{} from cache", "Get encoded collab:{} from cache",
@ -54,7 +58,10 @@ impl CollabCache {
// Retrieve from disk cache as fallback. After retrieval, the value is inserted into the memory cache. // Retrieve from disk cache as fallback. After retrieval, the value is inserted into the memory cache.
let object_id = params.object_id.clone(); let object_id = params.object_id.clone();
let encoded_collab = self.disk_cache.get_collab_encoded(uid, params).await?; let encoded_collab = self
.disk_cache
.get_collab_encoded_from_disk(uid, params)
.await?;
self self
.mem_cache .mem_cache
.insert_encode_collab(object_id, &encoded_collab) .insert_encode_collab(object_id, &encoded_collab)

View File

@ -10,7 +10,7 @@ use sqlx::{PgPool, Transaction};
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
use tokio::time::sleep; use tokio::time::sleep;
use tracing::{event, Level}; use tracing::{event, instrument, Level};
#[derive(Clone)] #[derive(Clone)]
pub struct CollabDiskCache { pub struct CollabDiskCache {
@ -38,7 +38,8 @@ impl CollabDiskCache {
Ok(()) Ok(())
} }
pub async fn get_collab_encoded( #[instrument(level = "trace", skip_all)]
pub async fn get_collab_encoded_from_disk(
&self, &self,
_uid: &i64, _uid: &i64,
params: QueryCollabParams, params: QueryCollabParams,

View File

@ -6,7 +6,7 @@ use anyhow::anyhow;
use app_error::AppError; use app_error::AppError;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::{error, trace}; use tracing::{error, instrument, trace};
#[derive(Clone)] #[derive(Clone)]
pub struct CollabMemCache { pub struct CollabMemCache {
@ -51,7 +51,8 @@ impl CollabMemCache {
} }
} }
pub async fn get_encode_collab(&self, object_id: &str) -> Option<EncodedCollab> { #[instrument(level = "trace", skip_all)]
pub async fn get_encode_collab_from_mem(&self, object_id: &str) -> Option<EncodedCollab> {
match self.get_encode_collab_bytes(object_id).await { match self.get_encode_collab_bytes(object_id).await {
Some(bytes) => match EncodedCollab::decode_from_bytes(&bytes) { Some(bytes) => match EncodedCollab::decode_from_bytes(&bytes) {
Ok(encoded_collab) => Some(encoded_collab), Ok(encoded_collab) => Some(encoded_collab),
@ -70,7 +71,9 @@ impl CollabMemCache {
} }
} }
#[instrument(level = "trace", skip_all, fields(object_id=%object_id))]
pub async fn insert_encode_collab(&self, object_id: String, encoded_collab: &EncodedCollab) { pub async fn insert_encode_collab(&self, object_id: String, encoded_collab: &EncodedCollab) {
trace!("Inserting encode collab into cache: {}", object_id);
match encoded_collab.encode_to_bytes() { match encoded_collab.encode_to_bytes() {
Ok(bytes) => { Ok(bytes) => {
if let Err(err) = self.set_bytes_in_redis(object_id, bytes).await { if let Err(err) = self.set_bytes_in_redis(object_id, bytes).await {

View File

@ -5,6 +5,7 @@ use crate::biz::snapshot::SnapshotControl;
use anyhow::Context; use anyhow::Context;
use app_error::AppError; use app_error::AppError;
use async_trait::async_trait; use async_trait::async_trait;
use collab::core::collab::DocStateSource;
use collab::core::collab_plugin::EncodedCollab; use collab::core::collab_plugin::EncodedCollab;
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab::preclude::Collab; use collab::preclude::Collab;
@ -120,7 +121,10 @@ where
// Await the response from the realtime server with a timeout // Await the response from the realtime server with a timeout
match timeout(timeout_duration, rx).await { match timeout(timeout_duration, rx).await {
Ok(Ok(Some(encode_collab))) => Some(encode_collab), Ok(Ok(Some(encode_collab))) => Some(encode_collab),
Ok(Ok(None)) => None, Ok(Ok(None)) => {
trace!("No encode collab found in editing collab");
None
},
Ok(Err(err)) => { Ok(Err(err)) => {
error!("Failed to get encode collab from realtime server: {}", err); error!("Failed to get encode collab from realtime server: {}", err);
None None
@ -313,7 +317,7 @@ pub fn check_encoded_collab_data(object_id: &str, data: &[u8]) -> Result<(), any
let _ = Collab::new_with_doc_state( let _ = Collab::new_with_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
object_id, object_id,
encoded_collab.doc_state.to_vec(), DocStateSource::FromDocState(encoded_collab.doc_state.to_vec()),
vec![], vec![],
false, false,
)?; )?;

View File

@ -1,5 +1,5 @@
use assert_json_diff::assert_json_eq; use assert_json_diff::assert_json_eq;
use collab::core::collab::MutexCollab; use collab::core::collab::{DocStateSource, MutexCollab};
use collab::core::collab_plugin::EncodedCollab; use collab::core::collab_plugin::EncodedCollab;
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab::preclude::Collab; use collab::preclude::Collab;
@ -85,7 +85,7 @@ async fn get_snapshot_data_test() {
let collab = MutexCollab::new_with_doc_state( let collab = MutexCollab::new_with_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
&object_id, &object_id,
encoded_collab.doc_state.to_vec(), DocStateSource::FromDocState(encoded_collab.doc_state.to_vec()),
vec![], vec![],
false, false,
) )