[Checkins] SVN: zc.bsddbstorage/branches/dev/ checkpoint

Jim Fulton jim at zope.com
Mon Oct 19 06:33:48 EDT 2009


Log message for revision 105132:
  checkpoint

Changed:
  U   zc.bsddbstorage/branches/dev/buildout.cfg
  U   zc.bsddbstorage/branches/dev/setup.py
  A   zc.bsddbstorage/branches/dev/src/zc/bsddbstorage/
  A   zc.bsddbstorage/branches/dev/src/zc/bsddbstorage/__init__.py

-=-
Modified: zc.bsddbstorage/branches/dev/buildout.cfg
===================================================================
--- zc.bsddbstorage/branches/dev/buildout.cfg	2009-10-19 10:32:36 UTC (rev 105131)
+++ zc.bsddbstorage/branches/dev/buildout.cfg	2009-10-19 10:33:48 UTC (rev 105132)
@@ -4,7 +4,7 @@
 
 [test]
 recipe = zc.recipe.testrunner
-eggs = 
+eggs = zc.bsddbstorage
 
 [py]
 recipe = zc.recipe.egg

Modified: zc.bsddbstorage/branches/dev/setup.py
===================================================================
--- zc.bsddbstorage/branches/dev/setup.py	2009-10-19 10:32:36 UTC (rev 105131)
+++ zc.bsddbstorage/branches/dev/setup.py	2009-10-19 10:33:48 UTC (rev 105132)
@@ -11,9 +11,9 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-name, version = 'zc.', '0'
+name, version = 'zc.bsddbstorage', '0'
 
-install_requires = ['setuptools']
+install_requires = ['setuptools', 'bsddb3']
 extras_require = dict(test=['zope.testing'])
 
 entry_points = """

Added: zc.bsddbstorage/branches/dev/src/zc/bsddbstorage/__init__.py
===================================================================
--- zc.bsddbstorage/branches/dev/src/zc/bsddbstorage/__init__.py	                        (rev 0)
+++ zc.bsddbstorage/branches/dev/src/zc/bsddbstorage/__init__.py	2009-10-19 10:33:48 UTC (rev 105132)
@@ -0,0 +1,374 @@
+##############################################################################
+#
+# Copyright (c) Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+
+from bsddb3 import db
+import os
+import ZODB.POSException
+import ZODB.TimeStamp
+
+
+class BSDDBStorage:
+
+    def __init__(self, envpath):
+        self.__name__ = envpath
+        if not os.path.isdir(envpath):
+            os.mkdir(envpath)
+
+        self.env = db.DBEnv()
+        self.env.open(envpath,
+                      db.DB_INIT_LOCK | db.DB_INIT_LOG | db.DB_INIT_MPOOL |
+                      db.DB_INIT_TXN | db.DB_RECOVER | db.DB_THREAD |
+                      db.DB_CREATE | db.DB_AUTO_COMMIT)
+
+        # data: {oid -> [tid+data]}
+        self.data = db.DB(self.env)
+        self.data.set_flags(db.DB_DUP)
+        self.data.open('data', dbtype=db.DB_HASH,
+                       flags=(db.DB_CREATE | db.DB_THREAD | db.DB_AUTO_COMMIT |
+                              db.DB_MULTIVERSION),
+                       )
+        self.datapath = os.path.abspath(os.path.join(envpath, 'data'))
+
+        # transaction_oids: {tid->[oids]}
+        self.transaction_oids = db.DB(self.env)
+        self.transaction_oids.set_flags(db.DB_DUP)
+        self.transaction_oids.open('transaction_oids', dbtype=db.DB_BTREE,
+                                   flags=(db.DB_CREATE | db.DB_THREAD |
+                                          db.DB_AUTO_COMMIT |
+                                          db.DB_MULTIVERSION),
+                                   )
+
+        # transactions: {tid ->transaction_pickle}
+        self.transactions = db.DB(self.env)
+        self.transaction.open('transactions', dbtype=db.DB_BTREE,
+                              flags=(db.DB_CREATE | db.DB_THREAD |
+                                     db.DB_AUTO_COMMIT | db.DB_MULTIVERSION),
+                              )
+
+    def txn(self, flags=0):
+        return TransactionContext(self.env.txn_begin(flags=flags))
+
+    def cursor(self, db, txn=None, flags=0):
+        return CursorContext(self.db.cursor(txn, flags))
+
+    def close(self):
+        self.data.close()
+        self.transaction_oids.close()
+        self.transactions.close()
+
+    def getName(self):
+        return self.__name__
+
+    def getSize(self):
+        return os.stat(self.datapath)
+
+    def _history_entry(self, record, txn):
+        tid = record[:8]
+        transaction = cPickle.loads(self.transactions.get(tid, txn=txn))
+        transaction.update(size=len(record-8))
+
+    def history(self, oid, size=1):
+        with self.txn(db.DB_TXN_SNAPSHOT) as txn:
+            with self.cursor(self.data, txn) as cursor:
+                k, record = cursor.get(oid, db.DB_PREV)
+                if k != oid or len(record) == 8:
+                    raise ZODB.POSException.POSKeyError(oid)
+
+                result = [_history_entry(record)]
+                while len(result) < size):
+                    kv = cursor.get(oid, db.PREV_DUP)
+                    if kv is None:
+                        break
+                    result.append(_history_entry(kb[1])
+
+                cursor.close()
+                return result
+
+    def isReadOnly(self):
+        return False
+
+    def lastTransaction(self):
+        with self.txn() as txn:
+            with self.cursor(self.data, txn) as cursor:
+                return cursor.get(db.DB_LAST)[0]
+
+    def __len__(self):
+        return self.data.stat(db.DB_FAST_STAT)['nkeys']
+
+    def load(self, oid, version=''):
+        with self.txn() as txn:
+            with self.cursor(self.data, txn) as cursor:
+                k, record = cursor.get(oid, db.DB_PREV)
+                if k != oid or len(record) == 8:
+                    raise ZODB.POSException.POSKeyError(oid)
+                return result[8:], result[:8]
+
+
+    def loadBefore(self, oid, tid):
+        with self.txn(db.DB_TXN_SNAPSHOT) as txn:
+            with self.cursor(self.data, txn) as cursor:
+                k, record = cursor.get(oid, db.DB_PREV)
+                if k != oid or len(record) == 8:
+                    raise ZODB.POSException.POSKeyError(oid)
+                nexttid = None
+                rtid = record[:8]
+                while rtid >= tid:
+                    krecord = cursor.get(oid, db.PREV_DUP)
+                    if krecord is None:
+                        return None
+                    nexttid = rtid
+                    record = krecord[1]
+                    rtid = record[:8]
+
+                return record[8:], rtid, nexttid
+
+    def loadSerial(oid, serial):
+        with self.txn(db.DB_TXN_SNAPSHOT) as txn:
+            with self.cursor(self.data, txn) as cursor:
+                k, record = cursor.get(oid, db.DB_PREV)
+                if k != oid or len(record) == 8:
+                    raise ZODB.POSException.POSKeyError(oid)
+                nexttid = None
+                rtid = record[:8]
+                while rtid >= tid:
+                    krecord = cursor.get(oid, db.PREV_DUP)
+                    if krecord is None:
+                        return None
+                    nexttid = rtid
+                    record = krecord[1]
+                    rtid = record[:8]
+
+                return record[8:], rtid, nexttid
+
+
+
+        """Load the object record for the give transaction id
+
+        If a matching data record can be found, it is returned,
+        otherwise, POSKeyError is raised.
+        """
+
+#     The following two methods are effectively part of the interface,
+#     as they are generally needed when one storage wraps
+#     another. This deserves some thought, at probably debate, before
+#     adding them.
+#
+#     def _lock_acquire():
+#         """Acquire the storage lock
+#         """
+
+#     def _lock_release():
+#         """Release the storage lock
+#         """
+
+    def new_oid():
+        """Allocate a new object id.
+
+        The object id returned is reserved at least as long as the
+        storage is opened.
+
+        The return value is a string.
+        """
+
+    def pack(pack_time, referencesf):
+        """Pack the storage
+
+        It is up to the storage to interpret this call, however, the
+        general idea is that the storage free space by:
+
+        - discarding object revisions that were old and not current as of the
+          given pack time.
+
+        - garbage collecting objects that aren't reachable from the
+          root object via revisions remaining after discarding
+          revisions that were not current as of the pack time.
+
+        The pack time is given as a UTC time in seconds since the
+        epoch.
+
+        The second argument is a function that should be used to
+        extract object references from database records.  This is
+        needed to determine which objects are referenced from object
+        revisions.
+        """
+
+    def registerDB(db):
+        """Register an IStorageDB.
+
+        Note that, for historical reasons, an implementation may
+        require a second argument, however, if required, the None will
+        be passed as the second argument.
+        """
+
+    def sortKey():
+        """Sort key used to order distributed transactions
+
+        When a transaction involved multiple storages, 2-phase commit
+        operations are applied in sort-key order.  This must be unique
+        among storages used in a transaction. Obviously, the storage
+        can't assure this, but it should construct the sort key so it
+        has a reasonable chance of being unique.
+
+        The result must be a string.
+        """
+
+    def store(oid, serial, data, version, transaction):
+        """Store data for the object id, oid.
+
+        Arguments:
+
+        oid
+            The object identifier.  This is either a string
+            consisting of 8 nulls or a string previously returned by
+            new_oid. 
+
+        serial
+            The serial of the data that was read when the object was
+            loaded from the database.  If the object was created in
+            the current transaction this will be a string consisting
+            of 8 nulls.
+
+        data
+            The data record. This is opaque to the storage.
+
+        version
+            This must be an empty string. It exists for backward compatibility.
+
+        transaction
+            A transaction object.  This should match the current
+            transaction for the storage, set by tpc_begin.
+
+        The new serial for the object is returned, but not necessarily
+        immediately.  It may be returned directly, or on a subsequent
+        store or tpc_vote call.
+
+        The return value may be:
+
+        - None
+
+        - A new serial (string) for the object, or
+
+        - An iterable of object-id and serial pairs giving new serials
+          for objects.
+
+        A serial, returned as a string or in a sequence of oid/serial
+        pairs, may be the special value
+        ZODB.ConflictResolution.ResolvedSerial to indicate that a
+        conflict occured and that the object should be invalidated.
+
+        Several different exceptions may be raised when an error occurs.
+
+        ConflictError
+          is raised when serial does not match the most recent serial
+          number for object oid and the conflict was not resolved by
+          the storage.
+
+        StorageTransactionError
+          is raised when transaction does not match the current
+          transaction.
+
+        StorageError or, more often, a subclass of it
+          is raised when an internal error occurs while the storage is
+          handling the store() call.
+        
+        """
+
+    def tpc_abort(transaction):
+        """Abort the transaction.
+
+        Any changes made by the transaction are discarded.
+
+        This call is ignored is the storage is not participating in
+        two-phase commit or if the given transaction is not the same
+        as the transaction the storage is commiting.
+        """
+
+    def tpc_begin(transaction):
+        """Begin the two-phase commit process.
+
+        If storage is already participating in a two-phase commit
+        using the same transaction, the call is ignored.
+
+        If the storage is already participating in a two-phase commit
+        using a different transaction, the call blocks until the
+        current transaction ends (commits or aborts).
+        """
+
+    def tpc_finish(transaction, func = lambda tid: None):
+        """Finish the transaction, making any transaction changes permanent.
+
+        Changes must be made permanent at this point.
+
+        This call is ignored if the storage isn't participating in
+        two-phase commit or if it is committing a different
+        transaction.  Failure of this method is extremely serious.
+
+        The second argument is a call-back function that must be
+        called while the storage transaction lock is held.  It takes
+        the new transaction id generated by the transaction.
+
+        """
+
+    def tpc_vote(transaction):
+        """Provide a storage with an opportunity to veto a transaction
+
+        This call is ignored if the storage isn't participating in
+        two-phase commit or if it is commiting a different
+        transaction.  Failure of this method is extremely serious.
+
+        If a transaction can be committed by a storage, then the
+        method should return.  If a transaction cannot be committed,
+        then an exception should be raised.  If this method returns
+        without an error, then there must not be an error if
+        tpc_finish or tpc_abort is called subsequently.
+
+        The return value can be either None or a sequence of object-id
+        and serial pairs giving new serials for objects who's ids were
+        passed to previous store calls in the same transaction.
+        After the tpc_vote call, new serials must have been returned,
+        either from tpc_vote or store for objects passed to store.
+
+        A serial returned in a sequence of oid/serial pairs, may be
+        the special value ZODB.ConflictResolution.ResolvedSerial to
+        indicate that a conflict occured and that the object should be
+        invalidated.
+
+        """
+
+
+
+class TransactionContext(object):
+
+    def __init__(self, txn):
+        self.txn = txn
+
+    def __enter__(self):
+        return self.txn
+
+    def __exit__(self, t, v, tb):
+        if t is not None:
+            self.txn.abort()
+        else:
+            self.txn.commit()
+
+class CursorContext(object):
+
+    def __init__(self, cursor):
+        self.cursor = cursor
+
+    def __enter__(self):
+        return self.cursor
+
+    def __exit__(self, t, v, tb):
+        self.cursor.close()


Property changes on: zc.bsddbstorage/branches/dev/src/zc/bsddbstorage/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native



More information about the checkins mailing list