)]}'
{"swift/proxy/controllers/base.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"005e9426d634ab01aeb4b36220f10cb577da5268","unresolved":false,"context_lines":[{"line_number":942,"context_line":"                req.environ[\u0027swift.non_client_disconnect\u0027] \u003d True"},{"line_number":943,"context_line":"            finally:"},{"line_number":944,"context_line":"                if part_iter:"},{"line_number":945,"context_line":"                    part_iter.close()"},{"line_number":946,"context_line":""},{"line_number":947,"context_line":"        except ChunkReadTimeout:"},{"line_number":948,"context_line":"            self.app.exception_occurred(node[0], _(\u0027Object\u0027),"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a061dce_2dc73765","line":945,"updated":"2016-04-06 14:45:24.000000000","message":"ok. this causes GeneratorExit to fire in iter_bytes_from_response_part(). We don\u0027t catch it here (line 956) but it does have the effect of exiting that generator\u0027s yield before the ChunkWriteTimeout pops, which eradicates the spurious Exception fetching fragments for \\\u0027/a/ec-discon/test\\\u0027: ChunkWriteTimeout messages.","commit_id":"7402d7d9cf91ed00a9c86fc1511592ac2eed5d6c"}],"swift/proxy/controllers/obj.py":[{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"54791c4f6110ca21fe82931fe82a6f8e912eb583","unresolved":false,"context_lines":[{"line_number":1251,"context_line":"                    for chunk in self.stashed_iter:"},{"line_number":1252,"context_line":"                        yield chunk"},{"line_number":1253,"context_line":"                except GeneratorExit:"},{"line_number":1254,"context_line":"                    self.logger.warning(_(\u0027Client disconnected on read\u0027))"},{"line_number":1255,"context_line":"            return app_iter()"},{"line_number":1256,"context_line":"        else:"},{"line_number":1257,"context_line":"            raise ValueError(\"Failed to call kickoff() before __iter__()\")"}],"source_content_type":"text/x-python","patch_set":1,"id":"da0c15f0_d8ddf743","line":1254,"updated":"2016-03-25 18:24:21.000000000","message":"Why stop the GeneratorExit here? Why not let it propagate upward to any enclosing generators?","commit_id":"c3e50acf1398d9d6aa8b07fabec5691441fa9fc4"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f9948c0b8e159965e2ef133506c9afc107c5e190","unresolved":false,"context_lines":[{"line_number":1251,"context_line":"                    for chunk in self.stashed_iter:"},{"line_number":1252,"context_line":"                        yield chunk"},{"line_number":1253,"context_line":"                except GeneratorExit:"},{"line_number":1254,"context_line":"                    self.logger.warning(_(\u0027Client disconnected on read\u0027))"},{"line_number":1255,"context_line":"            return app_iter()"},{"line_number":1256,"context_line":"        else:"},{"line_number":1257,"context_line":"            raise ValueError(\"Failed to call kickoff() before __iter__()\")"}],"source_content_type":"text/x-python","patch_set":1,"id":"da0c15f0_cc6d3806","line":1254,"in_reply_to":"da0c15f0_d8ddf743","updated":"2016-03-25 19:08:32.000000000","message":"I\u0027m pretty sure it *does* propagate? [1]  we could probably re-raise to be sure?\n\nThe reason it\u0027s useful to define the app_iter that we give out to eventlet.wsgi is so that we can explicitly handle the condition when it calls close before we raise stopiteration - which is the explict signal for a client disconnect.\n\nI\u0027m happy to default to your judgement if you think you have a better way.\n\n1. https://gist.github.com/clayg/121c561bf14d3930d61a","commit_id":"c3e50acf1398d9d6aa8b07fabec5691441fa9fc4"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"54791c4f6110ca21fe82931fe82a6f8e912eb583","unresolved":false,"context_lines":[{"line_number":1950,"context_line":"        # these are backend connections, not part of the outer iter returned"},{"line_number":1951,"context_line":"        # to eventlet wsgi - if they are ever closed - it\u0027s because we closed"},{"line_number":1952,"context_line":"        # them in ECAppIter"},{"line_number":1953,"context_line":"        req.environ[\u0027swift.non_client_disconnect\u0027] \u003d True"},{"line_number":1954,"context_line":"        getter \u003d ResumingGetter(self.app, req, \u0027Object\u0027, node_iter,"},{"line_number":1955,"context_line":"                                partition, req.swift_entity_path,"},{"line_number":1956,"context_line":"                                backend_headers,"}],"source_content_type":"text/x-python","patch_set":1,"id":"da0c15f0_988fff22","line":1953,"updated":"2016-03-25 18:24:21.000000000","message":"Doing this is a good idea, but mutating req.environ here will leak to everyone with a reference to this request. That\u0027s every other _fragment_GET_request() call, plus the original client request... and if that request is, say, a subrequest for a manifest segment, this will propagate to every subsequent subrequest.\n\nI\u0027d much rather have us make a copy of the environment before mutating it.","commit_id":"c3e50acf1398d9d6aa8b07fabec5691441fa9fc4"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f9948c0b8e159965e2ef133506c9afc107c5e190","unresolved":false,"context_lines":[{"line_number":1950,"context_line":"        # these are backend connections, not part of the outer iter returned"},{"line_number":1951,"context_line":"        # to eventlet wsgi - if they are ever closed - it\u0027s because we closed"},{"line_number":1952,"context_line":"        # them in ECAppIter"},{"line_number":1953,"context_line":"        req.environ[\u0027swift.non_client_disconnect\u0027] \u003d True"},{"line_number":1954,"context_line":"        getter \u003d ResumingGetter(self.app, req, \u0027Object\u0027, node_iter,"},{"line_number":1955,"context_line":"                                partition, req.swift_entity_path,"},{"line_number":1956,"context_line":"                                backend_headers,"}],"source_content_type":"text/x-python","patch_set":1,"id":"da0c15f0_2f494660","line":1953,"in_reply_to":"da0c15f0_988fff22","updated":"2016-03-25 19:08:32.000000000","message":"yeah, this is a hack - I think because ResumingGetter gets used in the backend context for EC and generates the real evetlet.wsgi app_iter in the GETorHEADHandler case we\u0027re in a bit of a kludge :\\\n\n... but I couldn\u0027t think of a better way to pull them apart.\n\nsince this is the real request I don\u0027t think I can just swap out it\u0027s environ attribute for a new dict w/o basically the same problem?  I could change the ResumingGetter to take an environ dict instead of a request and then inside her __init__ mutate the passed environ back into a Request object leaving the original reference to the req un-mutated?\n\nCan you think about it and push up a change over this one?","commit_id":"c3e50acf1398d9d6aa8b07fabec5691441fa9fc4"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"005e9426d634ab01aeb4b36220f10cb577da5268","unresolved":false,"context_lines":[{"line_number":1122,"context_line":"        # cleanup the frag queue feeding coros that may be currently"},{"line_number":1123,"context_line":"        # executing the internal_parts_iters."},{"line_number":1124,"context_line":"        if self.stashed_iter:"},{"line_number":1125,"context_line":"            self.stashed_iter.close()"},{"line_number":1126,"context_line":"        for it in self.internal_parts_iters:"},{"line_number":1127,"context_line":"            close_if_possible(it)"},{"line_number":1128,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"9a061dce_cdf79b89","line":1125,"updated":"2016-04-06 14:45:24.000000000","message":"My observation is that either this or the change in base.py are sufficient to remove the spurious ChunkWriteTimeout\u0027s. I\u0027m not sure I understand why - I guess that maybe the GeneratorExit raised by this close() propagates through nested generators? Amyway, I am happy to go with belt and braces.","commit_id":"7402d7d9cf91ed00a9c86fc1511592ac2eed5d6c"}],"test/unit/proxy/test_server.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"005e9426d634ab01aeb4b36220f10cb577da5268","unresolved":false,"context_lines":[{"line_number":5827,"context_line":"        exp \u003d \u0027HTTP/1.1 201\u0027"},{"line_number":5828,"context_line":"        self.assertEqual(headers[:len(exp)], exp)"},{"line_number":5829,"context_line":""},{"line_number":5830,"context_line":"        with mock.patch.object(_test_servers[0], \u0027client_timeout\u0027, new\u003d5):"},{"line_number":5831,"context_line":"            # get object"},{"line_number":5832,"context_line":"            fd.write(\u0027GET /v1/a/ec-discon/test HTTP/1.1\\r\\n\u0027"},{"line_number":5833,"context_line":"                     \u0027Host: localhost\\r\\n\u0027"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a061dce_cd6cbb37","line":5830,"updated":"2016-04-06 14:45:24.000000000","message":"If i understand this, decreasing the default timeout is simply increasing the probability that we\u0027d  see the ChunkWriteTimeout get logged when tests run?","commit_id":"7402d7d9cf91ed00a9c86fc1511592ac2eed5d6c"}]}
