Log message for revision 30822: Allow tempstorage to participate in MVCC. Also fix minor bug where conflict cache could be poisoned by a transaction that failed after store was called.
Changed: U Zope/trunk/lib/python/tempstorage/TemporaryStorage.py U Zope/trunk/lib/python/tempstorage/tests/testTemporaryStorage.py -=- Modified: Zope/trunk/lib/python/tempstorage/TemporaryStorage.py =================================================================== --- Zope/trunk/lib/python/tempstorage/TemporaryStorage.py 2005-06-16 20:41:31 UTC (rev 30821) +++ Zope/trunk/lib/python/tempstorage/TemporaryStorage.py 2005-06-16 21:42:33 UTC (rev 30822) @@ -28,6 +28,7 @@ from ZODB.BaseStorage import BaseStorage from ZODB.ConflictResolution import ConflictResolvingStorage, ResolvedSerial import time +import bisect # keep old object revisions for CONFLICT_CACHE_MAXAGE seconds CONFLICT_CACHE_MAXAGE = 60 @@ -135,6 +136,31 @@ finally: self._lock_release() + def loadBefore(self, oid, tid): + """Return most recent revision of oid before tid committed + (for MVCC) + .""" + # implementation stolen from ZODB.test_storage.MinimalMemoryStorage + self._lock_acquire() + try: + tids = [stid for soid, stid in self._conflict_cache if soid == oid] + if not tids: + raise KeyError, oid + tids.sort() + i = bisect.bisect_left(tids, tid) -1 + if i == -1: + return None + start_tid = tids[i] + j = i + 1 + if j == len(tids): + end_tid = None + else: + end_tid = tids[j] + data = self.loadSerial(oid, start_tid) + return data, start_tid, end_tid + finally: + self._lock_release() + def store(self, oid, serial, data, version, transaction): if transaction is not self._transaction: raise POSException.StorageTransactionError(self, transaction) @@ -163,8 +189,6 @@ oserial = serial newserial=self._tid self._tmp.append((oid, data)) - now = time.time() - self._conflict_cache[(oid, newserial)] = data, now return serial == oserial and newserial or ResolvedSerial finally: self._lock_release() @@ -238,6 +262,8 @@ index[oid] = serial opickle[oid] = data + now = time.time() + self._conflict_cache[(oid, serial)] = data, now if zeros: for oid in zeros.keys(): Modified: Zope/trunk/lib/python/tempstorage/tests/testTemporaryStorage.py =================================================================== --- Zope/trunk/lib/python/tempstorage/tests/testTemporaryStorage.py 2005-06-16 20:41:31 UTC (rev 30821) +++ Zope/trunk/lib/python/tempstorage/tests/testTemporaryStorage.py 2005-06-16 21:42:33 UTC (rev 30822) @@ -7,6 +7,11 @@ Synchronization, ConflictResolution, \ Corruption, RevisionStorage, MTStorage +from persistent import Persistent +import transaction +from ZODB.DB import DB +from ZODB.POSException import ReadConflictError + class TemporaryStorageTests( StorageTestBase.StorageTestBase, ## RevisionStorage.RevisionStorage, # not a revision storage, but passes @@ -49,6 +54,48 @@ TemporaryStorage.CONFLICT_CACHE_GCEVERY = old_gcevery TemporaryStorage.CONFLICT_CACHE_MAXAGE = old_maxage + def doreadconflict(self, db, mvcc): + tm1 = transaction.TransactionManager() + conn = db.open(mvcc=mvcc, transaction_manager=tm1) + r1 = conn.root() + obj = MinPO('root') + r1["p"] = obj + obj = r1["p"] + obj.child1 = MinPO('child1') + tm1.get().commit() + + # start a new transaction with a new connection + tm2 = transaction.TransactionManager() + cn2 = db.open(mvcc=mvcc, transaction_manager=tm2) + r2 = cn2.root() + + self.assertEqual(r1._p_serial, r2._p_serial) + + obj.child2 = MinPO('child2') + tm1.get().commit() + + # resume the transaction using cn2 + obj = r2["p"] + + # An attempt to access obj.child1 should fail with an RCE + # below if conn isn't using mvcc, because r2 was read earlier + # in the transaction and obj was modified by the other + # transaction. + + obj.child1 + return obj + + def checkWithoutMVCCRaisesReadConflict(self): + db = DB(self._storage) + self.assertRaises(ReadConflictError, self.doreadconflict, db, False) + + def checkWithMVCCDoesntRaiseReadConflict(self): + db = DB(self._storage) + ob = self.doreadconflict(db, True) + self.assertEquals(ob.__class__, MinPO) + self.assertEquals(getattr(ob, 'child1', MinPO()).value, 'child1') + self.failIf(getattr(ob, 'child2', None)) + def test_suite(): suite = unittest.makeSuite(TemporaryStorageTests, 'check') suite2 = unittest.makeSuite(Corruption.FileStorageCorruptTests, 'check') _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins