* one-shot transaction protection for install and remove atomicops

This commit is contained in:
Eray Özkural
2005-12-08 18:20:09 +00:00
parent 7ba956ac41
commit 32bd72c27f
7 changed files with 103 additions and 61 deletions
+36 -25
View File
@@ -9,26 +9,19 @@
# #
# Please read the COPYING file. # Please read the COPYING file.
# #
# Package install operation
# Author: Eray Ozkural <eray@uludag.org.tr> # Author: Eray Ozkural <eray@uludag.org.tr>
"Atomic package operations such as install/remove/upgrade"
# 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
import gettext import gettext
__trans = gettext.translation('pisi', fallback=True) __trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext _ = __trans.ugettext
import pisi import sys
import os
import bsddb3.db as db
import pisi
import pisi.context as ctx import pisi.context as ctx
import pisi.packagedb as packagedb import pisi.packagedb as packagedb
import pisi.dependency as dependency import pisi.dependency as dependency
@@ -78,6 +71,7 @@ class Install(AtomicOperation):
(self.pkginfo.name, self.pkginfo.version, (self.pkginfo.name, self.pkginfo.version,
self.pkginfo.release, self.pkginfo.build)) self.pkginfo.release, self.pkginfo.build))
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files) ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
self.ask_reinstall = ask_reinstall self.ask_reinstall = ask_reinstall
self.check_requirements() self.check_requirements()
self.check_relations() self.check_relations()
@@ -90,7 +84,15 @@ class Install(AtomicOperation):
ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files) ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files)
comariface.run_postinstall(self.pkginfo.name) comariface.run_postinstall(self.pkginfo.name)
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files) 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() self.update_environment()
ctx.ui.status() ctx.ui.status()
if self.upgrade: if self.upgrade:
@@ -228,11 +230,10 @@ class Install(AtomicOperation):
pisi.comariface.register(pcomar, self.metadata.package.name, pisi.comariface.register(pcomar, self.metadata.package.name,
scriptPath) scriptPath)
def update_databases(self): def update_databases(self, txn):
"update databases" "update databases"
if self.reinstall: if self.reinstall:
Remove(self.metadata.package.name).remove_db() Remove(self.metadata.package.name).remove_db(txn)
if not self.same_ver: if not self.same_ver:
Remove.remove_pisi_files(self.old_path) Remove.remove_pisi_files(self.old_path)
@@ -241,13 +242,14 @@ class Install(AtomicOperation):
self.metadata.package.version, self.metadata.package.version,
self.metadata.package.release, self.metadata.package.release,
self.metadata.package.build, self.metadata.package.build,
self.metadata.package.distribution) self.metadata.package.distribution,
txn)
# filesdb # filesdb
ctx.filesdb.add_files(self.metadata.package.name, self.files) ctx.filesdb.add_files(self.metadata.package.name, self.files, txn)
# installed packages # installed packages
packagedb.inst_packagedb.add_package(self.pkginfo) packagedb.inst_packagedb.add_package(self.pkginfo, txn)
def update_environment(self): def update_environment(self):
# check if we have any shared objects or anything under # check if we have any shared objects or anything under
@@ -328,7 +330,14 @@ class Remove(AtomicOperation):
for fileinfo in self.files.list: for fileinfo in self.files.list:
self.remove_file(fileinfo) 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()) self.remove_pisi_files(self.package.pkg_dir())
ctx.ui.status() ctx.ui.status()
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files) 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) remove_pisi_files = staticmethod(remove_pisi_files)
def remove_db(self): def remove_db(self, txn):
ctx.installdb.remove(self.package_name) ctx.installdb.remove(self.package_name, txn)
ctx.filesdb.remove_files(ctx.installdb.files(self.package_name)) ctx.filesdb.remove_files(ctx.installdb.files(self.package_name), txn)
#FIXME: this looks like a mistake! if packagedb.thirdparty_packagedb.has_package(self.package_name, txn):
packagedb.remove_package(self.package_name) 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): def remove_single(package_name):
+16
View File
@@ -41,3 +41,19 @@ initialized = False
#def register(_impl): #def register(_impl):
# """ Register a UI implementation""" # """ Register a UI implementation"""
# ui = _impl # 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)
+20 -15
View File
@@ -161,26 +161,31 @@ class InstallDB:
self.d.txn_proc(proc,txn) self.d.txn_proc(proc,txn)
def clear_pending(self, pkg): def clear_pending(self, pkg, txn = None):
pkg = str(pkg) pkg = str(pkg)
info = self.d[pkg] def proc(txn):
if self.is_installed(pkg): info = self.d.get(pkg, txn)
assert info.state == 'ip' if self.is_installed(pkg, txn):
info.state = 'i' assert info.state == 'ip'
self.d[pkg] = info info.state = 'i'
del self.dp[pkg] 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) pkg = str(pkg)
info = self.d[pkg] def proc(txn):
info.state = 'r' info = self.d.get(pkg, txn)
self.d[pkg] = info 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) pkg = str(pkg)
if self.d.has_key(pkg): def proc(txn):
del self.d[pkg] if self.d.has_key(pkg, txn):
self.d.delete(pkg, txn)
self.d.txn_proc(proc, txn)
db = None db = None
+5 -1
View File
@@ -36,7 +36,11 @@ class Error(pisi.Error):
def init_dbenv(): def init_dbenv():
ctx.dbenv = dbobj.DBEnv() ctx.dbenv = dbobj.DBEnv()
ctx.dbenv.open(pisi.context.config.db_dir(), 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): #def open(filename, flags='r', mode = 0644, filetype = db.DB_BTREE):
# db = LockedDBShelf(None, mode, filetype, None, True) # db = LockedDBShelf(None, mode, filetype, None, True)
+23 -17
View File
@@ -44,9 +44,9 @@ class PackageDB(object):
self.d.close() self.d.close()
self.dr.close() self.dr.close()
def has_package(self, name): def has_package(self, name, txn = None):
name = str(name) name = str(name)
return self.d.has_key(name) return self.d.has_key(name, txn)
def get_package(self, name): def get_package(self, name):
name = str(name) name = str(name)
@@ -95,12 +95,14 @@ class PackageDB(object):
def clear(self): def clear(self):
self.d.clear() self.d.clear()
def remove_package(self, name): def remove_package(self, name, txn = None):
name = str(name) name = str(name)
package_info = self.d[name] def proc(txn):
del self.d[name] package_info = self.d.get(name, txn)
#FIXME: what's happening to dr? self.d.delete(name, txn)
ctx.componentdb.remove_package(package_info.partOf, package_info.name) #FIXME: what's happening to dr?
ctx.componentdb.remove_package(package_info.partOf, package_info.name, txn)
self.d.txn_proc(proc, txn)
packagedbs = {} packagedbs = {}
@@ -114,18 +116,22 @@ def remove_db(name):
del pisi.packagedb.packagedbs[name] del pisi.packagedb.packagedbs[name]
#FIXME: erase database file? #FIXME: erase database file?
def has_package(name): def has_package(name, txn = None):
repo = which_repo(name) def proc(txn):
if repo or thirdparty_packagedb.has_package(name) or inst_packagedb.has_package(name): repo = which_repo(name)
return True if repo or thirdparty_packagedb.has_package(name, txn) or inst_packagedb.has_package(name, txn):
return False return True
return False
return ctx.txn_proc(proc, txn)
def which_repo(name): def which_repo(name, txn = None):
import pisi.repodb import pisi.repodb
for repo in pisi.repodb.db.list(): def proc(txn):
if get_db(repo).has_package(name): for repo in pisi.repodb.db.list():
return repo if get_db(repo).has_package(name, txn):
return None return repo
return None
return ctx.txn_proc(proc, txn)
def get_package(name): def get_package(name):
repo = which_repo(name) repo = which_repo(name)
+1 -1
View File
@@ -59,7 +59,7 @@ class RepoDB(object):
l = self.list() l = self.list()
return l[ix] return l[ix]
def swap(self, x,y): def swap(self, x, y):
l = self.d["order"] l = self.d["order"]
t = l[x] t = l[x]
l[x] = l[y] l[x] = l[y]