)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":31,"context_line":"of dicts it contains. That can be be directly transformed into Trait"},{"line_number":32,"context_line":"and ResourceClass objects. The order of the results of this method"},{"line_number":33,"context_line":"are not predictable, and sorting them would add cost for no benefit,"},{"line_number":34,"context_line":"so gabbi tests which had previously relied on the ordered of returned"},{"line_number":35,"context_line":"resource classes have been removed."},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"From the API, listing traits and resource classes (without filters) now"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"7faddb67_be812dc6","line":34,"range":{"start_line":34,"start_character":50,"end_line":34,"end_character":57},"updated":"2019-07-23 18:35:10.000000000","message":"order","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"}],"placement/attribute_cache.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"7faddb67_0d937951","updated":"2019-07-23 15:58:15.000000000","message":"[Later] I see that many of my comments apply to cut/pasted code from the original ResourceClassCache, and are therefore suitable for a fup.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":20,"context_line":"_TRAIT_TBL \u003d models.Trait.__table__"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"@db_api.placement_context_manager.reader"},{"line_number":24,"context_line":"def ensure_rc(ctx):"},{"line_number":25,"context_line":"    \"\"\"Ensures that a resource class cache has been created for the provided"},{"line_number":26,"context_line":"    context."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_0ad7df25","line":23,"range":{"start_line":23,"start_character":0,"end_line":23,"end_character":40},"updated":"2019-07-23 15:58:15.000000000","message":"It\u0027s possible this...","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":31,"context_line":"    return ResourceClassCache(ctx)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"@db_api.placement_context_manager.reader"},{"line_number":35,"context_line":"def ensure_trait(ctx):"},{"line_number":36,"context_line":"    \"\"\"Ensures that a trait cache has been created for the provided context."},{"line_number":37,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_cab9c7e9","line":34,"range":{"start_line":34,"start_character":0,"end_line":34,"end_character":40},"updated":"2019-07-23 15:58:15.000000000","message":"...and this don\u0027t need db contexts, since they\u0027re just initializing dict fields, and the db work is being done by _refresh_from_db, which has its own db context.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"@db_api.placement_context_manager.reader"},{"line_number":35,"context_line":"def ensure_trait(ctx):"},{"line_number":36,"context_line":"    \"\"\"Ensures that a trait cache has been created for the provided context."},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"    :param ctx: `placement.context.RequestContext` that may be used to grab a"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_2ad97b21","line":35,"range":{"start_line":35,"start_character":4,"end_line":35,"end_character":16},"updated":"2019-07-23 15:58:15.000000000","message":"I\u0027m not convinced this layer of indirection is needed anymore, since these are just being created from one place now. I.e. why can\u0027t RequestContext just say\n\n self.rc_cache \u003d attribute_cache.ResourceClassCache(self)\n self.trait_cache \u003d attribute_cache.TraitCache(self)\n\nBut if these are going to be kept, I would think their entire usefulness would be to ensure that the caches are singleton-per-context, which isn\u0027t happening atm.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"@db_api.placement_context_manager.reader"},{"line_number":35,"context_line":"def ensure_trait(ctx):"},{"line_number":36,"context_line":"    \"\"\"Ensures that a trait cache has been created for the provided context."},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"    :param ctx: `placement.context.RequestContext` that may be used to grab a"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_6d878db8","line":35,"range":{"start_line":35,"start_character":4,"end_line":35,"end_character":16},"in_reply_to":"7faddb67_2ad97b21","updated":"2019-07-23 16:18:22.000000000","message":"I think you\u0027re right, but I was proceeding stepwise. If we\u0027re happy to make things more right I can and will.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":42,"context_line":""},{"line_number":43,"context_line":""},{"line_number":44,"context_line":"@db_api.placement_context_manager.reader"},{"line_number":45,"context_line":"def _refresh_from_db(ctx, cache):"},{"line_number":46,"context_line":"    \"\"\"Grabs all resource classes or traits from the respective DB table and"},{"line_number":47,"context_line":"    populates the supplied cache object\u0027s internal integer and string"},{"line_number":48,"context_line":"    identifier dicts."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_0a327fa8","line":45,"range":{"start_line":45,"start_character":4,"end_line":45,"end_character":20},"updated":"2019-07-23 15:58:15.000000000","message":"This feels weird as a module level function rather than a method on AttributeCache, where `cache` would be `self`. \nBack of head said this was because the decorator expects the first arg to be the context - but I double checked and it\u0027s set up to handle instance \u0026 class methods as well [1]. Is there some other reason for this to be out here?\n\n[1] https://opendev.org/openstack/oslo.db/src/branch/master/oslo_db/sqlalchemy/enginefacade.py#L1002-L1006","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":42,"context_line":""},{"line_number":43,"context_line":""},{"line_number":44,"context_line":"@db_api.placement_context_manager.reader"},{"line_number":45,"context_line":"def _refresh_from_db(ctx, cache):"},{"line_number":46,"context_line":"    \"\"\"Grabs all resource classes or traits from the respective DB table and"},{"line_number":47,"context_line":"    populates the supplied cache object\u0027s internal integer and string"},{"line_number":48,"context_line":"    identifier dicts."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4d8e51d1","line":45,"range":{"start_line":45,"start_character":4,"end_line":45,"end_character":20},"in_reply_to":"7faddb67_0a327fa8","updated":"2019-07-23 16:18:22.000000000","message":"I reckon this is historical. Being within the object would be fine.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":58,"context_line":"    cache.all_cache \u003d {r[1]: r for r in res}"},{"line_number":59,"context_line":""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"class AttributeCache(object):"},{"line_number":62,"context_line":"    \"\"\"A cache of integer and string lookup values for string-based attributes."},{"line_number":63,"context_line":"    \"\"\""},{"line_number":64,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ea04c380","line":61,"range":{"start_line":61,"start_character":6,"end_line":61,"end_character":20},"updated":"2019-07-23 15:58:15.000000000","message":"This should be private, since it is (and should be) only referenced by its subclasses.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":59,"context_line":""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"class AttributeCache(object):"},{"line_number":62,"context_line":"    \"\"\"A cache of integer and string lookup values for string-based attributes."},{"line_number":63,"context_line":"    \"\"\""},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"    def __init__(self, ctx):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ea4a8381","line":62,"updated":"2019-07-23 15:58:15.000000000","message":"Would be nice for this docstring to mention that subclasses must define .table and .not_found.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":59,"context_line":""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"class AttributeCache(object):"},{"line_number":62,"context_line":"    \"\"\"A cache of integer and string lookup values for string-based attributes."},{"line_number":63,"context_line":"    \"\"\""},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"    def __init__(self, ctx):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_adb6e589","line":62,"in_reply_to":"7faddb67_ea4a8381","updated":"2019-07-23 16:18:22.000000000","message":"aye","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":68,"context_line":"        :param ctx: `placement.context.RequestContext` from which we can grab a"},{"line_number":69,"context_line":"                    `SQLAlchemy.Connection` object to use for any DB lookups."},{"line_number":70,"context_line":"        \"\"\""},{"line_number":71,"context_line":"        self.ctx \u003d ctx"},{"line_number":72,"context_line":"        self.id_cache \u003d {}"},{"line_number":73,"context_line":"        self.str_cache \u003d {}"},{"line_number":74,"context_line":"        self.all_cache \u003d {}"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"    def clear(self):"},{"line_number":77,"context_line":"        self.id_cache \u003d {}"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_8a72efe2","line":74,"range":{"start_line":71,"start_character":0,"end_line":74,"end_character":27},"updated":"2019-07-23 15:58:15.000000000","message":"These should be private","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":87,"context_line":"        :param attr_str: The string representation of the attribute to look up"},{"line_number":88,"context_line":"                         a numeric identifier for."},{"line_number":89,"context_line":"        :returns Integer identifier for the attribute."},{"line_number":90,"context_line":"        :raises `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":91,"context_line":"                if attribute cannot be found in the DB."},{"line_number":92,"context_line":"        \"\"\""},{"line_number":93,"context_line":"        attr_id \u003d self.id_cache.get(attr_str)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_8a20afb2","line":90,"range":{"start_line":90,"start_character":16,"end_line":90,"end_character":78},"updated":"2019-07-23 15:58:15.000000000","message":"\"An instance of the subclass\u0027s ``not_found`` exception\"\n\nYou know, so you can add a ConsumerCache and not have to update this docstring :P","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":87,"context_line":"        :param attr_str: The string representation of the attribute to look up"},{"line_number":88,"context_line":"                         a numeric identifier for."},{"line_number":89,"context_line":"        :returns Integer identifier for the attribute."},{"line_number":90,"context_line":"        :raises `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":91,"context_line":"                if attribute cannot be found in the DB."},{"line_number":92,"context_line":"        \"\"\""},{"line_number":93,"context_line":"        attr_id \u003d self.id_cache.get(attr_str)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ad6b45ef","line":90,"range":{"start_line":90,"start_character":16,"end_line":90,"end_character":78},"in_reply_to":"7faddb67_8a20afb2","updated":"2019-07-23 16:18:22.000000000","message":"true\n\nConsumerCache would make me far more antsy than these caches.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":98,"context_line":"        _refresh_from_db(self.ctx, self)"},{"line_number":99,"context_line":"        if attr_str in self.id_cache:"},{"line_number":100,"context_line":"            return self.id_cache[attr_str]"},{"line_number":101,"context_line":"        raise self.not_found(name\u003dattr_str)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"    def all_from_string(self, attr_str):"},{"line_number":104,"context_line":"        \"\"\"Given a string representation of an attribute -- e.g. \"DISK_GB\""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_aa824bff","line":101,"range":{"start_line":101,"start_character":19,"end_line":101,"end_character":28},"updated":"2019-07-23 15:58:15.000000000","message":"The fact that this and .table (in _refresh_from_db) only exist in subclasses leads me to want AttributeCache to be abstract. Whereupon it would also be nice if .not_found and .table were @abstractpropertyZ, but that might be overkill.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":98,"context_line":"        _refresh_from_db(self.ctx, self)"},{"line_number":99,"context_line":"        if attr_str in self.id_cache:"},{"line_number":100,"context_line":"            return self.id_cache[attr_str]"},{"line_number":101,"context_line":"        raise self.not_found(name\u003dattr_str)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"    def all_from_string(self, attr_str):"},{"line_number":104,"context_line":"        \"\"\"Given a string representation of an attribute -- e.g. \"DISK_GB\""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_1e2c0198","line":101,"range":{"start_line":101,"start_character":19,"end_line":101,"end_character":28},"in_reply_to":"7faddb67_2d7f55ac","updated":"2019-07-23 18:35:10.000000000","message":"Yeah, I agree, it\u0027s too awkward to be generally practical. It\u0027s just, right now AttributeCache can be instantiated, and you\u0027ll only get an AttributeError when you use it, and it\u0027ll be a slightly mysterious one that will take a minute to figure out. So like, it would be nice if there was a terse, convenient way to improve that.\n\n[Later] See new comment in __init__ in PS2.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":98,"context_line":"        _refresh_from_db(self.ctx, self)"},{"line_number":99,"context_line":"        if attr_str in self.id_cache:"},{"line_number":100,"context_line":"            return self.id_cache[attr_str]"},{"line_number":101,"context_line":"        raise self.not_found(name\u003dattr_str)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"    def all_from_string(self, attr_str):"},{"line_number":104,"context_line":"        \"\"\"Given a string representation of an attribute -- e.g. \"DISK_GB\""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_2d7f55ac","line":101,"range":{"start_line":101,"start_character":19,"end_line":101,"end_character":28},"in_reply_to":"7faddb67_aa824bff","updated":"2019-07-23 16:18:22.000000000","message":"I\u0027ve never been too keen on doing abstract classes in in Python, it seems... noisy?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":106,"context_line":""},{"line_number":107,"context_line":"        :param attr_str: The string representation of the attribute for which"},{"line_number":108,"context_line":"                         to look up the object."},{"line_number":109,"context_line":"        :returns: dict representing the attribute cfields, if the attrubte was"},{"line_number":110,"context_line":"                  found in the appropriate database table."},{"line_number":111,"context_line":"        :raises: `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":112,"context_line":"                 if attr_str cannot be found in the DB."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_aa448b75","line":109,"range":{"start_line":109,"start_character":50,"end_line":109,"end_character":57},"updated":"2019-07-23 15:58:15.000000000","message":"Is this a typo? Or what\u0027s a cfield?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":106,"context_line":""},{"line_number":107,"context_line":"        :param attr_str: The string representation of the attribute for which"},{"line_number":108,"context_line":"                         to look up the object."},{"line_number":109,"context_line":"        :returns: dict representing the attribute cfields, if the attrubte was"},{"line_number":110,"context_line":"                  found in the appropriate database table."},{"line_number":111,"context_line":"        :raises: `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":112,"context_line":"                 if attr_str cannot be found in the DB."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ca49c78b","line":109,"range":{"start_line":109,"start_character":66,"end_line":109,"end_character":74},"updated":"2019-07-23 15:58:15.000000000","message":"attribute","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":106,"context_line":""},{"line_number":107,"context_line":"        :param attr_str: The string representation of the attribute for which"},{"line_number":108,"context_line":"                         to look up the object."},{"line_number":109,"context_line":"        :returns: dict representing the attribute cfields, if the attrubte was"},{"line_number":110,"context_line":"                  found in the appropriate database table."},{"line_number":111,"context_line":"        :raises: `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":112,"context_line":"                 if attr_str cannot be found in the DB."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4d7c11b5","line":109,"range":{"start_line":109,"start_character":50,"end_line":109,"end_character":57},"in_reply_to":"7faddb67_aa448b75","updated":"2019-07-23 16:18:22.000000000","message":"it is a typo","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":108,"context_line":"                         to look up the object."},{"line_number":109,"context_line":"        :returns: dict representing the attribute cfields, if the attrubte was"},{"line_number":110,"context_line":"                  found in the appropriate database table."},{"line_number":111,"context_line":"        :raises: `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":112,"context_line":"                 if attr_str cannot be found in the DB."},{"line_number":113,"context_line":"        \"\"\""},{"line_number":114,"context_line":"        attr_id_str \u003d self.all_cache.get(attr_str)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4a55d71c","line":111,"range":{"start_line":111,"start_character":17,"end_line":111,"end_character":79},"updated":"2019-07-23 15:58:15.000000000","message":"ditto","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":108,"context_line":"                         to look up the object."},{"line_number":109,"context_line":"        :returns: dict representing the attribute cfields, if the attrubte was"},{"line_number":110,"context_line":"                  found in the appropriate database table."},{"line_number":111,"context_line":"        :raises: `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":112,"context_line":"                 if attr_str cannot be found in the DB."},{"line_number":113,"context_line":"        \"\"\""},{"line_number":114,"context_line":"        attr_id_str \u003d self.all_cache.get(attr_str)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ed4a9d81","line":111,"range":{"start_line":111,"start_character":17,"end_line":111,"end_character":79},"in_reply_to":"7faddb67_4a55d71c","updated":"2019-07-23 16:18:22.000000000","message":"aye","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":121,"context_line":"            return self.all_cache[attr_str]"},{"line_number":122,"context_line":"        raise self.not_found(name\u003dattr_str)"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def string_from_id(self, attr_id):"},{"line_number":125,"context_line":"        \"\"\"The reverse of the id_from_string() method. Given a supplied numeric"},{"line_number":126,"context_line":"        identifier for an attribute, we look up the corresponding string"},{"line_number":127,"context_line":"        representation, via a DB lookup. The results of these DB lookups are"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_6a269319","line":124,"range":{"start_line":124,"start_character":8,"end_line":124,"end_character":22},"updated":"2019-07-23 15:58:15.000000000","message":"These three methods have identical logic which could be DRYed for maintainability into a (private) helper that accepts the dict to be used (self.{id|all|str}_cache) and the key.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":121,"context_line":"            return self.all_cache[attr_str]"},{"line_number":122,"context_line":"        raise self.not_found(name\u003dattr_str)"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def string_from_id(self, attr_id):"},{"line_number":125,"context_line":"        \"\"\"The reverse of the id_from_string() method. Given a supplied numeric"},{"line_number":126,"context_line":"        identifier for an attribute, we look up the corresponding string"},{"line_number":127,"context_line":"        representation, via a DB lookup. The results of these DB lookups are"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_cd49e18b","line":124,"range":{"start_line":124,"start_character":8,"end_line":124,"end_character":22},"in_reply_to":"7faddb67_6a269319","updated":"2019-07-23 16:18:22.000000000","message":"yeah, as you realized later this is old code and I didn\u0027t want to mess with mechanism. agree that it might be worth exploration later","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":122,"context_line":"        raise self.not_found(name\u003dattr_str)"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def string_from_id(self, attr_id):"},{"line_number":125,"context_line":"        \"\"\"The reverse of the id_from_string() method. Given a supplied numeric"},{"line_number":126,"context_line":"        identifier for an attribute, we look up the corresponding string"},{"line_number":127,"context_line":"        representation, via a DB lookup. The results of these DB lookups are"},{"line_number":128,"context_line":"        cached since the lookups are so frequent."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_aa124b61","line":125,"range":{"start_line":125,"start_character":63,"end_line":125,"end_character":71},"updated":"2019-07-23 15:58:15.000000000","message":"redundant","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":128,"context_line":"        cached since the lookups are so frequent."},{"line_number":129,"context_line":""},{"line_number":130,"context_line":"        :param attr_id: The numeric representation of the attribute to look"},{"line_number":131,"context_line":"                      up a string identifier for."},{"line_number":132,"context_line":"        :returns: String identifier for the attribute."},{"line_number":133,"context_line":"        :raises `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":134,"context_line":"                if attr_id cannot be found in the DB."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ca178751","line":131,"range":{"start_line":131,"start_character":20,"end_line":131,"end_character":23},"updated":"2019-07-23 15:58:15.000000000","message":"odd indent","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":128,"context_line":"        cached since the lookups are so frequent."},{"line_number":129,"context_line":""},{"line_number":130,"context_line":"        :param attr_id: The numeric representation of the attribute to look"},{"line_number":131,"context_line":"                      up a string identifier for."},{"line_number":132,"context_line":"        :returns: String identifier for the attribute."},{"line_number":133,"context_line":"        :raises `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":134,"context_line":"                if attr_id cannot be found in the DB."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_8d5be952","line":131,"range":{"start_line":131,"start_character":20,"end_line":131,"end_character":23},"in_reply_to":"7faddb67_ca178751","updated":"2019-07-23 16:18:22.000000000","message":"based on global search and replace, will fix","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":130,"context_line":"        :param attr_id: The numeric representation of the attribute to look"},{"line_number":131,"context_line":"                      up a string identifier for."},{"line_number":132,"context_line":"        :returns: String identifier for the attribute."},{"line_number":133,"context_line":"        :raises `exception.ResourceClassNotFound` or `exception.TraitNotFound`"},{"line_number":134,"context_line":"                if attr_id cannot be found in the DB."},{"line_number":135,"context_line":"        \"\"\""},{"line_number":136,"context_line":"        attr_str \u003d self.str_cache.get(attr_id)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ea184385","line":133,"range":{"start_line":133,"start_character":16,"end_line":133,"end_character":78},"updated":"2019-07-23 15:58:15.000000000","message":"ditto","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":145,"context_line":""},{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_aacfab27","line":148,"range":{"start_line":148,"start_character":8,"end_line":148,"end_character":36},"updated":"2019-07-23 15:58:15.000000000","message":"or just\n\n if not self.all_cache:","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":145,"context_line":""},{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_2d58b554","line":148,"range":{"start_line":148,"start_character":8,"end_line":148,"end_character":36},"in_reply_to":"7faddb67_aacfab27","updated":"2019-07-23 16:18:22.000000000","message":"true","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_aa6b2bef","line":149,"range":{"start_line":149,"start_character":12,"end_line":149,"end_character":28},"updated":"2019-07-23 15:58:15.000000000","message":"It\u0027s slightly concerning that this is only happening when the cache wasn\u0027t initialized - i.e. we will not pick up changes made in the db since the cache was populated. This requires judicious consumption of the cache: in particular, clear()ing it when we make db changes to RCs/traits. This turns out to be happening correctly in the code, but that feels kind of tightly coupled to me.\n\nAlso, we seem to be being more careful in the *_from_* methods, which check, refresh, check again.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"3a6f1a77e3a6347bd7fd01c380cb50020aa33047","unresolved":false,"context_lines":[{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_3f1bf773","line":149,"range":{"start_line":149,"start_character":12,"end_line":149,"end_character":28},"in_reply_to":"7faddb67_3ed9bd21","updated":"2019-07-23 21:10:53.000000000","message":"Yeah, I thought about this a bit while doing other stuff, and I think my brain added much the same place you did:\n\ngiven that the cache is now only per request, and the only way we can add or remove a trait or resource class is in the scope of a single request (in which no other action happens), it might be better to not do any clear() at all\n\nSince this cache\u0027s purpose is \"get me the state of X at the start of request and then use that state\" and that state is tied to a single transactional context, keeping it as unencumbered as possible is probably the best thing.\n\nBut we\u0027ll want to document that well I suppose.\n\nIf we choose that path then there are probably some additional simplifications we can make, which continues to be good.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1f13e15f8fdc95e00ca0a4564bec96a0abc997d7","unresolved":false,"context_lines":[{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_51db477c","line":149,"range":{"start_line":149,"start_character":12,"end_line":149,"end_character":28},"in_reply_to":"7faddb67_3f1bf773","updated":"2019-07-23 22:14:55.000000000","message":"Yes, I\u0027m on board with eliminating clear() altogether, but I will feel a lot more comfortable about it if we can future-proof it somehow (against \"incidentally\" as noted above).","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"cb1d837a7d51a5445b034632a3231f0a2209ae6e","unresolved":false,"context_lines":[{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_76f51183","line":149,"range":{"start_line":149,"start_character":12,"end_line":149,"end_character":28},"in_reply_to":"7faddb67_51db477c","updated":"2019-07-24 10:06:08.000000000","message":"I\u0027m struggling to come up with a way to remove clear and future proof things that doesn\u0027t involve changing a lot of in the db tests and the way the gabbi fixtures operate. In the next patch I provide a bit more documentation on when/where using the cache is appropriate and put clear in any change operation.\n\nWe could remove clear entirely but that seems to introduce more risk than you\u0027re wanting to contemplate.\n\nFuture reviewers will to be clear about where clear needs to happen if the cache is used for other things.\n\nHowever, I think for the most part we shouldn\u0027t use the cache for other things and we can make that relatively easy by not adding more things.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_f026f4ba","line":149,"range":{"start_line":149,"start_character":12,"end_line":149,"end_character":28},"in_reply_to":"7faddb67_aa6b2bef","updated":"2019-07-23 16:18:22.000000000","message":"The care in the *from* needs to be different because we are concerned with individual entries, whereas here we want everything that is in the cache right now. If there is nothing in it we want to get fill it and return it. If it is still nothing, that\u0027s okay.\n\nOr perhaps you are saying something more than it initially appears?\n\nit is crucial that clear() happens in the places that it does. That\u0027s unfortunate but is necessary if we want to minimize db reads.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":146,"context_line":"    def get_all(self):"},{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_3ed9bd21","line":149,"range":{"start_line":149,"start_character":12,"end_line":149,"end_character":28},"in_reply_to":"7faddb67_f026f4ba","updated":"2019-07-23 18:35:10.000000000","message":"Yeah, I\u0027m saying if we\n\n 0: warm the cache\n 1: add or remove an entry in the db\n 2: get_all()\n\n2 will get the contents of the cache as they existed at 0 -- unless 1 is always careful about clearing the cache. Now...\n\n- We\u0027re clearing the cache in some places, but not in others, as noted in other comments. But I\u0027m not sure if clearing the cache is necessary in any/all of those places because\n- I don\u0027t think there\u0027s currently any single-context operation where we could possibly do 0,1,2. Because the only way to create or delete a trait or RC is via the explicit single-value PUT/DELETE operations on those entities (i.e. it doesn\u0027t happen incidentally as part of any other operation). And even PUT /resource_classes/$name at microversion 1.2-1.6, which is the only one of those that returns the new entity in the response, uses the local value rather than re-pulling it from the db (cache). But\n- That doesn\u0027t mean we shouldn\u0027t be rigorous and future-proof against such pratfalls.\n\nBut if we\u0027re going to do that, we should do it all the way, whereas right now there seem to be some places where we update the db but don\u0027t clear the cache.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":147,"context_line":"        \"\"\"Return all the resources in the cache with all their attributes.\"\"\""},{"line_number":148,"context_line":"        if len(self.all_cache) \u003d\u003d 0:"},{"line_number":149,"context_line":"            _refresh_from_db(self.ctx, self)"},{"line_number":150,"context_line":"        return self.all_cache.values()"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":""},{"line_number":153,"context_line":"class ResourceClassCache(AttributeCache):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_6a021341","line":150,"range":{"start_line":150,"start_character":15,"end_line":150,"end_character":38},"updated":"2019-07-23 15:58:15.000000000","message":"Note: this returns a generator in py3. This happens to work because all callers are iterating over the result (some multiple times, which I only recently found out actually works). And it\u0027s more efficient than converting to an extra list or set here. But it\u0027s worth being clear about in the docstring.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":155,"context_line":""},{"line_number":156,"context_line":"    def __init__(self, ctx):"},{"line_number":157,"context_line":"        super(ResourceClassCache, self).__init__(ctx)"},{"line_number":158,"context_line":"        self.table \u003d _RC_TBL"},{"line_number":159,"context_line":"        self.not_found \u003d exception.ResourceClassNotFound"},{"line_number":160,"context_line":""},{"line_number":161,"context_line":""},{"line_number":162,"context_line":"class TraitCache(AttributeCache):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4a7cf7b4","line":159,"range":{"start_line":158,"start_character":0,"end_line":159,"end_character":56},"updated":"2019-07-23 15:58:15.000000000","message":"Could define these at the class level and not override __init__.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":155,"context_line":""},{"line_number":156,"context_line":"    def __init__(self, ctx):"},{"line_number":157,"context_line":"        super(ResourceClassCache, self).__init__(ctx)"},{"line_number":158,"context_line":"        self.table \u003d _RC_TBL"},{"line_number":159,"context_line":"        self.not_found \u003d exception.ResourceClassNotFound"},{"line_number":160,"context_line":""},{"line_number":161,"context_line":""},{"line_number":162,"context_line":"class TraitCache(AttributeCache):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_903780e4","line":159,"range":{"start_line":158,"start_character":0,"end_line":159,"end_character":56},"in_reply_to":"7faddb67_4a7cf7b4","updated":"2019-07-23 16:18:22.000000000","message":"true, I think I had in mind that I was going to have to have some more methods, but turns out not, so yeah","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":20,"context_line":"_TRAIT_TBL \u003d models.Trait.__table__"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"class AttributeCache(object):"},{"line_number":24,"context_line":"    \"\"\"A cache of integer and string lookup values for string-based attributes."},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    Subclasses must define a `table` and `not_found` member describe the"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_be07ed18","line":23,"range":{"start_line":23,"start_character":6,"end_line":23,"end_character":20},"updated":"2019-07-23 18:35:10.000000000","message":"private?","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":23,"context_line":"class AttributeCache(object):"},{"line_number":24,"context_line":"    \"\"\"A cache of integer and string lookup values for string-based attributes."},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    Subclasses must define a `table` and `not_found` member describe the"},{"line_number":27,"context_line":"    database table which is the authoritative source of data and the exception"},{"line_number":28,"context_line":"    raised if data for an attribute is not found, respectively."},{"line_number":29,"context_line":"    \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_13c2baea","line":26,"range":{"start_line":26,"start_character":60,"end_line":26,"end_character":68},"updated":"2019-07-23 18:35:10.000000000","message":"\"describing\" or \"to describe\"","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"3a6f1a77e3a6347bd7fd01c380cb50020aa33047","unresolved":false,"context_lines":[{"line_number":23,"context_line":"class AttributeCache(object):"},{"line_number":24,"context_line":"    \"\"\"A cache of integer and string lookup values for string-based attributes."},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"    Subclasses must define a `table` and `not_found` member describe the"},{"line_number":27,"context_line":"    database table which is the authoritative source of data and the exception"},{"line_number":28,"context_line":"    raised if data for an attribute is not found, respectively."},{"line_number":29,"context_line":"    \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_1f2cfb97","line":26,"range":{"start_line":26,"start_character":60,"end_line":26,"end_character":68},"in_reply_to":"7faddb67_13c2baea","updated":"2019-07-23 21:10:53.000000000","message":"yeah, editing fault/mismatch","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":33,"context_line":""},{"line_number":34,"context_line":"        :param ctx: `placement.context.RequestContext` from which we can grab a"},{"line_number":35,"context_line":"                    `SQLAlchemy.Connection` object to use for any DB lookups."},{"line_number":36,"context_line":"        \"\"\""},{"line_number":37,"context_line":"        self._ctx \u003d ctx"},{"line_number":38,"context_line":"        self._id_cache \u003d {}"},{"line_number":39,"context_line":"        self._str_cache \u003d {}"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_fe3565a9","line":36,"updated":"2019-07-23 18:35:10.000000000","message":"Re abstract base class, I added this here and it seems to work:\n\n        # Poor man\u0027s abc enforcement\n        assert hasattr(self, \u0027table\u0027)\n        assert hasattr(self, \u0027not_found\u0027)\n\nWhich of course could be extended to provide better messaging etc.","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1f13e15f8fdc95e00ca0a4564bec96a0abc997d7","unresolved":false,"context_lines":[{"line_number":33,"context_line":""},{"line_number":34,"context_line":"        :param ctx: `placement.context.RequestContext` from which we can grab a"},{"line_number":35,"context_line":"                    `SQLAlchemy.Connection` object to use for any DB lookups."},{"line_number":36,"context_line":"        \"\"\""},{"line_number":37,"context_line":"        self._ctx \u003d ctx"},{"line_number":38,"context_line":"        self._id_cache \u003d {}"},{"line_number":39,"context_line":"        self._str_cache \u003d {}"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_31e24b31","line":36,"in_reply_to":"7faddb67_bf20479d","updated":"2019-07-23 22:14:55.000000000","message":"Developers like me doing something goofy in the future like\n- attempting to instantiate AttributeCache directly; or\n- creating a new cache subclass but forgetting to set up one of these attrs.\n\nYou would think future-me would be testing rigorously enough to make that unnecessary, but defensive seems better.","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"3a6f1a77e3a6347bd7fd01c380cb50020aa33047","unresolved":false,"context_lines":[{"line_number":33,"context_line":""},{"line_number":34,"context_line":"        :param ctx: `placement.context.RequestContext` from which we can grab a"},{"line_number":35,"context_line":"                    `SQLAlchemy.Connection` object to use for any DB lookups."},{"line_number":36,"context_line":"        \"\"\""},{"line_number":37,"context_line":"        self._ctx \u003d ctx"},{"line_number":38,"context_line":"        self._id_cache \u003d {}"},{"line_number":39,"context_line":"        self._str_cache \u003d {}"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_bf20479d","line":36,"in_reply_to":"7faddb67_fe3565a9","updated":"2019-07-23 21:10:53.000000000","message":"Who/what are we protecting against here?","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":53,"context_line":"        :param attr_str: The string representation of the attribute to look up"},{"line_number":54,"context_line":"                         a numeric identifier for."},{"line_number":55,"context_line":"        :returns Integer identifier for the attribute."},{"line_number":56,"context_line":"        :raises An instances of the subclass\u0027 not_found exception if attribute"},{"line_number":57,"context_line":"                cannot be found in the DB."},{"line_number":58,"context_line":"        \"\"\""},{"line_number":59,"context_line":"        attr_id \u003d self._id_cache.get(attr_str)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_53035210","line":56,"range":{"start_line":56,"start_character":19,"end_line":56,"end_character":28},"updated":"2019-07-23 18:35:10.000000000","message":"instance","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":55,"context_line":"        :returns Integer identifier for the attribute."},{"line_number":56,"context_line":"        :raises An instances of the subclass\u0027 not_found exception if attribute"},{"line_number":57,"context_line":"                cannot be found in the DB."},{"line_number":58,"context_line":"        \"\"\""},{"line_number":59,"context_line":"        attr_id \u003d self._id_cache.get(attr_str)"},{"line_number":60,"context_line":"        if attr_id is not None:"},{"line_number":61,"context_line":"            return attr_id"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_fe77a507","line":58,"updated":"2019-07-23 18:35:10.000000000","message":"nts, still DRYable (later)","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":72,"context_line":""},{"line_number":73,"context_line":"        :param attr_str: The string representation of the attribute for which"},{"line_number":74,"context_line":"                         to look up the object."},{"line_number":75,"context_line":"        :returns: dict representing the attribute fields, if the attrubute was"},{"line_number":76,"context_line":"                  found in the appropriate database table."},{"line_number":77,"context_line":"        :raises An instances of the subclass\u0027 not_found exception if attr_str"},{"line_number":78,"context_line":"                cannot be found in the DB."}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_5e3119ec","line":75,"range":{"start_line":75,"start_character":65,"end_line":75,"end_character":74},"updated":"2019-07-23 18:35:10.000000000","message":"attribute","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":74,"context_line":"                         to look up the object."},{"line_number":75,"context_line":"        :returns: dict representing the attribute fields, if the attrubute was"},{"line_number":76,"context_line":"                  found in the appropriate database table."},{"line_number":77,"context_line":"        :raises An instances of the subclass\u0027 not_found exception if attr_str"},{"line_number":78,"context_line":"                cannot be found in the DB."},{"line_number":79,"context_line":"        \"\"\""},{"line_number":80,"context_line":"        attr_id_str \u003d self._all_cache.get(attr_str)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_fe4b255c","line":77,"range":{"start_line":77,"start_character":19,"end_line":77,"end_character":28},"updated":"2019-07-23 18:35:10.000000000","message":"instance","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":96,"context_line":"        :param attr_id: The numeric representation of the attribute to look"},{"line_number":97,"context_line":"                        up a string identifier for."},{"line_number":98,"context_line":"        :returns: String identifier for the attribute."},{"line_number":99,"context_line":"        :raises An instances of the subclass\u0027 not_found exception if attr_id"},{"line_number":100,"context_line":"                cannot be found in the DB."},{"line_number":101,"context_line":"        \"\"\""},{"line_number":102,"context_line":"        attr_str \u003d self._str_cache.get(attr_id)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_de5ae928","line":99,"range":{"start_line":99,"start_character":19,"end_line":99,"end_character":28},"updated":"2019-07-23 18:35:10.000000000","message":"instance","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":120,"context_line":"        return self._all_cache.values()"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    @db_api.placement_context_manager.reader"},{"line_number":123,"context_line":"    def _refresh_from_db(self, ctx):"},{"line_number":124,"context_line":"        \"\"\"Grabs all resource classes or traits from the respective DB table and"},{"line_number":125,"context_line":"        populates the supplied cache object\u0027s internal integer and string"},{"line_number":126,"context_line":"        identifier dicts."}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_befe0d69","line":123,"range":{"start_line":123,"start_character":29,"end_line":123,"end_character":35},"updated":"2019-07-23 18:35:10.000000000","message":"Oh. Pity you can\u0027t nix this and use self._ctx here :(","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"3a6f1a77e3a6347bd7fd01c380cb50020aa33047","unresolved":false,"context_lines":[{"line_number":120,"context_line":"        return self._all_cache.values()"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    @db_api.placement_context_manager.reader"},{"line_number":123,"context_line":"    def _refresh_from_db(self, ctx):"},{"line_number":124,"context_line":"        \"\"\"Grabs all resource classes or traits from the respective DB table and"},{"line_number":125,"context_line":"        populates the supplied cache object\u0027s internal integer and string"},{"line_number":126,"context_line":"        identifier dicts."}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_7f3acfcd","line":123,"range":{"start_line":123,"start_character":29,"end_line":123,"end_character":35},"in_reply_to":"7faddb67_befe0d69","updated":"2019-07-23 21:10:53.000000000","message":"Yeah, I was hoping for that too, but oslo_db gets sad.","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    @db_api.placement_context_manager.reader"},{"line_number":123,"context_line":"    def _refresh_from_db(self, ctx):"},{"line_number":124,"context_line":"        \"\"\"Grabs all resource classes or traits from the respective DB table and"},{"line_number":125,"context_line":"        populates the supplied cache object\u0027s internal integer and string"},{"line_number":126,"context_line":"        identifier dicts."},{"line_number":127,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_be336d62","line":124,"range":{"start_line":124,"start_character":79,"end_line":124,"end_character":80},"updated":"2019-07-23 18:35:10.000000000","message":"\u003cgasp\u003e","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":125,"context_line":"        populates the supplied cache object\u0027s internal integer and string"},{"line_number":126,"context_line":"        identifier dicts."},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"        :param cache: Cache object to refresh."},{"line_number":129,"context_line":"        \"\"\""},{"line_number":130,"context_line":"        table \u003d self.table"},{"line_number":131,"context_line":"        sel \u003d sa.select([table.c.id, table.c.name, table.c.updated_at,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_3ef21d9c","line":128,"range":{"start_line":128,"start_character":15,"end_line":128,"end_character":46},"updated":"2019-07-23 18:35:10.000000000","message":"fix","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"f74787ea275de4361b70c087db6781c6bbdd4321","unresolved":false,"context_lines":[{"line_number":139,"context_line":"class ResourceClassCache(AttributeCache):"},{"line_number":140,"context_line":"    \"\"\"An AttributeCache for resource classes.\"\"\""},{"line_number":141,"context_line":""},{"line_number":142,"context_line":"    table \u003d _RC_TBL"},{"line_number":143,"context_line":"    not_found \u003d exception.ResourceClassNotFound"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_fe1245b4","line":142,"range":{"start_line":142,"start_character":4,"end_line":142,"end_character":9},"updated":"2019-07-23 18:35:10.000000000","message":"these could also be private\n\nand, maybe personal style preference, capitalized?","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1f13e15f8fdc95e00ca0a4564bec96a0abc997d7","unresolved":false,"context_lines":[{"line_number":139,"context_line":"class ResourceClassCache(AttributeCache):"},{"line_number":140,"context_line":"    \"\"\"An AttributeCache for resource classes.\"\"\""},{"line_number":141,"context_line":""},{"line_number":142,"context_line":"    table \u003d _RC_TBL"},{"line_number":143,"context_line":"    not_found \u003d exception.ResourceClassNotFound"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_11538fe4","line":142,"range":{"start_line":142,"start_character":4,"end_line":142,"end_character":9},"in_reply_to":"7faddb67_1f515b0f","updated":"2019-07-23 22:14:55.000000000","message":"Not hung up on the case, but private seems appropriate, same as for the instance attrs in __init__, because they aren\u0027t (and shouldn\u0027t be) referenced outside of the class.","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"3a6f1a77e3a6347bd7fd01c380cb50020aa33047","unresolved":false,"context_lines":[{"line_number":139,"context_line":"class ResourceClassCache(AttributeCache):"},{"line_number":140,"context_line":"    \"\"\"An AttributeCache for resource classes.\"\"\""},{"line_number":141,"context_line":""},{"line_number":142,"context_line":"    table \u003d _RC_TBL"},{"line_number":143,"context_line":"    not_found \u003d exception.ResourceClassNotFound"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"7faddb67_1f515b0f","line":142,"range":{"start_line":142,"start_character":4,"end_line":142,"end_character":9},"in_reply_to":"7faddb67_fe1245b4","updated":"2019-07-23 21:10:53.000000000","message":"I\u0027ve never been too sure about class constants and their case.\n\nAs for private, kinda meh on that, cuz ugly, but not really caring one way or another.","commit_id":"9ec1f40266386343392d54904d7341a529c38a09"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"55ff7a3ce693cb039afb3212a4ed3cdbab3ee52c","unresolved":false,"context_lines":[{"line_number":109,"context_line":"        :param attr_id: The numeric representation of the attribute to look"},{"line_number":110,"context_line":"                        up a string identifier for."},{"line_number":111,"context_line":"        :returns: String identifier for the attribute."},{"line_number":112,"context_line":"        :raises An instances of the subclass\u0027 _not_found exception if attr_id"},{"line_number":113,"context_line":"                cannot be found in the DB."},{"line_number":114,"context_line":"        \"\"\""},{"line_number":115,"context_line":"        attr_str \u003d self._str_cache.get(attr_id)"}],"source_content_type":"text/x-python","patch_set":3,"id":"7faddb67_c7652ae3","line":112,"range":{"start_line":112,"start_character":19,"end_line":112,"end_character":28},"updated":"2019-07-24 14:21:22.000000000","message":"instance","commit_id":"b09f2f917fb41479c5e5b98e9fe07d080d304e52"},{"author":{"_account_id":5754,"name":"Alex Xu","email":"hejie.xu@intel.com","username":"xuhj"},"change_message_id":"e19d6363d0711f9bae8008f7842c5a7801ebefa7","unresolved":false,"context_lines":[{"line_number":129,"context_line":"        In Python3 the return value is a generator."},{"line_number":130,"context_line":"        \"\"\""},{"line_number":131,"context_line":"        if not self._all_cache:"},{"line_number":132,"context_line":"            self._refresh_from_db(self._ctx)"},{"line_number":133,"context_line":"        return self._all_cache.values()"},{"line_number":134,"context_line":""},{"line_number":135,"context_line":"    @db_api.placement_context_manager.reader"},{"line_number":136,"context_line":"    def _refresh_from_db(self, ctx):"}],"source_content_type":"text/x-python","patch_set":3,"id":"7faddb67_b17b82f2","line":133,"range":{"start_line":132,"start_character":1,"end_line":133,"end_character":39},"updated":"2019-07-25 07:48:55.000000000","message":"since we don\u0027t have lock, I\u0027m imagining whether there can be race between this two lines. but even if I guess right, it should be very rare case.","commit_id":"b09f2f917fb41479c5e5b98e9fe07d080d304e52"}],"placement/exception.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":176,"context_line":""},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"class TraitNotFound(NotFound):"},{"line_number":179,"context_line":"    msg_fmt \u003d \"No such trait(s): %(name)s.\""},{"line_number":180,"context_line":""},{"line_number":181,"context_line":""},{"line_number":182,"context_line":"class ProjectNotFound(NotFound):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4da89176","line":179,"range":{"start_line":179,"start_character":35,"end_line":179,"end_character":39},"updated":"2019-07-23 15:58:15.000000000","message":"nts: in current code this can never be plural","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"}],"placement/objects/resource_class.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":144,"context_line":"        rc.update(updates)"},{"line_number":145,"context_line":"        rc.id \u003d next_id"},{"line_number":146,"context_line":"        context.session.add(rc)"},{"line_number":147,"context_line":"        return rc"},{"line_number":148,"context_line":""},{"line_number":149,"context_line":"    def destroy(self):"},{"line_number":150,"context_line":"        if self.id is None:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_eddb7d1e","line":147,"updated":"2019-07-23 15:58:15.000000000","message":"does this need to clear the cache?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":144,"context_line":"        rc.update(updates)"},{"line_number":145,"context_line":"        rc.id \u003d next_id"},{"line_number":146,"context_line":"        context.session.add(rc)"},{"line_number":147,"context_line":"        return rc"},{"line_number":148,"context_line":""},{"line_number":149,"context_line":"    def destroy(self):"},{"line_number":150,"context_line":"        if self.id is None:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_8d68c921","line":147,"in_reply_to":"7faddb67_eddb7d1e","updated":"2019-07-23 16:18:22.000000000","message":"If there\u0027s a cache miss later, the cache will be re-read, so no.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":156,"context_line":"                resource_class\u003dself.name)"},{"line_number":157,"context_line":""},{"line_number":158,"context_line":"        self._destroy(self._context, self.id, self.name)"},{"line_number":159,"context_line":"        self._context.rc_cache.clear()"},{"line_number":160,"context_line":""},{"line_number":161,"context_line":"    @staticmethod"},{"line_number":162,"context_line":"    @db_api.placement_context_manager.writer"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_6dcf8d57","line":159,"updated":"2019-07-23 15:58:15.000000000","message":"should this be in _destroy?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":156,"context_line":"                resource_class\u003dself.name)"},{"line_number":157,"context_line":""},{"line_number":158,"context_line":"        self._destroy(self._context, self.id, self.name)"},{"line_number":159,"context_line":"        self._context.rc_cache.clear()"},{"line_number":160,"context_line":""},{"line_number":161,"context_line":"    @staticmethod"},{"line_number":162,"context_line":"    @db_api.placement_context_manager.writer"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ad5a6580","line":159,"in_reply_to":"7faddb67_6dcf8d57","updated":"2019-07-23 16:18:22.000000000","message":"since within a single _context (and thus cache) we are single threaded, it  doesn\u0027t matter.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":187,"context_line":"            raise exception.ResourceClassCannotUpdateStandard("},{"line_number":188,"context_line":"                resource_class\u003dself.name)"},{"line_number":189,"context_line":"        self._save(self._context, self.id, self.name, updates)"},{"line_number":190,"context_line":"        self._context.rc_cache.clear()"},{"line_number":191,"context_line":""},{"line_number":192,"context_line":"    @staticmethod"},{"line_number":193,"context_line":"    @db_api.placement_context_manager.writer"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_2dd99521","line":190,"range":{"start_line":190,"start_character":8,"end_line":190,"end_character":38},"updated":"2019-07-23 15:58:15.000000000","message":"should this be in _save?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":187,"context_line":"            raise exception.ResourceClassCannotUpdateStandard("},{"line_number":188,"context_line":"                resource_class\u003dself.name)"},{"line_number":189,"context_line":"        self._save(self._context, self.id, self.name, updates)"},{"line_number":190,"context_line":"        self._context.rc_cache.clear()"},{"line_number":191,"context_line":""},{"line_number":192,"context_line":"    @staticmethod"},{"line_number":193,"context_line":"    @db_api.placement_context_manager.writer"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_cd5fa170","line":190,"range":{"start_line":190,"start_character":8,"end_line":190,"end_character":38},"in_reply_to":"7faddb67_2dd99521","updated":"2019-07-23 16:18:22.000000000","message":"see above","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"}],"placement/objects/trait.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        trait \u003d models.Trait()"},{"line_number":64,"context_line":"        trait.update(updates)"},{"line_number":65,"context_line":"        context.session.add(trait)"},{"line_number":66,"context_line":"        return trait"},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    def create(self):"},{"line_number":69,"context_line":"        if self.id is not None:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4dc5f15a","line":66,"updated":"2019-07-23 15:58:15.000000000","message":"does this need to clear the cache?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":127,"context_line":"                                              reason\u003d\u0027ID attribute not found\u0027)"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"        self._destroy_in_db(self._context, self.id, self.name)"},{"line_number":130,"context_line":"        self._context.trait_cache.clear()"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"def ensure_sync(ctx):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_8d90c950","line":130,"updated":"2019-07-23 15:58:15.000000000","message":"should this be done in _destroy_in_db instead?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":127,"context_line":"                                              reason\u003d\u0027ID attribute not found\u0027)"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"        self._destroy_in_db(self._context, self.id, self.name)"},{"line_number":130,"context_line":"        self._context.trait_cache.clear()"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"def ensure_sync(ctx):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_0dcb791e","line":130,"in_reply_to":"7faddb67_8d90c950","updated":"2019-07-23 16:18:22.000000000","message":"see my comments in resource class\n\nI figure here is better as we are not about the db here, we\u0027re about Traits.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":259,"context_line":""},{"line_number":260,"context_line":"@oslo_db_api.wrap_db_retry(max_retries\u003d5, retry_on_deadlock\u003dTrue)"},{"line_number":261,"context_line":"# Bug #1760322: If the caller raises an exception, we don\u0027t want the trait"},{"line_number":262,"context_line":"# sync rolled back; so use an .independent transaction"},{"line_number":263,"context_line":"@db_api.placement_context_manager.writer"},{"line_number":264,"context_line":"def _trait_sync(ctx):"},{"line_number":265,"context_line":"    \"\"\"Sync the os_traits symbols to the database."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_0d9ff9b4","line":262,"range":{"start_line":262,"start_character":23,"end_line":262,"end_character":54},"updated":"2019-07-23 15:58:15.000000000","message":"?","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":259,"context_line":""},{"line_number":260,"context_line":"@oslo_db_api.wrap_db_retry(max_retries\u003d5, retry_on_deadlock\u003dTrue)"},{"line_number":261,"context_line":"# Bug #1760322: If the caller raises an exception, we don\u0027t want the trait"},{"line_number":262,"context_line":"# sync rolled back; so use an .independent transaction"},{"line_number":263,"context_line":"@db_api.placement_context_manager.writer"},{"line_number":264,"context_line":"def _trait_sync(ctx):"},{"line_number":265,"context_line":"    \"\"\"Sync the os_traits symbols to the database."}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_8dff6933","line":262,"range":{"start_line":262,"start_character":23,"end_line":262,"end_character":54},"in_reply_to":"7faddb67_0d9ff9b4","updated":"2019-07-23 16:18:22.000000000","message":"this is probably a hold over from before the sync was localised to _only_ happen before the web service actually started doing anything (and being multi-threaded).\n\nSo, yes, this should probably be fixed, but is not related to this.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"}],"placement/tests/functional/db/test_resource_class_cache.py":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"1379bdfacc75b60ec4564525b1d8f2e0742b11a1","unresolved":false,"context_lines":[{"line_number":15,"context_line":""},{"line_number":16,"context_line":"from oslo_utils import timeutils"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from placement import attribute_cache as rc_cache"},{"line_number":19,"context_line":"from placement import exception"},{"line_number":20,"context_line":"from placement.tests.functional import base"},{"line_number":21,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_ad8265ff","line":18,"range":{"start_line":18,"start_character":41,"end_line":18,"end_character":49},"updated":"2019-07-23 15:58:15.000000000","message":"mmph. but okay.","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"abfa6d86022c1c480d28e143dce01b491c57dc0b","unresolved":false,"context_lines":[{"line_number":15,"context_line":""},{"line_number":16,"context_line":"from oslo_utils import timeutils"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from placement import attribute_cache as rc_cache"},{"line_number":19,"context_line":"from placement import exception"},{"line_number":20,"context_line":"from placement.tests.functional import base"},{"line_number":21,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7faddb67_4d1df1bb","line":18,"range":{"start_line":18,"start_character":41,"end_line":18,"end_character":49},"in_reply_to":"7faddb67_ad8265ff","updated":"2019-07-23 16:18:22.000000000","message":"I can make it more tidy, but for the first pass it seemed like the shortest distance...","commit_id":"72222b81b4ef05da740271616b7d6216b7b695d6"}]}
