diff --git a/swift/proxy/controllers/obj.py b/swift/proxy/controllers/obj.py index 67b9a8b760..387f9c232a 100644 --- a/swift/proxy/controllers/obj.py +++ b/swift/proxy/controllers/obj.py @@ -2125,7 +2125,8 @@ class ECGetResponseCollection(object): """ self.policy = policy self.buckets = {} - self.bad_buckets = {None: ECGetResponseBucket(self.policy, None)} + self.default_bad_bucket = ECGetResponseBucket(self.policy, None) + self.bad_buckets = {} self.node_iter_count = 0 def _get_bucket(self, timestamp): @@ -2254,6 +2255,9 @@ class ECGetResponseCollection(object): """ Return the bad_bucket with the smallest shortfall """ + if all(status == 404 for status in self.bad_buckets): + # NB: also covers an empty self.bad_buckets + return self.default_bad_bucket # we want "enough" 416s to prevent "extra" requests - but we keep # digging on 404s short, status = min((bucket.shortfall, status) diff --git a/test/unit/proxy/controllers/test_obj.py b/test/unit/proxy/controllers/test_obj.py index 6a64345457..07832c96fd 100644 --- a/test/unit/proxy/controllers/test_obj.py +++ b/test/unit/proxy/controllers/test_obj.py @@ -4003,6 +4003,33 @@ class TestECObjController(ECObjectControllerMixin, unittest.TestCase): self.assertEqual(resp.status_int, 416) self.assertEqual(len(log), 2 * self.replicas()) + @patch_policies( + [ECStoragePolicy(0, name='ec', is_default=True, + ec_type=DEFAULT_TEST_EC_TYPE, ec_ndata=4, + ec_nparity=4, ec_segment_size=4096)], + fake_ring_args=[{'replicas': 8}] + ) + def test_GET_ndata_equals_nparity_with_missing_and_errors(self): + # when ec_ndata == ec_nparity it is possible for the shortfall of a bad + # bucket (412's) to equal ec_ndata; verify that the 412 bucket is still + # chosen ahead of the initial 'dummy' bad bucket + POLICIES.default.object_ring.max_more_nodes = 8 + responses = [ + StubResponse(412, frag_index=0), + StubResponse(412, frag_index=1), + ] + + def get_response(req): + return responses.pop(0) if responses else StubResponse(404) + + req = swob.Request.blank('/v1/a/c/o', headers={ + 'Range': 'bytes=%s-' % 100000000000000}) + with capture_http_requests(get_response) as log: + resp = req.get_response(self.app) + + self.assertEqual(resp.status_int, 412) + self.assertEqual(len(log), 2 * 8) + def test_GET_with_success_and_507_will_503(self): responses = [ # only 9 good nodes StubResponse(200),