* one-shot transaction protection for install and remove atomicops
This commit is contained in:
+37
-26
@@ -9,26 +9,19 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Package install operation
|
||||
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
|
||||
# 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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
+20
-15
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+23
-17
@@ -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)
|
||||
|
||||
+1
-1
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user