feat: sync ETag concurrency + fix chunk-transfer hang + sync overlay + auto-VACUUM
Sync optimistic concurrency (ETag/If-Match) - webdav GET captures the response ETag; PUT sends it back as If-Match so the server rejects (412) our write when another device changed the file between our pull and push. A 412 re-runs the whole pull→merge→push (bounded to 3) so the other device's changes are folded in instead of clobbered. Servers without ETags → empty etag → no If-Match → falls back to last-write-wins (no regression). onWebdavResult gained a 4th etag arg. Chunked webdav PUT (big vaults no longer black-screen on sync) - The whole encrypted snapshot base64'd into a single cmd://webdav/put URL blew past WebView2's cap → black screen once the vault grew (20MB of attachments). PUT bodies now stream through the file/chunk transport and commit via a new webdav/put-commit (reads the accumulated buffer). Chunk-transfer hang fix (root cause of the stuck "Preparing…" sync) - All chunked transfers (saveFile/writeFile/webdav PUT) share one reqId-keyed resolver. A resolved chunk's stale 30s timeout would later delete the CURRENT chunk's resolver and fire the wrong res(), leaving that chunk's await pending forever. Extracted a single _streamChunks() helper whose ack CLEARS the pending timeout, so resolvers stay strictly one-at-a-time. Also fixed _webdavCall referencing the Bridge-local cmd() from module scope (latent ReferenceError). Sync busy overlay - syncStatus() now drives the global busy overlay too, so a running sync blocks stray clicks (e.g. the auto-backup "Choose…" picker) and reads like the manual backup. The account-mismatch confirm hideBusy()s first so it's visible above the overlay. Auto-VACUUM (reclaim space after deleting large attachments) - SQLite never shrinks the file on DELETE, so deleting big attachments left vault.db bloated (35MB for 11 tiny entries). DB.CompactIfBloated VACUUMs when >20% of pages are free AND >~2MB is reclaimable — called on startup and after each attachment delete. A healthy small vault pays nothing. (Verified: 35MB → 695KB after the deletes.) Rebuild: BuildAssets + F9 (UMainForm + PM.Database + PM.Handler.Attachments). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -499,8 +499,14 @@ device, never transmitted.
|
||||
restore attachments ; both sides have it = compare `updated_at`,
|
||||
PUT if remote newer.
|
||||
7. `loadEntries()` + `buildSyncSnapshot()` for the post-merge state.
|
||||
8. `webdav/put` push the merged snapshot.
|
||||
9. Toast `X added · Y updated · Z deleted`.
|
||||
8. `webdav/put` push the merged snapshot, with `If-Match: <etag>` where
|
||||
the etag was captured from the step-2 pull (optimistic concurrency).
|
||||
A **412** means another device changed the file between our pull and
|
||||
push → re-run the whole pull→merge→push (`runSyncNow(_attempt+1)`,
|
||||
bounded to 3) so their changes are folded in instead of clobbered.
|
||||
Servers without ETag support return an empty etag → no If-Match sent
|
||||
→ falls back to last-write-wins (same as before).
|
||||
9. Toast `pulled X new · Y updated · Z deleted · pushed N entries`.
|
||||
|
||||
Sensitive actions (export, change master pw, recovery code…) still
|
||||
require master pw via `askReauth` — sync never substitutes.
|
||||
|
||||
Reference in New Issue
Block a user