Avoid unnecessary DB writes (#29417)

Part of https://github.com/zed-industries/zed/issues/16472

* Adds debug logging to everywhere near INSERT/UPDATEs in the DB

So something like 
`env RUST_LOG=debug,wasmtime_cranelift=off,cranelift_codegen=off,vte=off
cargo run` could be used to view these (current zlog seems to process
the exclusions odd, so not sure this is the optimal RUST_LOG line) can
be used to debug any further writes.

* Removes excessive window stack serialization

Previously, it serialized unconditionally every 100ms.
Now, only if the stack had changed, which is now check every 500ms.

* Removes excessive terminal serialization

Previously, it serialized its `cwd` on every `ItemEvent::UpdateTab`
which was caused by e.g. any character output.
Now, only if the `cwd` has changed at the next event processing time.

Release Notes:

- Fixed more excessive DB writes
This commit is contained in:
Kirill Bulatov 2025-04-25 17:41:49 +03:00 committed by GitHub
parent 37fa437990
commit f106dfca42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 224 additions and 168 deletions

View file

@ -429,6 +429,9 @@ impl TerminalDb {
workspace_id: WorkspaceId,
working_directory: PathBuf,
) -> Result<()> {
log::debug!(
"Saving working directory {working_directory:?} for item {item_id} in workspace {workspace_id:?}"
);
let query =
"INSERT INTO terminals(item_id, workspace_id, working_directory, working_directory_path)
VALUES (?1, ?2, ?3, ?4)

View file

@ -112,6 +112,7 @@ pub struct TerminalView {
cursor_shape: CursorShape,
blink_state: bool,
blinking_terminal_enabled: bool,
cwd_serialized: bool,
blinking_paused: bool,
blink_epoch: usize,
hover_target_tooltip: Option<String>,
@ -207,6 +208,7 @@ impl TerminalView {
scroll_handle,
show_scrollbar: !Self::should_autohide_scrollbar(cx),
hide_scrollbar_task: None,
cwd_serialized: false,
_subscriptions: vec![
focus_in,
focus_out,
@ -843,167 +845,176 @@ fn subscribe_for_terminal_events(
cx: &mut Context<TerminalView>,
) -> Vec<Subscription> {
let terminal_subscription = cx.observe(terminal, |_, _, cx| cx.notify());
let mut previous_cwd = None;
let terminal_events_subscription = cx.subscribe_in(
terminal,
window,
move |terminal_view, _, event, window, cx| match event {
Event::Wakeup => {
cx.notify();
cx.emit(Event::Wakeup);
cx.emit(ItemEvent::UpdateTab);
cx.emit(SearchEvent::MatchesInvalidated);
move |terminal_view, terminal, event, window, cx| {
let current_cwd = terminal.read(cx).working_directory();
if current_cwd != previous_cwd {
previous_cwd = current_cwd;
terminal_view.cwd_serialized = false;
}
Event::Bell => {
terminal_view.has_bell = true;
cx.emit(Event::Wakeup);
}
Event::BlinkChanged(blinking) => {
if matches!(
TerminalSettings::get_global(cx).blinking,
TerminalBlink::TerminalControlled
) {
terminal_view.blinking_terminal_enabled = *blinking;
match event {
Event::Wakeup => {
cx.notify();
cx.emit(Event::Wakeup);
cx.emit(ItemEvent::UpdateTab);
cx.emit(SearchEvent::MatchesInvalidated);
}
}
Event::TitleChanged => {
cx.emit(ItemEvent::UpdateTab);
}
Event::Bell => {
terminal_view.has_bell = true;
cx.emit(Event::Wakeup);
}
Event::NewNavigationTarget(maybe_navigation_target) => {
match maybe_navigation_target.as_ref() {
None => {
terminal_view.hover_target_tooltip = None;
terminal_view.hover_tooltip_update = Task::ready(());
}
Some(MaybeNavigationTarget::Url(url)) => {
terminal_view.hover_target_tooltip = Some(url.clone());
terminal_view.hover_tooltip_update = Task::ready(());
}
Some(MaybeNavigationTarget::PathLike(path_like_target)) => {
let valid_files_to_open_task = possible_open_target(
&workspace,
&path_like_target.terminal_dir,
&path_like_target.maybe_path,
cx,
);
terminal_view.hover_tooltip_update =
cx.spawn(async move |terminal_view, cx| {
let file_to_open = valid_files_to_open_task.await;
terminal_view
.update(cx, |terminal_view, _| match file_to_open {
Some(
OpenTarget::File(path, _)
| OpenTarget::Worktree(path, _),
) => {
terminal_view.hover_target_tooltip =
Some(path.to_string(|path| {
path.to_string_lossy().to_string()
}));
}
None => {
terminal_view.hover_target_tooltip = None;
}
})
.ok();
});
Event::BlinkChanged(blinking) => {
if matches!(
TerminalSettings::get_global(cx).blinking,
TerminalBlink::TerminalControlled
) {
terminal_view.blinking_terminal_enabled = *blinking;
}
}
cx.notify()
}
Event::TitleChanged => {
cx.emit(ItemEvent::UpdateTab);
}
Event::Open(maybe_navigation_target) => match maybe_navigation_target {
MaybeNavigationTarget::Url(url) => cx.open_url(url),
Event::NewNavigationTarget(maybe_navigation_target) => {
match maybe_navigation_target.as_ref() {
None => {
terminal_view.hover_target_tooltip = None;
terminal_view.hover_tooltip_update = Task::ready(());
}
Some(MaybeNavigationTarget::Url(url)) => {
terminal_view.hover_target_tooltip = Some(url.clone());
terminal_view.hover_tooltip_update = Task::ready(());
}
Some(MaybeNavigationTarget::PathLike(path_like_target)) => {
let valid_files_to_open_task = possible_open_target(
&workspace,
&path_like_target.terminal_dir,
&path_like_target.maybe_path,
cx,
);
MaybeNavigationTarget::PathLike(path_like_target) => {
if terminal_view.hover_target_tooltip.is_none() {
return;
terminal_view.hover_tooltip_update =
cx.spawn(async move |terminal_view, cx| {
let file_to_open = valid_files_to_open_task.await;
terminal_view
.update(cx, |terminal_view, _| match file_to_open {
Some(
OpenTarget::File(path, _)
| OpenTarget::Worktree(path, _),
) => {
terminal_view.hover_target_tooltip =
Some(path.to_string(|path| {
path.to_string_lossy().to_string()
}));
}
None => {
terminal_view.hover_target_tooltip = None;
}
})
.ok();
});
}
}
let task_workspace = workspace.clone();
let path_like_target = path_like_target.clone();
cx.spawn_in(window, async move |terminal_view, cx| {
let open_target = terminal_view
.update(cx, |_, cx| {
possible_open_target(
&task_workspace,
&path_like_target.terminal_dir,
&path_like_target.maybe_path,
cx,
)
})?
.await;
if let Some(open_target) = open_target {
let path_to_open = open_target.path();
let opened_items = task_workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_paths(
vec![path_to_open.path.clone()],
OpenOptions {
visible: Some(OpenVisible::OnlyDirectories),
..Default::default()
},
None,
window,
cx.notify()
}
Event::Open(maybe_navigation_target) => match maybe_navigation_target {
MaybeNavigationTarget::Url(url) => cx.open_url(url),
MaybeNavigationTarget::PathLike(path_like_target) => {
if terminal_view.hover_target_tooltip.is_none() {
return;
}
let task_workspace = workspace.clone();
let path_like_target = path_like_target.clone();
cx.spawn_in(window, async move |terminal_view, cx| {
let open_target = terminal_view
.update(cx, |_, cx| {
possible_open_target(
&task_workspace,
&path_like_target.terminal_dir,
&path_like_target.maybe_path,
cx,
)
})
.context("workspace update")?
})?
.await;
if opened_items.len() != 1 {
debug_panic!(
"Received {} items for one path {path_to_open:?}",
opened_items.len(),
);
}
if let Some(open_target) = open_target {
let path_to_open = open_target.path();
let opened_items = task_workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_paths(
vec![path_to_open.path.clone()],
OpenOptions {
visible: Some(OpenVisible::OnlyDirectories),
..Default::default()
},
None,
window,
cx,
)
})
.context("workspace update")?
.await;
if opened_items.len() != 1 {
debug_panic!(
"Received {} items for one path {path_to_open:?}",
opened_items.len(),
);
}
if let Some(opened_item) = opened_items.first() {
if open_target.is_file() {
if let Some(Ok(opened_item)) = opened_item {
if let Some(row) = path_to_open.row {
let col = path_to_open.column.unwrap_or(0);
if let Some(active_editor) =
opened_item.downcast::<Editor>()
{
active_editor
.downgrade()
.update_in(cx, |editor, window, cx| {
editor.go_to_singleton_buffer_point(
language::Point::new(
row.saturating_sub(1),
col.saturating_sub(1),
),
window,
cx,
)
})
.log_err();
if let Some(opened_item) = opened_items.first() {
if open_target.is_file() {
if let Some(Ok(opened_item)) = opened_item {
if let Some(row) = path_to_open.row {
let col = path_to_open.column.unwrap_or(0);
if let Some(active_editor) =
opened_item.downcast::<Editor>()
{
active_editor
.downgrade()
.update_in(cx, |editor, window, cx| {
editor.go_to_singleton_buffer_point(
language::Point::new(
row.saturating_sub(1),
col.saturating_sub(1),
),
window,
cx,
)
})
.log_err();
}
}
}
} else if open_target.is_dir() {
task_workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |_, cx| {
cx.emit(project::Event::ActivateProjectPanel);
})
})?;
}
} else if open_target.is_dir() {
task_workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |_, cx| {
cx.emit(project::Event::ActivateProjectPanel);
})
})?;
}
}
}
anyhow::Ok(())
})
.detach_and_log_err(cx)
anyhow::Ok(())
})
.detach_and_log_err(cx)
}
},
Event::BreadcrumbsChanged => cx.emit(ItemEvent::UpdateBreadcrumbs),
Event::CloseTerminal => cx.emit(ItemEvent::CloseItem),
Event::SelectionsChanged => {
window.invalidate_character_coordinates();
cx.emit(SearchEvent::ActiveMatchChanged)
}
},
Event::BreadcrumbsChanged => cx.emit(ItemEvent::UpdateBreadcrumbs),
Event::CloseTerminal => cx.emit(ItemEvent::CloseItem),
Event::SelectionsChanged => {
window.invalidate_character_coordinates();
cx.emit(SearchEvent::ActiveMatchChanged)
}
},
);
@ -1539,6 +1550,9 @@ impl Item for TerminalView {
) {
if self.terminal().read(cx).task().is_none() {
if let Some((new_id, old_id)) = workspace.database_id().zip(self.workspace_id) {
log::debug!(
"Updating workspace id for the terminal, old: {old_id:?}, new: {new_id:?}",
);
cx.background_spawn(TERMINAL_DB.update_workspace_id(
new_id,
old_id,
@ -1587,6 +1601,7 @@ impl SerializableItem for TerminalView {
}
if let Some((cwd, workspace_id)) = terminal.working_directory().zip(self.workspace_id) {
self.cwd_serialized = true;
Some(cx.background_spawn(async move {
TERMINAL_DB
.save_working_directory(item_id, workspace_id, cwd)
@ -1597,8 +1612,8 @@ impl SerializableItem for TerminalView {
}
}
fn should_serialize(&self, event: &Self::Event) -> bool {
matches!(event, ItemEvent::UpdateTab)
fn should_serialize(&self, _: &Self::Event) -> bool {
!self.cwd_serialized
}
fn deserialize(