[Zope-Checkins] CVS: Zope2 - ConflictResolution.py:1.1.4.1 BaseStorage.py:1.10.54.1 Connection.py:1.43.2.3 FileStorage.py:1.49.8.2
Jim Fulton
jim@digiciool.com
Thu, 15 Mar 2001 08:10:37 -0500 (EST)
Update of /cvs-repository/Zope2/lib/python/ZODB
In directory korak:/home/jim/atmp/merge/2.3/lib/python/ZODB
Modified Files:
Tag: zope-2_3-branch
BaseStorage.py Connection.py FileStorage.py
Added Files:
Tag: zope-2_3-branch
ConflictResolution.py
Log Message:
Merged changes from Catalog-BTrees-Integration branch.
--- Added File ConflictResolution.py in package Zope2 ---
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
from cStringIO import StringIO
from cPickle import Unpickler, Pickler
import sys
#import traceback
bad_classes={}
bad_class=bad_classes.has_key
ResolvedSerial='rs'
def _classFactory(location, name,
_silly=('__doc__',), _globals={}):
return getattr(__import__(location, _globals, _globals, _silly),
name)
def state(self, oid, serial, prfactory):
p=self.loadSerial(oid, serial)
file=StringIO(p)
unpickler=Unpickler(file)
unpickler.persistent_load=prfactory
class_tuple=unpickler.load()
state=unpickler.load()
return state
class PersistentReference:
def __repr__(self):
return "PR(%s %s)" % (id(self), self.data)
def __getstate__(self):
raise "Can't pickle PersistentReference"
class PersistentReferenceFactory:
data=None
def __call__(self, oid,
getattr=getattr, None=None):
data=self.data
if not data: data=self.data={}
r=data.get(oid, None)
if r is None:
r=PersistentReference()
r.data=oid
data[oid]=r
return r
def persistent_id(object,
PersistentReference=PersistentReference,
getattr=getattr
):
if getattr(object, '__class__', 0) is not PersistentReference:
return None
return object.data
class ConflictResolvingStorage:
"Mix-in class that provides conflict resolution handling for storages"
def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle):
#class_tuple, old, committed, newstate = ('',''), 0, 0, 0
try:
file=StringIO(newpickle)
unpickler=Unpickler(file)
prfactory=PersistentReferenceFactory()
unpickler.persistent_load=prfactory
class_tuple=unpickler.load()[0]
if bad_class(class_tuple):
#sys.stderr.write(' b%s ' % class_tuple[1]); sys.stderr.flush()
return 0
newstate=unpickler.load()
klass=_classFactory(class_tuple[0], class_tuple[1])
klass._p_resolveConflict
inst=klass.__basicnew__()
try:
resolve=inst._p_resolveConflict
except AttributeError:
bad_classes[class_tuple]=1
#traceback.print_exc()
#sys.stderr.write(' b%s ' % class_tuple[1]); sys.stderr.flush()
return 0
old=state(self, oid, oldSerial, prfactory)
committed=state(self, oid, committedSerial, prfactory)
resolved=resolve(old, committed, newstate)
file=StringIO()
pickler=Pickler(file,1)
pickler.persistent_id=persistent_id
pickler.dump(class_tuple)
pickler.dump(resolved)
#sys.stderr.write(' r%s ' % class_tuple[1]); sys.stderr.flush()
return file.getvalue(1)
except Exception, v:
#print '='*70
#print v, v.args
#print '='*70
#print old
#print '='*70
#print committed
#print '='*70
#print newstate
#print '='*70
#traceback.print_exc()
#sys.stderr.write(' c%s ' % class_tuple[1]); sys.stderr.flush()
return 0
--- Updated File BaseStorage.py in package Zope2 --
--- BaseStorage.py 2000/08/07 20:42:28 1.10
+++ BaseStorage.py 2001/03/15 13:10:35 1.10.54.1
@@ -86,7 +86,8 @@
"""
__version__='$Revision$'[11:-2]
-import time, bpthread, UndoLogCompatible
+import ThreadLock, bpthread
+import time, UndoLogCompatible
from POSException import UndoError
from TimeStamp import TimeStamp
z64='\0'*8
@@ -101,7 +102,7 @@
self.__name__=name
# Allocate locks:
- l=bpthread.allocate_lock()
+ l=ThreadLock.allocate_lock()
self._lock_acquire=l.acquire
self._lock_release=l.release
l=bpthread.allocate_lock()
--- Updated File Connection.py in package Zope2 --
--- Connection.py 2001/02/09 14:08:27 1.43.2.2
+++ Connection.py 2001/03/15 13:10:35 1.43.2.3
@@ -94,8 +94,9 @@
from ExtensionClass import Base
from time import time
import Transaction, string, ExportImport, sys, traceback, TmpStore
-from zLOG import LOG, ERROR
+from zLOG import LOG, ERROR, BLATHER
from coptimizations import new_persistent_id
+from ConflictResolution import ResolvedSerial
ExtensionKlass=Base.__class__
@@ -230,7 +231,10 @@
This just deactivates the thing.
"""
- self._cache.invalidate(object._p_oid)
+ if object is self:
+ self._cache.invalidate(self._invalidated)
+ else:
+ self._cache.invalidate(object._p_oid)
def cacheFullSweep(self, dt=0): self._cache.full_sweep(dt)
def cacheMinimize(self, dt=0): self._cache.minimize(dt)
@@ -257,6 +261,8 @@
db._closeConnection(self)
def commit(self, object, transaction, _type=type, _st=type('')):
+ if object is self:
+ return # we registered ourself
oid=object._p_oid
invalid=self._invalid
if oid is None or object._p_jar is not self:
@@ -267,7 +273,12 @@
self._creating.append(oid)
elif object._p_changed:
- if invalid(oid) or invalid(None): raise ConflictError, `oid`
+ if (
+ (invalid(oid) and not hasattr(object, '_p_resolveConflict'))
+ or
+ invalid(None)
+ ):
+ raise ConflictError, `oid`
self._invalidating.append(oid)
else:
@@ -328,7 +339,13 @@
self._creating.append(oid)
else:
#XXX We should never get here
- if invalid(oid) or invalid(None): raise ConflictError, `oid`
+ if (
+ (invalid(oid) and
+ not hasattr(object, '_p_resolveConflict'))
+ or
+ invalid(None)
+ ):
+ raise ConflictError, `oid`
self._invalidating.append(oid)
klass = object.__class__
@@ -362,8 +379,12 @@
# Note that if s is false, then the storage defered the return
if _type(s) is _st:
# normal case
- object._p_serial=s
- object._p_changed=0
+ if s is ResolvedSerial:
+ # resolved conflict
+ object._p_changed=None
+ else:
+ object._p_serial=s
+ object._p_changed=0
else:
# defered returns
for oi, s in s:
@@ -389,6 +410,10 @@
tmp=self._tmp
if tmp is _None: return
src=self._storage
+
+ LOG('ZODB', BLATHER,
+ 'Commiting subtransaction of size %s' % src.getSize())
+
self._storage=tmp
self._tmp=_None
@@ -487,7 +512,13 @@
# notifications between the time we check and the time we
# read.
invalid=self._invalid
- if invalid(oid) or invalid(None): raise ConflictError, `oid`
+ if invalid(oid) or invalid(None):
+ if not hasattr(object.__class__, '_p_independent'):
+ get_transaction().register(self)
+ raise ConflictError(`oid`, `object.__class__`)
+ invalid=1
+ else:
+ invalid=0
file=StringIO(p)
unpickler=Unpickler(file)
@@ -502,6 +533,14 @@
for k,v in state.items(): d[k]=v
object._p_serial=serial
+
+ if invalid:
+ if object._p_independent():
+ try: del self._invalidated[oid]
+ except KeyError: pass
+ else:
+ get_transaction().register(self)
+ raise ConflictError(`oid`, `object.__class__`)
except:
t, v =sys.exc_info()[:2]
--- Updated File FileStorage.py in package Zope2 --
--- FileStorage.py 2001/02/09 01:53:39 1.49.8.1
+++ FileStorage.py 2001/03/15 13:10:36 1.49.8.2
@@ -197,6 +197,7 @@
register_subsystem('ZODB FS')
import BaseStorage
from cPickle import Pickler, Unpickler
+import ConflictResolution
try: from posix import fsync
except: fsync=None
@@ -240,7 +241,8 @@
packed_version='FS21'
-class FileStorage(BaseStorage.BaseStorage):
+class FileStorage(BaseStorage.BaseStorage,
+ ConflictResolution.ConflictResolvingStorage):
_packt=z64
def __init__(self, file_name, create=0, read_only=0, stop=None,
@@ -663,18 +665,23 @@
raise POSException.VersionLockError, (
`oid`, locked_version)
- if serial != oserial: raise POSException.ConflictError, (
- serial, oserial)
-
+ if serial != oserial:
+ data=self.tryToResolveConflict(oid, oserial, serial, data)
+ if not data:
+ raise POSException.ConflictError, (
+ serial, oserial)
+ else:
+ oserial=serial
+
tfile=self._tfile
write=tfile.write
pos=self._pos
here=pos+(tfile.tell()+self._thl)
self._tappend((oid, here))
- serial=self._serial
+ newserial=self._serial
write(pack(">8s8s8s8sH8s",
- oid,serial,p64(old),p64(pos),
- len(version),p64(len(data))
+ oid, newserial, p64(old), p64(pos),
+ len(version), p64(len(data))
)
)
if version:
@@ -695,7 +702,8 @@
raise FileStorageQuotaError, (
'The storage quota has been exceeded.')
- return serial
+ return (serial == oserial and newserial
+ or ConflictResolution.ResolvedSerial)
finally:
self._lock_release()