[Checkins] SVN: gocept.zeoraid/trunk/src/gocept/zeoraid/ - moved read-only check into decorator

Christian Theune ct at gocept.com
Wed Jan 16 03:31:27 EST 2008


Log message for revision 82912:
  - moved read-only check into decorator
  - added test for loadSerial
  - refactored tests a bit
  

Changed:
  U   gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py
  U   gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py
  U   gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py

-=-
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py	2008-01-16 01:55:53 UTC (rev 82911)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py	2008-01-16 08:31:26 UTC (rev 82912)
@@ -30,6 +30,15 @@
         return method(self, *args, **kw)
     return check_open
 
+
+def ensure_writable(method):
+    def check_writable(self, *args, **kw):
+        if self.isReadOnly():
+            raise ZODB.POSException.ReadOnlyError()
+        return method(self, *args, **kw)
+    return check_writable
+
+
 no_transaction_marker = object()
 
 def choose_transaction(version, transaction):
@@ -141,6 +150,8 @@
         for degraded_storages in tids.values():
             self.storages_degraded.extend(degraded_storages)
 
+        # Degrade storages that don't have the right max OID.
+
         # No storages are recovering initially
         self.storages_recovering = []
 
@@ -201,12 +212,14 @@
 
     def loadSerial(self, oid, serial):
         """Load the object record for the give transaction id."""
-        return self._apply_single_storage('loadSerial', (oid, serial))
+        return self._apply_single_storage(
+            'loadSerial', (oid, serial),
+            allowed_exceptions=ZODB.POSException.POSKeyError)
 
     # XXX
+    @ensure_writable
     def new_oid(self):
-        if self.isReadOnly():
-            raise ZODB.POSException.ReadOnlyError()
+        """Allocate a new object id."""
         self._lock_acquire()
         try:
             return self._apply_all_storages('new_oid')
@@ -214,9 +227,8 @@
             self._lock_release()
 
     # XXX
+    @ensure_writable
     def pack(self, t, referencesf):
-        if self.isReadOnly():
-            raise ZODB.POSException.ReadOnlyError()
         self._apply_all_storages('pack', (t, referencesf))
 
     # XXX
@@ -231,9 +243,8 @@
 
     # XXX
     @store_38_compatible
+    @ensure_writable
     def store(self, oid, oldserial, data, transaction):
-        if self.isReadOnly():
-            raise ZODB.POSException.ReadOnlyError()
         if transaction is not self._transaction:
             raise ZODB.POSException.StorageTransactionError(self, transaction)
 
@@ -270,10 +281,8 @@
             self._lock_release()
 
     # XXX
+    @ensure_writable
     def tpc_begin(self, transaction, tid=None, status=' '):
-        if self.isReadOnly():
-            raise ZODB.POSException.ReadOnlyError()
-
         self._lock_acquire()
         try:
             if self._transaction is transaction:
@@ -352,6 +361,7 @@
     # IBlobStorage
 
     @storeBlob_38_compatible
+    @ensure_writable
     def storeBlob(self, oid, oldserial, data, blob, transaction):
         """Stores data that has a BLOB attached."""
         # XXX
@@ -372,9 +382,8 @@
         return True
 
     # XXX
+    @ensure_writable
     def undo(self, transaction_id, transaction):
-        if self.isReadOnly():
-            raise ZODB.POSException.ReadOnlyError()
         self._lock_acquire()
         try:
             return self._apply_all_storages('undo',

Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py	2008-01-16 01:55:53 UTC (rev 82911)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py	2008-01-16 08:31:26 UTC (rev 82912)
@@ -17,6 +17,15 @@
         return FailingStorage(self.name)
 
 
+def failing_method(name):
+    """Produces a method that can be made to fail."""
+    def fail(self, *args, **kw):
+        if name == self._fail:
+            raise Exception()
+        return getattr(ZODB.FileStorage.FileStorage, name)(self, *args, **kw)
+    return fail
+
+
 class FailingStorage(ZODB.FileStorage.FileStorage):
 
     _fail = None
@@ -33,20 +42,18 @@
     def getExtensionMethods(self):
         return dict(fail=None)
 
-    def history(self, *args, **kw):
-        if 'history' == self._fail:
-            raise Exception()
-        return ZODB.FileStorage.FileStorage.history(self, *args, **kw)
+    history = failing_method('history')
+    loadSerial = failing_method('loadSerial')
 
-    def fail(self, method):
-        if method in ['history']:
+    def fail(self, method_name):
+        if method_name in ['history', 'loadSerial']:
             # Those methods are copied/references by the server code, we can't
             # rebind them here.
-            self._fail = method
+            self._fail = method_name
             return
 
-        old_method = getattr(self, method)
+        old_method = getattr(self, method_name)
         def failing_method(*args, **kw):
-            setattr(self, method, old_method)
+            setattr(self, method_name, old_method)
             raise Exception()
-        setattr(self, method, failing_method)
+        setattr(self, method_name, failing_method)

Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py	2008-01-16 01:55:53 UTC (rev 82911)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py	2008-01-16 08:31:26 UTC (rev 82912)
@@ -128,7 +128,7 @@
                 zconf, port)
             self._servers.append(adminaddr)
             self._storages.append(ZEOOpener(zport, storage='1',
-                                            cache_size=50,
+                                            cache_size=12,
                                             min_disconnect_poll=0.5, wait=1,
                                             wait_timeout=60))
         self._storage = gocept.zeoraid.storage.RAIDStorage('teststorage',
@@ -148,6 +148,9 @@
 
     backend_count = 2
 
+    def _disable_storage(self, index):
+        self._storage.raid_disable(self._storage.storages_optimal[index])
+
     def test_close(self):
         self._storage.close()
         self.assertEquals(self._storage.closed, True)
@@ -215,11 +218,11 @@
         self.assertEquals(1, len(self._backend(1).history(oid, '')))
         self.assertEquals(1, len(self._storage.history(oid, '')))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertEquals(1, len(self._backend(0).history(oid, '')))
         self.assertEquals(1, len(self._storage.history(oid, '')))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
                           self._storage.history, oid, '')
 
@@ -251,9 +254,9 @@
         self.assertEquals(lt, self._backend(1).lastTransaction())
 
     def test_lastTransaction_degrading(self):
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertEquals(ZODB.utils.z64, self._storage.lastTransaction())
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertEquals('failed', self._storage.raid_status())
         self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
                           self._storage.lastTransaction)
@@ -271,14 +274,14 @@
         self.assertEquals(1, len(self._backend(0)))
         self.assertEquals(1, len(self._backend(1)))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self._dostore(
             revid='\x00\x00\x00\x00\x00\x00\x00\x02')
         # See above. This shouldn't be 0 if ClientStorage worked correctly.
         self.assertEquals(2, len(self._storage))
         self.assertEquals(2, len(self._backend(0)))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
                           self._storage.__len__)
 
@@ -299,7 +302,7 @@
         self.assertEquals((data_record, serial), self._backend(0).load(oid))
         self.assertEquals((data_record, serial), self._backend(1).load(oid))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertEquals((data_record, serial), self._storage.load(oid))
         self.assertEquals((data_record, serial), self._backend(0).load(oid))
 
@@ -309,11 +312,24 @@
         self.assertEquals(self._storage.lastTransaction(), serial)
         self.assertEquals((data_record, serial), self._backend(0).load(oid))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
                           self._storage.load, oid)
 
+    def test_load_can_be_failed(self):
+        # ClientStorage does not directly call `load` but
+        # `loadEx` which in turn calls `load` on the storage.
+        # Unfortunately `storage.load` is also rebound onto the storage
+        # server so in the future the fail() might not work. To avoid
+        # hard-to-debug errors in the future, we test that fail('load')
+        # actually does make the `load` call fail.
+        oid = self._storage.new_oid()
+        self._backend(0).fail('load')
+        self.assertRaises(Exception, self._backend(0).load, oid)
+
     def test_load_degrading2(self):
+        # If this test fails weirdly, please check that the test above works
+        # correctly before losing hair.
         oid = self._storage.new_oid()
         self._dostore(oid=oid, revid='\x00\x00\x00\x00\x00\x00\x00\x01')
         self._backend(0).fail('load')
@@ -329,7 +345,6 @@
                           self._storage.load, oid)
         self.assertEquals('failed', self._storage.raid_status())
 
-
     def test_loadBefore_degrading1(self):
         oid = self._storage.new_oid()
         self.assertRaises(
@@ -354,13 +369,13 @@
         self.assertEquals((data_record, serial, end_tid),
                           self._backend(1).loadBefore(oid, revid2))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertEquals((data_record, serial, end_tid),
                           self._storage.loadBefore(oid, revid2))
         self.assertEquals((data_record, serial, end_tid),
                           self._backend(0).loadBefore(oid, revid2))
 
-        self._storage.raid_disable(self._storage.storages_optimal[0])
+        self._disable_storage(0)
         self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
                           self._storage.loadBefore, oid, revid2)
 
@@ -387,7 +402,69 @@
                           self._storage.loadBefore, oid, revid2)
         self.assertEquals('failed', self._storage.raid_status())
 
+    def test_loadSerial_degrading1(self):
+        oid = self._storage.new_oid()
+        self.assertRaises(
+            ZODB.POSException.POSKeyError,
+            self._storage.loadSerial,
+            oid, '\x00\x00\x00\x00\x00\x00\x00\x01')
+        self.assertRaises(
+            ZODB.POSException.POSKeyError,
+            self._backend(0).loadSerial,
+            oid, '\x00\x00\x00\x00\x00\x00\x00\x01')
+        self.assertRaises(
+            ZODB.POSException.POSKeyError,
+            self._backend(1).loadSerial,
+            oid, '\x00\x00\x00\x00\x00\x00\x00\x01')
+        self.assertEquals('optimal', self._storage.raid_status())
 
+        revid = self._dostoreNP(oid=oid, revid=None, data='foo')
+        self._dostoreNP(oid=oid, revid=revid, data='bar')
+
+        data_record = self._storage.loadSerial(oid, revid)
+        self.assertEquals('foo', data_record)
+        self.assertEquals(data_record,
+                          self._backend(0).loadSerial(oid, revid))
+        self.assertEquals(data_record,
+                          self._backend(1).loadSerial(oid, revid))
+
+        self._disable_storage(0)
+        self.assertEquals(data_record,
+                          self._storage.loadSerial(oid, revid))
+        self.assertEquals(data_record,
+                          self._backend(0).loadSerial(oid, revid))
+
+        self._disable_storage(0)
+        self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
+                          self._storage.loadSerial, oid, revid)
+
+
+    def test_loadSerial_degrading2(self):
+        oid = self._storage.new_oid()
+        revid = self._dostoreNP(oid=oid, revid=None, data='foo')
+        self._dostoreNP(oid=oid, revid=revid, data='bar')
+
+        data_record = self._storage.loadSerial(oid, revid)
+        self.assertEquals('foo', data_record)
+        self.assertEquals(data_record,
+                          self._backend(0).loadSerial(oid, revid))
+        self.assertEquals(data_record,
+                          self._backend(1).loadSerial(oid, revid))
+        self.assertEquals('optimal', self._storage.raid_status())
+
+        self._backend(0).fail('loadSerial')
+        self.assertEquals(data_record,
+                          self._storage.loadSerial(oid, revid))
+        self.assertEquals(data_record,
+                          self._backend(0).loadSerial(oid, revid))
+        self.assertEquals('degraded', self._storage.raid_status())
+
+        self._backend(0).fail('loadSerial')
+        self.assertRaises(gocept.zeoraid.interfaces.RAIDError,
+                          self._storage.loadSerial, oid, revid)
+        self.assertEquals('failed', self._storage.raid_status())
+
+
 class ZEOReplicationStorageTests(ZEOStorageBackendTests,
                                  ReplicationStorageTests,
                                  ThreadTests.ThreadTests):



More information about the Checkins mailing list