mirror of
https://github.com/borgbackup/borg.git
synced 2025-03-06 19:49:20 +00:00
prune/delete --checkpoint-interval=1800 and ctrl-c/SIGINT support, fixes #6284
manifest, repo and cache are committed every checkpoint interval. also, when ctrl-c is pressed, finish deleting the current archive, commit and then terminate.
This commit is contained in:
parent
762581a667
commit
0deb4352c1
3 changed files with 72 additions and 13 deletions
|
@ -14,6 +14,7 @@ try:
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import signal
|
import signal
|
||||||
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from ..logger import create_logger, setup_logging
|
from ..logger import create_logger, setup_logging
|
||||||
|
@ -119,6 +120,7 @@ class Archiver(
|
||||||
self.exit_code = EXIT_SUCCESS
|
self.exit_code = EXIT_SUCCESS
|
||||||
self.lock_wait = lock_wait
|
self.lock_wait = lock_wait
|
||||||
self.prog = prog
|
self.prog = prog
|
||||||
|
self.last_checkpoint = time.monotonic()
|
||||||
|
|
||||||
def print_error(self, msg, *args):
|
def print_error(self, msg, *args):
|
||||||
msg = args and msg % args or msg
|
msg = args and msg % args or msg
|
||||||
|
@ -433,6 +435,20 @@ class Archiver(
|
||||||
logger.debug("Enabling debug topic %s", topic)
|
logger.debug("Enabling debug topic %s", topic)
|
||||||
logging.getLogger(topic).setLevel("DEBUG")
|
logging.getLogger(topic).setLevel("DEBUG")
|
||||||
|
|
||||||
|
def maybe_checkpoint(self, *, checkpoint_func, checkpoint_interval):
|
||||||
|
checkpointed = False
|
||||||
|
sig_int_triggered = sig_int and sig_int.action_triggered()
|
||||||
|
if sig_int_triggered or checkpoint_interval and time.monotonic() - self.last_checkpoint > checkpoint_interval:
|
||||||
|
if sig_int_triggered:
|
||||||
|
logger.info("checkpoint requested: starting checkpoint creation...")
|
||||||
|
checkpoint_func()
|
||||||
|
checkpointed = True
|
||||||
|
self.last_checkpoint = time.monotonic()
|
||||||
|
if sig_int_triggered:
|
||||||
|
sig_int.action_completed()
|
||||||
|
logger.info("checkpoint requested: finished checkpoint creation!")
|
||||||
|
return checkpointed
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
os.umask(args.umask) # early, before opening files
|
os.umask(args.umask) # early, before opening files
|
||||||
self.lock_wait = args.lock_wait
|
self.lock_wait = args.lock_wait
|
||||||
|
|
|
@ -5,7 +5,7 @@ from .common import with_repository
|
||||||
from ..archive import Archive, Statistics
|
from ..archive import Archive, Statistics
|
||||||
from ..cache import Cache
|
from ..cache import Cache
|
||||||
from ..constants import * # NOQA
|
from ..constants import * # NOQA
|
||||||
from ..helpers import Manifest
|
from ..helpers import Manifest, sig_int
|
||||||
from ..helpers import log_multi, format_archive
|
from ..helpers import log_multi, format_archive
|
||||||
|
|
||||||
from ..logger import create_logger
|
from ..logger import create_logger
|
||||||
|
@ -57,11 +57,19 @@ class DeleteMixIn:
|
||||||
|
|
||||||
stats = Statistics(iec=args.iec)
|
stats = Statistics(iec=args.iec)
|
||||||
with Cache(repository, key, manifest, progress=args.progress, lock_wait=self.lock_wait, iec=args.iec) as cache:
|
with Cache(repository, key, manifest, progress=args.progress, lock_wait=self.lock_wait, iec=args.iec) as cache:
|
||||||
|
|
||||||
|
def checkpoint_func():
|
||||||
|
manifest.write()
|
||||||
|
repository.commit(compact=False, save_space=args.save_space)
|
||||||
|
cache.commit()
|
||||||
|
|
||||||
msg_delete = "Would delete archive: {} ({}/{})" if dry_run else "Deleting archive: {} ({}/{})"
|
msg_delete = "Would delete archive: {} ({}/{})" if dry_run else "Deleting archive: {} ({}/{})"
|
||||||
msg_not_found = "Archive {} not found ({}/{})."
|
msg_not_found = "Archive {} not found ({}/{})."
|
||||||
logger_list = logging.getLogger("borg.output.list")
|
logger_list = logging.getLogger("borg.output.list")
|
||||||
delete_count = 0
|
uncommitted_deletes = 0
|
||||||
for i, archive_name in enumerate(archive_names, 1):
|
for i, archive_name in enumerate(archive_names, 1):
|
||||||
|
if sig_int and sig_int.action_done():
|
||||||
|
break
|
||||||
try:
|
try:
|
||||||
archive_info = manifest.archives[archive_name]
|
archive_info = manifest.archives[archive_name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -80,12 +88,15 @@ class DeleteMixIn:
|
||||||
consider_part_files=args.consider_part_files,
|
consider_part_files=args.consider_part_files,
|
||||||
)
|
)
|
||||||
archive.delete(stats, progress=args.progress, forced=args.forced)
|
archive.delete(stats, progress=args.progress, forced=args.forced)
|
||||||
delete_count += 1
|
checkpointed = self.maybe_checkpoint(
|
||||||
if delete_count > 0:
|
checkpoint_func=checkpoint_func, checkpoint_interval=args.checkpoint_interval
|
||||||
# only write/commit if we actually changed something, see #6060.
|
)
|
||||||
manifest.write()
|
uncommitted_deletes = 0 if checkpointed else (uncommitted_deletes + 1)
|
||||||
repository.commit(compact=False, save_space=args.save_space)
|
if sig_int:
|
||||||
cache.commit()
|
# Ctrl-C / SIGINT: do not checkpoint (commit) again, we already have a checkpoint in this case.
|
||||||
|
self.print_error("Got Ctrl-C / SIGINT.")
|
||||||
|
elif uncommitted_deletes > 0:
|
||||||
|
checkpoint_func()
|
||||||
if args.stats:
|
if args.stats:
|
||||||
log_multi(str(stats), logger=logging.getLogger("borg.output.stats"))
|
log_multi(str(stats), logger=logging.getLogger("borg.output.stats"))
|
||||||
|
|
||||||
|
@ -160,4 +171,13 @@ class DeleteMixIn:
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
"--save-space", dest="save_space", action="store_true", help="work slower, but using less space"
|
"--save-space", dest="save_space", action="store_true", help="work slower, but using less space"
|
||||||
)
|
)
|
||||||
|
subparser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--checkpoint-interval",
|
||||||
|
metavar="SECONDS",
|
||||||
|
dest="checkpoint_interval",
|
||||||
|
type=int,
|
||||||
|
default=1800,
|
||||||
|
help="write checkpoint every SECONDS seconds (Default: 1800)",
|
||||||
|
)
|
||||||
define_archive_filters_group(subparser)
|
define_archive_filters_group(subparser)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from ..cache import Cache
|
||||||
from ..constants import * # NOQA
|
from ..constants import * # NOQA
|
||||||
from ..helpers import format_archive
|
from ..helpers import format_archive
|
||||||
from ..helpers import interval, prune_within, prune_split, PRUNING_PATTERNS
|
from ..helpers import interval, prune_within, prune_split, PRUNING_PATTERNS
|
||||||
from ..helpers import Manifest
|
from ..helpers import Manifest, sig_int
|
||||||
from ..helpers import log_multi
|
from ..helpers import log_multi
|
||||||
from ..helpers import ProgressIndicatorPercent
|
from ..helpers import ProgressIndicatorPercent
|
||||||
|
|
||||||
|
@ -68,12 +68,21 @@ class PruneMixIn:
|
||||||
to_delete = (set(archives) | checkpoints) - (set(keep) | set(keep_checkpoints))
|
to_delete = (set(archives) | checkpoints) - (set(keep) | set(keep_checkpoints))
|
||||||
stats = Statistics(iec=args.iec)
|
stats = Statistics(iec=args.iec)
|
||||||
with Cache(repository, key, manifest, lock_wait=self.lock_wait, iec=args.iec) as cache:
|
with Cache(repository, key, manifest, lock_wait=self.lock_wait, iec=args.iec) as cache:
|
||||||
|
|
||||||
|
def checkpoint_func():
|
||||||
|
manifest.write()
|
||||||
|
repository.commit(compact=False, save_space=args.save_space)
|
||||||
|
cache.commit()
|
||||||
|
|
||||||
list_logger = logging.getLogger("borg.output.list")
|
list_logger = logging.getLogger("borg.output.list")
|
||||||
# set up counters for the progress display
|
# set up counters for the progress display
|
||||||
to_delete_len = len(to_delete)
|
to_delete_len = len(to_delete)
|
||||||
archives_deleted = 0
|
archives_deleted = 0
|
||||||
|
uncommitted_deletes = 0
|
||||||
pi = ProgressIndicatorPercent(total=len(to_delete), msg="Pruning archives %3.0f%%", msgid="prune")
|
pi = ProgressIndicatorPercent(total=len(to_delete), msg="Pruning archives %3.0f%%", msgid="prune")
|
||||||
for archive in archives_checkpoints:
|
for archive in archives_checkpoints:
|
||||||
|
if sig_int and sig_int.action_done():
|
||||||
|
break
|
||||||
if archive in to_delete:
|
if archive in to_delete:
|
||||||
pi.show()
|
pi.show()
|
||||||
if args.dry_run:
|
if args.dry_run:
|
||||||
|
@ -85,6 +94,10 @@ class PruneMixIn:
|
||||||
repository, key, manifest, archive.name, cache, consider_part_files=args.consider_part_files
|
repository, key, manifest, archive.name, cache, consider_part_files=args.consider_part_files
|
||||||
)
|
)
|
||||||
archive.delete(stats, forced=args.forced)
|
archive.delete(stats, forced=args.forced)
|
||||||
|
checkpointed = self.maybe_checkpoint(
|
||||||
|
checkpoint_func=checkpoint_func, checkpoint_interval=args.checkpoint_interval
|
||||||
|
)
|
||||||
|
uncommitted_deletes = 0 if checkpointed else (uncommitted_deletes + 1)
|
||||||
else:
|
else:
|
||||||
if is_checkpoint(archive.name):
|
if is_checkpoint(archive.name):
|
||||||
log_message = "Keeping checkpoint archive:"
|
log_message = "Keeping checkpoint archive:"
|
||||||
|
@ -97,10 +110,11 @@ class PruneMixIn:
|
||||||
"{message:<40} {archive}".format(message=log_message, archive=format_archive(archive))
|
"{message:<40} {archive}".format(message=log_message, archive=format_archive(archive))
|
||||||
)
|
)
|
||||||
pi.finish()
|
pi.finish()
|
||||||
if to_delete and not args.dry_run:
|
if sig_int:
|
||||||
manifest.write()
|
# Ctrl-C / SIGINT: do not checkpoint (commit) again, we already have a checkpoint in this case.
|
||||||
repository.commit(compact=False, save_space=args.save_space)
|
self.print_error("Got Ctrl-C / SIGINT.")
|
||||||
cache.commit()
|
elif uncommitted_deletes > 0:
|
||||||
|
checkpoint_func()
|
||||||
if args.stats:
|
if args.stats:
|
||||||
log_multi(str(stats), logger=logging.getLogger("borg.output.stats"))
|
log_multi(str(stats), logger=logging.getLogger("borg.output.stats"))
|
||||||
return self.exit_code
|
return self.exit_code
|
||||||
|
@ -227,3 +241,12 @@ class PruneMixIn:
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
"--save-space", dest="save_space", action="store_true", help="work slower, but using less space"
|
"--save-space", dest="save_space", action="store_true", help="work slower, but using less space"
|
||||||
)
|
)
|
||||||
|
subparser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--checkpoint-interval",
|
||||||
|
metavar="SECONDS",
|
||||||
|
dest="checkpoint_interval",
|
||||||
|
type=int,
|
||||||
|
default=1800,
|
||||||
|
help="write checkpoint every SECONDS seconds (Default: 1800)",
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue