)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"ad48cf3dc41516ca9119c74ee7630891c97f1dcb","unresolved":false,"context_lines":[{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Add handlers for consumer_generation"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"The previous patch added the \u0027generation\u0027 field to the consumer table."},{"line_number":10,"context_line":"This patch adds a microversion for handling that in requests, but it"},{"line_number":11,"context_line":"doesn\u0027t yet do any conflict checking."},{"line_number":12,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"bf659307_df66e1c1","line":9,"range":{"start_line":9,"start_character":55,"end_line":9,"end_character":63},"updated":"2018-03-30 18:25:22.000000000","message":"consumers","commit_id":"488d4e167ecce53bc31d0c728fe1af26c4ddc416"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"bca1d739090e2db6a90eab14bdb16ae5027e33a9","unresolved":false,"context_lines":[{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Add handlers for consumer_generation"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"The previous patch added the \u0027generation\u0027 field to the consumer table."},{"line_number":10,"context_line":"This patch adds the code to increase the generation whenever allocations"},{"line_number":11,"context_line":"are changed for a consumer."},{"line_number":12,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":4,"id":"bf659307_86aa4b16","line":9,"range":{"start_line":9,"start_character":55,"end_line":9,"end_character":63},"updated":"2018-04-09 19:27:45.000000000","message":"consumers","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Add handlers for consumer_generation"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"The previous patch added the \u0027generation\u0027 field to the consumer table."},{"line_number":10,"context_line":"This patch adds the code to increase the generation whenever allocations"},{"line_number":11,"context_line":"are changed for a consumer."},{"line_number":12,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":4,"id":"bf659307_e315a7e6","line":9,"range":{"start_line":9,"start_character":55,"end_line":9,"end_character":63},"in_reply_to":"bf659307_86aa4b16","updated":"2018-04-09 19:57:39.000000000","message":"Done","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"}],"nova/api/openstack/placement/handlers/allocation.py":[{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"ad48cf3dc41516ca9119c74ee7630891c97f1dcb","unresolved":false,"context_lines":[{"line_number":319,"context_line":"def set_allocations(req):"},{"line_number":320,"context_line":"    context \u003d req.environ[\u0027placement.context\u0027]"},{"line_number":321,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":322,"context_line":"    use_consumer_gen \u003d want_version and want_version.matches((1, 22))"},{"line_number":323,"context_line":"    if use_consumer_gen:"},{"line_number":324,"context_line":"        valid_schema \u003d schema.POST_ALLOCATIONS_V1_22"},{"line_number":325,"context_line":"    else:"}],"source_content_type":"text/x-python","patch_set":2,"id":"bf659307_bfbd2dd9","line":322,"range":{"start_line":322,"start_character":23,"end_line":322,"end_character":36},"updated":"2018-03-30 18:25:22.000000000","message":"note that want_version will always be non-None, since the microversion parsing middleware will always inject it into the environ:\n\nhttps://github.com/openstack/nova/blob/master/nova/api/openstack/placement/deploy.py#L54\n\nhttps://github.com/openstack/microversion-parse/blob/master/microversion_parse/middleware.py#L75","commit_id":"488d4e167ecce53bc31d0c728fe1af26c4ddc416"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":91,"context_line":"                }"},{"line_number":92,"context_line":"            }"},{"line_number":93,"context_line":"        },"},{"line_number":94,"context_line":"        # project_id and user_id are added with microverion 1.12"},{"line_number":95,"context_line":"        \u0027project_id\u0027: PROJECT_ID,"},{"line_number":96,"context_line":"        \u0027user_id\u0027: USER_ID,"},{"line_number":97,"context_line":"        \u0027consumer_generation\u0027: CONSUMER_GENERATION"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_a1289996","line":94,"range":{"start_line":94,"start_character":48,"end_line":94,"end_character":59},"updated":"2018-04-02 21:00:37.000000000","message":"microversion","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":91,"context_line":"                }"},{"line_number":92,"context_line":"            }"},{"line_number":93,"context_line":"        },"},{"line_number":94,"context_line":"        # project_id and user_id are added with microverion 1.12"},{"line_number":95,"context_line":"        \u0027project_id\u0027: PROJECT_ID,"},{"line_number":96,"context_line":"        \u0027user_id\u0027: USER_ID,"},{"line_number":97,"context_line":"        \u0027consumer_generation\u0027: CONSUMER_GENERATION"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_c3ef43e7","line":94,"range":{"start_line":94,"start_character":48,"end_line":94,"end_character":59},"in_reply_to":"bf659307_a1289996","updated":"2018-04-09 19:57:39.000000000","message":"That\u0027s not part of this patch.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":94,"context_line":"        # project_id and user_id are added with microverion 1.12"},{"line_number":95,"context_line":"        \u0027project_id\u0027: PROJECT_ID,"},{"line_number":96,"context_line":"        \u0027user_id\u0027: USER_ID,"},{"line_number":97,"context_line":"        \u0027consumer_generation\u0027: CONSUMER_GENERATION"},{"line_number":98,"context_line":"    }"},{"line_number":99,"context_line":"    \"\"\""},{"line_number":100,"context_line":"    return _allocations_dict(allocations,"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_dc39f2d6","line":97,"range":{"start_line":97,"start_character":8,"end_line":97,"end_character":50},"updated":"2018-04-02 21:00:37.000000000","message":"Nix this until we\u0027re ready to update _allocations_dict.\n\nAnd at that point, include a comment akin to that on L94.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":94,"context_line":"        # project_id and user_id are added with microverion 1.12"},{"line_number":95,"context_line":"        \u0027project_id\u0027: PROJECT_ID,"},{"line_number":96,"context_line":"        \u0027user_id\u0027: USER_ID,"},{"line_number":97,"context_line":"        \u0027consumer_generation\u0027: CONSUMER_GENERATION"},{"line_number":98,"context_line":"    }"},{"line_number":99,"context_line":"    \"\"\""},{"line_number":100,"context_line":"    return _allocations_dict(allocations,"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_43aa5385","line":97,"range":{"start_line":97,"start_character":8,"end_line":97,"end_character":50},"in_reply_to":"bf659307_dc39f2d6","updated":"2018-04-09 19:57:39.000000000","message":"Done","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":131,"context_line":"def list_for_consumer(req):"},{"line_number":132,"context_line":"    \"\"\"List allocations associated with a consumer.\"\"\""},{"line_number":133,"context_line":"    context \u003d req.environ[\u0027placement.context\u0027]"},{"line_number":134,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":135,"context_line":"    consumer_id \u003d util.wsgi_path_item(req.environ, \u0027consumer_uuid\u0027)"},{"line_number":136,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":137,"context_line":""},{"line_number":138,"context_line":"    # NOTE(cdent): There is no way for a 404 to be returned here,"},{"line_number":139,"context_line":"    # only an empty result. We do not have a way to validate a"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_5c3de28c","line":136,"range":{"start_line":134,"start_character":0,"end_line":136,"end_character":65},"updated":"2018-04-02 21:00:37.000000000","message":"This is weird; why are we getting want_version twice?  Looks like a bad rebase.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":131,"context_line":"def list_for_consumer(req):"},{"line_number":132,"context_line":"    \"\"\"List allocations associated with a consumer.\"\"\""},{"line_number":133,"context_line":"    context \u003d req.environ[\u0027placement.context\u0027]"},{"line_number":134,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":135,"context_line":"    consumer_id \u003d util.wsgi_path_item(req.environ, \u0027consumer_uuid\u0027)"},{"line_number":136,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":137,"context_line":""},{"line_number":138,"context_line":"    # NOTE(cdent): There is no way for a 404 to be returned here,"},{"line_number":139,"context_line":"    # only an empty result. We do not have a way to validate a"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_43833305","line":136,"range":{"start_line":134,"start_character":0,"end_line":136,"end_character":65},"in_reply_to":"bf659307_0962b20a","updated":"2018-04-09 19:57:39.000000000","message":"Chris and I already identified it, and I removed it on my working copy. It will be gone in the next PS.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"ca9234d9a405291997df8a46909cc032ac48e145","unresolved":false,"context_lines":[{"line_number":131,"context_line":"def list_for_consumer(req):"},{"line_number":132,"context_line":"    \"\"\"List allocations associated with a consumer.\"\"\""},{"line_number":133,"context_line":"    context \u003d req.environ[\u0027placement.context\u0027]"},{"line_number":134,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":135,"context_line":"    consumer_id \u003d util.wsgi_path_item(req.environ, \u0027consumer_uuid\u0027)"},{"line_number":136,"context_line":"    want_version \u003d req.environ[microversion.MICROVERSION_ENVIRON]"},{"line_number":137,"context_line":""},{"line_number":138,"context_line":"    # NOTE(cdent): There is no way for a 404 to be returned here,"},{"line_number":139,"context_line":"    # only an empty result. We do not have a way to validate a"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_0962b20a","line":136,"range":{"start_line":134,"start_character":0,"end_line":136,"end_character":65},"in_reply_to":"bf659307_5c3de28c","updated":"2018-04-03 20:31:40.000000000","message":"It\u0027s something that\u0027s in master, something that snuck through code review at some point.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"bca1d739090e2db6a90eab14bdb16ae5027e33a9","unresolved":false,"context_lines":[{"line_number":192,"context_line":"    return response"},{"line_number":193,"context_line":""},{"line_number":194,"context_line":""},{"line_number":195,"context_line":"def _new_allocations(context, resource_provider_uuid, consumer_uuid,"},{"line_number":196,"context_line":"                     resources, project_id, user_id):"},{"line_number":197,"context_line":"    \"\"\"Create new allocation objects for a set of resources"},{"line_number":198,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_c8fc4240","line":195,"updated":"2018-04-09 19:27:45.000000000","message":"Unless I\u0027m misreading something it\u0027s actually *here* that you will want to add a consumer_generation parameter and construct the rp_obj.Allocation() object with that generation field.","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"70a6faa20745bad7e67987cb2f57a8362a43dd5d","unresolved":false,"context_lines":[{"line_number":352,"context_line":"        context, objects\u003dallocation_objects)"},{"line_number":353,"context_line":""},{"line_number":354,"context_line":"    try:"},{"line_number":355,"context_line":"        # TODO(edleafe): Is this OK? The only case now is migrations, where it"},{"line_number":356,"context_line":"        # would be safe, but perhaps we need to modify that to send a list of"},{"line_number":357,"context_line":"        # consumer generations, or somehow include the generation with each"},{"line_number":358,"context_line":"        # allocation."}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_edb27849","line":355,"updated":"2018-04-09 18:25:40.000000000","message":"I would vote no. It is possible to send any kind of allocations in via POST, they don\u0027t have to be the same consumer, so they won\u0027t be the same generation.\n\nI think what should happen is that each allocation should get a field (not reflected into the db) which is the generation of the consumer.\n\nThen, within create_all, the available allocations are traversed to create consumer/generation tuples to be checked.\n\nNote also that you may wish to shortcut the consumer generation checking (depending on how you are implementing it, haven\u0027t gotten that far yet) to exit early before doing much of the allocation validity checking. (Looking further in this patch it looks like perhaps you were considering that).","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"bca1d739090e2db6a90eab14bdb16ae5027e33a9","unresolved":false,"context_lines":[{"line_number":356,"context_line":"        # would be safe, but perhaps we need to modify that to send a list of"},{"line_number":357,"context_line":"        # consumer generations, or somehow include the generation with each"},{"line_number":358,"context_line":"        # allocation."},{"line_number":359,"context_line":"        allocations.create_all(consumer_generation\u003dconsumer_generation)"},{"line_number":360,"context_line":"        LOG.debug(\"Successfully wrote allocations %s\", allocations)"},{"line_number":361,"context_line":"    except exception.NotFound as exc:"},{"line_number":362,"context_line":"        raise webob.exc.HTTPBadRequest("}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_88940aea","line":359,"range":{"start_line":359,"start_character":32,"end_line":359,"end_character":70},"updated":"2018-04-09 19:27:45.000000000","message":"yeah, you don\u0027t want to add a parameter to AllocationList.create_all(). Instead, you will want to simply add the field to the Allocation object, along with consumer_uuid.","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":356,"context_line":"        # would be safe, but perhaps we need to modify that to send a list of"},{"line_number":357,"context_line":"        # consumer generations, or somehow include the generation with each"},{"line_number":358,"context_line":"        # allocation."},{"line_number":359,"context_line":"        allocations.create_all(consumer_generation\u003dconsumer_generation)"},{"line_number":360,"context_line":"        LOG.debug(\"Successfully wrote allocations %s\", allocations)"},{"line_number":361,"context_line":"    except exception.NotFound as exc:"},{"line_number":362,"context_line":"        raise webob.exc.HTTPBadRequest("}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_e31b67c7","line":359,"range":{"start_line":359,"start_character":32,"end_line":359,"end_character":70},"in_reply_to":"bf659307_88940aea","updated":"2018-04-09 19:57:39.000000000","message":"OK, I\u0027ll revise.","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":15888,"name":"Zhenyu Zheng","email":"zheng.zhenyu@outlook.com","username":"Kevin_Zheng"},"change_message_id":"7751213bb25bffa658665483996edad9c75fc8a2","unresolved":false,"context_lines":[{"line_number":356,"context_line":"        # would be safe, but perhaps we need to modify that to send a list of"},{"line_number":357,"context_line":"        # consumer generations, or somehow include the generation with each"},{"line_number":358,"context_line":"        # allocation."},{"line_number":359,"context_line":"        allocations.create_all(consumer_generation\u003dconsumer_generation)"},{"line_number":360,"context_line":"        LOG.debug(\"Successfully wrote allocations %s\", allocations)"},{"line_number":361,"context_line":"    except exception.NotFound as exc:"},{"line_number":362,"context_line":"        raise webob.exc.HTTPBadRequest("}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_9cb19e92","line":359,"range":{"start_line":359,"start_character":32,"end_line":359,"end_character":70},"in_reply_to":"bf659307_88940aea","updated":"2018-04-10 10:06:01.000000000","message":"good point","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":333,"context_line":"        if allocations:"},{"line_number":334,"context_line":"            for resource_provider_uuid in allocations:"},{"line_number":335,"context_line":"                resources \u003d allocations[resource_provider_uuid][\u0027resources\u0027]"},{"line_number":336,"context_line":""},{"line_number":337,"context_line":"                new_allocations \u003d _new_allocations(context,"},{"line_number":338,"context_line":"                                                   resource_provider_uuid,"},{"line_number":339,"context_line":"                                                   consumer_uuid,"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_7f1fca6f","line":336,"updated":"2018-04-10 22:30:25.000000000","message":"unrelated","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":333,"context_line":"        if allocations:"},{"line_number":334,"context_line":"            for resource_provider_uuid in allocations:"},{"line_number":335,"context_line":"                resources \u003d allocations[resource_provider_uuid][\u0027resources\u0027]"},{"line_number":336,"context_line":""},{"line_number":337,"context_line":"                new_allocations \u003d _new_allocations(context,"},{"line_number":338,"context_line":"                                                   resource_provider_uuid,"},{"line_number":339,"context_line":"                                                   consumer_uuid,"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_28283b95","line":336,"in_reply_to":"bf659307_7f1fca6f","updated":"2018-04-10 23:11:33.000000000","message":"Done","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":370,"context_line":"    except exception.ConcurrentUpdateDetected as exc:"},{"line_number":371,"context_line":"        raise webob.exc.HTTPConflict("},{"line_number":372,"context_line":"            _(\u0027Inventory and/or allocations changed while attempting to \u0027"},{"line_number":373,"context_line":"            \u0027allocate: %(error)s\u0027) % {\u0027error\u0027: exc})"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    req.response.status \u003d 204"},{"line_number":376,"context_line":"    req.response.content_type \u003d None"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_3f02b20c","line":373,"updated":"2018-04-10 22:30:25.000000000","message":"indent","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":370,"context_line":"    except exception.ConcurrentUpdateDetected as exc:"},{"line_number":371,"context_line":"        raise webob.exc.HTTPConflict("},{"line_number":372,"context_line":"            _(\u0027Inventory and/or allocations changed while attempting to \u0027"},{"line_number":373,"context_line":"            \u0027allocate: %(error)s\u0027) % {\u0027error\u0027: exc})"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    req.response.status \u003d 204"},{"line_number":376,"context_line":"    req.response.content_type \u003d None"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_480ecf3b","line":373,"in_reply_to":"bf659307_3f02b20c","updated":"2018-04-10 23:11:33.000000000","message":"I was following the previous indentation style, which I personally detest. I will change it to be more to my liking, and with any luck, more to yours.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":370,"context_line":"    except exception.ConcurrentUpdateDetected as exc:"},{"line_number":371,"context_line":"        raise webob.exc.HTTPConflict("},{"line_number":372,"context_line":"            _(\u0027Inventory and/or allocations changed while attempting to \u0027"},{"line_number":373,"context_line":"            \u0027allocate: %(error)s\u0027) % {\u0027error\u0027: exc})"},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    req.response.status \u003d 204"},{"line_number":376,"context_line":"    req.response.content_type \u003d None"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_fd30bb58","line":373,"in_reply_to":"bf659307_480ecf3b","updated":"2018-04-11 05:03:52.000000000","message":"I just meant the \u0027s should line up.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"}],"nova/api/openstack/placement/objects/resource_provider.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":284,"context_line":"    return new_generation"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"def _increment_consumer_generation(ctx, consumer_uuid, consumer_generation):"},{"line_number":288,"context_line":"    \"\"\"Increments the supplied consumer\u0027s generation value, supplying the"},{"line_number":289,"context_line":"    currently-known generation. Returns whether the increment succeeded."},{"line_number":290,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_dc925257","line":287,"updated":"2018-04-02 21:00:37.000000000","message":"So yeah, this is going to need to take want_version, or something.  Because the behavior needs to be different for consumer_generation\u003dNone for earlier (increment blindly) vs. later (fail if the consumer exists at all).","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":284,"context_line":"    return new_generation"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"def _increment_consumer_generation(ctx, consumer_uuid, consumer_generation):"},{"line_number":288,"context_line":"    \"\"\"Increments the supplied consumer\u0027s generation value, supplying the"},{"line_number":289,"context_line":"    currently-known generation. Returns whether the increment succeeded."},{"line_number":290,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_a316cfa6","line":287,"in_reply_to":"bf659307_dc925257","updated":"2018-04-09 19:57:39.000000000","message":"There is no want_version for this (yet), since the new microversion is added in the next patch in the series.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":1782,"context_line":"                \u0027user_id\u0027 in self and"},{"line_number":1783,"context_line":"                self.user_id is not None):"},{"line_number":1784,"context_line":"            return"},{"line_number":1785,"context_line":"        # TODO(edleafe): Document the microversion that adds"},{"line_number":1786,"context_line":"        # consumer_generation to allocations."},{"line_number":1787,"context_line":"        if not (\u0027consumer_generation\u0027 in self and"},{"line_number":1788,"context_line":"                self.consumer_generation is not None):"},{"line_number":1789,"context_line":"            return"},{"line_number":1790,"context_line":"        # Grab the project internal ID if it exists in the projects table"},{"line_number":1791,"context_line":"        pid \u003d _ensure_project(ctx, self.project_id)"},{"line_number":1792,"context_line":"        # Grab the user internal ID if it exists in the users table"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_464f5392","line":1789,"range":{"start_line":1785,"start_character":0,"end_line":1789,"end_character":18},"updated":"2018-04-02 21:00:37.000000000","message":"I\u0027m pretty confused by this method, so bear with me if I\u0027m way off base.\n\nWon\u0027t this new condition cause us to skip populating the consumers table at microversions 1.8-(now-1), which would be a regression?","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":1782,"context_line":"                \u0027user_id\u0027 in self and"},{"line_number":1783,"context_line":"                self.user_id is not None):"},{"line_number":1784,"context_line":"            return"},{"line_number":1785,"context_line":"        # TODO(edleafe): Document the microversion that adds"},{"line_number":1786,"context_line":"        # consumer_generation to allocations."},{"line_number":1787,"context_line":"        if not (\u0027consumer_generation\u0027 in self and"},{"line_number":1788,"context_line":"                self.consumer_generation is not None):"},{"line_number":1789,"context_line":"            return"},{"line_number":1790,"context_line":"        # Grab the project internal ID if it exists in the projects table"},{"line_number":1791,"context_line":"        pid \u003d _ensure_project(ctx, self.project_id)"},{"line_number":1792,"context_line":"        # Grab the user internal ID if it exists in the users table"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_43e6d3ea","line":1789,"range":{"start_line":1785,"start_character":0,"end_line":1789,"end_character":18},"in_reply_to":"bf659307_464f5392","updated":"2018-04-09 19:57:39.000000000","message":"Yes, that\u0027s correct. I\u0027ve already changed this substantially in my working copy.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":1798,"context_line":"        result \u003d ctx.session.execute(sel_stmt).fetchall()"},{"line_number":1799,"context_line":"        if not result:"},{"line_number":1800,"context_line":"            try:"},{"line_number":1801,"context_line":"                ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":1802,"context_line":"                    uuid\u003dself.consumer_id,"},{"line_number":1803,"context_line":"                    project_id\u003dpid,"},{"line_number":1804,"context_line":"                    user_id\u003duid))"},{"line_number":1805,"context_line":"            except db_exc.DBDuplicateEntry:"},{"line_number":1806,"context_line":"                # We assume at this time that a consumer project/user can\u0027t"},{"line_number":1807,"context_line":"                # change, so if we get here, we raced and should just pass"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_9cbffaa8","line":1804,"range":{"start_line":1801,"start_character":16,"end_line":1804,"end_character":33},"updated":"2018-04-02 21:00:37.000000000","message":"With the code as revised in the previous patch, will this default the generation to 0?\n\nIn which case I think this may need some tweakage to work with the special None-the-first-time case.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":1798,"context_line":"        result \u003d ctx.session.execute(sel_stmt).fetchall()"},{"line_number":1799,"context_line":"        if not result:"},{"line_number":1800,"context_line":"            try:"},{"line_number":1801,"context_line":"                ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":1802,"context_line":"                    uuid\u003dself.consumer_id,"},{"line_number":1803,"context_line":"                    project_id\u003dpid,"},{"line_number":1804,"context_line":"                    user_id\u003duid))"},{"line_number":1805,"context_line":"            except db_exc.DBDuplicateEntry:"},{"line_number":1806,"context_line":"                # We assume at this time that a consumer project/user can\u0027t"},{"line_number":1807,"context_line":"                # change, so if we get here, we raced and should just pass"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_a3080f91","line":1804,"range":{"start_line":1801,"start_character":16,"end_line":1804,"end_character":33},"in_reply_to":"bf659307_9cbffaa8","updated":"2018-04-09 19:57:39.000000000","message":"Sorry, but once again this is old. I should just push the new patches without waiting for the SQLA fix.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":2103,"context_line":"                `step_size`, `min_unit` or `max_unit` constraints in an"},{"line_number":2104,"context_line":"                inventory will be violated by any one of the allocations."},{"line_number":2105,"context_line":"        :raises `exception.ConcurrentUpdateDetected` if another thread updated"},{"line_number":2106,"context_line":"                the allocations for the consumerin between the time when this"},{"line_number":2107,"context_line":"                object was originally read and the call to set the allocations."},{"line_number":2108,"context_line":"        \"\"\""},{"line_number":2109,"context_line":"        _ensure_rc_cache(context)"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_26481f85","line":2106,"range":{"start_line":2106,"start_character":40,"end_line":2106,"end_character":50},"updated":"2018-04-02 21:00:37.000000000","message":"consumer / consumer in","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":2103,"context_line":"                `step_size`, `min_unit` or `max_unit` constraints in an"},{"line_number":2104,"context_line":"                inventory will be violated by any one of the allocations."},{"line_number":2105,"context_line":"        :raises `exception.ConcurrentUpdateDetected` if another thread updated"},{"line_number":2106,"context_line":"                the allocations for the consumerin between the time when this"},{"line_number":2107,"context_line":"                object was originally read and the call to set the allocations."},{"line_number":2108,"context_line":"        \"\"\""},{"line_number":2109,"context_line":"        _ensure_rc_cache(context)"}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_832aabe4","line":2106,"range":{"start_line":2106,"start_character":40,"end_line":2106,"end_character":50},"in_reply_to":"bf659307_26481f85","updated":"2018-04-09 19:57:39.000000000","message":"Done","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"a02600621de123f7cc12f5495191779970359527","unresolved":false,"context_lines":[{"line_number":2154,"context_line":"                rp \u003d alloc.resource_provider"},{"line_number":2155,"context_line":"                visited_rps[rp.uuid] \u003d rp"},{"line_number":2156,"context_line":"                continue"},{"line_number":2157,"context_line":"            consumer_id \u003d alloc.consumer_id"},{"line_number":2158,"context_line":"            consumer_generation \u003d alloc.consumer_generation"},{"line_number":2159,"context_line":"            # Only set consumer \u003c-\u003e project/user association if we haven\u0027t set"},{"line_number":2160,"context_line":"            # it already."}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_01562507","line":2157,"range":{"start_line":2157,"start_character":32,"end_line":2157,"end_character":43},"updated":"2018-04-02 21:00:37.000000000","message":"This is a UUID?\n\n[Later] Yup.  Should really be named consumer_uuid :(\n\nAt least you could name your local var that.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"ca9234d9a405291997df8a46909cc032ac48e145","unresolved":false,"context_lines":[{"line_number":2154,"context_line":"                rp \u003d alloc.resource_provider"},{"line_number":2155,"context_line":"                visited_rps[rp.uuid] \u003d rp"},{"line_number":2156,"context_line":"                continue"},{"line_number":2157,"context_line":"            consumer_id \u003d alloc.consumer_id"},{"line_number":2158,"context_line":"            consumer_generation \u003d alloc.consumer_generation"},{"line_number":2159,"context_line":"            # Only set consumer \u003c-\u003e project/user association if we haven\u0027t set"},{"line_number":2160,"context_line":"            # it already."}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_09107256","line":2157,"range":{"start_line":2157,"start_character":32,"end_line":2157,"end_character":43},"in_reply_to":"bf659307_01562507","updated":"2018-04-03 20:31:40.000000000","message":"For sake of historical reference, there was a time when there was no requirement for the consumer to be a uuid. I can\u0027t recall now why. Oh, how the years go by.","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":2154,"context_line":"                rp \u003d alloc.resource_provider"},{"line_number":2155,"context_line":"                visited_rps[rp.uuid] \u003d rp"},{"line_number":2156,"context_line":"                continue"},{"line_number":2157,"context_line":"            consumer_id \u003d alloc.consumer_id"},{"line_number":2158,"context_line":"            consumer_generation \u003d alloc.consumer_generation"},{"line_number":2159,"context_line":"            # Only set consumer \u003c-\u003e project/user association if we haven\u0027t set"},{"line_number":2160,"context_line":"            # it already."}],"source_content_type":"text/x-python","patch_set":4,"id":"bf659307_e34287ab","line":2157,"range":{"start_line":2157,"start_character":32,"end_line":2157,"end_character":43},"in_reply_to":"bf659307_09107256","updated":"2018-04-09 19:57:39.000000000","message":"Yeah, it\u0027s \u0027consumer_id\u0027 throughout, so I figured I\u0027d be consistent. The amount of headaches we create by micro-optimizing SQL...","commit_id":"430dc30798ab4709a6180f65e8a66b392cba6370"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"bca1d739090e2db6a90eab14bdb16ae5027e33a9","unresolved":false,"context_lines":[{"line_number":3710,"context_line":"        return alloc_request_objs, kept_summary_objs"},{"line_number":3711,"context_line":""},{"line_number":3712,"context_line":""},{"line_number":3713,"context_line":"@base.VersionedObjectRegistry.register_if(False)"},{"line_number":3714,"context_line":"class Consumer(base.VersionedObject):"},{"line_number":3715,"context_line":""},{"line_number":3716,"context_line":"    fields \u003d {"},{"line_number":3717,"context_line":"        \"id\": fields.IntegerField(read_only\u003dTrue),"},{"line_number":3718,"context_line":"        \"uuid\": fields.UUIDField(nullable\u003dFalse),"},{"line_number":3719,"context_line":"        \"name\": fields.StringField(nullable\u003dFalse),"},{"line_number":3720,"context_line":"        \"project_id\": fields.StringField(nullable\u003dFalse),"},{"line_number":3721,"context_line":"        \"user_id\": fields.StringField(nullable\u003dFalse),"},{"line_number":3722,"context_line":"        \"generation\": fields.IntegerField(nullable\u003dFalse),"},{"line_number":3723,"context_line":"    }"},{"line_number":3724,"context_line":""},{"line_number":3725,"context_line":"    @classmethod"},{"line_number":3726,"context_line":"    def get_by_uuid(cls, context, uuid):"},{"line_number":3727,"context_line":"        \"\"\"Returns a new Consumer object with the supplied UUID."},{"line_number":3728,"context_line":""},{"line_number":3729,"context_line":"        :raises NotFound if no such consumer could be found"},{"line_number":3730,"context_line":"        :param uuid: UUID of the consumer to search for"},{"line_number":3731,"context_line":"        \"\"\""},{"line_number":3732,"context_line":"        con \u003d sa.alias(_CONSUMER_TBL, name\u003d\"con\")"},{"line_number":3733,"context_line":"        cols \u003d [con.c.id,"},{"line_number":3734,"context_line":"                con.c.uuid,"},{"line_number":3735,"context_line":"                con.c.name,"},{"line_number":3736,"context_line":"                con.c.project_id,"},{"line_number":3737,"context_line":"                con.c.user_id,"},{"line_number":3738,"context_line":"                con.c.generation]"},{"line_number":3739,"context_line":"        sel \u003d sa.select(cols).select_from(con).where(con.c.uuid \u003d\u003d uuid)"},{"line_number":3740,"context_line":"        res \u003d context.session.execute(sel).fetchone()"},{"line_number":3741,"context_line":"        if not res:"},{"line_number":3742,"context_line":"            raise exception.NotFound(\"No consumer with uuid %s found\" % uuid)"},{"line_number":3743,"context_line":"        return cls._from_db_object(context, cls(), dict(res))"},{"line_number":3744,"context_line":""},{"line_number":3745,"context_line":"#        ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":3746,"context_line":"#                uuid\u003dconsumer_uuid,"}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_c87f62a9","line":3743,"range":{"start_line":3713,"start_character":0,"end_line":3743,"end_character":61},"updated":"2018-04-09 19:27:45.000000000","message":"I think it\u0027s probably a good idea to break this new object out into a separate patch. Thoughts?","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":3710,"context_line":"        return alloc_request_objs, kept_summary_objs"},{"line_number":3711,"context_line":""},{"line_number":3712,"context_line":""},{"line_number":3713,"context_line":"@base.VersionedObjectRegistry.register_if(False)"},{"line_number":3714,"context_line":"class Consumer(base.VersionedObject):"},{"line_number":3715,"context_line":""},{"line_number":3716,"context_line":"    fields \u003d {"},{"line_number":3717,"context_line":"        \"id\": fields.IntegerField(read_only\u003dTrue),"},{"line_number":3718,"context_line":"        \"uuid\": fields.UUIDField(nullable\u003dFalse),"},{"line_number":3719,"context_line":"        \"name\": fields.StringField(nullable\u003dFalse),"},{"line_number":3720,"context_line":"        \"project_id\": fields.StringField(nullable\u003dFalse),"},{"line_number":3721,"context_line":"        \"user_id\": fields.StringField(nullable\u003dFalse),"},{"line_number":3722,"context_line":"        \"generation\": fields.IntegerField(nullable\u003dFalse),"},{"line_number":3723,"context_line":"    }"},{"line_number":3724,"context_line":""},{"line_number":3725,"context_line":"    @classmethod"},{"line_number":3726,"context_line":"    def get_by_uuid(cls, context, uuid):"},{"line_number":3727,"context_line":"        \"\"\"Returns a new Consumer object with the supplied UUID."},{"line_number":3728,"context_line":""},{"line_number":3729,"context_line":"        :raises NotFound if no such consumer could be found"},{"line_number":3730,"context_line":"        :param uuid: UUID of the consumer to search for"},{"line_number":3731,"context_line":"        \"\"\""},{"line_number":3732,"context_line":"        con \u003d sa.alias(_CONSUMER_TBL, name\u003d\"con\")"},{"line_number":3733,"context_line":"        cols \u003d [con.c.id,"},{"line_number":3734,"context_line":"                con.c.uuid,"},{"line_number":3735,"context_line":"                con.c.name,"},{"line_number":3736,"context_line":"                con.c.project_id,"},{"line_number":3737,"context_line":"                con.c.user_id,"},{"line_number":3738,"context_line":"                con.c.generation]"},{"line_number":3739,"context_line":"        sel \u003d sa.select(cols).select_from(con).where(con.c.uuid \u003d\u003d uuid)"},{"line_number":3740,"context_line":"        res \u003d context.session.execute(sel).fetchone()"},{"line_number":3741,"context_line":"        if not res:"},{"line_number":3742,"context_line":"            raise exception.NotFound(\"No consumer with uuid %s found\" % uuid)"},{"line_number":3743,"context_line":"        return cls._from_db_object(context, cls(), dict(res))"},{"line_number":3744,"context_line":""},{"line_number":3745,"context_line":"#        ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":3746,"context_line":"#                uuid\u003dconsumer_uuid,"}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_c37503fc","line":3743,"range":{"start_line":3713,"start_character":0,"end_line":3743,"end_character":61},"in_reply_to":"bf659307_c87f62a9","updated":"2018-04-09 19:57:39.000000000","message":"I\u0027ve already deleted it from my local copy, as I didn\u0027t end up using it. I don\u0027t believe it\u0027s necessary, unless from an \"all tables must have objects\" perspective.","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":284,"context_line":"    return new_generation"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"def _increment_consumer_generation(ctx, consumer_uuid, consumer_dict):"},{"line_number":288,"context_line":"    \"\"\"Increments the supplied consumer\u0027s generation value, supplying the"},{"line_number":289,"context_line":"    currently-known generation. Returns whether the increment succeeded."},{"line_number":290,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_9a13cc66","line":287,"range":{"start_line":287,"start_character":4,"end_line":287,"end_character":34},"updated":"2018-04-10 22:30:25.000000000","message":"I\u0027m not loving the fact that this creates the consumer record.  I mainly came to this assessment after seeing how set_allocations now calls ensure_project_user, but there\u0027s no corresponding ensure_consumer.  More below.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":284,"context_line":"    return new_generation"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"def _increment_consumer_generation(ctx, consumer_uuid, consumer_dict):"},{"line_number":288,"context_line":"    \"\"\"Increments the supplied consumer\u0027s generation value, supplying the"},{"line_number":289,"context_line":"    currently-known generation. Returns whether the increment succeeded."},{"line_number":290,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_cb7659a5","line":287,"range":{"start_line":287,"start_character":4,"end_line":287,"end_character":34},"in_reply_to":"bf659307_9a13cc66","updated":"2018-04-10 23:11:33.000000000","message":"The problem I ran into was that if I create the consumer record earlier, along with the project and user, I have no way here to determine what it was when the allocation request was made, unless I carry along a glob of data describing that. It was simpler to examine the state of the consumer *before* it got changed/created, and then create it afterwards if needed.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":284,"context_line":"    return new_generation"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"def _increment_consumer_generation(ctx, consumer_uuid, consumer_dict):"},{"line_number":288,"context_line":"    \"\"\"Increments the supplied consumer\u0027s generation value, supplying the"},{"line_number":289,"context_line":"    currently-known generation. Returns whether the increment succeeded."},{"line_number":290,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_7d258b08","line":287,"range":{"start_line":287,"start_character":4,"end_line":287,"end_character":34},"in_reply_to":"bf659307_cb7659a5","updated":"2018-04-11 05:03:52.000000000","message":"I don\u0027t get this.  If you create the record with a NULL generation, you\u0027re completely covered, and the logic is pretty elegant and consistent.\n\nOh, generation is not nullable.  Well, we could make it so.  Any reason to do otherwise?  (Other than all the work Mike put in to figure out that model discrepancy.)  TBC, the migration should still insert 0 for existing records.  NULL would only be used for this case.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":294,"context_line":"                                be None for older code; in that case, we should"},{"line_number":295,"context_line":"                                not raise a conflict, so simply increment the"},{"line_number":296,"context_line":"                                generation and return the new value."},{"line_number":297,"context_line":"    :param consumer_dict: A dictionary containing the project_id, user_id,"},{"line_number":298,"context_line":"                          consumer_generation, and a flag that indicates if we"},{"line_number":299,"context_line":"                          enforce a match between the supplied"},{"line_number":300,"context_line":"                          consumer_generation value and the value in the DB."},{"line_number":301,"context_line":"                          Older versions did not include this logic, so we want"},{"line_number":302,"context_line":"                          to preserve the previous behavior of accepting those"},{"line_number":303,"context_line":"                          requests."}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_baced0b1","line":300,"range":{"start_line":297,"start_character":26,"end_line":300,"end_character":76},"updated":"2018-04-10 22:30:25.000000000","message":"We should blow this out into separate kwargs.  (Yes, even if future work adds more of them.)  Or make this into some kind of object (like, say, a Consumer object :P ).","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":294,"context_line":"                                be None for older code; in that case, we should"},{"line_number":295,"context_line":"                                not raise a conflict, so simply increment the"},{"line_number":296,"context_line":"                                generation and return the new value."},{"line_number":297,"context_line":"    :param consumer_dict: A dictionary containing the project_id, user_id,"},{"line_number":298,"context_line":"                          consumer_generation, and a flag that indicates if we"},{"line_number":299,"context_line":"                          enforce a match between the supplied"},{"line_number":300,"context_line":"                          consumer_generation value and the value in the DB."},{"line_number":301,"context_line":"                          Older versions did not include this logic, so we want"},{"line_number":302,"context_line":"                          to preserve the previous behavior of accepting those"},{"line_number":303,"context_line":"                          requests."}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_3d4953b6","line":300,"range":{"start_line":297,"start_character":26,"end_line":300,"end_character":76},"in_reply_to":"bf659307_0b60d1ce","updated":"2018-04-11 05:03:52.000000000","message":"The dict you\u0027re tracking them with is built up from local variables, so I don\u0027t see that being a big thang.  Course, if you want to keep that dict anyway, you could always call this method with **that_dict.  (But don\u0027t do that :)\n\nMy main beef with the unstructured dict is that it\u0027s... unstructured.  Easy to miss typos in keys, documentation is messier, etc.  As kwargs, you can document them separately, and we have clear understanding of the spelling of each, its data type, the semantics of various values, etc.  You could doc all of that under consumer_dict, but... messier.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":294,"context_line":"                                be None for older code; in that case, we should"},{"line_number":295,"context_line":"                                not raise a conflict, so simply increment the"},{"line_number":296,"context_line":"                                generation and return the new value."},{"line_number":297,"context_line":"    :param consumer_dict: A dictionary containing the project_id, user_id,"},{"line_number":298,"context_line":"                          consumer_generation, and a flag that indicates if we"},{"line_number":299,"context_line":"                          enforce a match between the supplied"},{"line_number":300,"context_line":"                          consumer_generation value and the value in the DB."},{"line_number":301,"context_line":"                          Older versions did not include this logic, so we want"},{"line_number":302,"context_line":"                          to preserve the previous behavior of accepting those"},{"line_number":303,"context_line":"                          requests."}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_0b60d1ce","line":300,"range":{"start_line":297,"start_character":26,"end_line":300,"end_character":76},"in_reply_to":"bf659307_baced0b1","updated":"2018-04-10 23:11:33.000000000","message":"I had them as separate args, but since I track them as a dict in the methods that call this one, it was cleaner to just pass the dict. I\u0027d like some other opinions on what is cleaner.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":326,"context_line":"            _CONSUMER_TBL.c.uuid \u003d\u003d consumer_uuid)"},{"line_number":327,"context_line":"    result \u003d ctx.session.execute(sel_stmt).fetchall()"},{"line_number":328,"context_line":"    if not result:"},{"line_number":329,"context_line":"        # There can\u0027t be any conflict, as no matching record exists. Add the"},{"line_number":330,"context_line":"        # record with a generation of 0, and return."},{"line_number":331,"context_line":"        ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":332,"context_line":"                uuid\u003dconsumer_uuid,"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_1a549cc9","line":329,"range":{"start_line":329,"start_character":10,"end_line":329,"end_character":37},"updated":"2018-04-10 22:30:25.000000000","message":"No - we have to make sure the consumer_generation from the input is None if enforce_cgen is True.  If not, fail.\n\n[Later] I see you did this in the subsequent change set.  I don\u0027t think that\u0027s right, given how the series is structured.  You\u0027ve got everything switched off by passing enforce_cgen\u003dFalse, so you should have the logic implemented in its final form in here.\n\nI can see the case for doing it the other way, which is \"don\u0027t implement a thing until you\u0027re going to use it\"; but in that case, nix all traces of enforce_cgen from this change set.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":326,"context_line":"            _CONSUMER_TBL.c.uuid \u003d\u003d consumer_uuid)"},{"line_number":327,"context_line":"    result \u003d ctx.session.execute(sel_stmt).fetchall()"},{"line_number":328,"context_line":"    if not result:"},{"line_number":329,"context_line":"        # There can\u0027t be any conflict, as no matching record exists. Add the"},{"line_number":330,"context_line":"        # record with a generation of 0, and return."},{"line_number":331,"context_line":"        ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":332,"context_line":"                uuid\u003dconsumer_uuid,"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_6bacedec","line":329,"range":{"start_line":329,"start_character":10,"end_line":329,"end_character":37},"in_reply_to":"bf659307_1a549cc9","updated":"2018-04-10 23:11:33.000000000","message":"There is no way a conflict can arise until we introduce a new microversion. That\u0027s why it\u0027s this way here, but different in the patch that adds the microversion.\n\n\u0027enforce_cgen\u0027 is not part of the API, but can be changed by the API once we add the microversion. It *is* part of the logic added in this patch, and as such, belongs here. If you have issues with the comment above and the rest of the code, I\u0027ll be happy to change the comment. But the point of breaking this into separate patches is to have the logic for handling consumer generations in place before we change the API.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":328,"context_line":"    if not result:"},{"line_number":329,"context_line":"        # There can\u0027t be any conflict, as no matching record exists. Add the"},{"line_number":330,"context_line":"        # record with a generation of 0, and return."},{"line_number":331,"context_line":"        ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":332,"context_line":"                uuid\u003dconsumer_uuid,"},{"line_number":333,"context_line":"                project_id\u003dpid,"},{"line_number":334,"context_line":"                user_id\u003duid,"},{"line_number":335,"context_line":"                generation\u003d0))"},{"line_number":336,"context_line":"        return 0"},{"line_number":337,"context_line":"    else:"},{"line_number":338,"context_line":"        if consumer_generation is None:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_88fce772","line":335,"range":{"start_line":331,"start_character":8,"end_line":335,"end_character":30},"updated":"2018-04-10 22:30:25.000000000","message":"I think we need to check for duplicates here (DBDuplicateEntry or something?)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":328,"context_line":"    if not result:"},{"line_number":329,"context_line":"        # There can\u0027t be any conflict, as no matching record exists. Add the"},{"line_number":330,"context_line":"        # record with a generation of 0, and return."},{"line_number":331,"context_line":"        ctx.session.execute(_CONSUMER_TBL.insert().values("},{"line_number":332,"context_line":"                uuid\u003dconsumer_uuid,"},{"line_number":333,"context_line":"                project_id\u003dpid,"},{"line_number":334,"context_line":"                user_id\u003duid,"},{"line_number":335,"context_line":"                generation\u003d0))"},{"line_number":336,"context_line":"        return 0"},{"line_number":337,"context_line":"    else:"},{"line_number":338,"context_line":"        if consumer_generation is None:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_4bc6c99c","line":335,"range":{"start_line":331,"start_character":8,"end_line":335,"end_character":30},"in_reply_to":"bf659307_88fce772","updated":"2018-04-10 23:11:33.000000000","message":"Done","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":334,"context_line":"                user_id\u003duid,"},{"line_number":335,"context_line":"                generation\u003d0))"},{"line_number":336,"context_line":"        return 0"},{"line_number":337,"context_line":"    else:"},{"line_number":338,"context_line":"        if consumer_generation is None:"},{"line_number":339,"context_line":"            new_generation \u003d 0"},{"line_number":340,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_da8e2426","line":337,"updated":"2018-04-10 22:30:25.000000000","message":"Because of all the branchiness, a comment here reminding me what\u0027s happening would be nice.  E.g. # There was no existing db record for this consumer.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":334,"context_line":"                user_id\u003duid,"},{"line_number":335,"context_line":"                generation\u003d0))"},{"line_number":336,"context_line":"        return 0"},{"line_number":337,"context_line":"    else:"},{"line_number":338,"context_line":"        if consumer_generation is None:"},{"line_number":339,"context_line":"            new_generation \u003d 0"},{"line_number":340,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_2bb91514","line":337,"in_reply_to":"bf659307_da8e2426","updated":"2018-04-10 23:11:33.000000000","message":"Done","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":335,"context_line":"                generation\u003d0))"},{"line_number":336,"context_line":"        return 0"},{"line_number":337,"context_line":"    else:"},{"line_number":338,"context_line":"        if consumer_generation is None:"},{"line_number":339,"context_line":"            new_generation \u003d 0"},{"line_number":340,"context_line":"        else:"},{"line_number":341,"context_line":"            new_generation \u003d consumer_generation + 1"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_c8f09f6a","line":338,"range":{"start_line":338,"start_character":8,"end_line":338,"end_character":39},"updated":"2018-04-10 22:30:25.000000000","message":"I\u0027m guessing this works (if it does) because\n\n ct.c.generation \u003d\u003d None\n\nwill never match, because generation is not nullable.\n\nSo having this condition here is pretty confusing.  If consumer_generation (the input) is None, but a record existed (which is how we got here) then we already know we want raise ConcurrentUpdateDetected; so it would be clearer to just do that explicitly here.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":335,"context_line":"                generation\u003d0))"},{"line_number":336,"context_line":"        return 0"},{"line_number":337,"context_line":"    else:"},{"line_number":338,"context_line":"        if consumer_generation is None:"},{"line_number":339,"context_line":"            new_generation \u003d 0"},{"line_number":340,"context_line":"        else:"},{"line_number":341,"context_line":"            new_generation \u003d consumer_generation + 1"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_eba6bdc1","line":338,"range":{"start_line":338,"start_character":8,"end_line":338,"end_character":39},"in_reply_to":"bf659307_c8f09f6a","updated":"2018-04-10 23:11:33.000000000","message":"I tried to keep the changes that can only happen with the new microversion in that patch. Note that what you suggests does indeed happen there.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":339,"context_line":"            new_generation \u003d 0"},{"line_number":340,"context_line":"        else:"},{"line_number":341,"context_line":"            new_generation \u003d consumer_generation + 1"},{"line_number":342,"context_line":"    upd_stmt \u003d ct.update().where(sa.and_("},{"line_number":343,"context_line":"            ct.c.uuid \u003d\u003d consumer_uuid,"},{"line_number":344,"context_line":"            ct.c.generation \u003d\u003d consumer_generation)).values("},{"line_number":345,"context_line":"                    generation\u003d(new_generation))"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_4889efc7","line":342,"updated":"2018-04-10 22:30:25.000000000","message":"Either indent the remainder of this method, or remove the `else` on L337 since you return right above it (my preference is for the latter).\n\nThis would have reduced some confusion for me, because I originally missed the return and thought the DB update was being done regardless of whether the generation already existed or was being created.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":339,"context_line":"            new_generation \u003d 0"},{"line_number":340,"context_line":"        else:"},{"line_number":341,"context_line":"            new_generation \u003d consumer_generation + 1"},{"line_number":342,"context_line":"    upd_stmt \u003d ct.update().where(sa.and_("},{"line_number":343,"context_line":"            ct.c.uuid \u003d\u003d consumer_uuid,"},{"line_number":344,"context_line":"            ct.c.generation \u003d\u003d consumer_generation)).values("},{"line_number":345,"context_line":"                    generation\u003d(new_generation))"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_cb8ad919","line":342,"in_reply_to":"bf659307_4889efc7","updated":"2018-04-10 23:11:33.000000000","message":"Done","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":344,"context_line":"            ct.c.generation \u003d\u003d consumer_generation)).values("},{"line_number":345,"context_line":"                    generation\u003d(new_generation))"},{"line_number":346,"context_line":"    res \u003d ctx.session.execute(upd_stmt)"},{"line_number":347,"context_line":"    if res.rowcount !\u003d 1 and enforce_cgen:"},{"line_number":348,"context_line":"        raise exception.ConcurrentUpdateDetected"},{"line_number":349,"context_line":"    return new_generation"},{"line_number":350,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_1a76dc07","line":347,"range":{"start_line":347,"start_character":24,"end_line":347,"end_character":41},"updated":"2018-04-10 22:30:25.000000000","message":"For this one, we want to raise ConcurrentUpdateDetected *regardless* of enforce_cgen.  Otherwise we\u0027re breaking our own contract of always incrementing the generation when allocations are changed.\n\n...but that would be a new API behavior for older microversions, so we can\u0027t.\n\nIn which case, if !enforce_cgen, we should keep trying to increment the generation in the db.  I.e. retry-until-success.  We can\u0027t just let an allocation change go by with no generation change.\n\n(/me argues with self: But if they\u0027re using the older microversion, they don\u0027t care.  But still, it ain\u0027t right.)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":344,"context_line":"            ct.c.generation \u003d\u003d consumer_generation)).values("},{"line_number":345,"context_line":"                    generation\u003d(new_generation))"},{"line_number":346,"context_line":"    res \u003d ctx.session.execute(upd_stmt)"},{"line_number":347,"context_line":"    if res.rowcount !\u003d 1 and enforce_cgen:"},{"line_number":348,"context_line":"        raise exception.ConcurrentUpdateDetected"},{"line_number":349,"context_line":"    return new_generation"},{"line_number":350,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_cb8f3921","line":347,"range":{"start_line":347,"start_character":24,"end_line":347,"end_character":41},"in_reply_to":"bf659307_1a76dc07","updated":"2018-04-10 23:11:33.000000000","message":"We must preserve old crappy behavior at all costs. :)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":344,"context_line":"            ct.c.generation \u003d\u003d consumer_generation)).values("},{"line_number":345,"context_line":"                    generation\u003d(new_generation))"},{"line_number":346,"context_line":"    res \u003d ctx.session.execute(upd_stmt)"},{"line_number":347,"context_line":"    if res.rowcount !\u003d 1 and enforce_cgen:"},{"line_number":348,"context_line":"        raise exception.ConcurrentUpdateDetected"},{"line_number":349,"context_line":"    return new_generation"},{"line_number":350,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_3d8cf3d5","line":347,"range":{"start_line":347,"start_character":24,"end_line":347,"end_character":41},"in_reply_to":"bf659307_cb8f3921","updated":"2018-04-11 05:03:52.000000000","message":"I\u0027m still asserting that we should retry-until-success for this to be \"right\".","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":1790,"context_line":"        \u0027user_id\u0027: fields.StringField(nullable\u003dTrue),"},{"line_number":1791,"context_line":"    }"},{"line_number":1792,"context_line":""},{"line_number":1793,"context_line":"    def ensure_project_user(self, ctx):"},{"line_number":1794,"context_line":"        \"\"\"Examines the project_id, user_id of the object and ensures that if"},{"line_number":1795,"context_line":"        project_id and user_id are set that there are records in the projects"},{"line_number":1796,"context_line":"        and users table for these entities."}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_e8048317","line":1793,"range":{"start_line":1793,"start_character":8,"end_line":1793,"end_character":27},"updated":"2018-04-10 22:30:25.000000000","message":"I thought a bonus of making project_id and user_id nullable was so that this could *remain* ensure_consumer_project_user.  Given the asymmetry where it\u0027s being invoked, that would be preferable, if possible.  If the record doesn\u0027t exist, it\u0027s created with a NULL generation, which makes the logic in _increment a tad simpler, but still work the same.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":1790,"context_line":"        \u0027user_id\u0027: fields.StringField(nullable\u003dTrue),"},{"line_number":1791,"context_line":"    }"},{"line_number":1792,"context_line":""},{"line_number":1793,"context_line":"    def ensure_project_user(self, ctx):"},{"line_number":1794,"context_line":"        \"\"\"Examines the project_id, user_id of the object and ensures that if"},{"line_number":1795,"context_line":"        project_id and user_id are set that there are records in the projects"},{"line_number":1796,"context_line":"        and users table for these entities."}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_ab9a055f","line":1793,"range":{"start_line":1793,"start_character":8,"end_line":1793,"end_character":27},"in_reply_to":"bf659307_e8048317","updated":"2018-04-10 23:11:33.000000000","message":"As noted above, this is executed before the call to _increment_consumer_generation, and would make comparing the state of the consumer when the call was made much more difficult.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":2092,"context_line":"    sel \u003d sel.where(consumer.c.uuid \u003d\u003d consumer_uuid)"},{"line_number":2093,"context_line":"    rec \u003d ctx.session.execute(sel).fetchone()"},{"line_number":2094,"context_line":"    if rec:"},{"line_number":2095,"context_line":"        return rec[\"generation\"]"},{"line_number":2096,"context_line":"    else:"},{"line_number":2097,"context_line":"        return None"},{"line_number":2098,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_7a8358aa","line":2095,"range":{"start_line":2095,"start_character":8,"end_line":2095,"end_character":32},"updated":"2018-04-10 22:30:25.000000000","message":"Will this return None if the record exists with a NULL generation?  (That\u0027s what we want, IIUC.)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":2092,"context_line":"    sel \u003d sel.where(consumer.c.uuid \u003d\u003d consumer_uuid)"},{"line_number":2093,"context_line":"    rec \u003d ctx.session.execute(sel).fetchone()"},{"line_number":2094,"context_line":"    if rec:"},{"line_number":2095,"context_line":"        return rec[\"generation\"]"},{"line_number":2096,"context_line":"    else:"},{"line_number":2097,"context_line":"        return None"},{"line_number":2098,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_0ba9b17a","line":2095,"range":{"start_line":2095,"start_character":8,"end_line":2095,"end_character":32},"in_reply_to":"bf659307_7a8358aa","updated":"2018-04-10 23:11:33.000000000","message":"\u0027generation\u0027 is not nullable. If there\u0027s a record, the migration will insert a value of 0.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":2180,"context_line":"                visited_rps[rp.uuid] \u003d rp"},{"line_number":2181,"context_line":"                continue"},{"line_number":2182,"context_line":"            consumer_id \u003d alloc.consumer_id"},{"line_number":2183,"context_line":"            if \"consumer_generation\" in alloc:"},{"line_number":2184,"context_line":"                consumer_generation \u003d alloc.consumer_generation"},{"line_number":2185,"context_line":"            else:"},{"line_number":2186,"context_line":"                consumer_generation \u003d None"},{"line_number":2187,"context_line":"            # Only set consumer \u003c-\u003e project/user association if we haven\u0027t set"},{"line_number":2188,"context_line":"            # it already."},{"line_number":2189,"context_line":"            if consumer_id not in seen_consumers:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_1aab3c1d","line":2186,"range":{"start_line":2183,"start_character":0,"end_line":2186,"end_character":42},"updated":"2018-04-10 22:30:25.000000000","message":"or\n\n consumer_generation \u003d getattr(alloc, \"consumer_generation\", None)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":2180,"context_line":"                visited_rps[rp.uuid] \u003d rp"},{"line_number":2181,"context_line":"                continue"},{"line_number":2182,"context_line":"            consumer_id \u003d alloc.consumer_id"},{"line_number":2183,"context_line":"            if \"consumer_generation\" in alloc:"},{"line_number":2184,"context_line":"                consumer_generation \u003d alloc.consumer_generation"},{"line_number":2185,"context_line":"            else:"},{"line_number":2186,"context_line":"                consumer_generation \u003d None"},{"line_number":2187,"context_line":"            # Only set consumer \u003c-\u003e project/user association if we haven\u0027t set"},{"line_number":2188,"context_line":"            # it already."},{"line_number":2189,"context_line":"            if consumer_id not in seen_consumers:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_2bd4b5e8","line":2186,"range":{"start_line":2183,"start_character":0,"end_line":2186,"end_character":42},"in_reply_to":"bf659307_1aab3c1d","updated":"2018-04-10 23:11:33.000000000","message":"No, that won\u0027t work. These pesky o.vo things will raise:\n\nNotImplementedError: Cannot load \u0027consumer_generation\u0027 in the base class\n\nif you try that.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":2180,"context_line":"                visited_rps[rp.uuid] \u003d rp"},{"line_number":2181,"context_line":"                continue"},{"line_number":2182,"context_line":"            consumer_id \u003d alloc.consumer_id"},{"line_number":2183,"context_line":"            if \"consumer_generation\" in alloc:"},{"line_number":2184,"context_line":"                consumer_generation \u003d alloc.consumer_generation"},{"line_number":2185,"context_line":"            else:"},{"line_number":2186,"context_line":"                consumer_generation \u003d None"},{"line_number":2187,"context_line":"            # Only set consumer \u003c-\u003e project/user association if we haven\u0027t set"},{"line_number":2188,"context_line":"            # it already."},{"line_number":2189,"context_line":"            if consumer_id not in seen_consumers:"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_9db41f84","line":2186,"range":{"start_line":2183,"start_character":0,"end_line":2186,"end_character":42},"in_reply_to":"bf659307_2bd4b5e8","updated":"2018-04-11 05:03:52.000000000","message":"ohhh.  Sounds like a bug in ovo.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":2217,"context_line":"        # changes always happen atomically."},{"line_number":2218,"context_line":"        for rp in visited_rps.values():"},{"line_number":2219,"context_line":"            rp.generation \u003d _increment_provider_generation(context, rp)"},{"line_number":2220,"context_line":"        # Likewise, check the generation for all the consumers. If some other"},{"line_number":2221,"context_line":"        # process changed the allocations for the consumer while this was"},{"line_number":2222,"context_line":"        # running, raise a ConcurrentUpdateDetected exception, which will"},{"line_number":2223,"context_line":"        # rollback the transaction and notify the caller."},{"line_number":2224,"context_line":"        for consumer_id, consumer_dict in seen_consumers.items():"},{"line_number":2225,"context_line":"            _increment_consumer_generation(context, consumer_id, consumer_dict)"},{"line_number":2226,"context_line":""},{"line_number":2227,"context_line":"    @classmethod"},{"line_number":2228,"context_line":"    def get_all_by_resource_provider(cls, context, rp):"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_4841ef38","line":2225,"range":{"start_line":2220,"start_character":0,"end_line":2225,"end_character":79},"updated":"2018-04-10 22:30:25.000000000","message":"Right, so it\u0027s confusing that this is now the thing that creates the consumer record.  I would strongly prefer that not be the case, and we go back to ensure_consumer_project_user as noted above.  (If you convince me otherwise, I\u0027ll at least insist on a prominent comment here stating that the consumer record is \"ensured\" herein.)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":2217,"context_line":"        # changes always happen atomically."},{"line_number":2218,"context_line":"        for rp in visited_rps.values():"},{"line_number":2219,"context_line":"            rp.generation \u003d _increment_provider_generation(context, rp)"},{"line_number":2220,"context_line":"        # Likewise, check the generation for all the consumers. If some other"},{"line_number":2221,"context_line":"        # process changed the allocations for the consumer while this was"},{"line_number":2222,"context_line":"        # running, raise a ConcurrentUpdateDetected exception, which will"},{"line_number":2223,"context_line":"        # rollback the transaction and notify the caller."},{"line_number":2224,"context_line":"        for consumer_id, consumer_dict in seen_consumers.items():"},{"line_number":2225,"context_line":"            _increment_consumer_generation(context, consumer_id, consumer_dict)"},{"line_number":2226,"context_line":""},{"line_number":2227,"context_line":"    @classmethod"},{"line_number":2228,"context_line":"    def get_all_by_resource_provider(cls, context, rp):"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_8b31415c","line":2225,"range":{"start_line":2220,"start_character":0,"end_line":2225,"end_character":79},"in_reply_to":"bf659307_4841ef38","updated":"2018-04-10 23:11:33.000000000","message":"Creating the consumer record earlier will make this call much more difficult, as it requires examining the state of the consumer record, including whether or not it exists. If the consumer record was created earlier in the process, we would have to snapshot the state before its creation, and pass that around. It\u0027s just much simpler to do it this way.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":2313,"context_line":"                \"user_id\": user_id,"},{"line_number":2314,"context_line":"                \"enforce_cgen\": enforce_cgen}"},{"line_number":2315,"context_line":"        _delete_allocations_for_consumer(ctx, consumer_uuid)"},{"line_number":2316,"context_line":"        _increment_consumer_generation(ctx, consumer_uuid, consumer_dict)"},{"line_number":2317,"context_line":""},{"line_number":2318,"context_line":"    def __repr__(self):"},{"line_number":2319,"context_line":"        strings \u003d [repr(x) for x in self.objects]"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_28b91b14","line":2316,"range":{"start_line":2316,"start_character":8,"end_line":2316,"end_character":73},"updated":"2018-04-10 22:30:25.000000000","message":"Sooo.... we keep the consumer around, even though we\u0027ve deleted all his allocations.\n\nIs that the right thing?  I\u0027m gonna say no.  Here\u0027s fer why:\n\nConsumers are ephemeral.  E.g. in the nova case, they\u0027re instances which, once deleted, never come back.  So we\u0027re leaking database records that will \"never\" be cleaned up.  (I think we have this same issue for aggregates, but those are less ephemeral and there are also likely to be fewer of them with less churn.)\n\nBut can we eff up, concurrency-wise, if we delete the consumer record?  I don\u0027t think so:\n\n- X prepares an allocation for a consumer he thinks is new.  generation\u003dNone.\n- Y creates an allocation for that same consumer, sends it to placement with generation\u003dNone, the consumer gets created with generation\u003d0.\n- Y deletes the allocations for that consumer, specifying generation\u003d0, whereupon we delete that consumer record.\n- X, who has been waiting with his prepared request all this time, sends down his allocation with generation\u003dNone.  Placement (re)creates that consumer record with generation\u003d0.\n\nIs there anything wrong with that?\n\nTL;DR: I think we should delete the consumer record rather than incrementing the generation.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":2313,"context_line":"                \"user_id\": user_id,"},{"line_number":2314,"context_line":"                \"enforce_cgen\": enforce_cgen}"},{"line_number":2315,"context_line":"        _delete_allocations_for_consumer(ctx, consumer_uuid)"},{"line_number":2316,"context_line":"        _increment_consumer_generation(ctx, consumer_uuid, consumer_dict)"},{"line_number":2317,"context_line":""},{"line_number":2318,"context_line":"    def __repr__(self):"},{"line_number":2319,"context_line":"        strings \u003d [repr(x) for x in self.objects]"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_1da80f1e","line":2316,"range":{"start_line":2316,"start_character":8,"end_line":2316,"end_character":73},"in_reply_to":"bf659307_0b1e91ba","updated":"2018-04-11 05:03:52.000000000","message":"wfm.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"dc0757a0c44edcf25c51cdc46578683a635e908b","unresolved":false,"context_lines":[{"line_number":2313,"context_line":"                \"user_id\": user_id,"},{"line_number":2314,"context_line":"                \"enforce_cgen\": enforce_cgen}"},{"line_number":2315,"context_line":"        _delete_allocations_for_consumer(ctx, consumer_uuid)"},{"line_number":2316,"context_line":"        _increment_consumer_generation(ctx, consumer_uuid, consumer_dict)"},{"line_number":2317,"context_line":""},{"line_number":2318,"context_line":"    def __repr__(self):"},{"line_number":2319,"context_line":"        strings \u003d [repr(x) for x in self.objects]"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_7dc34158","line":2316,"range":{"start_line":2316,"start_character":8,"end_line":2316,"end_character":73},"in_reply_to":"bf659307_1da80f1e","updated":"2018-04-11 11:38:54.000000000","message":"While sleeping it occurred to me that this should actually take the form:\n\n DELETE FROM Consumers\n     WHERE (consumer_uuid \u003d\u003d $specified_uuid\n        AND generation \u003d\u003d $specified_generation)\n\n if rows !\u003d 0:\n     raise ConcurrentUpdateDetected\n\nOtherwise we have a hole between when we \"successfully\" check/update the generation and when we delete the row.\n\nRegardless, I still don\u0027t think the deletion (or initial creation) should be done by _increment.  Let\u0027s talk about this today.","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":2313,"context_line":"                \"user_id\": user_id,"},{"line_number":2314,"context_line":"                \"enforce_cgen\": enforce_cgen}"},{"line_number":2315,"context_line":"        _delete_allocations_for_consumer(ctx, consumer_uuid)"},{"line_number":2316,"context_line":"        _increment_consumer_generation(ctx, consumer_uuid, consumer_dict)"},{"line_number":2317,"context_line":""},{"line_number":2318,"context_line":"    def __repr__(self):"},{"line_number":2319,"context_line":"        strings \u003d [repr(x) for x in self.objects]"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_0b1e91ba","line":2316,"range":{"start_line":2316,"start_character":8,"end_line":2316,"end_character":73},"in_reply_to":"bf659307_28b91b14","updated":"2018-04-10 23:11:33.000000000","message":"We still need the check, in order to prevent one process from deleting allocations created by another in a race. We could add an additional flag that, if the generation check passes, deletes the consumer record. Watcha think about that?","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"50be00ed4cc4c7399863e7b49e9659cebf8a14d0","unresolved":false,"context_lines":[{"line_number":339,"context_line":"            # change, so if we get here, we raced and should just pass"},{"line_number":340,"context_line":"            # if the consumer already exists."},{"line_number":341,"context_line":"            pass"},{"line_number":342,"context_line":"        return 0"},{"line_number":343,"context_line":"    # There is a consumer record."},{"line_number":344,"context_line":"    if consumer_generation is None:"},{"line_number":345,"context_line":"        new_generation \u003d 0"}],"source_content_type":"text/x-python","patch_set":7,"id":"bf659307_1d8fafcd","line":342,"range":{"start_line":342,"start_character":8,"end_line":342,"end_character":16},"updated":"2018-04-11 05:03:52.000000000","message":"Is this used for anything by the caller?  Maybe we should take it out (just have the method return - here and everywhere).  Otherwise I think in the DBDuplicateEntry case we would technically need to go retrieve the actual generation to return.\n\nBut I also don\u0027t think this behavior is quite right.  If we raced and a record now exists, we should proceed with the \"consumer record exists\" logic below.","commit_id":"316b85954ca6517cf166fcedc5ffc1e4cb7ba26c"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"b58ea1582bf9c47099543141be406f758a5ec692","unresolved":false,"context_lines":[{"line_number":2092,"context_line":""},{"line_number":2093,"context_line":""},{"line_number":2094,"context_line":"@db_api.api_context_manager.reader"},{"line_number":2095,"context_line":"def _generation_for_consumer(ctx, consumer_uuid):"},{"line_number":2096,"context_line":"    consumer \u003d sa.alias(_CONSUMER_TBL, name\u003d\"c\")"},{"line_number":2097,"context_line":"    sel \u003d sa.select([\"generation\"]).select_from(consumer)"},{"line_number":2098,"context_line":"    sel \u003d sel.where(consumer.c.uuid \u003d\u003d consumer_uuid)"}],"source_content_type":"text/x-python","patch_set":7,"id":"bf659307_b4e48e0e","line":2095,"range":{"start_line":2095,"start_character":0,"end_line":2095,"end_character":28},"updated":"2018-04-11 14:46:37.000000000","message":"I don\u0027t see where this function is being called anywhere?","commit_id":"316b85954ca6517cf166fcedc5ffc1e4cb7ba26c"}],"nova/tests/functional/api/openstack/placement/fixtures.py":[{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"70a6faa20745bad7e67987cb2f57a8362a43dd5d","unresolved":false,"context_lines":[{"line_number":129,"context_line":"            self.context, name\u003drp_name, uuid\u003drp_uuid)"},{"line_number":130,"context_line":"        rp.create()"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"        # Create some DISK_GB inventory and allocations for one consumer."},{"line_number":133,"context_line":"        consumer_id \u003d uuidutils.generate_uuid()"},{"line_number":134,"context_line":"        inventory \u003d rp_obj.Inventory("},{"line_number":135,"context_line":"            self.context, resource_provider\u003drp,"}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_0dcb2c53","line":132,"updated":"2018-04-09 18:25:40.000000000","message":"Should you punt these corrections to when you actually change the gabbi tests? I don\u0027t mind either way.","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"448f7b6fdab35b40710556146575b2841ea3ff10","unresolved":false,"context_lines":[{"line_number":129,"context_line":"            self.context, name\u003drp_name, uuid\u003drp_uuid)"},{"line_number":130,"context_line":"        rp.create()"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"        # Create some DISK_GB inventory and allocations for one consumer."},{"line_number":133,"context_line":"        consumer_id \u003d uuidutils.generate_uuid()"},{"line_number":134,"context_line":"        inventory \u003d rp_obj.Inventory("},{"line_number":135,"context_line":"            self.context, resource_provider\u003drp,"}],"source_content_type":"text/x-python","patch_set":5,"id":"bf659307_c3e3c3a3","line":132,"in_reply_to":"bf659307_0dcb2c53","updated":"2018-04-09 19:57:39.000000000","message":"My mistake. I initially added the functional tests in this patch, and then realized that there wasn\u0027t yet the microversion to run them. So I move the tests, but left these comment changes. I\u0027ve moved these to the next patch, too, to make things less confusing.","commit_id":"7359864ab9c2d82d327193eb10cde05a5ff4df5e"}],"nova/tests/unit/api/openstack/placement/objects/test_resource_provider.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":414,"context_line":"                                                      objects\u003d[obj])"},{"line_number":415,"context_line":"        self.assertRaises(exception.ObjectActionError, alloc_list.create_all)"},{"line_number":416,"context_line":""},{"line_number":417,"context_line":"    def test_create_with_old_generation_fails(self):"},{"line_number":418,"context_line":"        rp \u003d resource_provider.ResourceProvider("},{"line_number":419,"context_line":"                context\u003dself.context,"},{"line_number":420,"context_line":"                uuid\u003d_RESOURCE_PROVIDER_UUID,"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_28711b45","line":417,"range":{"start_line":417,"start_character":25,"end_line":417,"end_character":28},"updated":"2018-04-10 22:30:25.000000000","message":"`invalid`, maybe","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"d521e6abe2d273f63391420703b05cb790ff6c84","unresolved":false,"context_lines":[{"line_number":414,"context_line":"                                                      objects\u003d[obj])"},{"line_number":415,"context_line":"        self.assertRaises(exception.ObjectActionError, alloc_list.create_all)"},{"line_number":416,"context_line":""},{"line_number":417,"context_line":"    def test_create_with_old_generation_fails(self):"},{"line_number":418,"context_line":"        rp \u003d resource_provider.ResourceProvider("},{"line_number":419,"context_line":"                context\u003dself.context,"},{"line_number":420,"context_line":"                uuid\u003d_RESOURCE_PROVIDER_UUID,"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_4b6d6930","line":417,"range":{"start_line":417,"start_character":25,"end_line":417,"end_character":28},"in_reply_to":"bf659307_28711b45","updated":"2018-04-10 23:11:33.000000000","message":"Done","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"e04073df34fe74472c93d76878713967e3c83930","unresolved":false,"context_lines":[{"line_number":459,"context_line":"                objects\u003d[obj], enforce_consumer_generation\u003dTrue)"},{"line_number":460,"context_line":"        self.assertRaises(exception.ConcurrentUpdateDetected,"},{"line_number":461,"context_line":"                alloc_list.create_all)"},{"line_number":462,"context_line":""},{"line_number":463,"context_line":""},{"line_number":464,"context_line":"class TestAllocationListNoDB(test_objects._LocalTest):"},{"line_number":465,"context_line":"    USES_DB \u003d False"}],"source_content_type":"text/x-python","patch_set":6,"id":"bf659307_28a59b47","line":462,"updated":"2018-04-10 22:30:25.000000000","message":"Moar tests.  (I don\u0027t want to enumerate them because if my review is on point, the list will change.)","commit_id":"301c3ea608e1783f25a6bfe5ab936ec934ff0d91"}]}
