Some improvements and bug fixes.
authorItamar Turner-Trauring <itamar@blake.(none)>
Fri, 19 Apr 2013 18:12:11 +0000 (14:12 -0400)
committerDaira Hopwood <daira@jacaranda.org>
Wed, 9 Apr 2014 00:33:56 +0000 (01:33 +0100)
1. Discard body even if response code indicates a problem, when doing cloud backend HTTP requests. I believe this was triggering a bug in Twistd.
2. Google backend retries on 403 and other 4xx codes, not just 401.
3. More logging.

src/allmydata/storage/backends/cloud/cloud_common.py
src/allmydata/storage/backends/cloud/googlestorage/googlestorage_container.py
src/allmydata/test/test_storage.py

index 72ac66e6f594f144601c525ced8feff10cdffe07..a62d94f3525b9bc5e2b3b55cf19b3ece72a93581 100644 (file)
@@ -401,12 +401,14 @@ class ContainerRetryMixin:
                 retry = False
 
         if retry:
+            log.msg("Rescheduling failed task for retry in %d seconds." % (BACKOFF_SECONDS_BEFORE_RETRY[trynum-1],))
             d = task.deferLater(self._reactor, BACKOFF_SECONDS_BEFORE_RETRY[trynum-1], operation, *args, **kwargs)
             d.addErrback(self._handle_error, trynum+1, first_err_and_tb, description, operation, *args, **kwargs)
             return d
 
         # If we get an error response for which _react_to_error says we should not retry,
         # raise that error even if the request was itself a retry.
+        log.msg("Giving up, no retry for %s" % (err,))
         raise err.__class__, err, tb
 
 
@@ -673,6 +675,7 @@ class HTTPClientMixin:
                     what=what, code=response.code, phrase=response.phrase, level=log.OPERATIONAL)
 
             if response.code < 200 or response.code >= 300:
+                response.deliverBody(Discard())
                 raise self.ServiceError(None, response.code,
                                         message="unexpected response code %r %s" % (response.code, response.phrase))
 
index 369c9114fd4965daf2954b37e3cdc54aa65930d1..47c2ff241618a11d1ef80f7aa59cfeab9dbd3ad8 100644 (file)
@@ -29,6 +29,7 @@ except ImportError:
 
 from zope.interface import implements
 
+from allmydata.util import log
 from allmydata.storage.backends.cloud.cloud_common import IContainer, \
      ContainerItem, ContainerListing, CommonContainerMixin
 
@@ -70,7 +71,13 @@ class AuthenticationClient(object):
                 # Generally using a task-specific thread pool is better than using
                 # the reactor one. However, this particular call will only run
                 # once an hour, so it's not likely to tie up all the threads.
-                return self._deferToThread(self._credentials.refresh, httplib2.Http())
+                log.msg("Reauthenticating against Google Cloud Storage.")
+                def finished(result):
+                    log.msg("Done reauthenticating against Google Cloud Storage.")
+                    return result
+                d = self._deferToThread(self._credentials.refresh, httplib2.Http())
+                d.addBoth(finished)
+                return d
         return self._lock.run(run)
 
     def get_authorization_header(self):
@@ -108,7 +115,9 @@ class GoogleStorageContainer(CommonContainerMixin):
         self._project_id = project_id # Only need for bucket creation/deletion
 
     def _react_to_error(self, response_code):
-        if response_code == UNAUTHORIZED:
+        if response_code >= 400 and response_code < 500:
+            # Unauthorized/forbidden/etc. we should retry, eventually we will
+            # reauthenticate:
             return True
         else:
             return CommonContainerMixin._react_to_error(self, response_code)
index a1b6b2392ebdf8aa348fff385360facfbd466072..ae686272f9a3042e5665a398a9bcab806fd2eb57 100644 (file)
@@ -1118,6 +1118,16 @@ class GoogleStorageBackend(unittest.TestCase, CloudStorageBackendMixin):
         third.callback(done)
         self.assertEqual(result, [done])
 
+    def test_react_to_error(self):
+        """
+        GoogleStorageContainer._react_to_error() will return True (i.e. retry)
+        for any response code between 400 and 599.
+        """
+        self.assertFalse(self.container._react_to_error(399))
+        self.assertFalse(self.container._react_to_error(600))
+        for i in range(400, 600):
+            self.assertTrue(self.container._react_to_error(i))
+
     def test_head_object(self):
         """
         GoogleStorageContainer.head_object() sends the appropriate HTTP