[CalendarServer-changes] [15572] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue May 3 09:46:29 PDT 2016


Revision: 15572
          http://trac.calendarserver.org//changeset/15572
Author:   cdaboo at apple.com
Date:     2016-05-03 09:46:28 -0700 (Tue, 03 May 2016)
Log Message:
-----------
Add some more exception handlers to turn cross-pod errors into 503's.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/storebridge.py
    CalendarServer/trunk/twistedcaldav/test/test_resource.py

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2016-05-03 13:48:29 UTC (rev 15571)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2016-05-03 16:46:28 UTC (rev 15572)
@@ -58,6 +58,7 @@
 from txdav.caldav.datastore.util import normalizationLookup
 from txdav.common.icommondatastore import InternalDataStoreError, \
     SyncTokenValidException, ExternalShareFailed
+from txdav.common.datastore.podding.base import FailedCrossPodRequestError
 from txdav.xml import element
 from txdav.xml.element import dav_namespace
 
@@ -363,6 +364,12 @@
             # the transaction commit
             self._transactionError = False
             response = StatusResponse(responsecode.SERVICE_UNAVAILABLE, "Shared collection not valid - removing.")
+        except FailedCrossPodRequestError:
+            # This happens when a cross-pod connection attempt fails. Treat as a 503 so the client
+            # can try again once the pod is back up.
+            response = StatusResponse(responsecode.SERVICE_UNAVAILABLE, "Unable to do cross-pod request.")
+            response.headers.setHeader("Retry-After", time.time() + config.TransactionHTTPRetrySeconds)
+
         if transaction is None:
             transaction = self._associatedTransaction
         if transaction is not None:

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2016-05-03 13:48:29 UTC (rev 15571)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2016-05-03 16:46:28 UTC (rev 15572)
@@ -260,7 +260,9 @@
         @param arg: description of error or C{None}
         @type arg: C{str} or C{None}
         """
-        raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE, arg if arg is not None else str(err)))
+        response = StatusResponse(responsecode.SERVICE_UNAVAILABLE, arg if arg is not None else str(err))
+        response.headers.setHeader("Retry-After", time.time() + config.TransactionHTTPRetrySeconds)
+        raise HTTPError(response)
 
 
     @classmethod
@@ -398,7 +400,11 @@
         """
 
         if self._newStoreObject:
-            newStoreObject = yield self._newStoreObject.objectResourceWithName(name)
+            try:
+                newStoreObject = yield self._newStoreObject.objectResourceWithName(name)
+            except Exception as err:
+                self._handleStoreException(err, self.StoreExceptionsErrors)
+                raise
 
             similar = self._childClass(
                 newStoreObject,

Modified: CalendarServer/trunk/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_resource.py	2016-05-03 13:48:29 UTC (rev 15571)
+++ CalendarServer/trunk/twistedcaldav/test/test_resource.py	2016-05-03 16:46:28 UTC (rev 15572)
@@ -28,6 +28,9 @@
 from twistedcaldav.test.util import \
     InMemoryPropertyStore, StoreTestCase, SimpleStoreRequest
 
+from txdav.caldav.datastore.sql import Calendar
+from txdav.common.datastore.podding.base import FailedCrossPodRequestError
+from txdav.common.icommondatastore import ExternalShareFailed
 from txdav.xml import element
 from txdav.xml.element import HRef
 
@@ -90,11 +93,11 @@
 
 
 
-class TransactionTimeoutTests(StoreTestCase):
+class TransactionErrorTests(StoreTestCase):
 
     @inlineCallbacks
     def setUp(self):
-        yield super(TransactionTimeoutTests, self).setUp()
+        yield super(TransactionErrorTests, self).setUp()
 
 
     @inlineCallbacks
@@ -130,7 +133,77 @@
             self.fail("HTTPError not raised")
 
 
+    @inlineCallbacks
+    def test_conduitRetry(self):
+        """
+        Test that a cross-pod error during an HTTP request results in a 503 error
+        with a Retry-After header.
+        """
 
+        # Patch request handling to raise an exception
+        def _iCalendarRolledup(self, request):
+            raise FailedCrossPodRequestError()
+        self.patch(CalendarCollectionResource, "iCalendarRolledup", _iCalendarRolledup)
+
+        self.patch(self.store, "timeoutTransactions", 1)
+
+        # Run delayed request
+        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
+        request = SimpleStoreRequest(self, "GET", "/calendars/__uids__/user01/calendar/", authPrincipal=authPrincipal)
+        response = yield self.send(request)
+        self.assertEqual(response.code, responsecode.SERVICE_UNAVAILABLE)
+        self.assertTrue(response.headers.hasHeader("Retry-After"))
+        self.assertApproximates(int(response.headers.getRawHeaders("Retry-After")[0]), config.TransactionHTTPRetrySeconds, 1)
+
+
+    @inlineCallbacks
+    def test_failedShareRetry(self):
+        """
+        Test that a cross-pod error during an HTTP request results in a 503 error
+        without a Retry-After header.
+        """
+
+        # Patch request handling to raise an exception
+        def _iCalendarRolledup(self, request):
+            raise ExternalShareFailed()
+        self.patch(CalendarCollectionResource, "iCalendarRolledup", _iCalendarRolledup)
+
+        # Run delayed request
+        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
+        request = SimpleStoreRequest(self, "GET", "/calendars/__uids__/user01/calendar/", authPrincipal=authPrincipal)
+        response = yield self.send(request)
+        self.assertEqual(response.code, responsecode.SERVICE_UNAVAILABLE)
+        self.assertFalse(response.headers.hasHeader("Retry-After"))
+
+
+    @inlineCallbacks
+    def test_failedPodChildRetry(self):
+        """
+        Test that a cross-pod error during an HTTP request results in a 503 error
+        with a Retry-After header.
+        """
+
+        # Patch request handling to raise an exception
+        def _objectResourceWithName(self, name):
+            raise FailedCrossPodRequestError()
+        self.patch(Calendar, "objectResourceWithName", _objectResourceWithName)
+
+        self.patch(self.store, "timeoutTransactions", 1)
+
+        # Run delayed request
+        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
+        request = SimpleStoreRequest(self, "GET", "/calendars/__uids__/user01/calendar/1.ics", authPrincipal=authPrincipal)
+        try:
+            yield self.send(request)
+        except HTTPError as e:
+            self.assertEqual(e.response.code, responsecode.SERVICE_UNAVAILABLE)
+            self.assertTrue(e.response.headers.hasHeader("Retry-After"))
+            self.assertApproximates(int(e.response.headers.getRawHeaders("Retry-After")[0]), config.TransactionHTTPRetrySeconds, 1)
+        else:
+            self.fail("HTTPError not raised")
+
+
+
 class CommonHomeResourceTests(TestCase):
 
     def test_commonHomeliveProperties(self):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20160503/00561657/attachment-0001.html>


More information about the calendarserver-changes mailing list