1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2024-12-22 07:43:06 +00:00

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

This commit is contained in:
Thomas Waldmann 2024-11-02 15:17:29 +01:00
parent 9fabc19e6b
commit 299c05287f
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
4 changed files with 35 additions and 8 deletions

View file

@ -127,6 +127,15 @@ def report_and_delete(self):
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 @@ def build_parser_compact(self, subparsers, common_parser, mid_common_parser):
"""
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.

View file

@ -331,7 +331,7 @@ def create(self, name, id, ts, *, overwrite=False):
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 @@ def undelete_by_id(self, id):
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,
*,

View file

@ -1092,8 +1092,10 @@ def store_load(self, name):
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

View file

@ -534,9 +534,9 @@ def store_store(self, name, value):
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()