浏览代码

compact: remove soft-deleted archives/* entries, docs

Thomas Waldmann 7 月之前
父节点
当前提交
299c05287f
共有 4 个文件被更改,包括 35 次插入8 次删除
  1. 22 3
      src/borg/archiver/compact_cmd.py
  2. 7 1
      src/borg/manifest.py
  3. 4 2
      src/borg/remote.py
  4. 2 2
      src/borg/repository.py

+ 22 - 3
src/borg/archiver/compact_cmd.py

@@ -127,6 +127,15 @@ class ArchiveGarbageCollector:
             logger.warning(f"{len(self.reappeared_chunks)} previously missing objects re-appeared!" + run_repair)
             set_ec(EXIT_WARNING)
 
+        logger.info("Cleaning archives directory from deleted archives...")
+        archive_infos = self.manifest.archives.list(sort_by=["ts"], deleted=True)
+        for archive_info in archive_infos:
+            name, id, hex_id = archive_info.name, archive_info.id, bin_to_hex(archive_info.id)
+            try:
+                self.manifest.archives.nuke_by_id(id)
+            except KeyError:
+                self.print_warning(f"Archive {name} {hex_id} not found.")
+
         repo_size_before = self.repository_size
         logger.info("Determining unused objects...")
         unused = set()
@@ -166,9 +175,19 @@ class CompactMixIn:
             """
             Free repository space by deleting unused chunks.
 
-            borg compact analyzes all existing archives to find out which chunks are
-            actually used. There might be unused chunks resulting from borg delete or prune,
-            which can be removed to free space in the repository.
+            borg compact analyzes all existing archives to find out which repository
+            objects are actually used (referenced). It then removes all unused objects
+            to free repository space.
+
+            Unused objects may result from:
+
+            - borg delete or prune usage
+            - interrupted backups (maybe retry the backup first before running compact!)
+            - backup of source files that had an I/O error in the middle of their contents
+              and that were skipped due to this.
+
+            Important: after compacting it is not possible anymore to use ``borg undelete``
+            to recover previously deleted archives.
 
             Differently than borg 1.x, borg2's compact needs the borg key if the repo is
             encrypted.

+ 7 - 1
src/borg/manifest.py

@@ -331,7 +331,7 @@ class Archives:
             self._archives[name] = {"id": id, "time": ts}
 
     def delete_by_id(self, id):
-        # delete an archive
+        # soft-delete an archive
         assert isinstance(id, bytes)
         assert not self.legacy
         self.repository.store_move(f"archives/{bin_to_hex(id)}", delete=True)  # soft-delete
@@ -342,6 +342,12 @@ class Archives:
         assert not self.legacy
         self.repository.store_move(f"archives/{bin_to_hex(id)}", undelete=True)
 
+    def nuke_by_id(self, id):
+        # really delete an already soft-deleted archive
+        assert isinstance(id, bytes)
+        assert not self.legacy
+        self.repository.store_delete(f"archives/{bin_to_hex(id)}", deleted=True)
+
     def list(
         self,
         *,

+ 4 - 2
src/borg/remote.py

@@ -1092,8 +1092,10 @@ class RemoteRepository:
     def store_store(self, name, value):
         """actual remoting is done via self.call in the @api decorator"""
 
-    @api(since=parse_version("2.0.0b8"))
-    def store_delete(self, name):
+    @api(
+        since=parse_version("2.0.0b8"), deleted={"since": parse_version("2.0.0b13"), "previously": False}
+    )  # TODO -> b14)
+    def store_delete(self, name, *, deleted=False):
         """actual remoting is done via self.call in the @api decorator"""
 
     @api(since=parse_version("2.0.0b13"))  # TODO -> b14

+ 2 - 2
src/borg/repository.py

@@ -534,9 +534,9 @@ class Repository:
         self._lock_refresh()
         return self.store.store(name, value)
 
-    def store_delete(self, name):
+    def store_delete(self, name, *, deleted=False):
         self._lock_refresh()
-        return self.store.delete(name)
+        return self.store.delete(name, deleted=deleted)
 
     def store_move(self, name, new_name=None, *, delete=False, undelete=False, deleted=False):
         self._lock_refresh()