Simplify handling of syncing versions

Currently whenever a channel changes we send a huge amount of data to
each member. This is the first step in reducing that

Co-Authored-By: Max <max@zed.dev>
Co-Authored-By: bennetbo <bennetbo@gmx.de>
This commit is contained in:
Conrad Irwin 2024-01-25 15:39:55 -07:00
parent b72c037199
commit 716221cd38
13 changed files with 209 additions and 404 deletions

View file

@ -29,35 +29,47 @@ pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
pub type ChannelId = u64;
#[derive(Debug, Clone, Default)]
struct NotesVersion {
epoch: u64,
version: clock::Global,
}
pub struct ChannelStore {
pub channel_index: ChannelIndex,
channel_invitations: Vec<Arc<Channel>>,
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
observed_chat_messages: HashMap<ChannelId, u64>,
observed_notes_versions: HashMap<ChannelId, proto::NotesVersion>,
channel_states: HashMap<ChannelId, ChannelState>,
outgoing_invites: HashSet<(ChannelId, UserId)>,
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
opened_buffers: HashMap<ChannelId, OpenedModelHandle<ChannelBuffer>>,
opened_chats: HashMap<ChannelId, OpenedModelHandle<ChannelChat>>,
client: Arc<Client>,
user_store: Model<UserStore>,
_rpc_subscription: Subscription,
_rpc_subscriptions: [Subscription; 2],
_watch_connection_status: Task<Option<()>>,
disconnect_channel_buffers_task: Option<Task<()>>,
_update_channels: Task<()>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct Channel {
pub id: ChannelId,
pub name: SharedString,
pub visibility: proto::ChannelVisibility,
pub role: proto::ChannelRole,
pub latest_message_id: Option<u64>,
pub latest_note_version: Option<proto::NotesVersion>,
pub parent_path: Vec<u64>,
}
#[derive(Default)]
pub struct ChannelState {
latest_chat_message: Option<u64>,
latest_notes_versions: Option<NotesVersion>,
observed_chat_message: Option<u64>,
observed_notes_versions: Option<NotesVersion>,
}
impl Channel {
pub fn link(&self) -> String {
RELEASE_CHANNEL.link_prefix().to_owned()
@ -179,7 +191,7 @@ impl ChannelStore {
update_channels_tx,
client,
user_store,
_rpc_subscription: rpc_subscription,
_rpc_subscriptions: rpc_subscriptions,
_watch_connection_status: watch_connection_status,
disconnect_channel_buffers_task: None,
_update_channels: cx.spawn(|this, mut cx| async move {
@ -199,8 +211,7 @@ impl ChannelStore {
.await
.log_err();
}),
observed_chat_messages: Default::default(),
observed_notes_versions: Default::default(),
channel_states: Default::default(),
}
}
@ -312,39 +323,16 @@ impl ChannelStore {
})
}
pub fn has_channel_buffer_changed(&self, channel_id: ChannelId) -> Option<bool> {
self.channel_index
.by_id()
pub fn has_channel_buffer_changed(&self, channel_id: ChannelId) -> bool {
self.channel_states
.get(&channel_id)
.map(|channel| channel.unseen_note_version.is_some())
.is_some_and(|state| state.has_channel_buffer_changed())
}
pub fn has_new_messages(&self, channel_id: ChannelId) -> Option<bool> {
self.channel_index
.by_id()
pub fn has_new_messages(&self, channel_id: ChannelId) -> bool {
self.channel_states
.get(&channel_id)
.map(|channel| channel.unseen_message_id.is_some())
}
pub fn notes_changed(
&mut self,
channel_id: ChannelId,
epoch: u64,
version: &clock::Global,
cx: &mut ModelContext<Self>,
) {
self.channel_index.note_changed(channel_id, epoch, version);
cx.notify();
}
pub fn new_message(
&mut self,
channel_id: ChannelId,
message_id: u64,
cx: &mut ModelContext<Self>,
) {
self.channel_index.new_message(channel_id, message_id);
cx.notify();
.is_some_and(|state| state.has_new_messages())
}
pub fn acknowledge_message_id(
@ -353,8 +341,23 @@ impl ChannelStore {
message_id: u64,
cx: &mut ModelContext<Self>,
) {
self.channel_index
.acknowledge_message_id(channel_id, message_id);
self.channel_states
.entry(channel_id)
.or_insert_with(|| Default::default())
.acknowledge_message_id(message_id);
cx.notify();
}
pub fn update_latest_message_id(
&mut self,
channel_id: ChannelId,
message_id: u64,
cx: &mut ModelContext<Self>,
) {
self.channel_states
.entry(channel_id)
.or_insert_with(|| Default::default())
.update_latest_message_id(message_id);
cx.notify();
}
@ -365,9 +368,25 @@ impl ChannelStore {
version: &clock::Global,
cx: &mut ModelContext<Self>,
) {
self.channel_index
.acknowledge_note_version(channel_id, epoch, version);
cx.notify();
self.channel_states
.entry(channel_id)
.or_insert_with(|| Default::default())
.acknowledge_notes_version(epoch, version);
cx.notify()
}
pub fn update_latest_notes_version(
&mut self,
channel_id: ChannelId,
epoch: u64,
version: &clock::Global,
cx: &mut ModelContext<Self>,
) {
self.channel_states
.entry(channel_id)
.or_insert_with(|| Default::default())
.update_latest_notes_version(epoch, version);
cx.notify()
}
pub fn open_channel_chat(
@ -755,15 +774,24 @@ impl ChannelStore {
async fn handle_update_user_channels(
this: Model<Self>,
message: TypedEnvelope<proto::ObservedChannelMessage>,
message: TypedEnvelope<proto::UpdateUserChannels>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, _| {
// this.seen_channel_message_ids
// .insert(message.channel_id, message.message_id);
cx.notify();
})?;
this.update(&mut cx, |this, cx| {
for buffer_version in message.payload.observed_channel_buffer_version {
let version = language::proto::deserialize_version(&buffer_version.version);
this.acknowledge_notes_version(
buffer_version.channel_id,
buffer_version.epoch,
&version,
cx,
);
}
for message_id in message.payload.observed_channel_message_id {
this.acknowledge_message_id(message_id.channel_id, message_id.message_id, cx);
}
})
}
fn handle_connect(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
@ -938,8 +966,8 @@ impl ChannelStore {
let channels_changed = !payload.channels.is_empty()
|| !payload.delete_channels.is_empty()
|| !payload.unseen_channel_messages.is_empty()
|| !payload.unseen_channel_buffer_changes.is_empty();
|| !payload.latest_channel_message_ids.is_empty()
|| !payload.latest_channel_buffer_versions.is_empty();
if channels_changed {
if !payload.delete_channels.is_empty() {
@ -980,20 +1008,19 @@ impl ChannelStore {
}
}
for unseen_buffer_change in payload.unseen_channel_buffer_changes {
let version = language::proto::deserialize_version(&unseen_buffer_change.version);
index.note_changed(
unseen_buffer_change.channel_id,
unseen_buffer_change.epoch,
&version,
);
for latest_buffer_version in payload.latest_channel_buffer_versions {
let version = language::proto::deserialize_version(&latest_buffer_version.version);
self.channel_states
.entry(latest_buffer_version.channel_id)
.or_default()
.update_latest_notes_version(latest_buffer_version.epoch, &version)
}
for unseen_channel_message in payload.unseen_channel_messages {
index.new_messages(
unseen_channel_message.channel_id,
unseen_channel_message.message_id,
);
for latest_channel_message in payload.latest_channel_message_ids {
self.channel_states
.entry(latest_channel_message.channel_id)
.or_default()
.update_latest_message_id(latest_channel_message.message_id);
}
}
@ -1042,3 +1069,65 @@ impl ChannelStore {
}))
}
}
impl ChannelState {
fn has_channel_buffer_changed(&self) -> bool {
if let Some(latest_version) = &self.latest_notes_versions {
if let Some(observed_version) = &self.observed_notes_versions {
latest_version.epoch > observed_version.epoch
|| latest_version
.version
.changed_since(&observed_version.version)
} else {
true
}
} else {
false
}
}
fn has_new_messages(&self) -> bool {
let latest_message_id = self.latest_chat_message;
let observed_message_id = self.observed_chat_message;
latest_message_id.is_some_and(|latest_message_id| {
latest_message_id > observed_message_id.unwrap_or_default()
})
}
fn acknowledge_message_id(&mut self, message_id: u64) {
let observed = self.observed_chat_message.get_or_insert(message_id);
*observed = (*observed).max(message_id);
}
fn update_latest_message_id(&mut self, message_id: u64) {
self.latest_chat_message =
Some(message_id.max(self.latest_chat_message.unwrap_or_default()));
}
fn acknowledge_notes_version(&mut self, epoch: u64, version: &clock::Global) {
if let Some(existing) = &mut self.observed_notes_versions {
if existing.epoch == epoch {
existing.version.join(version);
return;
}
}
self.observed_notes_versions = Some(NotesVersion {
epoch,
version: version.clone(),
});
}
fn update_latest_notes_version(&mut self, epoch: u64, version: &clock::Global) {
if let Some(existing) = &mut self.latest_notes_versions {
if existing.epoch == epoch {
existing.version.join(version);
return;
}
}
self.latest_notes_versions = Some(NotesVersion {
epoch,
version: version.clone(),
});
}
}

View file

@ -37,43 +37,6 @@ impl ChannelIndex {
channels_by_id: &mut self.channels_by_id,
}
}
pub fn acknowledge_note_version(
&mut self,
channel_id: ChannelId,
epoch: u64,
version: &clock::Global,
) {
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
let channel = Arc::make_mut(channel);
if let Some((unseen_epoch, unseen_version)) = &channel.unseen_note_version {
if epoch > *unseen_epoch
|| epoch == *unseen_epoch && version.observed_all(unseen_version)
{
channel.unseen_note_version = None;
}
}
}
}
pub fn acknowledge_message_id(&mut self, channel_id: ChannelId, message_id: u64) {
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
let channel = Arc::make_mut(channel);
if let Some(unseen_message_id) = channel.unseen_message_id {
if message_id >= unseen_message_id {
channel.unseen_message_id = None;
}
}
}
}
pub fn note_changed(&mut self, channel_id: ChannelId, epoch: u64, version: &clock::Global) {
insert_note_changed(&mut self.channels_by_id, channel_id, epoch, version);
}
pub fn new_message(&mut self, channel_id: ChannelId, message_id: u64) {
insert_new_message(&mut self.channels_by_id, channel_id, message_id)
}
}
/// A guard for ensuring that the paths index maintains its sort and uniqueness
@ -85,14 +48,6 @@ pub struct ChannelPathsInsertGuard<'a> {
}
impl<'a> ChannelPathsInsertGuard<'a> {
pub fn note_changed(&mut self, channel_id: ChannelId, epoch: u64, version: &clock::Global) {
insert_note_changed(self.channels_by_id, channel_id, epoch, version);
}
pub fn new_messages(&mut self, channel_id: ChannelId, message_id: u64) {
insert_new_message(self.channels_by_id, channel_id, message_id)
}
pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
let mut ret = false;
if let Some(existing_channel) = self.channels_by_id.get_mut(&channel_proto.id) {
@ -114,8 +69,6 @@ impl<'a> ChannelPathsInsertGuard<'a> {
role: channel_proto.role(),
name: channel_proto.name.into(),
parent_path: channel_proto.parent_path,
latest_message_id: channel_proto.latest_message_id,
latest_note_version: channel_proto.latest_note_version,
}),
);
self.insert_root(channel_proto.id);
@ -153,32 +106,3 @@ fn channel_path_sorting_key<'a>(
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_ref()))
.chain(name)
}
fn insert_note_changed(
channels_by_id: &mut BTreeMap<ChannelId, Arc<Channel>>,
channel_id: u64,
epoch: u64,
version: &clock::Global,
) {
if let Some(channel) = channels_by_id.get_mut(&channel_id) {
let unseen_version = Arc::make_mut(channel)
.unseen_note_version
.get_or_insert((0, clock::Global::new()));
if epoch > unseen_version.0 {
*unseen_version = (epoch, version.clone());
} else {
unseen_version.1.join(version);
}
}
}
fn insert_new_message(
channels_by_id: &mut BTreeMap<ChannelId, Arc<Channel>>,
channel_id: u64,
message_id: u64,
) {
if let Some(channel) = channels_by_id.get_mut(&channel_id) {
let unseen_message_id = Arc::make_mut(channel).unseen_message_id.get_or_insert(0);
*unseen_message_id = message_id.max(*unseen_message_id);
}
}