From 742bb239e7bbb56a438977585d1b7d3cb1d21dcd Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 23 Feb 2024 18:16:25 +0000 Subject: [PATCH] fix(backups): simplify autobackups to avoid deadlocks --- selfprivacy_api/backup/jobs.py | 17 ++++++++++++++++- selfprivacy_api/backup/tasks.py | 29 ++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/selfprivacy_api/backup/jobs.py b/selfprivacy_api/backup/jobs.py index 0aacd86..1358c01 100644 --- a/selfprivacy_api/backup/jobs.py +++ b/selfprivacy_api/backup/jobs.py @@ -3,7 +3,7 @@ from typing import Optional, List from selfprivacy_api.models.backup.snapshot import Snapshot from selfprivacy_api.jobs import Jobs, Job, JobStatus from selfprivacy_api.services.service import Service -from selfprivacy_api.services import get_service_by_id +from selfprivacy_api.services import get_service_by_id, get_all_services def job_type_prefix(service: Service) -> str: @@ -14,6 +14,10 @@ def backup_job_type(service: Service) -> str: return f"{job_type_prefix(service)}.backup" +def autobackup_job_type() -> str: + return f"backups.autobackup" + + def restore_job_type(service: Service) -> str: return f"{job_type_prefix(service)}.restore" @@ -36,6 +40,17 @@ def is_something_running_for(service: Service) -> bool: return len(running_jobs) != 0 +def add_autobackup_job(services: List[Service]) -> Job: + service_names = [s.get_display_name() for s in services] + pretty_service_list: str = ", ".join(service_names) + job = Jobs.add( + type_id=autobackup_job_type(), + name=f"Automatic backup", + description=f"Scheduled backup for services : {pretty_service_list}", + ) + return job + + def add_backup_job(service: Service) -> Job: if is_something_running_for(service): message = ( diff --git a/selfprivacy_api/backup/tasks.py b/selfprivacy_api/backup/tasks.py index c0f6a1d..dc0e6bd 100644 --- a/selfprivacy_api/backup/tasks.py +++ b/selfprivacy_api/backup/tasks.py @@ -15,6 +15,7 @@ from huey import crontab from selfprivacy_api.services.service import Service from selfprivacy_api.services import get_service_by_id from selfprivacy_api.backup import Backups +from selfprivacy_api.backup.jobs import add_autobackup_job from selfprivacy_api.jobs import Jobs, JobStatus, Job @@ -72,26 +73,40 @@ def restore_snapshot( return True -def do_autobackup(): +def do_autobackup() -> None: """ Body of autobackup task, broken out to test it For some reason, we cannot launch periodic huey tasks inside tests """ time = datetime.utcnow().replace(tzinfo=timezone.utc) - for service in Backups.services_to_back_up(time): - handle = start_backup(service.get_id(), BackupReason.AUTO) - # To be on safe side, we do not do it in parallel - handle(blocking=True) + services_to_back_up = Backups.services_to_back_up(time) + job = add_autobackup_job(services_to_back_up) + + progress_per_service = 100 // len(services_to_back_up) + progress = 0 + Jobs.update(job, JobStatus.RUNNING, progress=progress) + + for service in services_to_back_up: + try: + Backups.back_up(service, BackupReason.AUTO) + except Exception as error: + Jobs.update( + job, + status=JobStatus.ERROR, + error=type(error).__name__ + ": " + str(error), + ) + return + progress = progress + progress_per_service + Jobs.update(job, JobStatus.RUNNING, progress=progress) @huey.periodic_task(validate_datetime=validate_datetime) -def automatic_backup() -> bool: +def automatic_backup() -> None: """ The worker periodic task that starts the automatic backup process. """ do_autobackup() - return True @huey.periodic_task(crontab(hour="*/" + str(SNAPSHOT_CACHE_TTL_HOURS)))