async fn resync_workspaces(
state: &Arc<Mutex<State>>,
wstore: &WaveStore,
) -> Result<(), Box<dyn Error>>Expand description
Snapshot state.workspaces and reconcile against SQLite —
INSERT/UPDATE only, no deletes. Workspace-scoped (tab/block
resync expands here as each entity’s RPC layer migrates).
Why no delete-phase: during the migration window, workspaces
can be created OUTSIDE the reducer (e.g., the still-wcore-direct
CreateWindow flow calls wcore::create_window_full which
creates a workspace under the hood). Those workspaces aren’t in
state.workspaces. If the subscriber lagged and we did a
delete-not-in-snapshot pass, we’d cascade-delete legitimate
workspaces — data loss triggered by bus pressure rather than a
user action. Far better for a stale workspace deleted-via-reducer
to linger on disk after a Lagged event than to lose a real one.
(codex P1 #615.)
Stale-after-Lagged scenario: reducer fires WorkspaceDeleted, the
subscriber drops it on Lagged, resync runs but only insert/updates.
SQLite still has the deleted workspace’s row. The next user-driven
DeleteWorkspace (which falls through to wcore::delete_workspace
for unknown-to-reducer rows) cleans it up. Acceptable behaviour
during the migration; tightens once all RPC is reducer-driven.
Strategy:
- Lock state, snapshot the workspace map (ids + names), release the lock.
- For each workspace in the snapshot: insert if missing, no-op if name matches, update if name differs.