diff --git a/swift/proxy/controllers/base.py b/swift/proxy/controllers/base.py index e9ee894125..17c343c943 100644 --- a/swift/proxy/controllers/base.py +++ b/swift/proxy/controllers/base.py @@ -1294,10 +1294,11 @@ class ResumingGetter(object): return True else: if 'handoff_index' in node and \ - possible_source.status == HTTP_NOT_FOUND and \ + (is_server_error(possible_source.status) or + possible_source.status == HTTP_NOT_FOUND) and \ not Timestamp(src_headers.get('x-backend-timestamp', 0)): - # throw out 404s from handoff nodes unless the data is really - # on disk and had been DELETEd + # throw out 5XX and 404s from handoff nodes unless the data is + # really on disk and had been DELETEd return False self.statuses.append(possible_source.status) self.reasons.append(possible_source.reason) diff --git a/test/unit/__init__.py b/test/unit/__init__.py index e3c8fb4ccf..c81d181fea 100644 --- a/test/unit/__init__.py +++ b/test/unit/__init__.py @@ -1182,6 +1182,14 @@ class StubResponse(object): def read(self, amt=0): return self.readable.read(amt) + def __repr__(self): + info = ['Status: %s' % self.status] + if self.headers: + info.append('Headers: %r' % dict(self.headers)) + if self.body: + info.append('Body: %r' % self.body) + return '' % ', '.join(info) + def encode_frag_archive_bodies(policy, body): """ diff --git a/test/unit/proxy/controllers/test_obj.py b/test/unit/proxy/controllers/test_obj.py index 19a7050538..b7ab7d335a 100644 --- a/test/unit/proxy/controllers/test_obj.py +++ b/test/unit/proxy/controllers/test_obj.py @@ -784,6 +784,23 @@ class CommonObjectControllerMixin(BaseObjectControllerMixin): self.assertEqual(req['method'], 'HEAD') self.assertEqual(req['path'], '/a/c/o') + def test_some_404s_and_507s(self): + self.policy.object_ring.max_more_nodes = (3 * self.replicas()) + req = swob.Request.blank('/v1/a/c/o', method='HEAD') + responses = [StubResponse( + 404, headers={'X-Backend-Timestamp': '2'})] * self.replicas() + responses += [StubResponse(507, headers={})] * ( + self.policy.object_ring.max_more_nodes - self.replicas()) + self.assertEqual(len(responses), 3 * self.replicas()) # sanity + + def get_response(req): + return responses.pop(0) + + with capture_http_requests(get_response): + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 404) + self.assertEqual(resp.headers['X-Backend-Timestamp'], '2') + def test_container_sync_delete(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) test_indexes = [None] + [int(p) for p in POLICIES]