2023-07-29 09:46:24 +08:00
|
|
|
use std::str::FromStr;
|
2023-08-17 23:46:39 +08:00
|
|
|
use std::sync::{Arc, Weak};
|
2023-07-29 09:46:24 +08:00
|
|
|
|
|
|
|
use anyhow::Error;
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
use collab::preclude::merge_updates_v1;
|
|
|
|
use collab_plugins::cloud_storage::{
|
|
|
|
CollabObject, MsgId, RemoteCollabSnapshot, RemoteCollabState, RemoteCollabStorage,
|
|
|
|
RemoteUpdateReceiver,
|
|
|
|
};
|
2023-08-12 17:36:31 +08:00
|
|
|
use parking_lot::Mutex;
|
2023-07-29 09:46:24 +08:00
|
|
|
use tokio::task::spawn_blocking;
|
|
|
|
|
|
|
|
use lib_infra::async_trait::async_trait;
|
|
|
|
use lib_infra::util::md5;
|
|
|
|
|
|
|
|
use crate::supabase::api::request::{
|
2023-08-17 23:46:39 +08:00
|
|
|
create_snapshot, get_snapshots_from_server, get_updates_from_server, FetchObjectUpdateAction,
|
|
|
|
UpdateItem,
|
2023-07-29 09:46:24 +08:00
|
|
|
};
|
2023-08-12 17:36:31 +08:00
|
|
|
use crate::supabase::api::util::{
|
|
|
|
ExtendedResponse, InsertParamsBuilder, SupabaseBinaryColumnEncoder,
|
|
|
|
};
|
2023-07-29 09:46:24 +08:00
|
|
|
use crate::supabase::api::{PostgresWrapper, SupabaseServerService};
|
|
|
|
use crate::supabase::define::*;
|
2023-08-17 23:46:39 +08:00
|
|
|
use crate::AppFlowyEncryption;
|
2023-07-29 09:46:24 +08:00
|
|
|
|
2023-08-12 17:36:31 +08:00
|
|
|
pub struct SupabaseCollabStorageImpl<T> {
|
|
|
|
server: T,
|
|
|
|
rx: Mutex<Option<RemoteUpdateReceiver>>,
|
2023-08-17 23:46:39 +08:00
|
|
|
encryption: Weak<dyn AppFlowyEncryption>,
|
2023-08-12 17:36:31 +08:00
|
|
|
}
|
2023-07-29 09:46:24 +08:00
|
|
|
|
2023-08-05 15:02:05 +08:00
|
|
|
impl<T> SupabaseCollabStorageImpl<T> {
|
2023-08-17 23:46:39 +08:00
|
|
|
pub fn new(
|
|
|
|
server: T,
|
|
|
|
rx: Option<RemoteUpdateReceiver>,
|
|
|
|
encryption: Weak<dyn AppFlowyEncryption>,
|
|
|
|
) -> Self {
|
2023-08-12 17:36:31 +08:00
|
|
|
Self {
|
|
|
|
server,
|
|
|
|
rx: Mutex::new(rx),
|
2023-08-17 23:46:39 +08:00
|
|
|
encryption,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn secret(&self) -> Option<String> {
|
|
|
|
match self.encryption.upgrade() {
|
|
|
|
None => None,
|
|
|
|
Some(encryption) => encryption.get_secret(),
|
2023-08-12 17:36:31 +08:00
|
|
|
}
|
2023-07-29 09:46:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-08-05 15:02:05 +08:00
|
|
|
impl<T> RemoteCollabStorage for SupabaseCollabStorageImpl<T>
|
2023-07-29 09:46:24 +08:00
|
|
|
where
|
|
|
|
T: SupabaseServerService,
|
|
|
|
{
|
|
|
|
fn is_enable(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn get_all_updates(&self, object: &CollabObject) -> Result<Vec<Vec<u8>>, Error> {
|
2023-08-12 17:36:31 +08:00
|
|
|
let postgrest = self.server.try_get_weak_postgrest()?;
|
|
|
|
let action =
|
|
|
|
FetchObjectUpdateAction::new(object.object_id.clone(), object.ty.clone(), postgrest);
|
2023-07-29 09:46:24 +08:00
|
|
|
let updates = action.run().await?;
|
|
|
|
Ok(updates)
|
|
|
|
}
|
|
|
|
|
2023-08-17 23:46:39 +08:00
|
|
|
async fn get_snapshots(&self, object_id: &str, limit: usize) -> Vec<RemoteCollabSnapshot> {
|
|
|
|
match self.server.try_get_postgrest() {
|
|
|
|
Ok(postgrest) => match get_snapshots_from_server(object_id, postgrest, limit).await {
|
|
|
|
Ok(snapshots) => snapshots,
|
|
|
|
Err(err) => {
|
|
|
|
tracing::error!(
|
|
|
|
"🔴fetch snapshots by oid:{} with limit: {} failed: {:?}",
|
|
|
|
object_id,
|
|
|
|
limit,
|
|
|
|
err
|
|
|
|
);
|
|
|
|
vec![]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Err(err) => {
|
|
|
|
tracing::error!("🔴get postgrest failed: {:?}", err);
|
|
|
|
vec![]
|
|
|
|
},
|
|
|
|
}
|
2023-07-29 09:46:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn get_collab_state(&self, object_id: &str) -> Result<Option<RemoteCollabState>, Error> {
|
2023-08-12 17:36:31 +08:00
|
|
|
let postgrest = self.server.try_get_postgrest()?;
|
2023-07-29 09:46:24 +08:00
|
|
|
let json = postgrest
|
|
|
|
.from("af_collab_state")
|
|
|
|
.select("*")
|
|
|
|
.eq("oid", object_id)
|
|
|
|
.order("snapshot_created_at.desc".to_string())
|
|
|
|
.limit(1)
|
|
|
|
.execute()
|
|
|
|
.await?
|
|
|
|
.get_json()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(
|
|
|
|
json
|
|
|
|
.as_array()
|
|
|
|
.and_then(|array| array.first())
|
|
|
|
.and_then(|value| {
|
|
|
|
let created_at = value.get("snapshot_created_at").and_then(|created_at| {
|
|
|
|
created_at
|
|
|
|
.as_str()
|
|
|
|
.map(|id| DateTime::<Utc>::from_str(id).ok())
|
|
|
|
.and_then(|date| date)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let current_edit_count = value.get("current_edit_count").and_then(|id| id.as_i64())?;
|
2023-08-18 15:13:34 +08:00
|
|
|
let snapshot_edit_count = value
|
|
|
|
.get("snapshot_edit_count")
|
2023-07-29 09:46:24 +08:00
|
|
|
.and_then(|id| id.as_i64())?;
|
|
|
|
|
|
|
|
Some(RemoteCollabState {
|
|
|
|
current_edit_count,
|
2023-08-18 15:13:34 +08:00
|
|
|
snapshot_edit_count,
|
|
|
|
snapshot_created_at: created_at.timestamp(),
|
2023-07-29 09:46:24 +08:00
|
|
|
})
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn create_snapshot(&self, object: &CollabObject, snapshot: Vec<u8>) -> Result<i64, Error> {
|
2023-08-12 17:36:31 +08:00
|
|
|
let postgrest = self.server.try_get_postgrest()?;
|
2023-07-29 09:46:24 +08:00
|
|
|
create_snapshot(&postgrest, object, snapshot).await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn send_update(
|
|
|
|
&self,
|
|
|
|
object: &CollabObject,
|
|
|
|
_id: MsgId,
|
|
|
|
update: Vec<u8>,
|
|
|
|
) -> Result<(), Error> {
|
2023-08-12 17:36:31 +08:00
|
|
|
if let Some(postgrest) = self.server.get_postgrest() {
|
2023-08-07 22:24:04 +08:00
|
|
|
let workspace_id = object
|
|
|
|
.get_workspace_id()
|
|
|
|
.ok_or(anyhow::anyhow!("Invalid workspace id"))?;
|
2023-08-17 23:46:39 +08:00
|
|
|
send_update(workspace_id, object, update, &postgrest, &self.secret()).await?;
|
2023-08-07 22:24:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2023-07-29 09:46:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn send_init_sync(
|
|
|
|
&self,
|
|
|
|
object: &CollabObject,
|
|
|
|
_id: MsgId,
|
|
|
|
init_update: Vec<u8>,
|
|
|
|
) -> Result<(), Error> {
|
2023-08-12 17:36:31 +08:00
|
|
|
let postgrest = self.server.try_get_postgrest()?;
|
2023-07-29 09:46:24 +08:00
|
|
|
let workspace_id = object
|
|
|
|
.get_workspace_id()
|
|
|
|
.ok_or(anyhow::anyhow!("Invalid workspace id"))?;
|
|
|
|
|
2023-08-12 17:36:31 +08:00
|
|
|
let update_items =
|
|
|
|
get_updates_from_server(&object.object_id, &object.ty, postgrest.clone()).await?;
|
2023-07-29 09:46:24 +08:00
|
|
|
|
|
|
|
// If the update_items is empty, we can send the init_update directly
|
|
|
|
if update_items.is_empty() {
|
2023-08-17 23:46:39 +08:00
|
|
|
send_update(
|
|
|
|
workspace_id,
|
|
|
|
object,
|
|
|
|
init_update,
|
|
|
|
&postgrest,
|
|
|
|
&self.secret(),
|
|
|
|
)
|
|
|
|
.await?;
|
2023-07-29 09:46:24 +08:00
|
|
|
} else {
|
|
|
|
// 2.Merge the updates into one and then delete the merged updates
|
|
|
|
let merge_result = spawn_blocking(move || merge_updates(update_items, init_update)).await??;
|
|
|
|
tracing::trace!("Merged updates count: {}", merge_result.merged_keys.len());
|
|
|
|
|
|
|
|
let value_size = merge_result.new_update.len() as i32;
|
|
|
|
let md5 = md5(&merge_result.new_update);
|
2023-08-17 23:46:39 +08:00
|
|
|
let (new_update, encrypt) =
|
|
|
|
SupabaseBinaryColumnEncoder::encode(merge_result.new_update, &self.secret())?;
|
2023-07-29 09:46:24 +08:00
|
|
|
let params = InsertParamsBuilder::new()
|
2023-08-12 17:36:31 +08:00
|
|
|
.insert("oid", object.object_id.clone())
|
2023-07-29 09:46:24 +08:00
|
|
|
.insert("new_value", new_update)
|
2023-08-17 23:46:39 +08:00
|
|
|
.insert("encrypt", encrypt)
|
2023-07-29 09:46:24 +08:00
|
|
|
.insert("md5", md5)
|
|
|
|
.insert("value_size", value_size)
|
|
|
|
.insert("partition_key", partition_key(&object.ty))
|
|
|
|
.insert("uid", object.uid)
|
|
|
|
.insert("workspace_id", workspace_id)
|
|
|
|
.insert("removed_keys", merge_result.merged_keys)
|
2023-08-12 17:36:31 +08:00
|
|
|
.insert("did", object.get_device_id())
|
2023-07-29 09:46:24 +08:00
|
|
|
.build();
|
|
|
|
|
|
|
|
postgrest
|
2023-08-17 23:46:39 +08:00
|
|
|
.rpc("flush_collab_updates_v3", params)
|
2023-07-29 09:46:24 +08:00
|
|
|
.execute()
|
|
|
|
.await?
|
|
|
|
.success()
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-08-12 17:36:31 +08:00
|
|
|
fn subscribe_remote_updates(&self, _object: &CollabObject) -> Option<RemoteUpdateReceiver> {
|
|
|
|
let rx = self.rx.lock().take();
|
|
|
|
if rx.is_none() {
|
|
|
|
tracing::warn!("The receiver is already taken");
|
|
|
|
}
|
|
|
|
rx
|
2023-07-29 09:46:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-18 15:13:34 +08:00
|
|
|
pub(crate) async fn send_update(
|
2023-07-29 09:46:24 +08:00
|
|
|
workspace_id: String,
|
|
|
|
object: &CollabObject,
|
|
|
|
update: Vec<u8>,
|
|
|
|
postgrest: &Arc<PostgresWrapper>,
|
2023-08-17 23:46:39 +08:00
|
|
|
encryption_secret: &Option<String>,
|
2023-07-29 09:46:24 +08:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
let value_size = update.len() as i32;
|
|
|
|
let md5 = md5(&update);
|
2023-08-17 23:46:39 +08:00
|
|
|
let (update, encrypt) = SupabaseBinaryColumnEncoder::encode(update, encryption_secret)?;
|
2023-07-29 09:46:24 +08:00
|
|
|
let builder = InsertParamsBuilder::new()
|
2023-08-12 17:36:31 +08:00
|
|
|
.insert("oid", object.object_id.clone())
|
2023-07-29 09:46:24 +08:00
|
|
|
.insert("partition_key", partition_key(&object.ty))
|
|
|
|
.insert("value", update)
|
2023-08-17 23:46:39 +08:00
|
|
|
.insert("encrypt", encrypt)
|
2023-07-29 09:46:24 +08:00
|
|
|
.insert("uid", object.uid)
|
|
|
|
.insert("md5", md5)
|
|
|
|
.insert("workspace_id", workspace_id)
|
2023-08-12 17:36:31 +08:00
|
|
|
.insert("did", object.get_device_id())
|
2023-07-29 09:46:24 +08:00
|
|
|
.insert("value_size", value_size);
|
|
|
|
|
|
|
|
let params = builder.build();
|
|
|
|
postgrest
|
2023-08-03 08:48:04 +08:00
|
|
|
.from(AF_COLLAB_UPDATE_TABLE)
|
2023-07-29 09:46:24 +08:00
|
|
|
.insert(params)
|
|
|
|
.execute()
|
|
|
|
.await?
|
|
|
|
.success()
|
|
|
|
.await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn merge_updates(update_items: Vec<UpdateItem>, new_update: Vec<u8>) -> Result<MergeResult, Error> {
|
|
|
|
let mut updates = vec![];
|
|
|
|
let mut merged_keys = vec![];
|
|
|
|
for item in update_items {
|
|
|
|
merged_keys.push(item.key);
|
|
|
|
updates.push(item.value);
|
|
|
|
}
|
|
|
|
if !new_update.is_empty() {
|
|
|
|
updates.push(new_update);
|
|
|
|
}
|
|
|
|
let updates = updates
|
|
|
|
.iter()
|
|
|
|
.map(|update| update.as_ref())
|
|
|
|
.collect::<Vec<&[u8]>>();
|
|
|
|
|
|
|
|
let new_update = merge_updates_v1(&updates)?;
|
|
|
|
Ok(MergeResult {
|
|
|
|
merged_keys,
|
|
|
|
new_update,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MergeResult {
|
|
|
|
merged_keys: Vec<i64>,
|
|
|
|
new_update: Vec<u8>,
|
|
|
|
}
|