1010 lines · 35.8 KB · Rust
RawDownload
use super::*;use prost::Message;use text::{EditOperation, UndoOperation}; pub struct LeftChannelBuffer {    pub channel_id: ChannelId,    pub collaborators: Vec<proto::Collaborator>,    pub connections: Vec<ConnectionId>,} impl Database {    /// Open a channel buffer. Returns the current contents, and adds you to the list of people    /// to notify on changes.    pub async fn join_channel_buffer(        &self,        channel_id: ChannelId,        user_id: UserId,        connection: ConnectionId,    ) -> Result<proto::JoinChannelBufferResponse> {        self.transaction(|tx| async move {            let channel = self.get_channel_internal(channel_id, &tx).await?;            self.check_user_is_channel_participant(&channel, user_id, &tx)                .await?;             let buffer = channel::Model {                id: channel_id,                ..Default::default()            }            .find_related(buffer::Entity)            .one(&*tx)            .await?;             let buffer = if let Some(buffer) = buffer {                buffer            } else {                let buffer = buffer::ActiveModel {                    channel_id: ActiveValue::Set(channel_id),                    ..Default::default()                }                .insert(&*tx)                .await?;                buffer_snapshot::ActiveModel {                    buffer_id: ActiveValue::Set(buffer.id),                    epoch: ActiveValue::Set(0),                    text: ActiveValue::Set(String::new()),                    operation_serialization_version: ActiveValue::Set(                        storage::SERIALIZATION_VERSION,                    ),                }                .insert(&*tx)                .await?;                buffer            };             // Join the collaborators            let mut collaborators = channel_buffer_collaborator::Entity::find()                .filter(channel_buffer_collaborator::Column::ChannelId.eq(channel_id))                .all(&*tx)                .await?;            let replica_ids = collaborators                .iter()                .map(|c| c.replica_id)                .collect::<HashSet<_>>();            let mut replica_id = ReplicaId(0);            while replica_ids.contains(&replica_id) {                replica_id.0 += 1;            }            let collaborator = channel_buffer_collaborator::ActiveModel {                channel_id: ActiveValue::Set(channel_id),                connection_id: ActiveValue::Set(connection.id as i32),                connection_server_id: ActiveValue::Set(ServerId(connection.owner_id as i32)),                user_id: ActiveValue::Set(user_id),                replica_id: ActiveValue::Set(replica_id),                ..Default::default()            }            .insert(&*tx)            .await?;            collaborators.push(collaborator);             let (base_text, operations, max_operation) =                self.get_buffer_state(&buffer, &tx).await?;             // Save the last observed operation            if let Some(op) = max_operation {                observed_buffer_edits::Entity::insert(observed_buffer_edits::ActiveModel {                    user_id: ActiveValue::Set(user_id),                    buffer_id: ActiveValue::Set(buffer.id),                    epoch: ActiveValue::Set(op.epoch),                    lamport_timestamp: ActiveValue::Set(op.lamport_timestamp),                    replica_id: ActiveValue::Set(op.replica_id),                })                .on_conflict(                    OnConflict::columns([                        observed_buffer_edits::Column::UserId,                        observed_buffer_edits::Column::BufferId,                    ])                    .update_columns([                        observed_buffer_edits::Column::Epoch,                        observed_buffer_edits::Column::LamportTimestamp,                    ])                    .to_owned(),                )                .exec(&*tx)                .await?;            }             Ok(proto::JoinChannelBufferResponse {                buffer_id: buffer.id.to_proto(),                replica_id: replica_id.to_proto() as u32,                base_text,                operations,                epoch: buffer.epoch as u64,                collaborators: collaborators                    .into_iter()                    .map(|collaborator| proto::Collaborator {                        peer_id: Some(collaborator.connection().into()),                        user_id: collaborator.user_id.to_proto(),                        replica_id: collaborator.replica_id.0 as u32,                        is_host: false,                    })                    .collect(),            })        })        .await    }     /// Rejoin a channel buffer (after a connection interruption)    pub async fn rejoin_channel_buffers(        &self,        buffers: &[proto::ChannelBufferVersion],        user_id: UserId,        connection_id: ConnectionId,    ) -> Result<Vec<RejoinedChannelBuffer>> {        self.transaction(|tx| async move {            let mut results = Vec::new();            for client_buffer in buffers {                let channel = self                    .get_channel_internal(ChannelId::from_proto(client_buffer.channel_id), &tx)                    .await?;                if self                    .check_user_is_channel_participant(&channel, user_id, &tx)                    .await                    .is_err()                {                    log::info!("user is not a member of channel");                    continue;                }                 let buffer = self.get_channel_buffer(channel.id, &tx).await?;                let mut collaborators = channel_buffer_collaborator::Entity::find()                    .filter(channel_buffer_collaborator::Column::ChannelId.eq(channel.id))                    .all(&*tx)                    .await?;                 // If the buffer epoch hasn't changed since the client lost                // connection, then the client's buffer can be synchronized with                // the server's buffer.                if buffer.epoch as u64 != client_buffer.epoch {                    log::info!("can't rejoin buffer, epoch has changed");                    continue;                }                 // Find the collaborator record for this user's previous lost                // connection. Update it with the new connection id.                let Some(self_collaborator) =                    collaborators.iter_mut().find(|c| c.user_id == user_id)                else {                    log::info!("can't rejoin buffer, no previous collaborator found");                    continue;                };                let old_connection_id = self_collaborator.connection();                *self_collaborator = channel_buffer_collaborator::ActiveModel {                    id: ActiveValue::Unchanged(self_collaborator.id),                    connection_id: ActiveValue::Set(connection_id.id as i32),                    connection_server_id: ActiveValue::Set(ServerId(connection_id.owner_id as i32)),                    connection_lost: ActiveValue::Set(false),                    ..Default::default()                }                .update(&*tx)                .await?;                 let client_version = version_from_wire(&client_buffer.version);                let serialization_version = self                    .get_buffer_operation_serialization_version(buffer.id, buffer.epoch, &tx)                    .await?;                 let mut rows = buffer_operation::Entity::find()                    .filter(                        buffer_operation::Column::BufferId                            .eq(buffer.id)                            .and(buffer_operation::Column::Epoch.eq(buffer.epoch)),                    )                    .stream(&*tx)                    .await?;                 // Find the server's version vector and any operations                // that the client has not seen.                let mut server_version = clock::Global::new();                let mut operations = Vec::new();                while let Some(row) = rows.next().await {                    let row = row?;                    let timestamp = clock::Lamport {                        replica_id: row.replica_id as u16,                        value: row.lamport_timestamp as u32,                    };                    server_version.observe(timestamp);                    if !client_version.observed(timestamp) {                        operations.push(proto::Operation {                            variant: Some(operation_from_storage(row, serialization_version)?),                        })                    }                }                 results.push(RejoinedChannelBuffer {                    old_connection_id,                    buffer: proto::RejoinedChannelBuffer {                        channel_id: client_buffer.channel_id,                        version: version_to_wire(&server_version),                        operations,                        collaborators: collaborators                            .into_iter()                            .map(|collaborator| proto::Collaborator {                                peer_id: Some(collaborator.connection().into()),                                user_id: collaborator.user_id.to_proto(),                                replica_id: collaborator.replica_id.0 as u32,                                is_host: false,                            })                            .collect(),                    },                });            }             Ok(results)        })        .await    }     /// Clear out any buffer collaborators who are no longer collaborating.    pub async fn clear_stale_channel_buffer_collaborators(        &self,        channel_id: ChannelId,        server_id: ServerId,    ) -> Result<RefreshedChannelBuffer> {        self.transaction(|tx| async move {            let db_collaborators = channel_buffer_collaborator::Entity::find()                .filter(channel_buffer_collaborator::Column::ChannelId.eq(channel_id))                .all(&*tx)                .await?;             let mut connection_ids = Vec::new();            let mut collaborators = Vec::new();            let mut collaborator_ids_to_remove = Vec::new();            for db_collaborator in &db_collaborators {                if !db_collaborator.connection_lost                    && db_collaborator.connection_server_id == server_id                {                    connection_ids.push(db_collaborator.connection());                    collaborators.push(proto::Collaborator {                        peer_id: Some(db_collaborator.connection().into()),                        replica_id: db_collaborator.replica_id.0 as u32,                        user_id: db_collaborator.user_id.to_proto(),                        is_host: false,                    })                } else {                    collaborator_ids_to_remove.push(db_collaborator.id);                }            }             channel_buffer_collaborator::Entity::delete_many()                .filter(channel_buffer_collaborator::Column::Id.is_in(collaborator_ids_to_remove))                .exec(&*tx)                .await?;             Ok(RefreshedChannelBuffer {                connection_ids,                collaborators,            })        })        .await    }     /// Close the channel buffer, and stop receiving updates for it.    pub async fn leave_channel_buffer(        &self,        channel_id: ChannelId,        connection: ConnectionId,    ) -> Result<LeftChannelBuffer> {        self.transaction(|tx| async move {            self.leave_channel_buffer_internal(channel_id, connection, &tx)                .await        })        .await    }     /// Close the channel buffer, and stop receiving updates for it.    pub async fn channel_buffer_connection_lost(        &self,        connection: ConnectionId,        tx: &DatabaseTransaction,    ) -> Result<()> {        channel_buffer_collaborator::Entity::update_many()            .filter(                Condition::all()                    .add(channel_buffer_collaborator::Column::ConnectionId.eq(connection.id as i32))                    .add(                        channel_buffer_collaborator::Column::ConnectionServerId                            .eq(connection.owner_id as i32),                    ),            )            .set(channel_buffer_collaborator::ActiveModel {                connection_lost: ActiveValue::set(true),                ..Default::default()            })            .exec(tx)            .await?;        Ok(())    }     /// Close all open channel buffers    pub async fn leave_channel_buffers(        &self,        connection: ConnectionId,    ) -> Result<Vec<LeftChannelBuffer>> {        self.transaction(|tx| async move {            #[derive(Debug, Clone, Copy, EnumIter, DeriveColumn)]            enum QueryChannelIds {                ChannelId,            }             let channel_ids: Vec<ChannelId> = channel_buffer_collaborator::Entity::find()                .select_only()                .column(channel_buffer_collaborator::Column::ChannelId)                .filter(Condition::all().add(                    channel_buffer_collaborator::Column::ConnectionId.eq(connection.id as i32),                ))                .into_values::<_, QueryChannelIds>()                .all(&*tx)                .await?;             let mut result = Vec::new();            for channel_id in channel_ids {                let left_channel_buffer = self                    .leave_channel_buffer_internal(channel_id, connection, &tx)                    .await?;                result.push(left_channel_buffer);            }             Ok(result)        })        .await    }     async fn leave_channel_buffer_internal(        &self,        channel_id: ChannelId,        connection: ConnectionId,        tx: &DatabaseTransaction,    ) -> Result<LeftChannelBuffer> {        let result = channel_buffer_collaborator::Entity::delete_many()            .filter(                Condition::all()                    .add(channel_buffer_collaborator::Column::ChannelId.eq(channel_id))                    .add(channel_buffer_collaborator::Column::ConnectionId.eq(connection.id as i32))                    .add(                        channel_buffer_collaborator::Column::ConnectionServerId                            .eq(connection.owner_id as i32),                    ),            )            .exec(tx)            .await?;        if result.rows_affected == 0 {            Err(anyhow!("not a collaborator on this project"))?;        }         let mut collaborators = Vec::new();        let mut connections = Vec::new();        let mut rows = channel_buffer_collaborator::Entity::find()            .filter(                Condition::all().add(channel_buffer_collaborator::Column::ChannelId.eq(channel_id)),            )            .stream(tx)            .await?;        while let Some(row) = rows.next().await {            let row = row?;            let connection = row.connection();            connections.push(connection);            collaborators.push(proto::Collaborator {                peer_id: Some(connection.into()),                replica_id: row.replica_id.0 as u32,                user_id: row.user_id.to_proto(),                is_host: false,            });        }         drop(rows);         if collaborators.is_empty() {            self.snapshot_channel_buffer(channel_id, tx).await?;        }         Ok(LeftChannelBuffer {            channel_id,            collaborators,            connections,        })    }     pub async fn get_channel_buffer_collaborators(        &self,        channel_id: ChannelId,    ) -> Result<Vec<UserId>> {        self.transaction(|tx| async move {            self.get_channel_buffer_collaborators_internal(channel_id, &tx)                .await        })        .await    }     async fn get_channel_buffer_collaborators_internal(        &self,        channel_id: ChannelId,        tx: &DatabaseTransaction,    ) -> Result<Vec<UserId>> {        #[derive(Debug, Clone, Copy, EnumIter, DeriveColumn)]        enum QueryUserIds {            UserId,        }         let users: Vec<UserId> = channel_buffer_collaborator::Entity::find()            .select_only()            .column(channel_buffer_collaborator::Column::UserId)            .filter(                Condition::all().add(channel_buffer_collaborator::Column::ChannelId.eq(channel_id)),            )            .into_values::<_, QueryUserIds>()            .all(tx)            .await?;         Ok(users)    }     pub async fn update_channel_buffer(        &self,        channel_id: ChannelId,        user: UserId,        operations: &[proto::Operation],    ) -> Result<(HashSet<ConnectionId>, i32, Vec<proto::VectorClockEntry>)> {        self.transaction(move |tx| async move {            let channel = self.get_channel_internal(channel_id, &tx).await?;             let mut requires_write_permission = false;            for op in operations.iter() {                match op.variant {                    None | Some(proto::operation::Variant::UpdateSelections(_)) => {}                    Some(_) => requires_write_permission = true,                }            }            if requires_write_permission {                self.check_user_is_channel_member(&channel, user, &tx)                    .await?;            } else {                self.check_user_is_channel_participant(&channel, user, &tx)                    .await?;            }             let buffer = buffer::Entity::find()                .filter(buffer::Column::ChannelId.eq(channel_id))                .one(&*tx)                .await?                .ok_or_else(|| anyhow!("no such buffer"))?;             let serialization_version = self                .get_buffer_operation_serialization_version(buffer.id, buffer.epoch, &tx)                .await?;             let operations = operations                .iter()                .filter_map(|op| operation_to_storage(op, &buffer, serialization_version))                .collect::<Vec<_>>();             let max_version;             if !operations.is_empty() {                let max_operation = operations                    .iter()                    .max_by_key(|op| (op.lamport_timestamp.as_ref(), op.replica_id.as_ref()))                    .unwrap();                 max_version = vec![proto::VectorClockEntry {                    replica_id: *max_operation.replica_id.as_ref() as u32,                    timestamp: *max_operation.lamport_timestamp.as_ref() as u32,                }];                 // get current channel participants and save the max operation above                self.save_max_operation(                    user,                    buffer.id,                    buffer.epoch,                    *max_operation.replica_id.as_ref(),                    *max_operation.lamport_timestamp.as_ref(),

Lines 1–500 of 1010.