From 32bd72c27f09893aee3b388f8aff1af986e59860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20=C3=96zkural?= Date: Thu, 8 Dec 2005 18:20:09 +0000 Subject: [PATCH] * one-shot transaction protection for install and remove atomicops --- pisi/atomicoperations.py | 63 +++++++++++++++++++++++----------------- pisi/context.py | 16 ++++++++++ pisi/dbshelve.py | 2 +- pisi/installdb.py | 35 ++++++++++++---------- pisi/lockeddbshelve.py | 6 +++- pisi/packagedb.py | 40 ++++++++++++++----------- pisi/repodb.py | 2 +- 7 files changed, 103 insertions(+), 61 deletions(-) diff --git a/pisi/atomicoperations.py b/pisi/atomicoperations.py index 130d6fd8..b3e6cee1 100644 --- a/pisi/atomicoperations.py +++ b/pisi/atomicoperations.py @@ -9,26 +9,19 @@ # # Please read the COPYING file. # - -# Package install operation - # Author: Eray Ozkural - -# for python 2.3 compatibility -import sys -ver = sys.version_info -if ver[0] <= 2 and ver[1] < 4: - from sets import Set as set - -import os +"Atomic package operations such as install/remove/upgrade" import gettext __trans = gettext.translation('pisi', fallback=True) _ = __trans.ugettext -import pisi +import sys +import os +import bsddb3.db as db +import pisi import pisi.context as ctx import pisi.packagedb as packagedb import pisi.dependency as dependency @@ -78,6 +71,7 @@ class Install(AtomicOperation): (self.pkginfo.name, self.pkginfo.version, self.pkginfo.release, self.pkginfo.build)) ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files) + self.ask_reinstall = ask_reinstall self.check_requirements() self.check_relations() @@ -90,7 +84,15 @@ class Install(AtomicOperation): ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files) comariface.run_postinstall(self.pkginfo.name) ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files) - self.update_databases() + + txn = ctx.dbenv.txn_begin() + try: + self.update_databases(txn) + txn.commit() + except db.DBError, e: + txn.abort() + raise e + self.update_environment() ctx.ui.status() if self.upgrade: @@ -228,11 +230,10 @@ class Install(AtomicOperation): pisi.comariface.register(pcomar, self.metadata.package.name, scriptPath) - def update_databases(self): + def update_databases(self, txn): "update databases" - if self.reinstall: - Remove(self.metadata.package.name).remove_db() + Remove(self.metadata.package.name).remove_db(txn) if not self.same_ver: Remove.remove_pisi_files(self.old_path) @@ -241,13 +242,14 @@ class Install(AtomicOperation): self.metadata.package.version, self.metadata.package.release, self.metadata.package.build, - self.metadata.package.distribution) + self.metadata.package.distribution, + txn) # filesdb - ctx.filesdb.add_files(self.metadata.package.name, self.files) + ctx.filesdb.add_files(self.metadata.package.name, self.files, txn) # installed packages - packagedb.inst_packagedb.add_package(self.pkginfo) + packagedb.inst_packagedb.add_package(self.pkginfo, txn) def update_environment(self): # check if we have any shared objects or anything under @@ -327,8 +329,15 @@ class Remove(AtomicOperation): for fileinfo in self.files.list: self.remove_file(fileinfo) - - self.remove_db() + + txn = ctx.dbenv.txn_begin() + try: + self.remove_db(txn) + txn.commit() + except db.DBError, e: + txn.abort() + raise e + self.remove_pisi_files(self.package.pkg_dir()) ctx.ui.status() ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files) @@ -372,11 +381,13 @@ class Remove(AtomicOperation): remove_pisi_files = staticmethod(remove_pisi_files) - def remove_db(self): - ctx.installdb.remove(self.package_name) - ctx.filesdb.remove_files(ctx.installdb.files(self.package_name)) - #FIXME: this looks like a mistake! - packagedb.remove_package(self.package_name) + def remove_db(self, txn): + ctx.installdb.remove(self.package_name, txn) + ctx.filesdb.remove_files(ctx.installdb.files(self.package_name), txn) + if packagedb.thirdparty_packagedb.has_package(self.package_name, txn): + packagedb.thirdparty_packagedb.remove_package(self.package_name, txn) + if packagedb.inst_packagedb.has_package(self.package_name, txn): + packagedb.inst_packagedb.remove_package(self.package_name, txn) def remove_single(package_name): diff --git a/pisi/context.py b/pisi/context.py index e2f570ce..12a2218b 100644 --- a/pisi/context.py +++ b/pisi/context.py @@ -41,3 +41,19 @@ initialized = False #def register(_impl): # """ Register a UI implementation""" # ui = _impl + + +def txn_proc(proc, txn): + # can be used to txn protect a method automatically + assert dbenv + if not txn: + autotxn = dbenv.txn_begin() + try: + retval = proc(autotxn) + except db.DBError, e: + autotxn.abort() + raise e + autotxn.commit() + return retval + else: + return proc(txn) diff --git a/pisi/dbshelve.py b/pisi/dbshelve.py index a957cf50..397af51c 100644 --- a/pisi/dbshelve.py +++ b/pisi/dbshelve.py @@ -51,7 +51,7 @@ class DBShelf: return self.db.has_key(key, txn) else: return self.db.has_key(key) - + def txn_proc(self, proc, txn): # can be used to txn protect a method automatically if not txn: diff --git a/pisi/installdb.py b/pisi/installdb.py index eaff85be..75b14f08 100644 --- a/pisi/installdb.py +++ b/pisi/installdb.py @@ -161,26 +161,31 @@ class InstallDB: self.d.txn_proc(proc,txn) - def clear_pending(self, pkg): + def clear_pending(self, pkg, txn = None): pkg = str(pkg) - info = self.d[pkg] - if self.is_installed(pkg): - assert info.state == 'ip' - info.state = 'i' - self.d[pkg] = info - del self.dp[pkg] + def proc(txn): + info = self.d.get(pkg, txn) + if self.is_installed(pkg, txn): + assert info.state == 'ip' + info.state = 'i' + self.d.put(pkg, info, txn) + self.dp.delete(pkg, txn) + self.d.txn_proc(proc,txn) - def remove(self, pkg): + def remove(self, pkg, txn = None): pkg = str(pkg) - info = self.d[pkg] - info.state = 'r' - self.d[pkg] = info + def proc(txn): + info = self.d.get(pkg, txn) + info.state = 'r' + self.d.put(pkg, info, txn) + self.d.txn_proc(proc, txn) - def purge(self, pkg): + def purge(self, pkg, txn = None): pkg = str(pkg) - if self.d.has_key(pkg): - del self.d[pkg] - + def proc(txn): + if self.d.has_key(pkg, txn): + self.d.delete(pkg, txn) + self.d.txn_proc(proc, txn) db = None diff --git a/pisi/lockeddbshelve.py b/pisi/lockeddbshelve.py index 41d9ddfa..4b7cd935 100644 --- a/pisi/lockeddbshelve.py +++ b/pisi/lockeddbshelve.py @@ -36,7 +36,11 @@ class Error(pisi.Error): def init_dbenv(): ctx.dbenv = dbobj.DBEnv() ctx.dbenv.open(pisi.context.config.db_dir(), - db.DB_CREATE | db.DB_INIT_MPOOL | db.DB_INIT_TXN | db.DB_INIT_LOG ) + db.DB_CREATE | # allow db to create files + db.DB_INIT_MPOOL | # cache + db.DB_INIT_TXN | # transaction subsystem + db.DB_INIT_LOG | # logging subsystem + db.DB_RECOVER) # run normal recovery #def open(filename, flags='r', mode = 0644, filetype = db.DB_BTREE): # db = LockedDBShelf(None, mode, filetype, None, True) diff --git a/pisi/packagedb.py b/pisi/packagedb.py index 029bd732..345e4678 100644 --- a/pisi/packagedb.py +++ b/pisi/packagedb.py @@ -44,9 +44,9 @@ class PackageDB(object): self.d.close() self.dr.close() - def has_package(self, name): + def has_package(self, name, txn = None): name = str(name) - return self.d.has_key(name) + return self.d.has_key(name, txn) def get_package(self, name): name = str(name) @@ -95,12 +95,14 @@ class PackageDB(object): def clear(self): self.d.clear() - def remove_package(self, name): + def remove_package(self, name, txn = None): name = str(name) - package_info = self.d[name] - del self.d[name] - #FIXME: what's happening to dr? - ctx.componentdb.remove_package(package_info.partOf, package_info.name) + def proc(txn): + package_info = self.d.get(name, txn) + self.d.delete(name, txn) + #FIXME: what's happening to dr? + ctx.componentdb.remove_package(package_info.partOf, package_info.name, txn) + self.d.txn_proc(proc, txn) packagedbs = {} @@ -114,18 +116,22 @@ def remove_db(name): del pisi.packagedb.packagedbs[name] #FIXME: erase database file? -def has_package(name): - repo = which_repo(name) - if repo or thirdparty_packagedb.has_package(name) or inst_packagedb.has_package(name): - return True - return False +def has_package(name, txn = None): + def proc(txn): + repo = which_repo(name) + if repo or thirdparty_packagedb.has_package(name, txn) or inst_packagedb.has_package(name, txn): + return True + return False + return ctx.txn_proc(proc, txn) -def which_repo(name): +def which_repo(name, txn = None): import pisi.repodb - for repo in pisi.repodb.db.list(): - if get_db(repo).has_package(name): - return repo - return None + def proc(txn): + for repo in pisi.repodb.db.list(): + if get_db(repo).has_package(name, txn): + return repo + return None + return ctx.txn_proc(proc, txn) def get_package(name): repo = which_repo(name) diff --git a/pisi/repodb.py b/pisi/repodb.py index e26fc226..d477320f 100644 --- a/pisi/repodb.py +++ b/pisi/repodb.py @@ -59,7 +59,7 @@ class RepoDB(object): l = self.list() return l[ix] - def swap(self, x,y): + def swap(self, x, y): l = self.d["order"] t = l[x] l[x] = l[y]