def make_requests(self, req, ring, part, method, path, headers, query_string=''): """ Sends an HTTP request to multiple nodes and aggregates the results. It attempts the primary nodes concurrently, then iterates over the handoff nodes as needed. :param req: a request sent by the client :param ring: the ring used for finding backend servers :param part: the partition number :param method: the method to send to the backend :param path: the path to send to the backend (full path ends up being /<$device>/<$part>/<$path>) :param headers: a list of dicts, where each dict represents one backend request that should be made. :param query_string: optional query string to send to the backend :returns: a swob.Response object """ start_nodes = ring.get_part_nodes(part) nodes = GreenthreadSafeIterator(self.app.iter_nodes(ring, part)) pile = GreenAsyncPile(len(start_nodes)) for head in headers: pile.spawn(self._make_request, nodes, part, method, path, head, query_string, self.app.logger.thread_locals) response = [] statuses = [] for resp in pile: if not resp: continue response.append(resp) statuses.append(resp[0]) if self.have_quorum(statuses, len(start_nodes)): break # give any pending requests *some* chance to finish pile.waitall(self.app.post_quorum_timeout) while len(response) < len(start_nodes): response.append((HTTP_SERVICE_UNAVAILABLE, '', '', '')) statuses, reasons, resp_headers, bodies = zip(*response) return self.best_response(req, statuses, reasons, bodies, '%s %s' % (self.server_type, req.method), headers=resp_headers)
def _get_put_responses(self, req, conns, nodes): statuses = [] reasons = [] bodies = [] etags = set() def get_conn_response(conn): try: with Timeout(self.app.node_timeout): if conn.resp: return (conn, conn.resp) else: return (conn, conn.getresponse()) except (Exception, Timeout): self.app.exception_occurred( conn.node, _('Object'), _('Trying to get final status of PUT to %s') % req.path) return (None, None) pile = GreenAsyncPile(len(conns)) for conn in conns: pile.spawn(get_conn_response, conn) def _handle_response(conn, response): statuses.append(response.status) reasons.append(response.reason) bodies.append(response.read()) if response.status == HTTP_INSUFFICIENT_STORAGE: self.app.error_limit(conn.node, _('ERROR Insufficient Storage')) elif response.status >= HTTP_INTERNAL_SERVER_ERROR: self.app.error_occurred( conn.node, _('ERROR %(status)d %(body)s From Object Server ' 're: %(path)s') % { 'status': response.status, 'body': bodies[-1][:1024], 'path': req.path }) elif is_success(response.status): etags.add(response.getheader('etag').strip('"')) for (conn, response) in pile: if response: _handle_response(conn, response) if self.have_quorum(statuses, len(nodes)): break # give any pending requests *some* chance to finish finished_quickly = pile.waitall(self.app.post_quorum_timeout) for (conn, response) in finished_quickly: if response: _handle_response(conn, response) while len(statuses) < len(nodes): statuses.append(HTTP_SERVICE_UNAVAILABLE) reasons.append('') bodies.append('') return statuses, reasons, bodies, etags
def _get_put_responses(self, req, conns, nodes): statuses = [] reasons = [] bodies = [] etags = set() def get_conn_response(conn): try: with Timeout(self.app.node_timeout): if conn.resp: return (conn, conn.resp) else: return (conn, conn.getresponse()) except (Exception, Timeout): self.app.exception_occurred( conn.node, _('Object'), _('Trying to get final status of PUT to %s') % req.path) return (None, None) pile = GreenAsyncPile(len(conns)) for conn in conns: pile.spawn(get_conn_response, conn) for (conn, response) in pile: if response: statuses.append(response.status) reasons.append(response.reason) bodies.append(response.read()) if response.status >= HTTP_INTERNAL_SERVER_ERROR: self.app.error_occurred( conn.node, _('ERROR %(status)d %(body)s From Object Server ' 're: %(path)s') % {'status': response.status, 'body': bodies[-1][:1024], 'path': req.path}) elif is_success(response.status): etags.add(response.getheader('etag').strip('"')) if self.have_quorum(statuses, len(nodes)): break # give any pending requests *some* chance to finish pile.waitall(self.app.post_quorum_timeout) while len(statuses) < len(nodes): statuses.append(HTTP_SERVICE_UNAVAILABLE) reasons.append('') bodies.append('') return statuses, reasons, bodies, etags
def _get_put_responses(self, req, conns, nodes): statuses = [] reasons = [] bodies = [] etags = set() pile = GreenAsyncPile(len(conns)) for conn in conns: pile.spawn(self._get_conn_response, conn, req) def _handle_response(conn, response): statuses.append(response.status) reasons.append(response.reason) bodies.append(response.read()) if response.status == HTTP_INSUFFICIENT_STORAGE: self.app.error_limit(conn.node, _('ERROR Insufficient Storage')) elif response.status >= HTTP_INTERNAL_SERVER_ERROR: self.app.error_occurred( conn.node, _('ERROR %(status)d %(body)s From Object Server ' 're: %(path)s') % {'status': response.status, 'body': bodies[-1][:1024], 'path': req.path}) elif is_success(response.status): etags.add(response.getheader('etag').strip('"')) for (conn, response) in pile: if response: _handle_response(conn, response) if self.have_quorum(statuses, len(nodes)): break # give any pending requests *some* chance to finish finished_quickly = pile.waitall(self.app.post_quorum_timeout) for (conn, response) in finished_quickly: if response: _handle_response(conn, response) while len(statuses) < len(nodes): statuses.append(HTTP_SERVICE_UNAVAILABLE) reasons.append('') bodies.append('') return statuses, reasons, bodies, etags