Avoid unnecessary DB writes (#29417)

Kirill Bulatov created

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

Change summary

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   | 293 ++++++++++++----------
crates/vim/src/state.rs                     |   2 
crates/workspace/src/persistence.rs         |   9 
16 files changed, 225 insertions(+), 169 deletions(-)

Detailed changes

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",

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

crates/command_palette/src/persistence.rs 🔗

@@ -67,7 +67,12 @@ impl CommandPaletteDB {
         command_name: impl Into<String>,
         user_query: impl Into<String>,
     ) -> 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
     }
 

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

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

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 ((?), (?))
         }
     }

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")

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, ?, ?)")

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,

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

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,

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<Task<()>>,
+    _serialization_task: Task<()>,
     _subscriptions: Vec<Subscription>,
 }
 
@@ -67,17 +67,21 @@ impl AppSession {
     pub fn new(session: Session, cx: &Context<Self>) -> 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<Self>) -> 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<AnyWindowHandle>) {
-    let window_ids = windows
-        .into_iter()
-        .map(|window| window.window_id().as_u64())
-        .collect::<Vec<_>>();
+fn window_stack(cx: &App) -> Option<Vec<u64>> {
+    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

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)

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<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);
-            }
-
-            Event::Bell => {
-                terminal_view.has_bell = true;
-                cx.emit(Event::Wakeup);
+        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::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::Open(maybe_navigation_target) => match maybe_navigation_target {
-                MaybeNavigationTarget::Url(url) => cx.open_url(url),
+                Event::TitleChanged => {
+                    cx.emit(ItemEvent::UpdateTab);
+                }
 
-                MaybeNavigationTarget::PathLike(path_like_target) => {
-                    if terminal_view.hover_target_tooltip.is_none() {
-                        return;
+                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();
+                                });
+                        }
                     }
-                    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(

crates/vim/src/state.rs 🔗

@@ -1644,6 +1644,7 @@ impl VimDb {
         path: Arc<Path>,
         marks: HashMap<String, Vec<Point>>,
     ) -> 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<Path>,
     ) -> 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

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!(