)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54ac9be5dc204a80d02595d6b8606e9fea8dbded","unresolved":false,"context_lines":[{"line_number":4,"context_line":"Commit:     Kota Tsuyuzaki \u003ctsuyuzaki.kota@lab.ntt.co.jp\u003e"},{"line_number":5,"context_line":"CommitDate: 2017-03-10 00:46:40 -0800"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Small fixes for ec duplciation"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"To address Alistair\u0027s comment at"},{"line_number":10,"context_line":"https://review.openstack.org/#/c/219165."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":6,"id":"9a30ddce_ee008403","line":7,"updated":"2017-03-10 13:10:36.000000000","message":"sp: duplication","commit_id":"0903c6e9724ee1a8b144f74e469a4c04a4c81026"}],"swift/obj/reconstructor.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":309,"context_line":"                # instead we ignore it."},{"line_number":310,"context_line":"                self.logger.debug("},{"line_number":311,"context_line":"                    \u0027Found existing frag #%s while rebuilding to %s\u0027,"},{"line_number":312,"context_line":"                    fi_to_rebuild, resp.full_path)"},{"line_number":313,"context_line":"                continue"},{"line_number":314,"context_line":""},{"line_number":315,"context_line":"            timestamp \u003d resp.headers.get(\u0027X-Backend-Timestamp\u0027)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_a5dacb76","line":312,"updated":"2017-03-07 12:54:05.000000000","message":"sorry, but this still seems wrong - before its said \"from the path of the node we are reconstructing to\", now its says \"to the path of node we received the frag from\"\n\nI suggest \n\n  \u0027Found existing frag #%s at %s while rebuilding to %s\u0027,\n                    fi_to_rebuild, resp.full_path,\n                    self._full_path(\n                        node, job[\u0027partition\u0027],\n                        datafile_metadata[\u0027name\u0027],\n                        job[\u0027policy\u0027]))\n\nthat s long but this is just debug level.","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":4608,"name":"Kota Tsuyuzaki","email":"bloodeagle40234@gmail.com","username":"tsuyuzaki-kota"},"change_message_id":"4111d3d6885c3f29a0961bd905b9f04fdb9106d3","unresolved":false,"context_lines":[{"line_number":309,"context_line":"                # instead we ignore it."},{"line_number":310,"context_line":"                self.logger.debug("},{"line_number":311,"context_line":"                    \u0027Found existing frag #%s while rebuilding to %s\u0027,"},{"line_number":312,"context_line":"                    fi_to_rebuild, resp.full_path)"},{"line_number":313,"context_line":"                continue"},{"line_number":314,"context_line":""},{"line_number":315,"context_line":"            timestamp \u003d resp.headers.get(\u0027X-Backend-Timestamp\u0027)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_521b0173","line":312,"in_reply_to":"9a30ddce_a5dacb76","updated":"2017-03-10 08:54:46.000000000","message":"Oops, nice catch. Maybe I was confused for the term of \"node\". Here, the \"node\" is the target to reconstruct but in the _get_response method, it\u0027s as the source node from.\n\nThe suggestion looks good to me for the semantics.\n\nWill fix.","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54ac9be5dc204a80d02595d6b8606e9fea8dbded","unresolved":false,"context_lines":[{"line_number":203,"context_line":"                \u0027device\u0027: node[\u0027device\u0027],"},{"line_number":204,"context_line":"                \u0027part\u0027: part, \u0027path\u0027: path,"},{"line_number":205,"context_line":"                \u0027policy\u0027: policy,"},{"line_number":206,"context_line":"                \u0027frag_index\u0027: frag_index,"},{"line_number":207,"context_line":"            }"},{"line_number":208,"context_line":""},{"line_number":209,"context_line":"    def _get_response(self, node, part, path, headers, policy):"}],"source_content_type":"text/x-python","patch_set":6,"id":"9a30ddce_eef7a956","side":"PARENT","line":206,"updated":"2017-03-10 13:10:36.000000000","message":"ok. I think it makes sense to drop this frag index from the full path because it is what we expect inthe common case but not necessarily what the node will return (primary could temporarily have more than one frag after a rebalance, proxy may have sent a frag preference header to request a specific frag that is not the one that matches the node index in current ring)","commit_id":"cf1c44dff0706ec4fa491aa7d3d956d3795ae675"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54ac9be5dc204a80d02595d6b8606e9fea8dbded","unresolved":false,"context_lines":[{"line_number":295,"context_line":"                self.logger.warning("},{"line_number":296,"context_line":"                    \u0027Invalid resp from %s \u0027"},{"line_number":297,"context_line":"                    \u0027(invalid X-Object-Sysmeta-Ec-Frag-Index)\u0027,"},{"line_number":298,"context_line":"                    resp.full_path)"},{"line_number":299,"context_line":"                continue"},{"line_number":300,"context_line":""},{"line_number":301,"context_line":"            if fi_to_rebuild \u003d\u003d unique_index:"}],"source_content_type":"text/x-python","patch_set":6,"id":"9a30ddce_6e0f3988","line":298,"updated":"2017-03-10 13:10:36.000000000","message":"ok. before, resp.full_path included the frag index we were *expecting* from the node, which isn\u0027t necessarily the frag index the node has and certainly isn\u0027t the invalid frag index that caused the error.\n\noff-topic: it might be useful to log the invalid value to assist in debugging.","commit_id":"0903c6e9724ee1a8b144f74e469a4c04a4c81026"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54ac9be5dc204a80d02595d6b8606e9fea8dbded","unresolved":false,"context_lines":[{"line_number":306,"context_line":"                # instead we ignore it."},{"line_number":307,"context_line":"                self.logger.debug("},{"line_number":308,"context_line":"                    \u0027Found existing frag #%s at %s while rebuilding to %s\u0027,"},{"line_number":309,"context_line":"                    fi_to_rebuild, resp.full_path,"},{"line_number":310,"context_line":"                    self._full_path("},{"line_number":311,"context_line":"                        node, job[\u0027partition\u0027], datafile_metadata[\u0027name\u0027],"},{"line_number":312,"context_line":"                        job[\u0027policy\u0027]))"}],"source_content_type":"text/x-python","patch_set":6,"id":"9a30ddce_8e6f0d2a","line":309,"updated":"2017-03-10 13:10:36.000000000","message":"ok. here we include the frag index explicitly, so no need for it to be in resp.full_path","commit_id":"0903c6e9724ee1a8b144f74e469a4c04a4c81026"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54ac9be5dc204a80d02595d6b8606e9fea8dbded","unresolved":false,"context_lines":[{"line_number":323,"context_line":"            etag \u003d resp.headers.get(\u0027X-Object-Sysmeta-Ec-Etag\u0027)"},{"line_number":324,"context_line":"            if not etag:"},{"line_number":325,"context_line":"                self.logger.warning(\u0027Invalid resp from %s (missing Etag)\u0027,"},{"line_number":326,"context_line":"                                    resp.full_path)"},{"line_number":327,"context_line":"                continue"},{"line_number":328,"context_line":""},{"line_number":329,"context_line":"            if etag !\u003d etag_buckets.setdefault(timestamp, etag):"}],"source_content_type":"text/x-python","patch_set":6,"id":"9a30ddce_eea5e946","line":326,"updated":"2017-03-10 13:10:36.000000000","message":"off topic: it would be useful to include the *actual* response frag index in this and other warnings to assist in debug (if a node actually has \u003e1 frags, which did it return that had missing etag?)","commit_id":"0903c6e9724ee1a8b144f74e469a4c04a4c81026"}],"test/unit/obj/test_reconstructor.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3153,"context_line":"                    (list(ec_archive_dict), error_line))"},{"line_number":3154,"context_line":"            # remove the found etag which should not be found in the"},{"line_number":3155,"context_line":"            # following error lines"},{"line_number":3156,"context_line":"            del ec_archive_dict[(expected_etag, ts)]"},{"line_number":3157,"context_line":""},{"line_number":3158,"context_line":"            expected \u003d \u0027Unable to get enough responses (%s/10) to \u0027 \\"},{"line_number":3159,"context_line":"                       \u0027reconstruct 10.0.0.1:1001/sdb/0/a/c/o policy#0 \u0027 \\"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_8502273d","line":3156,"updated":"2017-03-07 12:54:05.000000000","message":"+1","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3196,"context_line":"        responses \u003d [(200, body, make_header(body))"},{"line_number":3197,"context_line":"                     for body in ec_archive_bodies]"},{"line_number":3198,"context_line":"        # Because reconstructor spawns the get_response in swift.common.utils."},{"line_number":3199,"context_line":"        # GreenAsyncPile, the responses could be non-odered (i.e. first it"},{"line_number":3200,"context_line":"        # could be available) so that, to emurate that, shuffle here the"},{"line_number":3201,"context_line":"        # responses"},{"line_number":3202,"context_line":"        random.shuffle(responses)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_858947cd","line":3199,"range":{"start_line":3199,"start_character":53,"end_line":3199,"end_character":59},"updated":"2017-03-07 12:54:05.000000000","message":"nit:ordered","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3197,"context_line":"                     for body in ec_archive_bodies]"},{"line_number":3198,"context_line":"        # Because reconstructor spawns the get_response in swift.common.utils."},{"line_number":3199,"context_line":"        # GreenAsyncPile, the responses could be non-odered (i.e. first it"},{"line_number":3200,"context_line":"        # could be available) so that, to emurate that, shuffle here the"},{"line_number":3201,"context_line":"        # responses"},{"line_number":3202,"context_line":"        random.shuffle(responses)"},{"line_number":3203,"context_line":"        codes, body_iter, headers \u003d zip(*responses)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_a5864bb7","line":3200,"range":{"start_line":3200,"start_character":43,"end_line":3200,"end_character":49},"updated":"2017-03-07 12:54:05.000000000","message":"nit:emulate","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3199,"context_line":"        # GreenAsyncPile, the responses could be non-odered (i.e. first it"},{"line_number":3200,"context_line":"        # could be available) so that, to emurate that, shuffle here the"},{"line_number":3201,"context_line":"        # responses"},{"line_number":3202,"context_line":"        random.shuffle(responses)"},{"line_number":3203,"context_line":"        codes, body_iter, headers \u003d zip(*responses)"},{"line_number":3204,"context_line":"        with mocked_http_conn(*codes, body_iter\u003dbody_iter, headers\u003dheaders):"},{"line_number":3205,"context_line":"            df \u003d self.reconstructor.reconstruct_fa("}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_bb4e68bf","line":3202,"updated":"2017-03-07 12:54:05.000000000","message":"this is a nice idea but ot doesn\u0027t really work out: the node requests are issued in node index order, so the responses get a full_path added based on the node index. If the responses are shuffled, then their simulated frag index header will not then match the full_path added to the response. So when you run the test you see debug like:\n\n  object-reconstructor DEBUG: Found existing frag #1 while rebuilding to 10.0.0.2:1002/sdc/0/a/c/o policy#0 frag#2\n\nwhich doesn\u0027t make sense (i.e. frag #1 !\u003d frag #2 )\n\nAlso, shuffling means that sometimes the test does not actually find the identical frag index, which defeats the purpose of the test.","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":4608,"name":"Kota Tsuyuzaki","email":"bloodeagle40234@gmail.com","username":"tsuyuzaki-kota"},"change_message_id":"4111d3d6885c3f29a0961bd905b9f04fdb9106d3","unresolved":false,"context_lines":[{"line_number":3199,"context_line":"        # GreenAsyncPile, the responses could be non-odered (i.e. first it"},{"line_number":3200,"context_line":"        # could be available) so that, to emurate that, shuffle here the"},{"line_number":3201,"context_line":"        # responses"},{"line_number":3202,"context_line":"        random.shuffle(responses)"},{"line_number":3203,"context_line":"        codes, body_iter, headers \u003d zip(*responses)"},{"line_number":3204,"context_line":"        with mocked_http_conn(*codes, body_iter\u003dbody_iter, headers\u003dheaders):"},{"line_number":3205,"context_line":"            df \u003d self.reconstructor.reconstruct_fa("}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_f0f06dff","line":3202,"in_reply_to":"9a30ddce_bb4e68bf","updated":"2017-03-10 08:54:46.000000000","message":"Ok, good point but your suggestion also doesn\u0027t work in the debug message level, I found while reviewing again by myself.\n\nThat is because the reconstructor will try to the nodes like:\n\n node[0], node[1].... (skip node[broken])... node[N]\n\nbut the response iterator will return\n\n response[0], response[1].... response[broken].... ressponse[N-1]\n\nAnd then, all responses after response[broken], has mis-match between node_index (in fact, policy.get_backend_index(node_index) and frag_index. the node_index will be frag_index + 1.\n\n\nThat behavior could be frustrated to us but I think the reason why we are doing like as that, is to emulate the case that the frag which is same with the target exists somewhere (e.g. while rebalancing) even if it\u0027s normal EC (not EC duplication). Actually, this test exists before ec duplication.\n\n\nThinking *real* Swift cluster, IIRC, the inconsistency between node_index and *frag_index* in the response header can happen, right? I think it could be from Optimistic GET that can return available fragment instead of the current assignment? Since the fact, IMO, the point we should fix is _full_path message to remove the frag_index info which may be wrong.","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3214,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":3215,"context_line":"        # the found own frag will be reported in the debug message"},{"line_number":3216,"context_line":"        debug_log_lines \u003d self.logger.get_lines_for_level(\u0027debug\u0027)"},{"line_number":3217,"context_line":"        # redandant frag found at most duplication_factor + 1 times"},{"line_number":3218,"context_line":"        self.assertLessEqual("},{"line_number":3219,"context_line":"            len(debug_log_lines),"},{"line_number":3220,"context_line":"            self.policy.ec_duplication_factor + 1)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_857787d3","line":3217,"updated":"2017-03-07 12:54:05.000000000","message":"nit:redundant","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3214,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":3215,"context_line":"        # the found own frag will be reported in the debug message"},{"line_number":3216,"context_line":"        debug_log_lines \u003d self.logger.get_lines_for_level(\u0027debug\u0027)"},{"line_number":3217,"context_line":"        # redandant frag found at most duplication_factor + 1 times"},{"line_number":3218,"context_line":"        self.assertLessEqual("},{"line_number":3219,"context_line":"            len(debug_log_lines),"},{"line_number":3220,"context_line":"            self.policy.ec_duplication_factor + 1)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_18c13ae5","line":3217,"range":{"start_line":3217,"start_character":10,"end_line":3217,"end_character":67},"updated":"2017-03-07 12:54:05.000000000","message":"this is not true? it could only be found duplication_factor times. But there is one more debug message at end of the reconstruction.\n\nIn fact, AFAICT even when run with duplication factor \u003d 2 this test will never find more than one of the redundnant frags because the first ec_ndata+1 responses are sufficient to reconstruct i.e. the duplicate indexes are never read.\n\nI think that is OK. The test validates that reconstructor can handle an identical frag. The test could be enhanced to somehow deliberately force duplicate frags to be read - I thought about how to do that but it felt like it would involve a lot of hacking on the test e.g. forcing some 404 responses from first ec_n_data requests. TBH, I think the test scope if OK as it is.","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":4608,"name":"Kota Tsuyuzaki","email":"bloodeagle40234@gmail.com","username":"tsuyuzaki-kota"},"change_message_id":"4111d3d6885c3f29a0961bd905b9f04fdb9106d3","unresolved":false,"context_lines":[{"line_number":3214,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":3215,"context_line":"        # the found own frag will be reported in the debug message"},{"line_number":3216,"context_line":"        debug_log_lines \u003d self.logger.get_lines_for_level(\u0027debug\u0027)"},{"line_number":3217,"context_line":"        # redandant frag found at most duplication_factor + 1 times"},{"line_number":3218,"context_line":"        self.assertLessEqual("},{"line_number":3219,"context_line":"            len(debug_log_lines),"},{"line_number":3220,"context_line":"            self.policy.ec_duplication_factor + 1)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_5553ebe1","line":3217,"range":{"start_line":3217,"start_character":10,"end_line":3217,"end_character":67},"in_reply_to":"9a30ddce_18c13ae5","updated":"2017-03-10 08:54:46.000000000","message":"Ah, \n\n\u003e this is not true? it could only be found duplication_factor times. But there is one more debug message at end of the reconstruction.\n\nCorrect. And exactly, to be more robust test for the ec duplication case, I\u0027m realizing to think that we should make it in the TestObjectReconstructorECDuplicationFactor with some error responses (e.g. 404s) cases so I\u0027ll squash your suggestion simply to this.","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3304,"context_line":"        part_nodes \u003d self.policy.object_ring.get_part_nodes(0)"},{"line_number":3305,"context_line":"        node \u003d part_nodes[1]"},{"line_number":3306,"context_line":"        ts \u003d make_timestamp_iter()"},{"line_number":3307,"context_line":"        timestamp \u003d next(ts)"},{"line_number":3308,"context_line":"        metadata \u003d {"},{"line_number":3309,"context_line":"            \u0027name\u0027: \u0027/a/c/o\u0027,"},{"line_number":3310,"context_line":"            \u0027Content-Length\u0027: 0,"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_91c18583","line":3307,"updated":"2017-03-07 12:54:05.000000000","message":"+1","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":3333,"context_line":"            # To drop the header from the response[0], set None as the value"},{"line_number":3334,"context_line":"            # explicitly instead of deleting the key because if no key exists"},{"line_number":3335,"context_line":"            # in the dict, fake_http_connect will insert some key/value pairs"},{"line_number":3336,"context_line":"            # automatically (e.g. X-Backend-Timestamp)"},{"line_number":3337,"context_line":"            responses[0][2].update({missing_header: None})"},{"line_number":3338,"context_line":""},{"line_number":3339,"context_line":"            codes, body_iter, headers \u003d zip(*responses)"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_b1584958","line":3336,"updated":"2017-03-07 12:54:05.000000000","message":"thanks","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"}],"test/unit/proxy/controllers/test_obj.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":4305,"context_line":""},{"line_number":4306,"context_line":"        # sanity, tester should set unique frag index to the variable"},{"line_number":4307,"context_line":"        self.assertEqual("},{"line_number":4308,"context_line":"            unique, self.policy.get_backend_index(unique))"},{"line_number":4309,"context_line":"        # create a dummy list of putters, check no handoffs"},{"line_number":4310,"context_line":"        putters \u003d []"},{"line_number":4311,"context_line":"        for index in range(self.policy.object_ring.replica_count):"}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_cc48c45e","line":4308,"updated":"2017-03-07 12:54:05.000000000","message":"ok so this validates that unique is not a duplicate node index","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"923caac4956b474714ee5829b8662578b1cdae8e","unresolved":false,"context_lines":[{"line_number":4334,"context_line":"        # is different from both of the \"unique\" and \"duplicated\""},{"line_number":4335,"context_line":"        # but it\u0027s only 1 copy missing so that the handoff node should"},{"line_number":4336,"context_line":"        # be assigned to either \"unique\" or \"duplicated\" prior to"},{"line_number":4337,"context_line":"        # \"one_more_missing\""},{"line_number":4338,"context_line":"        got \u003d controller._determine_chunk_destinations(putters, self.policy)"},{"line_number":4339,"context_line":"        self.assertEqual(len(expected) - 2, len(got))"},{"line_number":4340,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"9a30ddce_2c5880fa","line":4337,"updated":"2017-03-07 12:54:05.000000000","message":"ok,\n\nI prefer my proposal on this patch :P\n\n  https://review.openstack.org/#/c/442108/1/test/unit/proxy/controllers/test_obj.py@4302\n\nbut this is  great improvement here thanks!","commit_id":"71a10262d26ecfd569cc43aa2ca8e7fe298b7cd7"}]}
