From f106dfca42fd95c0962d13d54d54b47092add23c Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 25 Apr 2025 17:41:49 +0300 Subject: [PATCH] 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 --- Cargo.lock | 3 + crates/command_palette/Cargo.toml | 1 + crates/command_palette/src/persistence.rs | 7 +- crates/component_preview/Cargo.toml | 1 + crates/component_preview/src/persistence.rs | 3 + crates/db/src/kvp.rs | 14 +- crates/editor/src/items.rs | 1 + crates/editor/src/persistence.rs | 2 + crates/editor/src/scroll.rs | 3 + crates/image_viewer/Cargo.toml | 1 + crates/image_viewer/src/image_viewer.rs | 13 +- crates/session/src/session.rs | 38 ++- crates/terminal_view/src/persistence.rs | 3 + crates/terminal_view/src/terminal_view.rs | 291 ++++++++++---------- crates/vim/src/state.rs | 2 + crates/workspace/src/persistence.rs | 9 + 16 files changed, 224 insertions(+), 168 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98a035ff74..cc00d4b4df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3158,6 +3158,7 @@ dependencies = [ "go_to_line", "gpui", "language", + "log", "menu", "picker", "postage", @@ -3208,6 +3209,7 @@ dependencies = [ "db", "gpui", "languages", + "log", "notifications", "project", "serde", @@ -7100,6 +7102,7 @@ dependencies = [ "editor", "file_icons", "gpui", + "log", "project", "schemars", "serde", diff --git a/crates/command_palette/Cargo.toml b/crates/command_palette/Cargo.toml index f471d5d271..c97d142152 100644 --- a/crates/command_palette/Cargo.toml +++ b/crates/command_palette/Cargo.toml @@ -20,6 +20,7 @@ command_palette_hooks.workspace = true db.workspace = true fuzzy.workspace = true gpui.workspace = true +log.workspace = true picker.workspace = true postage.workspace = true serde.workspace = true diff --git a/crates/command_palette/src/persistence.rs b/crates/command_palette/src/persistence.rs index f750f0f871..6c7da79e3b 100644 --- a/crates/command_palette/src/persistence.rs +++ b/crates/command_palette/src/persistence.rs @@ -67,7 +67,12 @@ impl CommandPaletteDB { command_name: impl Into, user_query: impl Into, ) -> Result<()> { - self.write_command_invocation_internal(command_name.into(), user_query.into()) + let command_name = command_name.into(); + let user_query = user_query.into(); + log::debug!( + "Writing command invocation: command_name={command_name}, user_query={user_query}" + ); + self.write_command_invocation_internal(command_name, user_query) .await } diff --git a/crates/component_preview/Cargo.toml b/crates/component_preview/Cargo.toml index e01e7d2208..bfa19fa60d 100644 --- a/crates/component_preview/Cargo.toml +++ b/crates/component_preview/Cargo.toml @@ -21,6 +21,7 @@ component.workspace = true gpui.workspace = true languages.workspace = true notifications.workspace = true +log.workspace = true project.workspace = true ui.workspace = true ui_input.workspace = true diff --git a/crates/component_preview/src/persistence.rs b/crates/component_preview/src/persistence.rs index a3fb0c698b..8925b8c3db 100644 --- a/crates/component_preview/src/persistence.rs +++ b/crates/component_preview/src/persistence.rs @@ -23,6 +23,9 @@ impl ComponentPreviewDb { workspace_id: WorkspaceId, active_page_id: String, ) -> Result<()> { + log::debug!( + "Saving active page: item_id={item_id:?}, workspace_id={workspace_id:?}, active_page_id={active_page_id}" + ); let query = "INSERT INTO component_previews(item_id, workspace_id, active_page_id) VALUES (?1, ?2, ?3) ON CONFLICT DO UPDATE SET diff --git a/crates/db/src/kvp.rs b/crates/db/src/kvp.rs index c9d994d34d..ef86c5a032 100644 --- a/crates/db/src/kvp.rs +++ b/crates/db/src/kvp.rs @@ -18,8 +18,13 @@ impl KeyValueStore { } } + pub async fn write_kvp(&self, key: String, value: String) -> anyhow::Result<()> { + log::debug!("Writing key-value pair for key {key}"); + self.write_kvp_inner(key, value).await + } + query! { - pub async fn write_kvp(key: String, value: String) -> Result<()> { + async fn write_kvp_inner(key: String, value: String) -> Result<()> { INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?)) } } @@ -78,8 +83,13 @@ impl GlobalKeyValueStore { } } + pub async fn write_kvp(&self, key: String, value: String) -> anyhow::Result<()> { + log::debug!("Writing global key-value pair for key {key}"); + self.write_kvp_inner(key, value).await + } + query! { - pub async fn write_kvp(key: String, value: String) -> Result<()> { + async fn write_kvp_inner(key: String, value: String) -> Result<()> { INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?)) } } diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index ce3bc11bd4..476a05b29f 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1251,6 +1251,7 @@ impl SerializableItem for Editor { language, mtime, }; + log::debug!("Serializing editor {item_id:?} in workspace {workspace_id:?}"); DB.save_serialized_editor(item_id, workspace_id, editor) .await .context("failed to save serialized editor") diff --git a/crates/editor/src/persistence.rs b/crates/editor/src/persistence.rs index d1b660fe68..0ab2cab987 100644 --- a/crates/editor/src/persistence.rs +++ b/crates/editor/src/persistence.rs @@ -276,6 +276,7 @@ impl EditorDb { workspace_id: WorkspaceId, selections: Vec<(usize, usize)>, ) -> Result<()> { + log::debug!("Saving selections for editor {editor_id} in workspace {workspace_id:?}"); let mut first_selection; let mut last_selection = 0_usize; for (count, placeholders) in std::iter::once("(?1, ?2, ?, ?)") @@ -327,6 +328,7 @@ VALUES {placeholders}; workspace_id: WorkspaceId, folds: Vec<(usize, usize)>, ) -> Result<()> { + log::debug!("Saving folds for editor {editor_id} in workspace {workspace_id:?}"); let mut first_fold; let mut last_fold = 0_usize; for (count, placeholders) in std::iter::once("(?1, ?2, ?, ?)") diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index a7c209bdff..625fdaa6f2 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -270,6 +270,9 @@ impl ScrollManager { cx.foreground_executor() .spawn(async move { + log::debug!( + "Saving scroll position for item {item_id:?} in workspace {workspace_id:?}" + ); DB.save_scroll_position( item_id, workspace_id, diff --git a/crates/image_viewer/Cargo.toml b/crates/image_viewer/Cargo.toml index 9bcd6c1753..309de4a25f 100644 --- a/crates/image_viewer/Cargo.toml +++ b/crates/image_viewer/Cargo.toml @@ -21,6 +21,7 @@ db.workspace = true editor.workspace = true file_icons.workspace = true gpui.workspace = true +log.workspace = true project.workspace = true schemars.workspace = true serde.workspace = true diff --git a/crates/image_viewer/src/image_viewer.rs b/crates/image_viewer/src/image_viewer.rs index b9efa8aaa5..0a8043a1c6 100644 --- a/crates/image_viewer/src/image_viewer.rs +++ b/crates/image_viewer/src/image_viewer.rs @@ -261,6 +261,7 @@ impl SerializableItem for ImageView { Some(cx.background_spawn({ async move { + log::debug!("Saving image at path {image_path:?}"); IMAGE_VIEWER .save_image_path(item_id, workspace_id, image_path) .await @@ -399,18 +400,6 @@ mod persistence { } impl ImageViewerDb { - query! { - pub async fn update_workspace_id( - new_id: WorkspaceId, - old_id: WorkspaceId, - item_id: ItemId - ) -> Result<()> { - UPDATE image_viewers - SET workspace_id = ? - WHERE workspace_id = ? AND item_id = ? - } - } - query! { pub async fn save_image_path( item_id: ItemId, diff --git a/crates/session/src/session.rs b/crates/session/src/session.rs index 5705d23d3e..f027df8762 100644 --- a/crates/session/src/session.rs +++ b/crates/session/src/session.rs @@ -1,7 +1,7 @@ use std::time::Duration; use db::kvp::KEY_VALUE_STORE; -use gpui::{AnyWindowHandle, AppContext as _, Context, Subscription, Task, WindowId}; +use gpui::{App, AppContext as _, Context, Subscription, Task, WindowId}; use util::ResultExt; use uuid::Uuid; @@ -59,7 +59,7 @@ impl Session { pub struct AppSession { session: Session, - _serialization_task: Option>, + _serialization_task: Task<()>, _subscriptions: Vec, } @@ -67,17 +67,21 @@ impl AppSession { pub fn new(session: Session, cx: &Context) -> Self { let _subscriptions = vec![cx.on_app_quit(Self::app_will_quit)]; - let _serialization_task = Some(cx.spawn(async move |_, cx| { + let _serialization_task = cx.spawn(async move |_, cx| { + let mut current_window_stack = Vec::new(); loop { - if let Some(windows) = cx.update(|cx| cx.window_stack()).ok().flatten() { - store_window_stack(windows).await; + if let Some(windows) = cx.update(|cx| window_stack(cx)).ok().flatten() { + if windows != current_window_stack { + store_window_stack(&windows).await; + current_window_stack = windows; + } } cx.background_executor() - .timer(Duration::from_millis(100)) + .timer(Duration::from_millis(500)) .await; } - })); + }); Self { session, @@ -87,8 +91,8 @@ impl AppSession { } fn app_will_quit(&mut self, cx: &mut Context) -> Task<()> { - if let Some(windows) = cx.window_stack() { - cx.background_spawn(store_window_stack(windows)) + if let Some(window_stack) = window_stack(cx) { + cx.background_spawn(async move { store_window_stack(&window_stack).await }) } else { Task::ready(()) } @@ -107,13 +111,17 @@ impl AppSession { } } -async fn store_window_stack(windows: Vec) { - let window_ids = windows - .into_iter() - .map(|window| window.window_id().as_u64()) - .collect::>(); +fn window_stack(cx: &App) -> Option> { + Some( + cx.window_stack()? + .into_iter() + .map(|window| window.window_id().as_u64()) + .collect(), + ) +} - if let Ok(window_ids_json) = serde_json::to_string(&window_ids) { +async fn store_window_stack(windows: &[u64]) { + if let Ok(window_ids_json) = serde_json::to_string(windows) { KEY_VALUE_STORE .write_kvp(SESSION_WINDOW_STACK_KEY.to_string(), window_ids_json) .await diff --git a/crates/terminal_view/src/persistence.rs b/crates/terminal_view/src/persistence.rs index 6d29630a79..6235e3365a 100644 --- a/crates/terminal_view/src/persistence.rs +++ b/crates/terminal_view/src/persistence.rs @@ -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) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 9a4d9814e7..74853569bc 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -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, @@ -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, ) -> Vec { 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::() - { - 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::() + { + 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( diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 6b1a87aec7..8a38e59861 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -1644,6 +1644,7 @@ impl VimDb { path: Arc, marks: HashMap>, ) -> Result<()> { + log::debug!("Setting path {path:?} for {} marks", marks.len()); let result = self .write(move |conn| { let mut query = conn.exec_bound(sql!( @@ -1694,6 +1695,7 @@ impl VimDb { mark_name: String, path: Arc, ) -> Result<()> { + log::debug!("Setting global mark path {path:?} for {mark_name}"); self.write(move |conn| { conn.exec_bound(sql!( INSERT OR REPLACE INTO vim_global_marks_paths diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index 4608a7302f..02dd681185 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -739,6 +739,7 @@ impl WorkspaceDb { /// Saves a workspace using the worktree roots. Will garbage collect any workspaces /// that used this workspace previously pub(crate) async fn save_workspace(&self, workspace: SerializedWorkspace) { + log::debug!("Saving workspace at location: {:?}", workspace.location); self.write(move |conn| { conn.with_savepoint("update_worktrees", || { // Clear out panes and pane_groups @@ -909,6 +910,7 @@ impl WorkspaceDb { { Ok(project) } else { + log::debug!("Inserting SSH project at host {host}"); self.insert_ssh_project(host, port, paths, user) .await? .ok_or_else(|| anyhow!("failed to insert ssh project")) @@ -1209,6 +1211,9 @@ impl WorkspaceDb { pane_group: &SerializedPaneGroup, parent: Option<(GroupId, usize)>, ) -> Result<()> { + if parent.is_none() { + log::debug!("Saving a pane group for workspace {workspace_id:?}"); + } match pane_group { SerializedPaneGroup::Group { axis, @@ -1387,6 +1392,10 @@ impl WorkspaceDb { relative_worktree_path: String, toolchain: Toolchain, ) -> Result<()> { + log::debug!( + "Setting toolchain for workspace, worktree: {worktree_id:?}, relative path: {relative_worktree_path:?}, toolchain: {}", + toolchain.name + ); self.write(move |conn| { let mut insert = conn .exec_bound(sql!(