branch: elpa/elfeed
commit 92050986b3e2e28703058648ae884a789036732e
Author: Daniel Mendler <[email protected]>
Commit: Daniel Mendler <[email protected]>

    elfeed-db-pack: Write the new files first, then rename
    
    The two renaming operations are not atomic, but still a little safer. Note 
that
    you should keep backups in any case.
    
    We should use a separate data directory and rename it atomically in the 
end, see
---
 elfeed-db.el | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/elfeed-db.el b/elfeed-db.el
index 7527c8cc99..016a4809ad 100644
--- a/elfeed-db.el
+++ b/elfeed-db.el
@@ -271,7 +271,7 @@ The FEED-OR-ID may be a feed struct or a feed ID (url)."
   (mkdir elfeed-db-directory t)
   (let* ((coding-system-for-write 'utf-8)
          (dest (expand-file-name "index" elfeed-db-directory))
-         (temp (concat dest "-tmp"))
+         (temp (concat dest ".tmp"))
          (write-region-inhibit-fsync nil))
     ;; We write to a temporary file and rename to avoid corrupting the database
     ;; on crash. `file-precious-flag' is insufficient as it only works for
@@ -600,11 +600,16 @@ If STATS-P is true, return the space cleared in bytes."
 
 (defun elfeed-db-pack ()
   "Pack all content into a single archive for efficient storage."
-  (let ((coding-system-for-write 'utf-8)
-        (next-archive (make-hash-table :test 'equal))
-        (packed ()))
+  (let* ((content-dest (elfeed-ref-archive-filename ".gz"))
+         (content-temp (file-name-with-extension content-dest ".tmp.gz"))
+         (index-dest (elfeed-ref-archive-filename ".index"))
+         (index-temp (file-name-with-extension index-dest ".tmp.index"))
+         (next-archive (make-hash-table :test 'equal))
+         (coding-system-for-write 'utf-8)
+         (write-region-inhibit-fsync nil)
+         (packed ()))
     (make-directory (expand-file-name "data" elfeed-db-directory) t)
-    (with-temp-file (elfeed-ref-archive-filename ".gz")
+    (with-temp-file content-temp
       (with-elfeed-db-visit (entry _)
         (let ((ref (elfeed-entry-content entry))
               (start (1- (point))))
@@ -614,12 +619,16 @@ If STATS-P is true, return the space cleared in bytes."
               (insert content)
               (setf (gethash (elfeed-ref-id ref) next-archive)
                     (cons start (1- (point)))))))))
-    (with-temp-file (elfeed-ref-archive-filename ".index")
+    (with-temp-file index-temp
       (let ((standard-output (current-buffer))
             (print-level nil)
             (print-length nil)
             (print-circle nil))
         (prin1 next-archive)))
+    ;; Rename two files, non-atomically! Instead use separate temporary data
+    ;; directory, see https://github.com/emacs-elfeed/elfeed/pull/569.
+    (rename-file content-temp content-dest t)
+    (rename-file index-temp index-dest t)
     (setf elfeed-ref-cache nil)
     (setf elfeed-ref-archive next-archive)
     (mapc #'elfeed-ref-delete packed)

Reply via email to