2
0
Эх сурвалжийг харах

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

Thomas Waldmann 7 сар өмнө
parent
commit
299c05287f

+ 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)
             logger.warning(f"{len(self.reappeared_chunks)} previously missing objects re-appeared!" + run_repair)
             set_ec(EXIT_WARNING)
             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
         repo_size_before = self.repository_size
         logger.info("Determining unused objects...")
         logger.info("Determining unused objects...")
         unused = set()
         unused = set()
@@ -166,9 +175,19 @@ class CompactMixIn:
             """
             """
             Free repository space by deleting unused chunks.
             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
             Differently than borg 1.x, borg2's compact needs the borg key if the repo is
             encrypted.
             encrypted.

+ 7 - 1
src/borg/manifest.py

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

+ 4 - 2
src/borg/remote.py

@@ -1092,8 +1092,10 @@ class RemoteRepository:
     def store_store(self, name, value):
     def store_store(self, name, value):
         """actual remoting is done via self.call in the @api decorator"""
         """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"""
         """actual remoting is done via self.call in the @api decorator"""
 
 
     @api(since=parse_version("2.0.0b13"))  # TODO -> b14
     @api(since=parse_version("2.0.0b13"))  # TODO -> b14

+ 2 - 2
src/borg/repository.py

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