fix(broker): reconcile deletes into cache + resync before marking outbox done
Two write-path cache bugs surfaced during Gate 2 live testing: P1 (delete didn't work / ghost event): syncCalendar only UPSERTED events present on Fastmail and never removed cache rows for events that disappeared. A successful CalDAV delete left the row in calendar_events forever, so GET /api/events kept returning it and the UI showed a ghost that 'wouldn't delete' (even after refresh). Add a prune step: delete calendar_events rows for this calendar whose uid is absent from the server response (scoped to cal.id so it never touches another calendar or the other member's rows — BUG B). Empty server result prunes the whole calendar's cache. P2 (edit needed a manual refresh): the outbox worker marked a row 'done' BEFORE triggerTargetedResync refreshed the cache. The PWA's SyncStateToast invalidates ['events'] the instant sync-status flips to 'done', so it refetched stale cache. Re-sync first, then mark done — 'done' now guarantees the cache reflects the write. Tests: +2 prune regressions (present-subset prune, empty-server prune-all).
This commit is contained in:
@@ -410,12 +410,17 @@ export async function runOutboxDrain(): Promise<void> {
|
||||
failedCreateGroups.add(row.groupId)
|
||||
}
|
||||
} else if (result.success) {
|
||||
// Success — mark done, trigger targeted re-sync to refresh the cache (D-06)
|
||||
// Success — refresh the local cache BEFORE marking done. The PWA's
|
||||
// SyncStateToast polls sync-status and invalidates ['events'] the
|
||||
// instant it sees status='done'; if we marked done first, that refetch
|
||||
// raced the re-sync and returned stale cache (deleted event still
|
||||
// present, edit not yet applied) — forcing a manual refresh. Re-syncing
|
||||
// first means 'done' guarantees the cache already reflects the write.
|
||||
await triggerTargetedResync(row.calendarUrl, row.userId)
|
||||
await db
|
||||
.update(calendarOutbox)
|
||||
.set({ status: 'done' })
|
||||
.where(eq(calendarOutbox.id, row.id))
|
||||
await triggerTargetedResync(row.calendarUrl, row.userId)
|
||||
} else if (result.hardFail) {
|
||||
// Hard fail — mark failed immediately, no retry (D-07)
|
||||
await db
|
||||
|
||||
Reference in New Issue
Block a user