Throttle the sending of UpdateFollowers messages (#8918)
## Problem We're trying to figure out why we sometimes see high latency when collaborating, even though the collab server logs indicate that messages are not taking long to process. We think that high volumes of certain types of messages, including `UpdateFollowers` may cause a lot of messages to queue up, causing delays before collab sees certain messages. ## Fix This PR reduces the number of `UpdateFollowers` messages that clients send to collab when scrolling around or moving the cursor, using a time-based throttle. The downside of this change is that scrolling will not be as smooth when following someone. The advantage is that it will be much easier to keep up with the stream of updates, since they will be sent much less frequently. ## Release Notes: - Fixed slowness that could occur when collaborating due to excessive messages being sent to support following. --------- Co-authored-by: Nathan <nathan@zed.dev> Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: Thorsten <thorsten@zed.dev> Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
This commit is contained in:
parent
c8383e3b18
commit
6036830049
7 changed files with 109 additions and 45 deletions
|
@ -13,6 +13,7 @@ path = "src/picker.rs"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
editor.workspace = true
|
||||
gpui.workspace = true
|
||||
menu.workspace = true
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
div, list, prelude::*, uniform_list, AnyElement, AppContext, ClickEvent, DismissEvent,
|
||||
|
@ -15,11 +16,16 @@ enum ElementContainer {
|
|||
UniformList(UniformListScrollHandle),
|
||||
}
|
||||
|
||||
struct PendingUpdateMatches {
|
||||
delegate_update_matches: Option<Task<()>>,
|
||||
_task: Task<Result<()>>,
|
||||
}
|
||||
|
||||
pub struct Picker<D: PickerDelegate> {
|
||||
pub delegate: D,
|
||||
element_container: ElementContainer,
|
||||
editor: View<Editor>,
|
||||
pending_update_matches: Option<Task<()>>,
|
||||
pending_update_matches: Option<PendingUpdateMatches>,
|
||||
confirm_on_update: Option<bool>,
|
||||
width: Option<Length>,
|
||||
max_height: Option<Length>,
|
||||
|
@ -281,15 +287,32 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
}
|
||||
|
||||
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
|
||||
let update = self.delegate.update_matches(query, cx);
|
||||
let delegate_pending_update_matches = self.delegate.update_matches(query, cx);
|
||||
|
||||
self.matches_updated(cx);
|
||||
self.pending_update_matches = Some(cx.spawn(|this, mut cx| async move {
|
||||
update.await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.matches_updated(cx);
|
||||
})
|
||||
.ok();
|
||||
}));
|
||||
// This struct ensures that we can synchronously drop the task returned by the
|
||||
// delegate's `update_matches` method and the task that the picker is spawning.
|
||||
// If we simply capture the delegate's task into the picker's task, when the picker's
|
||||
// task gets synchronously dropped, the delegate's task would keep running until
|
||||
// the picker's task has a chance of being scheduled, because dropping a task happens
|
||||
// asynchronously.
|
||||
self.pending_update_matches = Some(PendingUpdateMatches {
|
||||
delegate_update_matches: Some(delegate_pending_update_matches),
|
||||
_task: cx.spawn(|this, mut cx| async move {
|
||||
let delegate_pending_update_matches = this.update(&mut cx, |this, _| {
|
||||
this.pending_update_matches
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.delegate_update_matches
|
||||
.take()
|
||||
.unwrap()
|
||||
})?;
|
||||
delegate_pending_update_matches.await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.matches_updated(cx);
|
||||
})
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue