[Zope-Checkins] CVS: StandaloneZODB/ZODB - fsIndex.py:1.1.6.1 BaseStorage.py:1.16.8.1 Connection.py:1.61.8.1 DB.py:1.36.6.1 DemoStorage.py:1.8.8.1 ExportImport.py:1.12.8.1 FileStorage.py:1.76.8.1 MappingStorage.py:1.4.8.1 Mount.py:1.13.4.1 POSException.py:1.8.8.1 PersistentMapping.py:1.17.4.1 TimeStamp.c:1.10.8.1 Transaction.py:1.32.8.1 UndoLogCompatible.py:1.4.8.1 ZApplication.py:1.9.8.1 bpthread.py:1.3.8.1 cPersistence.c:1.47.8.1 cPickleCache.c:1.37.8.1 coptimizations.c:1.14.8.1 dbmStorage.py:1.2.8.1 winlock.c:1.6.8.1 Setup:NONE

Jeremy Hylton jeremy@zope.com
Tue, 18 Dec 2001 12:04:20 -0500


Update of /cvs-repository/StandaloneZODB/ZODB
In directory cvs.zope.org:/tmp/cvs-serv28929/ZODB

Modified Files:
      Tag: Standby-branch
	BaseStorage.py Connection.py DB.py DemoStorage.py 
	ExportImport.py FileStorage.py MappingStorage.py Mount.py 
	POSException.py PersistentMapping.py TimeStamp.c 
	Transaction.py UndoLogCompatible.py ZApplication.py 
	bpthread.py cPersistence.c cPickleCache.c coptimizations.c 
	dbmStorage.py winlock.c 
Added Files:
      Tag: Standby-branch
	fsIndex.py 
Removed Files:
      Tag: Standby-branch
	Setup 
Log Message:
Merge StandaloneZODB-1_0-branch into Standby-branch


=== Added File StandaloneZODB/ZODB/fsIndex.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""Implement an OID to File-position (long integer) mapping
"""
# 
# To save space, we do two things:
# 
#     1. We split the keys (OIDS) into 6-byte prefixes and 2-byte suffixes.
#        We use the prefixes as keys in a mapping from prefix to mappings
#        of suffix to data:
# 
#           data is  {prefix -> {suffix -> data}}
# 
#     2. We limit the data size to 48 bits. This should allow databases
#        as large as 256 terabytes.
# 
# Mostof the space is consumed by items in the mappings from 2-byte
# suffix to 6-byte data. This should reduce the overall memory usage to
# 8-16 bytes per OID.
# 
# We use p64 to convert integers to 8-byte strings and lop off the two
# high-order bytes when saving. On loading data, we add the leading
# bytes back before using U64 to convert the data back to (long)
# integers.

from BTrees._fsBTree import fsBTree as _fsBTree

import struct

# convert between numbers and six-byte strings

_t32 = 1L<< 32

def num2str(n):
    h, l = divmod(long(n), _t32)
    return struct.pack(">HI", h, l)

def str2num(s):
    h, l = struct.unpack(">HI", s)
    if h:
        return (long(h) << 32) + l
    else:
       return l

class fsIndex:

    def __init__(self):
        self._data = {}

    def __getitem__(self, key):
        return str2num(self._data[key[:6]][key[6:]])

    def get(self, key, default=None):
        tree = self._data.get(key[:6], default)
        if tree is default:
            return default
        v = tree.get(key[6:], default)
        if v is default:
            return default
        return str2num(v)

    def __setitem__(self, key, value):
        value = num2str(value)
        treekey = key[:6]
        tree = self._data.get(treekey)
        if tree is None:
            tree = _fsBTree()
            self._data[treekey] = tree
        tree[key[6:]] = value

    def __len__(self):
        r = 0
        for tree in self._data.values():
            r += len(tree)
        return r

    def update(self, mapping):
        for k, v in mapping.items():
            self[k] = v

    def has_key(self, key):
        v=self.get(key, self)
        return v is not self

    def clear(self):
        self._data.clear()

    def keys(self):
        r = []
        for prefix, tree in self._data.items():
            for suffix in tree.keys():
                r.append(prefix + suffix)
        return r

    def items(self):
        r = []
        for prefix, tree in self._data.items():
            for suffix, v in tree.items():
                r.append(((prefix + suffix), str2num(v)))
        return r

    def values(self):
        r = []
        for prefix, tree in self._data.items():
            for v in tree.values():
                r.append(str2num(v))
        return r


=== StandaloneZODB/ZODB/BaseStorage.py 1.16 => 1.16.8.1 ===


=== StandaloneZODB/ZODB/Connection.py 1.61 => 1.61.8.1 ===
 
 from cPickleCache import PickleCache
-from POSException import ConflictError
+from POSException import ConflictError, ReadConflictError
 from ExtensionClass import Base
 import ExportImport, TmpStore
 from zLOG import LOG, ERROR, BLATHER
@@ -248,7 +248,7 @@
                 or
                 invalid(None)
                 ):
-                raise ConflictError, `oid`
+                raise ConflictError(object=object)
             self._invalidating.append(oid)
 
         else:
@@ -315,7 +315,7 @@
                     or
                     invalid(None)
                     ):
-                    raise ConflictError, `oid`
+                    raise ConflictError(object=object)
                 self._invalidating.append(oid)
                 
             klass = object.__class__
@@ -459,7 +459,7 @@
             if invalid(oid) or invalid(None):
                 if not hasattr(object.__class__, '_p_independent'):
                     get_transaction().register(self)
-                    raise ConflictError(`oid`, `object.__class__`)
+                    raise ReadConflictError(object=object)
                 invalid=1
             else:
                 invalid=0
@@ -484,7 +484,7 @@
                     except KeyError: pass
                 else:
                     get_transaction().register(self)
-                    raise ConflictError(`oid`, `object.__class__`)
+                    raise ConflictError(object=object)
 
         except ConflictError:
             raise
@@ -544,7 +544,7 @@
 
     def tpc_begin(self, transaction, sub=None):
         if self._invalid(None): # Some nitwit invalidated everything!
-            raise ConflictError, "transaction already invalidated"
+            raise ConflictError("transaction already invalidated")
         self._invalidating=[]
         self._creating=[]
 


=== StandaloneZODB/ZODB/DB.py 1.36 => 1.36.6.1 ===


=== StandaloneZODB/ZODB/DemoStorage.py 1.8 => 1.8.8.1 ===
                     nv=old
 
-                if serial != oserial: raise POSException.ConflictError
+                if serial != oserial:
+                    raise POSException.ConflictError(serials=(oserial, serial))
                 
             serial=self._serial
             r=[oid, serial, old, version and (version, nv) or None, data]


=== StandaloneZODB/ZODB/ExportImport.py 1.12 => 1.12.8.1 ===
 ##############################################################################
 
-"""Support for database export and import.
-"""
+"""Support for database export and import."""
 
 import POSException, string
 


=== StandaloneZODB/ZODB/FileStorage.py 1.76 => 1.76.8.1 ===
 __version__='$Revision$'[11:-2]
 
-import struct, time, os, bpthread, string, base64, sys
+import struct, time, os, string, base64, sys
 from struct import pack, unpack
-from cPickle import loads
 import POSException
 from POSException import UndoError
 from TimeStamp import TimeStamp
@@ -127,9 +126,15 @@
 from zLOG import LOG, WARNING, ERROR, PANIC, register_subsystem
 register_subsystem('ZODB FS')
 import BaseStorage
-from cPickle import Pickler, Unpickler
+from cPickle import Pickler, Unpickler, loads
 import ConflictResolution
 
+try:
+    from fsIndex import fsIndex
+except ImportError:
+    def fsIndex():
+        return {}
+
 try: from posix import fsync
 except: fsync=None
 
@@ -262,7 +267,8 @@
         self._index_get=index.get
         self._vindex_get=vindex.get
 
-    def __len__(self): return len(self._index)
+    def __len__(self):
+        return len(self._index)
 
     def _newIndexes(self):
         # hook to use something other than builtin dict
@@ -632,8 +638,8 @@
                 if serial != oserial:
                     data=self.tryToResolveConflict(oid, oserial, serial, data)
                     if not data:
-                        raise POSException.ConflictError, (
-                            serial, oserial)
+                        raise POSException.ConflictError(oid=oid,
+                                                serials=(oserial, serial))
             else:
                 oserial=serial
                     
@@ -1218,7 +1224,7 @@
     
             rootl=[z64]
             pop=rootl.pop
-            pindex={}
+            pindex=fsIndex()
             referenced=pindex.has_key
             _load=self._load
             _loada=self._loada
@@ -1248,7 +1254,7 @@
     
             # Index for non-version data.  This is a temporary structure
             # to reduce I/O during packing
-            nvindex={}
+            nvindex=fsIndex()
     
             # Cache a bunch of methods
             seek=file.seek


=== StandaloneZODB/ZODB/MappingStorage.py 1.4 => 1.4.8.1 ===
                 old=self._index[oid]
                 oserial=old[:8]
-                if serial != oserial: raise POSException.ConflictError
+                if serial != oserial:
+                    raise POSException.ConflictError(serials=(oserial, serial))
                 
             serial=self._serial
             self._tindex.append((oid,serial+data))


=== StandaloneZODB/ZODB/Mount.py 1.13 => 1.13.4.1 ===


=== StandaloneZODB/ZODB/POSException.py 1.8 => 1.8.8.1 ===
 # 
 ##############################################################################
-'''BoboPOS-defined exceptions
+"""BoboPOS-defined exceptions
 
-$Id$'''
-__version__='$Revision$'[11:-2]
+$Id$"""
+__version__ = '$Revision$'.split()[-2:][0]
 
 from string import join
-StringType=type('')
-DictType=type({})
+from types import StringType, DictType
+from ZODB import utils
 
 class POSError(Exception):
     """Persistent object system error
@@ -28,10 +28,94 @@
     """
 
 class ConflictError(TransactionError):
-    """Two transactions tried to modify the same object at once
+    """Two transactions tried to modify the same object at once.  This
+    transaction should be resubmitted.
 
-    This transaction should be resubmitted.
+    Instance attributes:
+      oid : string
+        the OID (8-byte packed string) of the object in conflict
+      class_name : string
+        the fully-qualified name of that object's class
+      message : string
+        a human-readable explanation of the error
+      serials : (string, string)
+        a pair of 8-byte packed strings; these are the serial numbers
+        (old and new) of the object in conflict.  (Serial numbers are
+        closely related [equal?] to transaction IDs; a ConflictError may
+        be triggered by a serial number mismatch.)
+
+    The caller should pass either object or oid as a keyword argument,
+    but not both of them.  If object is passed, it should be a
+    persistent object with an _p_oid attribute.
     """
+
+    def __init__(self, message=None, object=None, oid=None, serials=None):
+        if message is None:
+            self.message = "database conflict error"
+        else:
+            self.message = message
+
+        if object is None:
+            self.oid = None
+            self.class_name = None
+        else:
+            self.oid = object._p_oid
+            klass = object.__class__
+            self.class_name = klass.__module__ + "." + klass.__name__
+
+        if oid is not None:
+            assert self.oid is None
+            self.oid = oid
+
+        self.serials = serials
+
+    def __str__(self):
+        extras = []
+        if self.oid:
+            extras.append("oid %016x" % utils.U64(self.oid))
+        if self.class_name:
+            extras.append("class %s" % self.class_name)
+        if self.serials:
+            extras.append("serial was %016x, now %016x" %
+                          tuple(map(utils.U64, self.serials)))
+        if extras:
+            return "%s (%s)" % (self.message, ", ".join(extras))
+        else:
+            return self.message
+
+    def get_oid(self):
+        return self.oid
+
+    def get_class_name(self):
+        return self.class_name
+
+    def get_old_serial(self):
+        return self.serials[0]
+
+    def get_new_serial(self):
+        return self.serials[1]
+
+    def get_serials(self):
+        return self.serials
+
+
+class ReadConflictError(ConflictError):
+    """A conflict detected at read time -- attempt to read an object
+    that has changed in another transaction (eg. another thread
+    or process).
+    """
+    def __init__(self, message=None, object=None, serials=None):
+        if message is None:
+            message = "database read conflict error"
+        ConflictError.__init__(self, message=message, object=object,
+                               serials=serials)
+
+class BTreesConflictError(ConflictError):
+    """A special subclass for BTrees conflict errors, which return
+    an undocumented four-tuple."""
+    def __init__(self, *btree_args):
+        ConflictError.__init__(self, message="BTrees conflict error")
+        self.btree = btree_args
 
 class VersionError(POSError):
     """An error in handling versions occurred


=== StandaloneZODB/ZODB/PersistentMapping.py 1.17 => 1.17.4.1 ===


=== StandaloneZODB/ZODB/TimeStamp.c 1.10 => 1.10.8.1 ===


=== StandaloneZODB/ZODB/Transaction.py 1.32 => 1.32.8.1 ===


=== StandaloneZODB/ZODB/UndoLogCompatible.py 1.4 => 1.4.8.1 ===
 # 
 ##############################################################################
-"""Provide backward compatability with storages that have undoLog, but not undoInfo."""
-
+"""backwards compatibility for storages that have undoLog but not undoInfo."""
 
 class UndoLogCompatible:
 


=== StandaloneZODB/ZODB/ZApplication.py 1.9 => 1.9.8.1 ===


=== StandaloneZODB/ZODB/bpthread.py 1.3 => 1.3.8.1 ===


=== StandaloneZODB/ZODB/cPersistence.c 1.47 => 1.47.8.1 ===


=== StandaloneZODB/ZODB/cPickleCache.c 1.37 => 1.37.8.1 ===


=== StandaloneZODB/ZODB/coptimizations.c 1.14 => 1.14.8.1 ===


=== StandaloneZODB/ZODB/dbmStorage.py 1.2 => 1.2.8.1 ===


=== StandaloneZODB/ZODB/winlock.c 1.6 => 1.6.8.1 ===

=== Removed File StandaloneZODB/ZODB/Setup ===