Keep CardDAV rebuilds and cold reads within their budgets, rebased #777
Open
kayg
wants to merge 2 commits from
issue/carddav-read-perf-fix-r2 into issue/carddav-read-r2
pull from: issue/carddav-read-perf-fix-r2
merge into: kayg:issue/carddav-read-r2
kayg:main
kayg:feat/style-sheet-fixes
kayg:feat/contrast
kayg:fix/shared-row-swipes
kayg:feat/pass8
kayg:t3/paged-search-reconcile-final
kayg:storage/wal-checkpoint-off-save-1051
kayg:review/calendar-2
kayg:dev
kayg:feat/collections-db
kayg:fix/instant-keys-1056
kayg:feat/money-round
kayg:perf/stalls-1054
kayg:wip/tiering-v3-cold4
kayg:wip/tiering-v3-cold5
kayg:port/rows-actions
kayg:port/rows-drop
kayg:port/hidden-blocks
kayg:port/rows
kayg:port/port-tables
kayg:port/port-tables-2
kayg:t3/mail-1011-liveness
kayg:port/devices
kayg:port/port-attachments
kayg:port/port-attachments-2
kayg:port/port-attachments-3
kayg:docs/design-consistency
kayg:docs/engineering-standards
kayg:fix/rusqlite-0.41-rustsec-2026-0459
kayg:feat/iphone-fixes
kayg:shared/restore-and-stale-918-919
kayg:fix/phone-top-band-contrast
kayg:feat/notes-approve
kayg:perf/soak-max-gate
kayg:feat/files-s1-identity
kayg:fix/lifecycle-event-id
kayg:feat/quickfix
kayg:wip/tiering-v3-cold
kayg:perf/web-mockup-frame-fixes
kayg:port/contacts-merge
kayg:port/contacts-edit
kayg:test/shared-room-no-deleted-text
kayg:perf/first-paint-credits
kayg:fix/shared-history-after-grant
kayg:fix/phone-swipe-regressions
kayg:feat/contrast-r1
kayg:review/sharing-pr3-review
kayg:feat/tiering-v3-s2-tags
kayg:feat/folder-drop
kayg:fix/deskpills-lazy-share
kayg:feat/sharing-s1-pr3
kayg:tmp/money-round-before
kayg:fix/phone-pane-backdrop-roots
kayg:fix/palette-preload-chips
kayg:fix/invite-defaults
kayg:port/tasks-matrix
kayg:fix/restore-green-gates
kayg:fix/sharing-fast-path-hardening
kayg:t3/photos-s1
kayg:feat/tiering-v3-s1-namespace
kayg:notes-archive-hide
kayg:feat/tasks-actions
kayg:t3/photos-plan-slice-one
kayg:feat/whiteboard-s1
kayg:t3/paged-search-reconcile
kayg:t3/smooth-pane-transitions
kayg:t3/index-position-startup-scan
kayg:fix/private-staging-759b-r2
kayg:feat/dav-transport
kayg:feat/imap-notes-fetch
kayg:feat/imap-notes-projection
kayg:feat/imap-notes-readonly
kayg:feat/imap-notes-auth
kayg:feat/imap-notes-server-foundation
kayg:docs/undo-block-ids-plan
kayg:fix/undo-768
kayg:proto/block-identity-v7d
kayg:issue/carddav-read-r2
kayg:docs/imap-766-plan-v2
kayg:docs/imap-notes-plan
kayg:docs/quota-plan-2
kayg:fix/parallel-test-flakes
kayg:feat/contacts-companion-pure
kayg:docs/calendar-backend-v2-plan
kayg:feat/calendar-ical-converter
kayg:docs/tasks-writes-v2-plan
kayg:docs/carddav-writes-v2-plan
kayg:docs/contacts-import-v2-plan
kayg:issue/610-pm-midnight
kayg:feat/calendar-v2-s1-containment
kayg:docs/dav-recorder-v2-plan
kayg:plan/previews-865
kayg:plan/save-log-durable-entry
kayg:fix/e2e-data-on-ram
kayg:feat/appwindow3
kayg:feat/appwindow2
kayg:feat/appwindow
kayg:feat/money-p1
kayg:feat/keys-sheet
kayg:review/calendar-2-themes
kayg:feat/smooth-cursor
kayg:fix/float-style
kayg:review/calendar-2-float
kayg:release/2026-11-06-hotfix
kayg:archive/port/rows-drop-code
kayg:archive/port/rows-actions
kayg:archive/port/rows
kayg:scratch/tasks-894-ui-gates
kayg:review/calendar-2-night-wip
kayg:release/2026-11-05
kayg:archive/feat/tiering-v3-s1-namespace
kayg:archive/flaky/rows-drop-fixed
kayg:archive/flaky/rows-actions
kayg:archive/feat/tiering-v3-reader
kayg:archive/feat/tiering-v3-roots
kayg:archive/flaky/rows
kayg:sim/bm15-final
kayg:sim/bm15-fix
kayg:sim/bm15-bis-932
kayg:sim/bm15-bis-909
kayg:sim/bm15
kayg:sim/bm15-bis-887
kayg:perf/tiering-option-b
kayg:port/style-3
kayg:port/style-2
kayg:port/style
kayg:fix/sw-lazy-precache
kayg:fix/vim-w-waits-for-save
kayg:fix/signout-test-races
kayg:fp-scratch/all
kayg:sim/bm15-first-paint
kayg:archive/bm15/rows-drop
kayg:archive/bm15/rows-actions
kayg:fix/palette-snippet-contrast
kayg:archive/bm15/rows
kayg:archive/bm15/hidden
kayg:archive/plan/save-log-durable-entry
kayg:mock/money-fix2
kayg:archive/fix/e2e-data-on-ram
kayg:mock/appearance-all
kayg:mock/pill-press
kayg:archive/docs/design-consistency
kayg:mock/auto-swipes
kayg:mock/mail-actions
kayg:archive/feat/mail-p1
kayg:mock/money-static
kayg:archive/review/standards-917
kayg:archive/docs/engineering-standards
kayg:perf/tiering-761-fix
kayg:mock/type-audit
kayg:mock/third-pane
kayg:feat/contacts-import-v2-s1
kayg:perf/executor-hot-path
kayg:archive/port/hidden-blocks
kayg:archive/port/devices
kayg:scratch/executor-on-894
kayg:scratch/executor-spans2
kayg:scratch/executor-spans
kayg:train/2026-11-03-bm14c
kayg:archive/port/rows-drop
kayg:feat/tiering-761-d-scale
kayg:feat/tiering-761-c-index
kayg:feat/tiering-761-b-reader
kayg:archive/review/sharing-pr3-review-9fec3d1f
kayg:train/2026-11-03-bm14b
kayg:perf/ports-lazy-budgets
kayg:feat/tiering-761-a-migration
kayg:train/2026-11-03-bm14
kayg:train/2026-11-03-bm12
kayg:archive/review/sharing-pr3-review
kayg:archive/feat/sharing-s1-pr3
kayg:train/2026-11-03-bm11
kayg:archive/feat/imap-notes-fetch
kayg:train/2026-11-03-bm10
kayg:archive/review/sharing-pr3-review-code
kayg:archive/plan/previews-865
kayg:train/2026-11-02-bm9
kayg:archive/port/port-attachments-3
kayg:archive/port/contacts-merge
kayg:fix/motion-audit-859
kayg:train/2026-11-02-bm8
kayg:archive/scratch/bm8-prep
kayg:archive/port/port-pickers
kayg:archive/port/port-tables-2
kayg:archive/port/port-tables
kayg:archive/feat/sharing-s1-pr3-code
kayg:archive/scratch/bm7-fixed
kayg:train/2026-11-02-bm7
kayg:archive/fix/parallel-test-flakes-code-minipc01
kayg:train/2026-11-02-bm6
kayg:docs/imap-766-readonly-slice
kayg:feat/dav-recorder-v3
kayg:research/788-diagnosis
kayg:archive/fix/parallel-test-flakes
kayg:archive/perf/pr792-cold-index-edit
kayg:docs/sharing-slice-1
kayg:archive/docs/imap-766-plan-v2
kayg:feat/dav-recorder-v2
kayg:archive/docs/dav-recorder-v2-plan
kayg:spike/imap-766-s0-cases
kayg:archive/fix/undo-768
kayg:feat/dav-recorder
kayg:archive/docs/contacts-import-v2-plan
kayg:archive/feat/calendar-ical-converter
kayg:feat/contacts-import
kayg:perf/cold-growth-on-764
kayg:perf/cold-growth-on-tiering
kayg:archive/docs/tasks-writes-v2-plan
kayg:archive/docs/calendar-backend-v2-plan
kayg:archive/feat/contacts-companion-pure
kayg:feat/tasks-set-status
kayg:feat/tasks-move-reorder
kayg:archive/docs/carddav-writes-v2-plan
kayg:archive/feat/calendar-v2-s1-containment
kayg:feat/tasks-delete-promote-duplicate
kayg:archive/feat/dav-transport
kayg:archive/fix/carddav-rebuild-r1
kayg:archive/fix/carddav-retention-r1
kayg:feat/carddav-writes
kayg:perf/quota-on-tiering
kayg:perf/quota-simple
kayg:perf/cold-growth-on-quota-stack
kayg:feat/calendar-identity
kayg:feat/calendar-event-parser
kayg:feat/calendar-props-maps
kayg:port/glass-when-ring
kayg:feat/tiering-761-s1
kayg:wip/10by32-01-impl-759b-scratch-20261030
kayg:wip/6by32-01-bm3-webkit-pause-20261030
kayg:wip/10by32-01-bm3-chromium-pause-20261030
kayg:wip/6by32-01-tier-761-s1-pause-20261030
kayg:spike/imap-766-s0
kayg:wip/6by32-02-calternal-pause-20261030
kayg:wip/8by64-01-review-688-pause-20261030
kayg:wip/6by32-01-verify-744-pause-20261030
kayg:wip/6by32-01-verify-742-pause-20261030
kayg:fix/private-staging-759b
kayg:wip/6by32-01-review-748-pause-20261030
kayg:wip/6by32-01-review-733c-pause-20261030
kayg:wip/6by32-01-review-733b-pause-20261030
kayg:wip/6by32-01-perf-idle-8-pause-20261030
kayg:wip/6by32-01-perf767-base-pause-20261030
kayg:wip/6by32-01-perf767b-767-pause-20261030
kayg:wip/6by32-01-perf767b-764-pause-20261030
kayg:wip/6by32-01-perf767-767-pause-20261030
kayg:wip/6by32-01-perf767-764-pause-20261030
kayg:wip/6by32-01-lists-b-pause-20261030
kayg:wip/6by32-01-ids-verify5-pause-20261030
kayg:wip/6by32-01-ids-verify4-pause-20261030
kayg:review/calendar-month
kayg:wip/10by32-02-undo-768-20261030
kayg:test/ids-verify3
kayg:wip/10by32-02-bm3-chromium-20261030
kayg:wip/10by32-01-review-753-20261030
kayg:wip/10by32-01-perf-idle-2-20261030
kayg:wip/10by32-01-bm2-webkit2-20261030
kayg:plan/759b-staging
kayg:train/2026-10-30-bm2
kayg:review/758b-tests
kayg:review/758-tests
kayg:review/764d-tests
kayg:review/767b-tests
kayg:review/preview-2026-10-29
kayg:review/767-tests
kayg:docs/tiering-731-plan
kayg:wip/c6b02-when2
kayg:wip/c6b02-tasks-create-fix5
kayg:wip/c6b02-settings-frost-mock
kayg:wip/c6b02-settings-frost-base
kayg:wip/c6b02-settings-frost
kayg:wip/c6b02-ring
kayg:wip/c6b02-review-733d
kayg:wip/c6b02-bm1-full-a
kayg:wip/compute02-when-field
kayg:wip/compute02-webkit-lists-repro
kayg:wip/compute02-train-bl2-dev
kayg:wip/compute02-dev-green-pw164
kayg:docs/deploy-plan
kayg:review/764c-tests
kayg:review/764b-tests
kayg:review/748g-tests
kayg:review/764-tests
kayg:review/759b-tests
kayg:fix/storage-owner-liveness-642
kayg:fix/storage-fix-7
kayg:wip/compute02-storage-fix-7
kayg:review/759-tests
kayg:feat/tiering-731
kayg:wip/tiering-731-first-attempt
kayg:wip/compute02-storage-fix-1
kayg:review/754g-tests
kayg:perf/idle-honest-2
kayg:review/754f-tests
kayg:review/754e-tests
kayg:review/755-tests
kayg:review/754d-tests
kayg:review/754c-tests
kayg:review/754b-tests
kayg:review/754-tests
kayg:fix/storage-private-tmp-642-dev
kayg:wip/compute02-verify-750
kayg:test/ids-verify5
kayg:test/ids-verify4
kayg:fix/storage-exact-file-quarantine-642
kayg:wip/compute02-verify-740
kayg:perf/one-flush-save
kayg:review/calendar-static
kayg:wip/compute02-verify-737
kayg:review/indexing-ring
kayg:review/when-field-2
kayg:wip/compute02-review-733
kayg:proto/block-identity-2-review
kayg:proto/block-identity-review
kayg:wip/c6b02-tc-design
kayg:review/when-field
kayg:issue/tasks-create-ids
kayg:issue/tasks-create
kayg:review/combined
kayg:wip/compute02-review-combined
kayg:review/task-polish
kayg:review/top-buttons
kayg:review/q1-q5
kayg:train/2026-10-26-bl2
kayg:archive/wt-398h
kayg:archive/train-bm-proptest-seed
kayg:archive/integ325-293
kayg:archive/integ325-ci
kayg:archive/client-auth-review-baseline
kayg:archive/wt-sim-4853
kayg:archive/wt-sim-4811
kayg:archive/wt-448m
kayg:archive/wt-398a-allocator
kayg:issue/calendar-adr
kayg:archive/old/train/2026-10-23bk2
kayg:archive/old/train/2026-10-23bk
kayg:issue/469-mockup-9a
kayg:issue/smart-tasks-mockup
kayg:issue/469-menus
kayg:issue/652-signout-flake
kayg:issue/652-history-fix7
kayg:issue/652-history-fix6
kayg:archive/old/issue/spring-check-perf
kayg:issue/spring-check-perf
kayg:issue/659-rename-fast
kayg:archive/old/train/2026-10-21ar
kayg:issue/search-hardening-3
kayg:issue/652-history-fix5
kayg:issue/660-port-sheets-glass
kayg:issue/652-history-fix4
kayg:issue/652-history-fix3
kayg:issue/652-history-fix2
kayg:issue/652-history-flake
kayg:train/2026-10-21an
kayg:issue/settings-admin-port
kayg:issue/search-port
kayg:train/2026-10-20am
kayg:issue/storage-split-fix2
kayg:issue/storage-split
kayg:archive/old/issue/folder-ids-fix
kayg:train/2026-10-20al
kayg:archive/old/issue/folder-ids
kayg:train/2026-10-20ak
kayg:issue/tables-mockup
kayg:train/2026-10-19aj
kayg:train/2026-10-19ai
kayg:issue/469-merge-fixes
kayg:issue/sidebar-band
kayg:archive/old/issue/608-settings-spec
kayg:train/2026-10-19ah
kayg:issue/swipe-offscreen
kayg:train/2026-10-19ag
kayg:archive/old/issue/590-nlp-adr
kayg:archive/old/issue/587-event-after-write-2
kayg:train/2026-10-19af
kayg:archive/old/issue/587-event-after-write
kayg:issue/469-pill-room-c
kayg:issue/469-pill-room
kayg:train/2026-10-19ae
kayg:issue/469-narrow-handle
kayg:issue/397-ios-swipe-2
kayg:archive/old/issue/adrs-photos-files-tasks
kayg:issue/469-desk-fixes
kayg:archive/old/issue/auto-update-adr
kayg:issue/469-filter-gap
kayg:issue/469-port-1b
kayg:issue/442-opacity-flake
kayg:archive/397-ios-swipe-2
kayg:issue/327-web-fast-3
kayg:archive/old/issue/469-port-1
kayg:issue/469-port-1
kayg:issue/327-web-fast-2
kayg:archive/old/issue/327-web-fast
kayg:archive/old/issue/425-stt-bench-vq1
kayg:archive/old/issue/445-no-bleed
kayg:archive/old/issue/400-swipe-mockup-2
kayg:issue/426-client-review
kayg:issue/426-client-2
kayg:issue/426-client
kayg:archive/old/issue/476-safe-area-lab
kayg:archive/445-top-light-2
kayg:archive/old/issue/445-top-light
kayg:issue/471-blocks-mockup
kayg:archive/old/issue/448-links-app-2
kayg:archive/old/issue/448-links-app
kayg:archive/old/issue/398-cold-links
kayg:issue/434-442-small-2
kayg:issue/434-442-small
kayg:archive/old/issue/398-unload
kayg:archive/old/issue/445-bottom-band
kayg:issue/445-bottom-band
kayg:issue/351-rem-3
kayg:archive/312-views-mockup
kayg:issue/351-rem-2
kayg:issue/351-rem
kayg:archive/old/issue/327-first-note
kayg:issue/395-width-phone
kayg:archive/old/issue/327-typing
kayg:issue/396-ios-edges
kayg:issue/397-ios-swipe
kayg:archive/398-allocator
kayg:archive/old/issue/327-notes-index
kayg:issue/345-347-contrast-empty-2
kayg:archive/old/issue/44-arenas
kayg:issue/44-arenas
kayg:archive/integ325
kayg:archive/old/issue/327-perf-harness
kayg:archive/old/issue/44-thp
kayg:issue/44-thp
kayg:archive/old/issue/285-sticky-pills
kayg:archive/old/issue/44-trim-3
kayg:issue/44-trim-3
kayg:archive/old/issue/165-cards
kayg:archive/old/issue/165-picker-2
kayg:archive/old/issue/212-multi-select
No reviewers
Labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/calternal!777
Loading…
Reference in a new issue
No description provided.
Delete branch "issue/carddav-read-perf-fix-r2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Round 2 shared epoch fix implemented, FINAL review pending
Head
670a883c. Status table, exact gates and limits. Held-reader tokens are validated against committed epochs; mid-read replacement fails closed. Updated #777 is re-merged into #794 at21e678b2, pushed after green gates. Never merged into dev.Earlier implementation records below are historical.
Round 1 fixes implemented, review pending
Head
3c8ddf53: disk-backed staging, bounded card/ID batches and inherited token expiry. Plan and bounds; status table, exact gates and open risks. All scoped VM gates passed. Never merged into dev.The original implementation record below is historical; its unbounded-retention/rebuild gaps are superseded by this round.
Replacement for #739 (same change, rebased on the replacement for #734). Refs #657. This branch contains the replacement #776 commit as well, so the diff against dev shows both; once that PR merges only the perf commit remains. Measurements in #739 are from the earlier base.
Gates (cua@calternal-dev-02-std, aarch64, Nix dev shell, via slot, -j4)
cargo fmt --all -- --check: cleancargo clippy -j4 -p calternal-carddav -p calternal-contacts -p calternal-contacts-actions --all-targets -- -D warnings: cleancargo test -j4 -p calternal-carddav -p calternal-contacts -p calternal-contacts-actions: all pass (carddav 7 cache + 2 journal + 10 read + 1 recovery = 20; contacts suites incl. wire_index, hostile, break_*; contacts-actions 130)370d170e. No conflicts and no test breakage from the rebase.Review against ADR 0064 (independent; not the author)
Matches: bridge never writes files (read-only methods, writes get 405); vCard 3.0 precomputed with strong ETags, reads never run the converter; stable user-id URLs and
/.well-known/carddavredirect; paged sync with fixed snapshot, continuation tokens and 507 marker (RFC 6578 3.6); deletes reported as 404; expired/foreign tokens givevalid-sync-token; rebuild is atomic and rotates the epoch; routes unmounted and an authenticator is required; depth infinity refused.Gaps and risks (none block landing; D4 and later should pick them up):
revisionsis append-only and never pruned. Each edit adds a full card row; disk grows with edit count and only a rebuild compacts it. Needs a compaction rule (rebuild also rotates the epoch, so devices resync).changed_idsmaps a fixed list of journal kinds. A new journal kind that touches a contact (for example Trash or tier moves) would be missed until a rebuild. Needs a test that enumerates note-affecting kinds, or a fallback rebuild on unknown kinds. Companion cards (FO1/D2) are not covered yet.<id>.vcfonly; D6 must add the alias table),If-Match/412.rebuild(backup install is atomic by design, covered by the rollback test, not by a real crash).Independent review, round 1
Reviewed head
370d170e1b2f441c7621c2697c5e27c7d0169303, focusing on the delta over #776 (49ccd9a3), ADRs 0063/0064 and the original #734/#739 performance/migration discussions. Transport admission, auth and caps belong to #794.Findings
crates/carddav/src/cache.rs:205creates the staging database in memory and:216copies every wire payload/property index into it while the caller's completeVec<Update>remains alive (crates/carddav/src/source.rs:98). A photo-heavy book accumulated across multiple valid imports can require gigabytes for each copy; a per-request/response cap does not bound the full book, and separate users can rebuild concurrently. This changes the previous disk-backed transaction into a server OOM risk during a cold rebuild. Fix: use a disk-backed temporary SQLite staging database on the state tier, or enforce a measured staging-memory budget with a safe disk-backed fallback before constructing the full in-memory image. Preserve the atomic backup install, old cache on failure and epoch rotation. Gate with a photo-heavy full-book rebuild and concurrent per-user rebuilds under a memory limit, not only small text-card fixtures.crates/carddav/src/cache.rs:181still retains a full card/index for every edit indefinitely. The new live revision pointer removes the current-row duplicate, not historical payload growth. See the round 1 finding on #776; it must be addressed in the stack.Checks and limits
The migration keeps revision sequence, cursor and epoch together in a transaction; its test exercises an in-progress initial sync and a later deletion. The committed head is updated with the live pointer/cursor. The backup replacement updates the in-memory epoch only after
StepResult::Done; the existing failed/busy rebuild tests check rollback and retry. I found no additional concrete ETag, cross-user or migration omission in this delta. Crash safety follows the SQLite replacement transaction, but there is no actual kill/reopen test during backup installation.Static review only. No build/test/install ran on the control host.
git diff --check 49ccd9a3 370d170epassed. The historical perf samples and reported VM gates were inspected, not independently rerun on this rebased head.Verdict: fix first. No P0/P1 finding in this delta; bound rebuild memory and resolve the inherited retention issue.
round 1 implementation plan (no dev merge):
then push #776 and #777 in order, merge updated #777 into #794, rerun its specified gates and push only if green. #797 remains a downstream consumer; its independent write fixes are outside this task. cheap P2s will be fixed where established; remaining delivery gaps will be listed. review round 1 of maximum 3.
round 1 implementation status at
3c8ddf53(includes #776f3d6361d, merged in stack order). pushed, never merged into dev. independent review remains pending.VM:
cua@calternal-dev-02-std, existing/home/cua/Developer/Worktrees/carddav-r2, Nix shell through~/bin/slot. logs~/carddav-rebuild-r1-red.log(expected baseline failure),~/carddav-rebuild-r1-green2.log(all tests pass; initial clippy rejected the test's marker-file write), and~/carddav-rebuild-r1-final.log(test-only lint annotation, kill/reopen rerun, clippy and fmt all exit 0). no user files use that test marker helper.Decisions: rebuild uses O(batch payload + bounded SQLite caches) additional process memory and O(book bytes) staging disk, followed by SQLite's atomic backup install. epoch/cursor change only after installation succeeds. WAL NORMAL policy is unchanged. a fixed staging name plus an OS file lock bounds abandoned staging to one image and prevents another live builder from deleting it; the next rebuild discards crash leftovers. production scans IDs in indexed immutable order and replays the pre-scan journal cursor, so moving/renaming notes cannot move them behind a title/time cursor. legacy custom Source implementations retain a compatibility fallback that lists IDs; production JournalSource overrides it.
Remaining work/limits: native Apple replay and perf-VM latency gates were not run; groups/photos product delivery and future journal-kind exhaustiveness remain outside this review. existing incremental journal replay still buffers its affected-card set, and the per-book readiness lock can delay HTTP reads during rebuild; this change bounds the cold rebuild, not every source plugin/recovery allocation. SQLite reuses freed history pages but does not shrink a previously oversized cache file. kill tests cover all tested staging batch boundaries, not power failure or a deterministic interruption inside SQLite backup. no extra review round was launched. #794 downstream integration/gates are running separately; #797's independent write fixes remain on its own branch.
downstream integration complete: updated #777 was merged into #794 at
80ec1fa3, all requested gates passed before push. Exact commands, status table and source identity. #797 unchanged; never merged into dev. round 1 implementation complete, independent review pending.Independent review, round 2
Reviewed
3c8ddf534f, round-1 #issuecomment-71159, implementation #issuecomment-71217, and merge into #79480ec1fa39f(#issuecomment-71242).rebuild(&[Update])inventories remain outside that bounded production path.new finding: R2-1, P2: a held reader can silently miss a complete replacement
Locations: sync.rs:19, cache.rs:263. The SQLite replacement is atomic, but epoch validity is cached separately in each
Cacheobject. Only the writer refreshes that field.Independent failing probe on source-identical #794: seed
a.vcfat sequence 1; keep a second Cache open and save its token; writer rebuilds a one-card book containing onlyb.vcf. Writer rejects the saved token; held reader returnsOk(Page { rows: [], token: <old epoch>:d:1, more: false }). A client following that reader is told nothing changed, despite both a deletion and addition. The crash test reads the old generation while staging, then reopens/uses the installing handle; it misses this post-install case.Fix in the base stack: obtain epoch/head/floor and rows in one SQLite read snapshot when validating/issuing tokens, including
token/ctag, or enforce a single-handle storage invariant. Add a successful-replacement regression with an independently held reader. Inherited from the base cache; the sequence reset here makes the empty-delta manifestation immediate.P2 rationale: the mounted adapter currently shares a single mutex-protected Book per user. No production single-Book HTTP failure was reproduced. This is a demonstrated public-cache multi-handle correctness gap, rather than a new admission escape or established deployed P1.
rebuild and integration checks
database or disk is full. This exercises staging apply failure, not ENOSPC during destination backup installation.i64::MAXfail-closed probes pass. Clock jumps do not affect sequence-based expiry.26edc647, no changes tocrates/dav, adapter/properties or transport/network tests; merged sync retains resource and aggregate byte guards. Earlier source-matched transport gate results were inspected, not independently rerun as a new transport review.verification
Fresh independent runtime checks used dev-01
~/Developer/Worktrees/dav794-retention-r1; SHA-256 comparison confirms 445/445 Rust/Cargo files match80ec1fa3. Its Git HEAD remains the pre-merge commit with the source-matched merge changes in the working tree. #777's staging/retention/source implementation is preserved in that merge; its runtime checks here are on the merged tree, not a separate pristine3c8ddf53checkout.In Nix through
~/bin/slot, with-j4:Committed tests: 13 cache + 2 crash functions + 1 memory parent pass. New window/deletion and sequence probes pass. Held-reader assertion fails, reproducing R2-1. The first RLIMIT-only disk probe failed its FULL-error discriminator because EFBIG yields SQLITE_IOERR; after stage-only ENOSPC injection, cleanup/retry passes. Thus the broad probe command exits 101; it is not an all-green gate. Logs:
~/review-carddav-r2b.log,~/review-carddav-r2-enospc-b.log(exit 0). Untracked probe/shim files remain on the VM; tracked sources are unchanged.Read-only
git diff --checkpassed for the retention update, rebuild delta and #794 merge. Native Apple replay, perf-VM latency and kill during backup were not rerun or claimed.verdict: fix first, for R2-1 in the base stack. New findings: P0 0, P1 0, P2 1, P3 0 (same shared finding as #776, not two independent defects). No code pushed, merge or production access.
R2-1 implementation plan (shared #776/#777): remove the per-handle cached epoch. Sync reads epoch/head/floor/rows in one SQLite read transaction, then releases that snapshot and checks the current committed epoch before returning. Token/ctag use the same guard. Old tokens return RFC 6578 invalid-token; a rebuild observed during a read fails closed.
Bounds: constant metadata/epoch work per request, existing bounded sync page and retention costs unchanged; one read transaction, no book-sized copy. No new lock across rebuild staging.
Gates: reproduce held-reader failure first; cache/read tests on #776, inherited tests plus rebuild suites on #777; re-merge #777 into #794 and run requested dav/carddav/auth/server, mount, contract, clippy -D warnings and fmt before pushing #794. No dev merge or additional review round.
R2-1 implementation status: ready for FINAL review
Shared finding from #776#issuecomment-71275 / #777#issuecomment-71278 implemented in the base and propagated in stack order. Heads: #776
113af4f819, #777670a883c93. Re-merged #777 into #794:21e678b258, pushed only after every requested gate passed. Never merged into dev. No additional independent review round started.Removed the per-handle cached epoch. Each sync pins epoch/head/floor/rows in one SQLite read transaction. After releasing that snapshot, it queries the current committed epoch again and discards the result with
InvalidTokenif it changed.tokenandctaguse the same guard. Every emitted sync token carries the epoch read for that snapshot. Existing HTTP mapping returns RFC 6578 403valid-sync-token, followed by empty-token full resync. A rebuild after the final epoch check is ordered after that read; the returned old-epoch token is then rejected on the next request.InvalidToken, issues current token/ctag, and fully resyncs replacement rows. Equal revision counts exercise #777's former silent empty delta.InvalidTokenafter the current committed epoch check.valid-sync-token, then 207 with b.vcf and fresh token.-D warnings; workspace fmt.dav_is_mounted/contract-D warnings; workspace fmt clean.VM: claude@calternal-dev-01-compute02. Worktrees:
~/Developer/Worktrees/carddav-epoch-r2,carddav-epoch-stack-r2,dav794-epoch-r2. All builds/tests/fmt used Nix, with heavy work through one~/bin/slot,-j4. Reused target:/home/claude/Developer/Worktrees/dav794-retention-r1/target; log records recompilation from each checked worktree. Authoritative log~/carddav-epoch-final2.logcontainsBASE_GREEN,STACK_GREEN,TRANSPORT_GREEN;~/carddav-epoch-final2.exit= 0.Exact commands in the Nix shell, inside the slot. On each of #776 and #777:
On the refreshed #794:
Failing-before evidence:
~/carddav-epoch-red.log, baseline407eabf0(original base plus regression), exits 101 because the held reader accepts the obsolete epoch. Two subsequent test-fixture mistakes were corrected: an accidentally changed existing retention expectation, and the HTTP fake source cursor triggering an unrelated second rebuild. The final source above passes all gates; earlier runs are not claimed green.Decisions/limits: constant extra epoch queries and a read transaction; existing bounded page/retention/staging behavior preserved. No new book-sized allocation or rebuild lock. Under repeated concurrent rebuilds a request may fail and require full resync instead of returning stale success. Native Apple replay, perf-VM latency budgets and deterministic kill/power loss inside backup installation remain the previously documented limits; none is claimed here. FINAL review remains pending.
Independent review, round 3 (final)
Reviewed #777 at
670a883c93, the shared round-2 finding (#776#issuecomment-71275 / #777#issuecomment-71278), implementation status (#776#issuecomment-71333 / #777#issuecomment-71338), and propagation into #79421e678b2.verdict: ready. New findings: P0 0, P1 0, P2 0, P3 0. The shared R2-1 finding is closed. No new P0/P1 appeared, so kayg's final-round “stop and simplify” trigger is not met.
valid-sync-token, followed by an empty-token 207 containing replacement b.vcf. No successful empty delta under the obsolete token.InvalidTokenafter the transaction ends and the current committed epoch differs.New findings
None at P0–P3 within this review's scope.
Verification and limits
Fresh independent checks on dev-01, in Nix through
~/bin/slot, reused target/home/claude/Developer/Worktrees/dav794-retention-r1/target:#776: 21 passed, #777: 25 passed, no failures. Both runs include the held-reader cache/HTTP regressions and the deterministic mid-read rebuild test. Logs:
~/review-carddav-r3-final/gates.log(throughTREE_GREEN=carddav-epoch-r2) and~/review-carddav-r3-final/remainder.log(throughTREE_GREEN=carddav-epoch-stack-r2). After those passes, #794's attempted rerun remained queued for a slot and was cancelled before Cargo started; no fresh merged-tree or transport runtime pass is claimed.Independent SHA-256 comparison matches 422/422, 426/426, 446/446 Rust/Cargo files against the specified #776/#777/#794 heads. The #794 VM worktree has an older Git HEAD but its compared source matches
21e678b2. Inspected prior source-matched~/carddav-epoch-final2.logand exit file (0): all three epoch regressions pass on each tree, withBASE_GREEN,STACK_GREEN,TRANSPORT_GREEN; clippy/fmt and broader transport/auth/socket gates are prior implementation evidence, not fresh runs in this review. Read-onlygit diff --checkpasses for f3d6361d..113af4f8, 3c8ddf53..670a883c and 80ec1fa3..21e678b2.Native Apple replay, perf-VM latency gates and interruption inside backup installation remain the documented limits. No new allocation proportional to book size, write lock or transport-policy change was introduced by this fix. No code changed/pushed, merge or production access.
kayg referenced this pull request2026-11-05 23:21:11 +00:00
housekeeping #1009: rebased+failing, left open for review.
restacked CardDAV read fixes on #776; ids_page uses current db.reader, and dev restart tests remain.
head:
89e9ed150f204128b8f5db02d5cb427371906de9; based on devcd7f84432febfc63b788fbb9b9f9950452982d74, pushed with the original exact force-with-lease.frontend frozen install, check, lint, build and unit tests passed. bundle gate passed. first-paint brotli vs dev: JS 230777 B (+0 B), CSS 18386 B (+0 B).
Rust gate results (Mac VM):
cargo fmt -p calternal-carddav -p calternal-contacts-actions -p calternal-contacts -p calternal-notes -- --checkcargo check -j4 --locked -p calternal-carddav -p calternal-contacts-actions -p calternal-contacts -p calternal-notes --all-targetscargo clippy -j4 --locked -p calternal-carddav -p calternal-contacts-actions -p calternal-contacts -p calternal-notes --all-targets -- -D warningscargo test -j4 --locked -p calternal-carddav -p calternal-contacts-actions -p calternal-contacts -p calternal-notesthe Notes suite was stopped after six minutes with two executor tests still pending; other CardDAV/contact suites completed. this is an incomplete run, not an assertion failure or a performance measurement.
all executed gates used the VM Nix environment, shared Mac lock and slot. exact commands, source heads, logs and dev baseline. frontend inputs are identical to the tested frontend tree when a Rust-only repair changed the head.
original-head cherry/patch inventory,
pr-777.md; unmatched entries are missing-patch candidates, with semantic overlap/replacements noted above.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.