maintain channel subscriptions in RAM (#9512)

This avoids a giant database query on every leave/join event.

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2024-03-18 15:14:16 -06:00 committed by GitHub
parent 963618a4a6
commit cd9b865e0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 222 additions and 165 deletions

View file

@ -91,7 +91,9 @@ id_type!(NotificationKindId);
id_type!(HostedProjectId);
/// ChannelRole gives you permissions for both channels and calls.
#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Default, Hash)]
#[derive(
Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Default, Hash, Serialize,
)]
#[sea_orm(rs_type = "String", db_type = "String(None)")]
pub enum ChannelRole {
/// Admin can read/write and change permissions.

View file

@ -45,11 +45,7 @@ impl Database {
name: &str,
parent_channel_id: Option<ChannelId>,
admin_id: UserId,
) -> Result<(
Channel,
Option<channel_member::Model>,
Vec<channel_member::Model>,
)> {
) -> Result<(channel::Model, Option<channel_member::Model>)> {
let name = Self::sanitize_channel_name(name)?;
self.transaction(move |tx| async move {
let mut parent = None;
@ -90,12 +86,7 @@ impl Database {
);
}
let channel_members = channel_member::Entity::find()
.filter(channel_member::Column::ChannelId.eq(channel.root_id()))
.all(&*tx)
.await?;
Ok((Channel::from_model(channel), membership, channel_members))
Ok((channel, membership))
})
.await
}
@ -181,7 +172,7 @@ impl Database {
channel_id: ChannelId,
visibility: ChannelVisibility,
admin_id: UserId,
) -> Result<(Channel, Vec<channel_member::Model>)> {
) -> Result<channel::Model> {
self.transaction(move |tx| async move {
let channel = self.get_channel_internal(channel_id, &tx).await?;
self.check_user_is_channel_admin(&channel, admin_id, &tx)
@ -214,12 +205,7 @@ impl Database {
model.visibility = ActiveValue::Set(visibility);
let channel = model.update(&*tx).await?;
let channel_members = channel_member::Entity::find()
.filter(channel_member::Column::ChannelId.eq(channel.root_id()))
.all(&*tx)
.await?;
Ok((Channel::from_model(channel), channel_members))
Ok(channel)
})
.await
}
@ -245,21 +231,12 @@ impl Database {
&self,
channel_id: ChannelId,
user_id: UserId,
) -> Result<(Vec<ChannelId>, Vec<UserId>)> {
) -> Result<(ChannelId, Vec<ChannelId>)> {
self.transaction(move |tx| async move {
let channel = self.get_channel_internal(channel_id, &tx).await?;
self.check_user_is_channel_admin(&channel, user_id, &tx)
.await?;
let members_to_notify: Vec<UserId> = channel_member::Entity::find()
.filter(channel_member::Column::ChannelId.eq(channel.root_id()))
.select_only()
.column(channel_member::Column::UserId)
.distinct()
.into_values::<_, QueryUserIds>()
.all(&*tx)
.await?;
let channels_to_remove = self
.get_channel_descendants_excluding_self([&channel], &tx)
.await?
@ -273,7 +250,7 @@ impl Database {
.exec(&*tx)
.await?;
Ok((channels_to_remove, members_to_notify))
Ok((channel.root_id(), channels_to_remove))
})
.await
}
@ -343,7 +320,7 @@ impl Database {
channel_id: ChannelId,
admin_id: UserId,
new_name: &str,
) -> Result<(Channel, Vec<channel_member::Model>)> {
) -> Result<channel::Model> {
self.transaction(move |tx| async move {
let new_name = Self::sanitize_channel_name(new_name)?.to_string();
@ -355,12 +332,7 @@ impl Database {
model.name = ActiveValue::Set(new_name.clone());
let channel = model.update(&*tx).await?;
let channel_members = channel_member::Entity::find()
.filter(channel_member::Column::ChannelId.eq(channel.root_id()))
.all(&*tx)
.await?;
Ok((Channel::from_model(channel), channel_members))
Ok(channel)
})
.await
}
@ -984,7 +956,7 @@ impl Database {
channel_id: ChannelId,
new_parent_id: ChannelId,
admin_id: UserId,
) -> Result<(Vec<Channel>, Vec<channel_member::Model>)> {
) -> Result<(ChannelId, Vec<Channel>)> {
self.transaction(|tx| async move {
let channel = self.get_channel_internal(channel_id, &tx).await?;
self.check_user_is_channel_admin(&channel, admin_id, &tx)
@ -1039,12 +1011,7 @@ impl Database {
.map(|c| Channel::from_model(c))
.collect::<Vec<_>>();
let channel_members = channel_member::Entity::find()
.filter(channel_member::Column::ChannelId.eq(root_id))
.all(&*tx)
.await?;
Ok((channels, channel_members))
Ok((root_id, channels))
})
.await
}

View file

@ -52,12 +52,7 @@ impl Database {
);
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
let channel_members;
if let Some(channel) = &channel {
channel_members = self.get_channel_participants(channel, &tx).await?;
} else {
channel_members = Vec::new();
if channel.is_none() {
// Delete the room if it becomes empty.
if room.participants.is_empty() {
project::Entity::delete_many()
@ -70,8 +65,7 @@ impl Database {
Ok(RefreshedRoom {
room,
channel_id: channel.map(|channel| channel.id),
channel_members,
channel,
stale_participant_user_ids,
canceled_calls_to_user_ids,
})
@ -349,8 +343,7 @@ impl Database {
let room = self.get_room(room_id, &tx).await?;
Ok(JoinRoom {
room,
channel_id: None,
channel_members: vec![],
channel: None,
})
})
.await
@ -446,11 +439,9 @@ impl Database {
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
let channel = channel.ok_or_else(|| anyhow!("no channel for room"))?;
let channel_members = self.get_channel_participants(&channel, tx).await?;
Ok(JoinRoom {
room,
channel_id: Some(channel.id),
channel_members,
channel: Some(channel),
})
}
@ -736,16 +727,10 @@ impl Database {
}
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
let channel_members = if let Some(channel) = &channel {
self.get_channel_participants(&channel, &tx).await?
} else {
Vec::new()
};
Ok(RejoinedRoom {
room,
channel_id: channel.map(|channel| channel.id),
channel_members,
channel,
rejoined_projects,
reshared_projects,
})
@ -902,15 +887,9 @@ impl Database {
false
};
let channel_members = if let Some(channel) = &channel {
self.get_channel_participants(channel, &tx).await?
} else {
Vec::new()
};
let left_room = LeftRoom {
room,
channel_id: channel.map(|channel| channel.id),
channel_members,
channel,
left_projects,
canceled_calls_to_user_ids,
deleted,

View file

@ -109,10 +109,9 @@ async fn test_channels(db: &Arc<Database>) {
assert!(db.get_channel(crdb_id, a_id).await.is_err());
// Remove a channel tree
let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
let (_, mut channel_ids) = db.delete_channel(rust_id, a_id).await.unwrap();
channel_ids.sort();
assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]);
assert_eq!(user_ids, &[a_id]);
assert!(db.get_channel(rust_id, a_id).await.is_err());
assert!(db.get_channel(cargo_id, a_id).await.is_err());