[Zodb-checkins] CVS: ZODB3/BDBStorage - BDBMinimalStorage.py:1.12.6.2

Barry Warsaw barry@wooz.org
Tue, 21 Jan 2003 17:29:36 -0500


Update of /cvs-repository/ZODB3/BDBStorage
In directory cvs.zope.org:/tmp/cvs-serv27095

Modified Files:
      Tag: ZODB3-3_1-branch
	BDBMinimalStorage.py 
Log Message:
Backporting of various changes from the 3.2 branch.  Specifically:

- generalize the table for storage metadata, i.e. packtime -> info

- get ZERO from the package

- close the pack race condition


=== ZODB3/BDBStorage/BDBMinimalStorage.py 1.12.6.1 => 1.12.6.2 ===
--- ZODB3/BDBStorage/BDBMinimalStorage.py:1.12.6.1	Tue Jan  7 14:38:52 2003
+++ ZODB3/BDBStorage/BDBMinimalStorage.py	Tue Jan 21 17:29:34 2003
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2001 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -13,22 +13,21 @@
 ##############################################################################
 
 """Berkeley storage without undo or versioning.
-"""
 
-__version__ = '$Revision$'[-2:][0]
+$Revision$
+"""
 
 from ZODB import POSException
 from ZODB.utils import p64, U64
 from ZODB.referencesf import referencesf
 from ZODB.ConflictResolution import ConflictResolvingStorage, ResolvedSerial
 
-from BDBStorage import db
+from BDBStorage import db, ZERO
 from BerkeleyBase import BerkeleyBase, PackStop, _WorkThread
 
 ABORT = 'A'
 COMMIT = 'C'
 PRESENT = 'X'
-ZERO = '\0'*8
 
 try:
     True, False
@@ -78,6 +77,13 @@
         #     no pending entry.  It is a database invariant that if the
         #     pending table is empty, the oids table must also be empty.
         #
+        # info -- {key -> value}
+        #     This table contains storage metadata information.  The keys and
+        #     values are simple strings of variable length.   Here are the
+        #     valid keys:
+        #
+        #         version - the version of the database (reserved for ZODB4)
+        #
         # packmark -- [oid]
         #     Every object reachable from the root during a classic pack
         #     operation will have its oid present in this table.
@@ -89,6 +95,8 @@
         #     references exist, such that the objects can be completely packed
         #     away.
         #
+        self._packing = False
+        self._info = self._setupDB('info')
         self._serials = self._setupDB('serials', db.DB_DUP)
         self._pickles = self._setupDB('pickles')
         self._refcounts = self._setupDB('refcounts')
@@ -170,6 +178,8 @@
                     if soid <> oid:
                         break
                     if stid <> tid:
+                        # This is the previous revision of the object, so
+                        # decref its referents and clean up its pickles.
                         cs.delete()
                         data = self._pickles.get(oid+stid, txn=txn)
                         assert data is not None
@@ -187,8 +197,16 @@
             if co: co.close()
             if cs: cs.close()
         # We're done with this table
-        self._oids.truncate(txn)
         self._pending.truncate(txn)
+        # If we're in the middle of a pack, we need to add to the packmark
+        # table any objects that were modified in this transaction.
+        # Otherwise, there's a race condition where mark might have happened,
+        # then the object is added, then sweep runs, deleting the object
+        # created in the interrim.
+        if self._packing:
+            for oid in self._oids.keys():
+                self._packmark.put(oid, PRESENT, txn=txn)
+        self._oids.truncate(txn)
         # Now, to finish up, we need apply the refcount deltas to the
         # refcounts table, and do recursive collection of all refcount == 0
         # objects.
@@ -350,6 +368,7 @@
         # A simple wrapper around the bulk of packing, but which acquires a
         # lock that prevents multiple packs from running at the same time.
         self._packlock.acquire()
+        self._packing = True
         try:
             # We don't wrap this in _withtxn() because we're going to do the
             # operation across several Berkeley transactions, which allows
@@ -360,6 +379,7 @@
             # collect object revisions
             self._dopack()
         finally:
+            self._packing = False
             self._packlock.release()
         self.log('classic pack finished')
 
@@ -394,7 +414,6 @@
         # we'll save the mark data in the packmark table.  The oidqueue is a
         # BerkeleyDB Queue that holds the list of object ids to look at next,
         # and by using this we don't need to keep an in-memory dictionary.
-        assert len(self._packmark) == 0
         assert len(self._oidqueue) == 0
         # Quick exit for empty storages
         if not self._serials: