Skip to content

fix(sync): 修复 WebDAV 同步卡死(原 #781 的止血部分,不动云端格式) - #807

Open
codedogQBY wants to merge 2 commits into
mainfrom
codex/sync-responsive-split
Open

codedogQBY wants to merge 2 commits into
mainfrom
codex/sync-responsive-split

Conversation

@codedogQBY

Copy link
Copy Markdown
Owner

从 #781 拆出来的可安全验证部分,对应 #741(同步时应用整体卡死:点书籍无响应、菜单点不动、最终判未响应)。

为什么拆

#781 里其实是两件事,风险完全不一样:

  1. 同步时 UI 卡死 —— 传输层、进度回调、DB 写等待三个问题,改动小、不动云端数据格式;
  2. 按书同步引擎重构 —— 新远端布局,云端格式无向后兼容,升级要清空远端 /readany/sync/ 重传一次。

第 2 件没法在现有书库上随手验(要动真实云端数据),所以先把第 1 件独立出来合掉。

改动(对应 #781 的前两个 commit,作者 @jenken827)

1. 桌面端缺的原生流式传输(卡死主因)

webdav-client.ts 一直有 platform.uploadFile / downloadFile 快路径,但桌面端从来没实现过,所以每本 multi-MB 的书都走兜底路径:@tauri-apps/plugin-http 把 body 变成 Array.from(new Uint8Array(buffer)) —— 千万级元素的 JS 数组,在渲染主线程上序列化,单次阻塞数秒。

本 PR 补上 Tauri 侧实现:新增 Rust 命令 webdav_upload_file / webdav_download_file(reqwest,tokio 线程池里直接在磁盘与服务器之间流式读写,字节不进 webview 进程,进度经 IPC Channel 上报)。

2. 响应性止血

  • 传输分块进度节流到每任务 300ms 一次(任务开始/结束仍立即上报),避免每秒几十次 zustand → React 重渲染;
  • waitForSyncToSettle 上限 12s → 1.5s —— 这是「同步时点书打不开」的直接原因(同步期间任何 DB 写入先空等最多 12 秒);
  • applyChanges 每 25 行让出一次主线程。

为什么这个能验

  • 云端格式零改动:老引擎、老 device-*.json 布局照旧,不需要清空远端、不需要重新全量同步;
  • 本地结构零改动;
  • LAN 同步不受影响。

验证

已跑:

  • pnpm --filter @readany/core test — 81 files / 598 passed
  • pnpm --filter app exec tsc --noEmit — clean
  • cargo check(packages/app/src-tauri)— clean
  • 相对 main 的适配:tauri-platform-service.ts 的 import 与当前 main 的 import type 写法对齐(1 行)

待真机验证(桌面端 + WebDAV):

  • 同步一本大书(几十 MB 的 PDF)时,UI 仍可点击、可打开书籍,不再判「未响应」
  • 同步期间打开书籍不再卡最多 12 秒
  • 首次全量同步(书库较大)期间菜单/切页正常
  • 同步完成后远端文件、进度与旧版一致(格式没变)

与 #781 的关系

#781 剩余的三个 commit(按书同步引擎 + 可配置并发 + 单条目容错)保留在 #781 继续推进,那部分要单独排一次「清空远端重同步」的验证。

During a sync the app froze: clicks stopped registering, opening a book
did nothing, and the window eventually died. Three compounding causes:

- Transfer progress was pushed to the store on EVERY chunk. With 3-5
  concurrent transfers that sustains a synchronous React update storm on
  the renderer main thread (readest hit the same bug — their Sentry
  READEST-2 — and ships a progress throttle). Chunk progress is now
  throttled to one emit per 300ms per task; task start/completion still
  emit immediately.
- Every DB write wrapped in runWithDbRetry waited up to 12s for a running
  sync to finish before even attempting the write (waitForSyncToSettle),
  so opening a book stalled for the full timeout. The settle wait is now
  capped at 1.5s — genuine SQLite lock contention is already handled by
  the retry loop itself.
- applyChanges yielded the main thread only every 100 applied records;
  large remote snapshots could hog it for seconds between yields. Now
  every 25.
…ss the webview heap

plugin-http serializes request bodies via Array.from(new Uint8Array(body))
into a JSON number array over IPC: every multi-megabyte book upload froze
the renderer main thread for seconds and stalled the whole app during
sync. Implement the desktop uploadFile/downloadFile platform methods as
Tauri commands that stream directly between disk and the WebDAV server
(reqwest on the tokio pool, 300s timeout, optional insecure-TLS). The
per-book engine already prefers these entry points, so book/cover
transfers now bypass the webview entirely and downloads report progress
over an IPC channel (throttled by the engine's progress gate).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants