aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-30 17:42:21 -0400
committerCraig Jennings <c@cjennings.net>2026-06-30 17:42:21 -0400
commitedb27d7e15161e3b12af0fa5b2c3bde8295bb5d7 (patch)
tree6dc3c58e8b87324663f3f0abcfd2f7cff62fbf76 /modules
parentdd8e1576cdfa282efbbc610737b039721841c60c (diff)
downloaddotemacs-edb27d7e15161e3b12af0fa5b2c3bde8295bb5d7.tar.gz
dotemacs-edb27d7e15161e3b12af0fa5b2c3bde8295bb5d7.zip
fix(calendar-sync): skip overlapping syncs for the same calendar
A timer tick that fired while a calendar's previous fetch was still running launched a second concurrent sync for that calendar, wasting work and racing to write the same org file. The dispatcher now skips a calendar whose status is already syncing and logs the skipped tick. The sentinel resets the status on process exit, so the skip clears on its own. load-state also clears a stale syncing status left by a crash, so a calendar can't be skipped forever.
Diffstat (limited to 'modules')
-rw-r--r--modules/calendar-sync-source.el15
-rw-r--r--modules/calendar-sync.el18
2 files changed, 28 insertions, 5 deletions
diff --git a/modules/calendar-sync-source.el b/modules/calendar-sync-source.el
index d9efc885b..15c91c594 100644
--- a/modules/calendar-sync-source.el
+++ b/modules/calendar-sync-source.el
@@ -90,7 +90,13 @@ Hash table mapping calendar name (string) to state plist with:
(let ((cal-states (alist-get 'calendar-states state)))
(clrhash calendar-sync--calendar-states)
(dolist (entry cal-states)
- (puthash (car entry) (cdr entry) calendar-sync--calendar-states)))))
+ (let ((st (cdr entry)))
+ ;; A persisted `syncing' status is stale in a fresh process
+ ;; (no sync is actually running), so reset it; otherwise the
+ ;; in-flight guard would skip that calendar forever.
+ (when (eq (plist-get st :status) 'syncing)
+ (setq st (plist-put (copy-sequence st) :status 'never)))
+ (puthash (car entry) st calendar-sync--calendar-states))))))
(error
(calendar-sync--log-silently "calendar-sync: Error loading state: %s" (error-message-string err))))))
@@ -98,6 +104,13 @@ Hash table mapping calendar name (string) to state plist with:
"Get state plist for CALENDAR-NAME, or nil if not found."
(gethash calendar-name calendar-sync--calendar-states))
+(defun calendar-sync--syncing-p (calendar-name)
+ "Return non-nil when CALENDAR-NAME has an in-flight sync.
+Used to skip an overlapping sync when a timer tick fires while the previous
+sync for the same calendar is still running."
+ (eq (plist-get (calendar-sync--get-calendar-state calendar-name) :status)
+ 'syncing))
+
(defun calendar-sync--set-calendar-state (calendar-name state)
"Set STATE plist for CALENDAR-NAME."
(puthash calendar-name state calendar-sync--calendar-states))
diff --git a/modules/calendar-sync.el b/modules/calendar-sync.el
index 297d1fe61..804d71faf 100644
--- a/modules/calendar-sync.el
+++ b/modules/calendar-sync.el
@@ -211,10 +211,20 @@ fetcher) or :account + :calendar-id (the \\='api fetcher). Dispatches on the
:fetcher key, defaulting to the .ics path.
Updates calendar state and saves to disk on completion.
The fetch and conversion run in external processes so parsing and writing large
-calendar files do not block the interactive Emacs thread."
- (if (eq (plist-get calendar :fetcher) 'api)
- (calendar-sync--sync-calendar-api calendar)
- (calendar-sync--sync-calendar-ics calendar)))
+calendar files do not block the interactive Emacs thread.
+
+Skips a calendar whose previous sync is still in flight, so a timer tick that
+fires before a slow fetch finishes does not launch a second overlapping sync for
+the same calendar."
+ (let ((name (plist-get calendar :name)))
+ (cond
+ ((calendar-sync--syncing-p name)
+ (calendar-sync--log-silently
+ "calendar-sync: [%s] sync already in flight; skipping overlapping tick" name))
+ ((eq (plist-get calendar :fetcher) 'api)
+ (calendar-sync--sync-calendar-api calendar))
+ (t
+ (calendar-sync--sync-calendar-ics calendar)))))
(defun calendar-sync--require-calendars ()
"Return non-nil if calendars are configured, else warn and return nil."