[Zodb-checkins] SVN: ZODB/branches/3.3/ Port from ZODB 3.2.

Tim Peters tim.one at comcast.net
Mon Feb 28 16:42:51 EST 2005


Log message for revision 29350:
  Port from ZODB 3.2.
  
  Change FileStorage .restore() and .store() to update max oid in use.
  
  This is the last of the checkins to fix critical bugs involving rare cases
  where a FileStorage could end up reusing old oids for new objects.
  

Changed:
  U   ZODB/branches/3.3/NEWS.txt
  U   ZODB/branches/3.3/src/ZODB/BaseStorage.py
  U   ZODB/branches/3.3/src/ZODB/FileStorage/FileStorage.py
  U   ZODB/branches/3.3/src/ZODB/tests/testFileStorage.py

-=-
Modified: ZODB/branches/3.3/NEWS.txt
===================================================================
--- ZODB/branches/3.3/NEWS.txt	2005-02-28 19:58:58 UTC (rev 29349)
+++ ZODB/branches/3.3/NEWS.txt	2005-02-28 21:42:51 UTC (rev 29350)
@@ -32,6 +32,18 @@
 FileStorage
 -----------
 
+- The ``.store()`` and ``.restore()`` methods didn't update the storage's
+  belief about the largest oid in use when passed an oid larger than the
+  largest oid the storage already knew about.  Because ``.restore()`` in
+  particular is used  by ``copyTransactionsFrom()``, and by the first stage
+  of ZRS recovery, a large database could be created that believed the only
+  oid in use was oid 0 (the special oid reserved for the root object).  In
+  rare cases, it could go on from there assigning duplicate oids to new
+  objects, starting over from oid 1 again.  This has been repaired.  A
+  new ``set_max_oid()`` method was added to the ``BaseStorage`` class so
+  that derived storages can update the largest oid in use in a threadsafe
+  way.
+
 - A FileStorage's index file tried to maintain the index's largest oid as a
   separate piece of data, incrementally updated over the storage's lifetime.
   This scheme was more complicated than necessary, so was also more brittle

Modified: ZODB/branches/3.3/src/ZODB/BaseStorage.py
===================================================================
--- ZODB/branches/3.3/src/ZODB/BaseStorage.py	2005-02-28 19:58:58 UTC (rev 29349)
+++ ZODB/branches/3.3/src/ZODB/BaseStorage.py	2005-02-28 21:42:51 UTC (rev 29350)
@@ -157,6 +157,17 @@
             if d < 255: return last[:-1]+chr(d+1)+'\0'*(8-len(last))
             else:       return self.new_oid(last[:-1])
 
+    # Update the maximum oid in use, under protection of a lock.  The
+    # maximum-in-use attribute is changed only if possible_new_max_oid is
+    # larger than its current value.
+    def set_max_oid(self, possible_new_max_oid):
+        self._lock_acquire()
+        try:
+            if possible_new_max_oid > self._oid:
+                self._oid = possible_new_max_oid
+        finally:
+            self._lock_release()
+
     def registerDB(self, db, limit):
         pass # we don't care
 
@@ -371,10 +382,7 @@
         # using store().  However, if we use store, then
         # copyTransactionsFrom() may fail with VersionLockError or
         # ConflictError.
-        if hasattr(self, 'restore'):
-            restoring = 1
-        else:
-            restoring = 0
+        restoring = hasattr(self, 'restore')
         fiter = other.iterator()
         for transaction in fiter:
             tid=transaction.tid

Modified: ZODB/branches/3.3/src/ZODB/FileStorage/FileStorage.py
===================================================================
--- ZODB/branches/3.3/src/ZODB/FileStorage/FileStorage.py	2005-02-28 19:58:58 UTC (rev 29349)
+++ ZODB/branches/3.3/src/ZODB/FileStorage/FileStorage.py	2005-02-28 21:42:51 UTC (rev 29350)
@@ -632,6 +632,8 @@
 
         self._lock_acquire()
         try:
+            if oid > self._oid:
+                self.set_max_oid(oid)
             old = self._index_get(oid, 0)
             cached_tid = None
             pnv = None
@@ -758,6 +760,8 @@
 
         self._lock_acquire()
         try:
+            if oid > self._oid:
+                self.set_max_oid(oid)
             prev_pos = 0
             if prev_txn is not None:
                 prev_txn_pos = self._txn_find(prev_txn, 0)

Modified: ZODB/branches/3.3/src/ZODB/tests/testFileStorage.py
===================================================================
--- ZODB/branches/3.3/src/ZODB/tests/testFileStorage.py	2005-02-28 19:58:58 UTC (rev 29349)
+++ ZODB/branches/3.3/src/ZODB/tests/testFileStorage.py	2005-02-28 21:42:51 UTC (rev 29350)
@@ -205,6 +205,39 @@
 
         self.failUnless(self._storage._records_before_save > 20)
 
+    def checkStoreBumpsOid(self):
+        # If .store() is handed an oid bigger than the storage knows
+        # about already, it's crucial that the storage bump its notion
+        # of the largest oid in use.
+        t = transaction.Transaction()
+        self._storage.tpc_begin(t)
+        giant_oid = '\xee' * 8
+        # Store an object.
+        # oid, serial, data, version, transaction
+        r1 = self._storage.store(giant_oid, '\0'*8, 'data', '', t)
+        # Finish the transaction.
+        r2 = self._storage.tpc_vote(t)
+        self._storage.tpc_finish(t)
+        # Before ZODB 3.2.6, this failed, with ._oid == z64.
+        self.assertEqual(self._storage._oid, giant_oid)
+
+    def checkRestoreBumpsOid(self):
+        # As above, if .restore() is handed an oid bigger than the storage
+        # knows about already, it's crucial that the storage bump its notion
+        # of the largest oid in use.  Because copyTransactionsFrom(), and
+        # ZRS recovery, use the .restore() method, this is plain critical.
+        t = transaction.Transaction()
+        self._storage.tpc_begin(t)
+        giant_oid = '\xee' * 8
+        # Store an object.
+        # oid, serial, data, version, prev_txn, transaction
+        r1 = self._storage.restore(giant_oid, '\0'*8, 'data', '', None, t)
+        # Finish the transaction.
+        r2 = self._storage.tpc_vote(t)
+        self._storage.tpc_finish(t)
+        # Before ZODB 3.2.6, this failed, with ._oid == z64.
+        self.assertEqual(self._storage._oid, giant_oid)
+
     def checkCorruptionInPack(self):
         # This sets up a corrupt .fs file, with a redundant transaction
         # length mismatch.  The implementation of pack in many releases of



More information about the Zodb-checkins mailing list