Load "lighter" db resources when appropriate

Sometimes we know we will only access particular fields of a resource
object, rather than *all* of them. This commit allows the caller to
specify (optionally) the fields that should be populated when the
resource object is instantiated. This saves memory, trips to the db,
and in some cases avoids extra join queries (e.g. for resource.data or
resource.rsrc_prop_data).

Change-Id: I405888f46451d2657aa28f610f8ca555215ff5cf
Partial-Bug: #1680658
changes/15/454415/3
Crag Wolfe 6 years ago
parent 157ede1949
commit 93fab308e8
  1. 7
      heat/db/sqlalchemy/api.py
  2. 3
      heat/engine/check_resource.py
  3. 12
      heat/engine/resource.py
  4. 17
      heat/objects/resource.py
  5. 10
      heat/tests/test_resource.py

@ -173,7 +173,7 @@ def raw_template_files_get(context, files_id):
return result
def resource_get(context, resource_id, refresh=False):
def resource_get(context, resource_id, refresh=False, refresh_data=False):
result = context.session.query(models.Resource).get(resource_id)
if not result:
@ -181,8 +181,9 @@ def resource_get(context, resource_id, refresh=False):
resource_id)
if refresh:
context.session.refresh(result)
# ensure data is loaded (lazy or otherwise)
result.data
if refresh_data:
# ensure data is loaded (lazy or otherwise)
result.data
return result

@ -57,7 +57,8 @@ class CheckResource(object):
def _try_steal_engine_lock(self, cnxt, resource_id):
rs_obj = resource_objects.Resource.get_obj(cnxt,
resource_id)
resource_id,
fields=('engine_id', ))
if rs_obj.engine_id not in (None, self.engine_id):
if not listener_client.EngineListenerClient(
rs_obj.engine_id).is_alive(cnxt):

@ -418,7 +418,8 @@ class Resource(status.ResourceStatus):
if self._rsrc_metadata is not None:
return self._rsrc_metadata
rs = resource_objects.Resource.get_obj(self.stack.context, self.id,
refresh=True)
refresh=True,
fields=('rsrc_metadata', ))
self._rsrc_metadata = rs.rsrc_metadata
return rs.rsrc_metadata
@ -439,8 +440,10 @@ class Resource(status.ResourceStatus):
if self.id is None or self.action == self.INIT:
raise exception.ResourceNotAvailable(resource_name=self.name)
refresh = merge_metadata is not None
db_res = resource_objects.Resource.get_obj(self.stack.context, self.id,
refresh=refresh)
db_res = resource_objects.Resource.get_obj(
self.stack.context, self.id, refresh=refresh,
fields=('rsrc_metadata', 'atomic_key', 'engine_id',
'action', 'status'))
if db_res.action == self.DELETE:
self._db_res_is_deleted = True
LOG.debug("resource %(name)s, id: %(id)s is DELETE_%(st)s, "
@ -1618,7 +1621,8 @@ class Resource(status.ResourceStatus):
try:
db_res = resource_objects.Resource.get_obj(
self.context, self.replaced_by)
self.context, self.replaced_by,
fields=('current_template_id', 'atomic_key'))
except exception.NotFound:
LOG.info("Could not find replacement of resource %(name)s "
"with id %(id)s while updating needed_by.",

@ -100,10 +100,13 @@ class Resource(
}
@staticmethod
def _from_db_object(resource, context, db_resource):
def _from_db_object(resource, context, db_resource, only_fields=None):
if db_resource is None:
return None
for field in resource.fields:
if (only_fields is not None and field not in only_fields
and field != 'id'):
continue
if field == 'data':
resource['data'] = [resource_data.ResourceData._from_db_object(
resource_data.ResourceData(context), resd
@ -150,10 +153,16 @@ class Resource(
return self._properties_data
@classmethod
def get_obj(cls, context, resource_id, refresh=False):
def get_obj(cls, context, resource_id, refresh=False, fields=None):
if fields is None or 'data' in fields:
refresh_data = refresh
else:
refresh_data = False
resource_db = db_api.resource_get(context, resource_id,
refresh=refresh)
return cls._from_db_object(cls(context), context, resource_db)
refresh=refresh,
refresh_data=refresh_data)
return cls._from_db_object(cls(context), context, resource_db,
only_fields=fields)
@classmethod
def get_all(cls, context):

@ -582,6 +582,16 @@ class ResourceTest(common.HeatTestCase):
res.store()
self.assertIsNotNone(res.updated_time)
def test_resource_object_get_obj_fields(self):
snippet = rsrc_defn.ResourceDefinition('aresource',
'GenericResourceType')
res = resource.Resource('aresource', snippet, self.stack)
res.store()
res_obj = resource_objects.Resource.get_obj(
res.context, res.id, refresh=False, fields=('status', ))
self.assertEqual(res_obj.status, res.COMPLETE)
self.assertRaises(AttributeError, getattr, res_obj, 'action')
def test_resource_object_resource_properties_data(self):
cfg.CONF.set_override('encrypt_parameters_and_properties', True,
enforce_type=True)

Loading…
Cancel
Save