From fc7b28a8bcd73ff289da362e38136c7249f63df0 Mon Sep 17 00:00:00 2001 From: echel0n Date: Thu, 5 Jun 2014 16:11:07 -0700 Subject: [PATCH 1/2] Automatic DB restores now done if upgrade fails due to corrupt DB or incorrect db numbering --- sickbeard/databases/mainDB.py | 1 - sickbeard/db.py | 30 +++++++++++++++++++++++---- sickbeard/helpers.py | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index dc5b3d8d..de060bf4 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -150,7 +150,6 @@ def backupDatabase(version): else: logger.log(u"Proceeding with upgrade") - # ====================== # = Main DB Migrations = # ====================== diff --git a/sickbeard/db.py b/sickbeard/db.py index 2819d806..67cca9ee 100644 --- a/sickbeard/db.py +++ b/sickbeard/db.py @@ -29,8 +29,6 @@ import sickbeard from sickbeard import encodingKludge as ek from sickbeard import logger from sickbeard.exceptions import ex -from sickbeard.common import cpu_presets -from itertools import ifilter db_lock = threading.Lock() @@ -243,6 +241,8 @@ class DBConnection: def hasTable(self, tableName): return len(self.action("SELECT 1 FROM sqlite_master WHERE name = ?;", (tableName, )).fetchall()) > 0 + def close(self): + self.connection.close() def sanityCheckDatabase(connection, sanity_check): sanity_check(connection).check() @@ -268,6 +268,13 @@ def upgradeDatabase(connection, schema): def prettyName(class_name): return ' '.join([x.group() for x in re.finditer("([A-Z])([a-z0-9]+)", class_name)]) +def restoreDatabase(version): + logger.log(u"Restoring database before trying upgrade again") + if not sickbeard.helpers.restoreVersionedFile(dbFilename(suffix='v'+ str(version)), version): + logger.log_error_and_exit(u"Database restore failed, abort upgrading database") + return False + else: + return True def _processUpgrade(connection, upgradeClass): instance = upgradeClass(connection) @@ -277,8 +284,23 @@ def _processUpgrade(connection, upgradeClass): try: instance.execute() except sqlite3.DatabaseError, e: - print "Error in " + str(upgradeClass.__name__) + ": " + ex(e) - raise + # attemping to restore previous DB backup and perform upgrade + try: + instance.execute() + except: + restored = False + result = connection.select("SELECT db_version FROM db_version") + if result: + version = int(result[0]["db_version"]) + connection.close() + if restoreDatabase(version): + # initialize the main SB database + upgradeDatabase(DBConnection(), sickbeard.mainDB.InitialSchema) + restored = True + + if not restored: + print "Error in " + str(upgradeClass.__name__) + ": " + ex(e) + raise logger.log(upgradeClass.__name__ + " upgrade completed", logger.DEBUG) else: logger.log(upgradeClass.__name__ + " upgrade not required", logger.DEBUG) diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index 5007de4d..c7ae7ab8 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -886,6 +886,44 @@ def backupVersionedFile(old_file, version): return True +def restoreVersionedFile(backup_file, version): + numTries = 0 + + new_file, backup_version = os.path.splitext(backup_file) + restore_file = new_file + '.' + 'v' + str(version) + + if not ek.ek(os.path.isfile, new_file): + logger.log(u"Not restoring, " + new_file + " doesn't exist", logger.DEBUG) + return False + + try: + logger.log(u"Trying to backup " + new_file + " to " + new_file + '.rtmp before restoring backup', logger.DEBUG) + shutil.move(new_file, new_file + '.rtmp') + except Exception, e: + logger.log(u"Error while trying to backup DB file " + restore_file + " before proceeding with restore: " + ex(e), logger.WARNING) + return False + + while not ek.ek(os.path.isfile, new_file): + if not ek.ek(os.path.isfile, restore_file): + logger.log(u"Not restoring, " + restore_file + " doesn't exist", logger.DEBUG) + break + + try: + logger.log(u"Trying to restore " + restore_file + " to " + new_file, logger.DEBUG) + shutil.copy(restore_file, new_file) + logger.log(u"Restore done", logger.DEBUG) + break + except Exception, e: + logger.log(u"Error while trying to restore " + restore_file + ": " + ex(e), logger.WARNING) + numTries += 1 + time.sleep(1) + logger.log(u"Trying again.", logger.DEBUG) + + if numTries >= 10: + logger.log(u"Unable to restore " + restore_file + " to " + new_file + " please do it manually.", logger.ERROR) + return False + + return True # try to convert to int, if it fails the default will be returned def tryInt(s, s_default=0): From 34009bb9b877ada1e057095d111a98733dce30d0 Mon Sep 17 00:00:00 2001 From: echel0n Date: Thu, 5 Jun 2014 16:14:57 -0700 Subject: [PATCH 2/2] MainDB is backed up before being restored now to .r* versioned backup backups --- sickbeard/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index c7ae7ab8..33ca05b5 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -897,8 +897,8 @@ def restoreVersionedFile(backup_file, version): return False try: - logger.log(u"Trying to backup " + new_file + " to " + new_file + '.rtmp before restoring backup', logger.DEBUG) - shutil.move(new_file, new_file + '.rtmp') + logger.log(u"Trying to backup " + new_file + " to " + new_file + "." + "r" + str(version) + " before restoring backup", logger.DEBUG) + shutil.move(new_file, new_file + '.' + 'r' + str(version)) except Exception, e: logger.log(u"Error while trying to backup DB file " + restore_file + " before proceeding with restore: " + ex(e), logger.WARNING) return False