Skip to content

Commit aa04c3e

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Ignore 404s from handoffs when choosing response code"
2 parents ec8166b + d0f6a0c commit aa04c3e

File tree

2 files changed

+31
-19
lines changed

2 files changed

+31
-19
lines changed

swift/proxy/controllers/base.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,18 @@ def is_good_source(status, server_type):
11071107
return is_success(status) or is_redirection(status)
11081108

11091109

1110+
def is_useful_response(resp, node):
1111+
if not resp:
1112+
return False
1113+
if ('handoff_index' in node
1114+
and resp.status == 404
1115+
and resp.headers.get('x-backend-timestamp') is None):
1116+
# a 404 from a handoff are not considered authoritative unless they
1117+
# have an x-backend-timestamp that indicates that there is a tombstone
1118+
return False
1119+
return True
1120+
1121+
11101122
class ByteCountEnforcer(object):
11111123
"""
11121124
Enforces that successive calls to file_like.read() give at least
@@ -1956,7 +1968,7 @@ def transfer_headers(self, src_headers, dst_headers):
19561968
def generate_request_headers(self, orig_req=None, additional=None,
19571969
transfer=False):
19581970
"""
1959-
Create a list of headers to be used in backend requests
1971+
Create a dict of headers to be used in backend requests
19601972
19611973
:param orig_req: the original request sent by the client to the proxy
19621974
:param additional: additional headers to send to the backend
@@ -2088,14 +2100,14 @@ def _make_request(self, nodes, part, method, path, headers, query,
20882100
if (self.app.check_response(node, self.server_type, resp,
20892101
method, path)
20902102
and not is_informational(resp.status)):
2091-
return resp.status, resp.reason, resp.getheaders(), \
2092-
resp.read()
2103+
return resp, resp.read(), node
20932104

20942105
except (Exception, Timeout):
20952106
self.app.exception_occurred(
20962107
node, self.server_type,
20972108
'Trying to %(method)s %(path)s' %
20982109
{'method': method, 'path': path})
2110+
return None, None, None
20992111

21002112
def make_requests(self, req, ring, part, method, path, headers,
21012113
query_string='', overrides=None, node_count=None,
@@ -2118,6 +2130,8 @@ def make_requests(self, req, ring, part, method, path, headers,
21182130
the returned status of a request.
21192131
:param node_count: optional number of nodes to send request to.
21202132
:param node_iterator: optional node iterator.
2133+
:param body: byte string to use as the request body.
2134+
Try to keep it small.
21212135
:returns: a swob.Response object
21222136
"""
21232137
nodes = GreenthreadSafeIterator(node_iterator or NodeIter(
@@ -2128,25 +2142,25 @@ def make_requests(self, req, ring, part, method, path, headers,
21282142
for head in headers:
21292143
pile.spawn(self._make_request, nodes, part, method, path,
21302144
head, query_string, body, self.logger.thread_locals)
2131-
response = []
2145+
results = []
21322146
statuses = []
2133-
for resp in pile:
2134-
if not resp:
2147+
for resp, body, node in pile:
2148+
if not is_useful_response(resp, node):
21352149
continue
2136-
response.append(resp)
2137-
statuses.append(resp[0])
2150+
results.append((resp.status, resp.reason, resp.getheaders(), body))
2151+
statuses.append(resp.status)
21382152
if self.have_quorum(statuses, node_number):
21392153
break
21402154
# give any pending requests *some* chance to finish
21412155
finished_quickly = pile.waitall(self.app.post_quorum_timeout)
2142-
for resp in finished_quickly:
2143-
if not resp:
2156+
for resp, body, node in finished_quickly:
2157+
if not is_useful_response(resp, node):
21442158
continue
2145-
response.append(resp)
2146-
statuses.append(resp[0])
2147-
while len(response) < node_number:
2148-
response.append((HTTP_SERVICE_UNAVAILABLE, '', '', b''))
2149-
statuses, reasons, resp_headers, bodies = zip(*response)
2159+
results.append((resp.status, resp.reason, resp.getheaders(), body))
2160+
statuses.append(resp.status)
2161+
while len(results) < node_number:
2162+
results.append((HTTP_SERVICE_UNAVAILABLE, '', '', b''))
2163+
statuses, reasons, resp_headers, bodies = zip(*results)
21502164
return self.best_response(req, statuses, reasons, bodies,
21512165
'%s %s' % (self.server_type, req.method),
21522166
overrides=overrides, headers=resp_headers)

test/unit/proxy/controllers/test_obj.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,8 +1464,7 @@ def test_POST_insufficient_primaries_others_fail_handoffs_404(self):
14641464
handoff_codes = [404] * primary_failure
14651465
with mocked_http_conn(*(primary_codes + handoff_codes)):
14661466
resp = req.get_response(self.app)
1467-
# TODO: this should really be a 503
1468-
self.assertEqual(404, resp.status_int,
1467+
self.assertEqual(503, resp.status_int,
14691468
'replicas = %s' % self.replicas())
14701469

14711470
def test_POST_insufficient_primaries_others_fail_handoffs_fail(self):
@@ -1487,8 +1486,7 @@ def test_POST_all_primaries_fail_insufficient_handoff_succeeds(self):
14871486
handoff_codes = [202] * handoff_success + [404] * handoff_not_found
14881487
with mocked_http_conn(*(primary_codes + handoff_codes)):
14891488
resp = req.get_response(self.app)
1490-
# TODO: this should really be a 503
1491-
self.assertEqual(404, resp.status_int,
1489+
self.assertEqual(503, resp.status_int,
14921490
'replicas = %s' % self.replicas())
14931491

14941492
def test_POST_all_primaries_fail_sufficient_handoff_succeeds(self):

0 commit comments

Comments
 (0)