From 0c77fdde97acf6d5b706d573240f512e6ea3fa42 Mon Sep 17 00:00:00 2001 From: Manu <3916435+m3nu@users.noreply.github.com> Date: Tue, 16 Nov 2021 10:32:40 +0400 Subject: [PATCH] Address DB Locking issues (#1109) --- src/vorta/__main__.py | 4 +-- src/vorta/borg/borg_job.py | 56 ++++++++++++++++++++------------------ tests/conftest.py | 6 ++-- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/vorta/__main__.py b/src/vorta/__main__.py index edc335f6..fa2cdd84 100644 --- a/src/vorta/__main__.py +++ b/src/vorta/__main__.py @@ -2,7 +2,7 @@ import os import signal import sys -import peewee +from peewee import SqliteDatabase from vorta._version import __version__ from vorta.i18n import trans_late, translate from vorta.config import SETTINGS_DIR @@ -50,7 +50,7 @@ def main(): init_logger(background=want_background) # Init database - sqlite_db = peewee.SqliteDatabase(os.path.join(SETTINGS_DIR, 'settings.db'), pragmas={'journal_mode': 'wal', }) + sqlite_db = SqliteDatabase(os.path.join(SETTINGS_DIR, 'settings.db'), pragmas={'journal_mode': 'wal', }) init_db(sqlite_db) # Init app after database is available diff --git a/src/vorta/borg/borg_job.py b/src/vorta/borg/borg_job.py index efe06a3f..773b734d 100644 --- a/src/vorta/borg/borg_job.py +++ b/src/vorta/borg/borg_job.py @@ -21,7 +21,8 @@ from vorta.utils import borg_compat, pretty_bytes from vorta.keyring.abc import VortaKeyring from vorta.keyring.db import VortaDBKeyring -temp_mutex = Lock() +keyring_lock = Lock() +db_lock = Lock() logger = logging.getLogger(__name__) FakeRepo = namedtuple('Repo', ['url', 'id', 'extra_borg_arguments', 'encryption']) @@ -144,27 +145,26 @@ class BorgJob(JobInterface, BackupProfileMixin): return ret # Try to get password from chosen keyring backend. - temp_mutex.acquire() - cls.keyring = VortaKeyring.get_keyring() - logger.debug("Using %s keyring to store passwords.", cls.keyring.__class__.__name__) - ret['password'] = cls.keyring.get_password('vorta-repo', profile.repo.url) + with keyring_lock: + cls.keyring = VortaKeyring.get_keyring() + logger.debug("Using %s keyring to store passwords.", cls.keyring.__class__.__name__) + ret['password'] = cls.keyring.get_password('vorta-repo', profile.repo.url) - # Check if keyring is locked - if profile.repo.encryption != 'none' and not cls.keyring.is_unlocked: - ret['message'] = trans_late('messages', - 'Please unlock your system password manager or disable it under Misc') - return ret + # Check if keyring is locked + if profile.repo.encryption != 'none' and not cls.keyring.is_unlocked: + ret['message'] = trans_late('messages', + 'Please unlock your system password manager or disable it under Misc') + return ret - # Try to fall back to DB Keyring, if we use the system keychain. - if ret['password'] is None and cls.keyring.is_system: - logger.debug('Password not found in primary keyring. Falling back to VortaDBKeyring.') - ret['password'] = VortaDBKeyring().get_password('vorta-repo', profile.repo.url) + # Try to fall back to DB Keyring, if we use the system keychain. + if ret['password'] is None and cls.keyring.is_system: + logger.debug('Password not found in primary keyring. Falling back to VortaDBKeyring.') + ret['password'] = VortaDBKeyring().get_password('vorta-repo', profile.repo.url) - # Give warning and continue if password is found there. - if ret['password'] is not None: - logger.warning('Found password in database, but secure storage was available. ' - 'Consider re-adding the repo to use it.') - temp_mutex.release() + # Give warning and continue if password is found there. + if ret['password'] is not None: + logger.warning('Found password in database, but secure storage was available. ' + 'Consider re-adding the repo to use it.') # Password is required for encryption, cannot continue if ret['password'] is None and not isinstance(profile.repo, FakeRepo) and profile.repo.encryption != 'none': @@ -204,12 +204,13 @@ class BorgJob(JobInterface, BackupProfileMixin): def run(self): self.started_event() - log_entry = EventLogModel(category=self.params.get('category', 'user'), - subcommand=self.cmd[1], - profile=self.params.get('profile_id', None) - ) - log_entry.save() - logger.info('Running command %s', ' '.join(self.cmd)) + with db_lock: + log_entry = EventLogModel(category=self.params.get('category', 'user'), + subcommand=self.cmd[1], + profile=self.params.get('profile_id', None) + ) + log_entry.save() + logger.info('Running command %s', ' '.join(self.cmd)) p = Popen(self.cmd, stdout=PIPE, stderr=PIPE, bufsize=1, universal_newlines=True, env=self.env, cwd=self.cwd, start_new_session=True) @@ -285,9 +286,10 @@ class BorgJob(JobInterface, BackupProfileMixin): log_entry.returncode = p.returncode log_entry.repo_url = self.params.get('repo_url', None) log_entry.end_time = dt.now() - log_entry.save() + with db_lock: + log_entry.save() + self.process_result(result) - self.process_result(result) self.finished_event(result) def process_result(self, result): diff --git a/tests/conftest.py b/tests/conftest.py index a43f5705..3338ad3c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ import os -import peewee +from peewee import SqliteDatabase import pytest import sys from datetime import datetime as dt @@ -26,7 +26,7 @@ def pytest_configure(config): def qapp(tmpdir_factory): # DB is required to init QApplication. New DB used for every test. tmp_db = tmpdir_factory.mktemp('Vorta').join('settings.sqlite') - mock_db = peewee.SqliteDatabase(str(tmp_db)) + mock_db = SqliteDatabase(str(tmp_db)) vorta.models.init_db(mock_db) from vorta.application import VortaApp @@ -43,7 +43,7 @@ def qapp(tmpdir_factory): @pytest.fixture(scope='function', autouse=True) def init_db(qapp, qtbot, tmpdir_factory): tmp_db = tmpdir_factory.mktemp('Vorta').join('settings.sqlite') - mock_db = peewee.SqliteDatabase(str(tmp_db), pragmas={'journal_mode': 'wal', }) + mock_db = SqliteDatabase(str(tmp_db), pragmas={'journal_mode': 'wal', }) vorta.models.init_db(mock_db) default_profile = BackupProfileModel(name='Default')